setspec 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. {setspec-0.3.0 → setspec-0.4.0}/CHANGELOG.md +38 -0
  2. {setspec-0.3.0 → setspec-0.4.0}/PKG-INFO +1 -1
  3. setspec-0.4.0/docs/prompts-adoption-checklist.md +86 -0
  4. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/__about__.py +1 -1
  5. setspec-0.4.0/src/setspec/goldens/prompt.manifest/1.0/full.json +19 -0
  6. setspec-0.4.0/src/setspec/goldens/prompt.manifest/1.0/minimal.json +14 -0
  7. setspec-0.4.0/src/setspec/goldens/prompt.record/1.0/full.json +54 -0
  8. setspec-0.4.0/src/setspec/goldens/prompt.record/1.0/minimal.json +10 -0
  9. setspec-0.4.0/src/setspec/prompts.py +940 -0
  10. setspec-0.4.0/src/setspec/schemas/prompt.manifest/1.0.json +37 -0
  11. setspec-0.4.0/src/setspec/schemas/prompt.record/1.0.json +70 -0
  12. setspec-0.4.0/tests/unit/test_prompts.py +350 -0
  13. {setspec-0.3.0 → setspec-0.4.0}/.editorconfig +0 -0
  14. {setspec-0.3.0 → setspec-0.4.0}/.github/workflows/ci.yml +0 -0
  15. {setspec-0.3.0 → setspec-0.4.0}/.github/workflows/release.yml +0 -0
  16. {setspec-0.3.0 → setspec-0.4.0}/.gitignore +0 -0
  17. {setspec-0.3.0 → setspec-0.4.0}/.importlinter +0 -0
  18. {setspec-0.3.0 → setspec-0.4.0}/.pre-commit-config.yaml +0 -0
  19. {setspec-0.3.0 → setspec-0.4.0}/CONTRIBUTING.md +0 -0
  20. {setspec-0.3.0 → setspec-0.4.0}/LICENSE +0 -0
  21. {setspec-0.3.0 → setspec-0.4.0}/PHASE4_ISSUES.md +0 -0
  22. {setspec-0.3.0 → setspec-0.4.0}/README.md +0 -0
  23. {setspec-0.3.0 → setspec-0.4.0}/SECURITY.md +0 -0
  24. {setspec-0.3.0 → setspec-0.4.0}/docs/README.md +0 -0
  25. {setspec-0.3.0 → setspec-0.4.0}/docs/packages/setspec/development-plan.md +0 -0
  26. {setspec-0.3.0 → setspec-0.4.0}/docs/packages/setspec/spec.md +0 -0
  27. {setspec-0.3.0 → setspec-0.4.0}/docs/schemas.md +0 -0
  28. {setspec-0.3.0 → setspec-0.4.0}/pyproject.toml +0 -0
  29. {setspec-0.3.0 → setspec-0.4.0}/requirements/README.md +0 -0
  30. {setspec-0.3.0 → setspec-0.4.0}/requirements/ci.lock +0 -0
  31. {setspec-0.3.0 → setspec-0.4.0}/requirements/release.in +0 -0
  32. {setspec-0.3.0 → setspec-0.4.0}/requirements/release.lock +0 -0
  33. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/__init__.py +0 -0
  34. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/artifacts.py +0 -0
  35. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/base.py +0 -0
  36. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/benchmark/v1.py +0 -0
  37. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/capability/v1.py +0 -0
  38. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/envelope.py +0 -0
  39. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/error/v1.py +0 -0
  40. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/errors.py +0 -0
  41. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/event/v1.py +0 -0
  42. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goal/v1.py +0 -0
  43. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.calibration_report/1.0/full.json +0 -0
  44. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.calibration_report/1.0/gate_failed.json +0 -0
  45. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.calibration_report/1.0/minimal.json +0 -0
  46. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.evidence_bundle/1.0/full.json +0 -0
  47. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.evidence_bundle/1.0/minimal.json +0 -0
  48. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.evidence_bundle/1.0/unsupported.json +0 -0
  49. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.goal_pack/1.0/full.json +0 -0
  50. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.goal_pack/1.0/minimal.json +0 -0
  51. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.goal_pack/1.0/starter_unforked.json +0 -0
  52. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.result/1.0/full.json +0 -0
  53. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.result/1.0/minimal.json +0 -0
  54. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.result/1.0/unsupported.json +0 -0
  55. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.run_summary/1.0/full.json +0 -0
  56. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.run_summary/1.0/minimal.json +0 -0
  57. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/benchmark.run_summary/1.0/unsupported.json +0 -0
  58. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/capability.evidence/1.0/full.json +0 -0
  59. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/capability.evidence/1.0/goal.json +0 -0
  60. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/capability.evidence/1.0/minimal.json +0 -0
  61. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/capability.evidence/1.0/unsupported.json +0 -0
  62. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/machine.profile/1.0/full.json +0 -0
  63. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/machine.profile/1.0/minimal.json +0 -0
  64. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/machine.profile/1.0/unsupported.json +0 -0
  65. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/model.identity/1.0/full.json +0 -0
  66. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/model.identity/1.0/minimal.json +0 -0
  67. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/goldens/model.identity/1.0/unsupported.json +0 -0
  68. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/machine/v1.py +0 -0
  69. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/metrics.py +0 -0
  70. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/model/v1.py +0 -0
  71. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/provenance.py +0 -0
  72. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/py.typed +0 -0
  73. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/benchmark.calibration_report/1.0.json +0 -0
  74. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/benchmark.evidence_bundle/1.0.json +0 -0
  75. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/benchmark.goal_pack/1.0.json +0 -0
  76. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/benchmark.result/1.0.json +0 -0
  77. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/benchmark.run_summary/1.0.json +0 -0
  78. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/capability.evidence/1.0.json +0 -0
  79. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/machine.profile/1.0.json +0 -0
  80. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/schemas/model.identity/1.0.json +0 -0
  81. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/serialization.py +0 -0
  82. {setspec-0.3.0 → setspec-0.4.0}/src/setspec/vocabulary.py +0 -0
  83. {setspec-0.3.0 → setspec-0.4.0}/tests/conftest.py +0 -0
  84. {setspec-0.3.0 → setspec-0.4.0}/tests/contract/test_cross_version.py +0 -0
  85. {setspec-0.3.0 → setspec-0.4.0}/tests/contract/test_goldens.py +0 -0
  86. {setspec-0.3.0 → setspec-0.4.0}/tests/contract/test_schema_snapshots.py +0 -0
  87. {setspec-0.3.0 → setspec-0.4.0}/tests/contract/test_version_negotiation.py +0 -0
  88. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_base.py +0 -0
  89. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_envelope.py +0 -0
  90. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_errors.py +0 -0
  91. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_events.py +0 -0
  92. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_metrics.py +0 -0
  93. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_payloads_benchmark.py +0 -0
  94. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_payloads_capability.py +0 -0
  95. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_payloads_goal.py +0 -0
  96. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_serialization.py +0 -0
  97. {setspec-0.3.0 → setspec-0.4.0}/tests/unit/test_vocabulary.py +0 -0
@@ -7,6 +7,44 @@ packaging and release standards §3.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] — 2026-08-29
11
+
12
+ ### Added
13
+
14
+ - **`setspec.prompts`** (Phase 5): prompt records and prompt packs, moved from
15
+ `freeweight.services.prompts` — `PromptRecord`, `PromptLibrary`, `load_record()`, `load_pack()`,
16
+ `build_manifest()`/`write_manifest()`, and the pack's three hashes
17
+ (`prompt_record_hash`/`prompt_subset_hash`/`pack_hash`, ADR-0028). Rendering runs through a
18
+ sandboxed, loader-less Jinja2 `Environment` with `StrictUndefined` (prompt-management-standards
19
+ §2.1): a referenced-but-unsupplied variable is an error, a template cannot reach the filesystem,
20
+ and dunder attribute access is refused. `load_pack()`'s `root` parameter is required — the
21
+ package carries no opinion about an application's pack layout; an application wanting a default
22
+ location supplies its own thin wrapper (FreeWeight's `freeweight.services.prompts.load_pack`
23
+ keeps its old default this way).
24
+
25
+ FreeWeight's own prompt pack adopts this module in the same change (pulling its P12 forward):
26
+ `freeweight.services.prompts` is now a re-export shim over `setspec.prompts`, verified to
27
+ produce a byte-identical `pack_hash` and per-record hash for every shipped prompt before and
28
+ after the move, with FreeWeight's own gate green afterwards. Two applications reading the same
29
+ pack format from two processes is the scenario ADR-0028's hashes exist for; the FreeWeight move
30
+ is the first proof of it.
31
+
32
+ - **JSON Schema for `prompt.record` and `prompt.manifest`**, committed as package data under
33
+ `setspec/schemas/prompt.record/1.0.json` and `setspec/schemas/prompt.manifest/1.0.json`, with
34
+ golden examples under `setspec/goldens/prompt.{record,manifest}/1.0/{full,minimal}.json`. Both
35
+ are hand-authored rather than generated: `PromptRecord` is a plain dataclass, not a pydantic
36
+ envelope payload — a prompt record is a pack file on disk, never wrapped in a `SchemaEnvelope` —
37
+ so neither schema is registered in `PUBLISHED_SCHEMAS`/reachable through `json_schema_for()`,
38
+ and the goldens are illustrative examples rather than inputs to the envelope contract-test
39
+ matrix in `tests/contract/test_goldens.py`. Every golden round-trips through the real
40
+ `load_pack()` loader (not just schema validation) and its `pack_sha256` is the module's own
41
+ computed hash, not a hand-typed value.
42
+
43
+ ### Changed
44
+
45
+ - Bumped to 0.4.0 for the `setspec.prompts` addition (packaging standards §3: additive,
46
+ non-breaking).
47
+
10
48
  ## [0.3.0] — 2026-08-28
11
49
 
12
50
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: setspec
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Every versioned data contract that crosses an application boundary: benchmark results, capability evidence, event/error envelopes, prompt records.
5
5
  Project-URL: Homepage, https://github.com/JPKell/SetSpec
6
6
  Project-URL: Documentation, https://github.com/JPKell/SetSpec/tree/main/docs
@@ -0,0 +1,86 @@
1
+ # `setspec.prompts` Adoption Checklist
2
+
3
+ For an application that owns a prompt pack today (its own `PromptRecord`/`load_pack`-shaped code,
4
+ or an equivalent) and wants to move onto `setspec.prompts` instead of maintaining a second copy of
5
+ prompt-record loading, hashing and rendering. Written against the first adopter, FreeWeight P12
6
+ (§5 below is that migration, completed); the steps are what the *next* adopter — IdeaPress or
7
+ LoadCoach, whichever writes its first prompt pack second — should follow.
8
+
9
+ ## 1. What to delete outright
10
+
11
+ Anything that duplicates, byte-for-byte in behaviour, what `setspec.prompts` now provides:
12
+ `PromptRecord`/`VariableSpec`/`RenderedPrompt`/`PromptReference`/`PromptLibrary`, `load_record()`,
13
+ `load_pack()`, `build_manifest()`/`write_manifest()`, `prompt_record_hash()`/`prompt_subset_hash()`/
14
+ `pack_hash()`, and the sandboxed `StrictUndefined` Jinja2 environment. If your pack format matches
15
+ prompt-management-standards §2.1–§3 (schema_version/prompt_id/version/template/purpose/metadata,
16
+ a manifest with pack_id/pack_version/prompts/pack_sha256), there should be nothing left to keep.
17
+
18
+ ## 2. The one thing every adopter supplies itself: pack location
19
+
20
+ `load_pack(root: Path, *, override_root: Path | None = None)` takes `root` as a required
21
+ positional argument — the package has no opinion about where an application's pack lives on disk,
22
+ and no default. If your call sites currently call a zero-argument `load_pack()`, write a one-line
23
+ wrapper in your own package that supplies your pack's fixed location and forwards everything else:
24
+
25
+ ```python
26
+ from pathlib import Path
27
+ from setspec.prompts import load_pack as _setspec_load_pack, PromptLibrary
28
+
29
+ PACK_ROOT = Path(__file__).resolve().parent.parent / "prompts"
30
+
31
+ def load_pack(root: Path = PACK_ROOT, *, override_root: Path | None = None) -> PromptLibrary:
32
+ return _setspec_load_pack(root, override_root=override_root)
33
+ ```
34
+
35
+ Do this even if every current call site could be edited to pass `root` explicitly — a re-export
36
+ shim at your old import path means the migration touches one file instead of every call site, and
37
+ is the difference between a multi-file diff and a four-file one (§5).
38
+
39
+ ## 3. Behaviour to re-verify, not assume
40
+
41
+ * **Hashing is over `canonical_json`, not file bytes.** If you ever hashed a record file's raw
42
+ bytes, that is not the same value `prompt_record_hash()` produces; re-indenting a file must not
43
+ change its hash, and canonical-JSON hashing already guarantees that here.
44
+ * **Declared-and-unused is a load-time error, same as undeclared-and-used.** A variable in the
45
+ record's `variables` block that the template never references fails validation — not just the
46
+ reverse. Audit existing records for this before the first `load_pack()` call in CI.
47
+ See [`goldens/prompt.record/1.0/full.json`](../src/setspec/goldens/prompt.record/1.0/full.json)
48
+ for a record where every declared variable — including a non-string one with `min`/`max` — is
49
+ used in the template.
50
+ * **The Jinja2 environment has no loader.** `{% include %}` / `{% extends %}` fail at render time
51
+ (`PromptRenderError`), not load time — a template that used either against your old environment
52
+ needs rewriting, not just re-pointing.
53
+ * **`pack_hash()` is provenance, never a fingerprint input** (ADR-0028 §1). If your evidence
54
+ payloads folded a pack-wide hash into a capability fingerprint, stop — only
55
+ `prompt_subset_hash()` over the *specific prompts a benchmark declares* belongs there.
56
+
57
+ ## 4. Tests that must still pass, unchanged
58
+
59
+ Whatever exercises your prompt pack today — rendering determinism, unknown-variable rejection,
60
+ sandboxing (no filesystem reach, no dunder attribute access from a template) — should still pass
61
+ against `setspec.prompts` with no change to the test's assertions, only to its imports. A test that
62
+ needed a new assertion to pass found a real behaviour difference; treat that as a bug to resolve,
63
+ not a test to relax, before shipping the migration.
64
+
65
+ ## 5. Verification: FreeWeight P12 (completed)
66
+
67
+ FreeWeight adopted `setspec.prompts` in the same change that added it to `setspec` (Phase 5 pulled
68
+ FreeWeight's own P12 forward, rather than leaving `setspec.prompts` unused until a later phase).
69
+ The proof this checklist rests on:
70
+
71
+ 1. **Before**: captured the pack hash and every individual record hash from FreeWeight's original,
72
+ pre-migration `freeweight.services.prompts` — `pack_hash: sha256:b1b0ffd0a5941fee5e0013d2a826732ea02a285b229bdc006ebd6dd25ff4ceb4`
73
+ plus 18 individual record hashes spanning `benchmarks.agent.goal` through `goals.judge.rubric`.
74
+ 2. **After, direct**: loaded the same pack through `setspec.prompts.load_pack()` directly and
75
+ recomputed every hash — identical.
76
+ 3. **After, through the shim**: rewrote `freeweight/services/prompts.py` as a thin re-export over
77
+ `setspec.prompts` (§2's pattern, `PACK_ROOT` default preserved) and recomputed through
78
+ FreeWeight's own public import path — identical again.
79
+ 4. **Full gate, unchanged**: FreeWeight's complete pre-PR gate (ruff, mypy strict over 265 files,
80
+ import-linter, pytest) passed with no test assertions changed — 2297 passed, 28 skipped, 21
81
+ deselected (performance). The only non-shim source edit was 3 import statements in
82
+ `tests/security/test_goal_pack_import.py`, which reached into the module's private
83
+ `_environment()` directly for a sandboxing assertion; every other call site needed no change.
84
+
85
+ Final diff: 4 files changed. The next adopter should expect a diff of similar shape — one shim
86
+ file, a dependency bump, and an edit only where a test reached past the public API.
@@ -1,3 +1,3 @@
1
1
  """Single source of the package version, read by hatchling at build time."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.0"
@@ -0,0 +1,19 @@
1
+ {
2
+ "pack_id": "freeweight.core",
3
+ "pack_version": "1.4.0",
4
+ "schema_version": "1.0",
5
+ "generated_at": "2026-08-21T00:00:00Z",
6
+ "prompts": [
7
+ {
8
+ "prompt_id": "stages.draft.article",
9
+ "version": "2.1.0",
10
+ "sha256": "sha256:2316b1ae73d0298e17a78677e2da9fd5f0b664ae09cc3dd7625f26921a9630c5"
11
+ },
12
+ {
13
+ "prompt_id": "stages.notify.digest",
14
+ "version": "1.0.0",
15
+ "sha256": "sha256:21829b05324468ff96ddb14ea45d4ca3a3409c4d0928f1475089aa0467c276a0"
16
+ }
17
+ ],
18
+ "pack_sha256": "sha256:39e13d67c588be76c08d82073e8de6953c73974740d1ceedf1d66394d5e4efef"
19
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "pack_id": "ideapress.core",
3
+ "pack_version": "1.0.0",
4
+ "schema_version": "1.0",
5
+ "generated_at": "1970-01-01T00:00:00Z",
6
+ "prompts": [
7
+ {
8
+ "prompt_id": "stages.notify.digest",
9
+ "version": "1.0.0",
10
+ "sha256": "sha256:21829b05324468ff96ddb14ea45d4ca3a3409c4d0928f1475089aa0467c276a0"
11
+ }
12
+ ],
13
+ "pack_sha256": "sha256:2f0a1d2047e54d2dac5da63af4ee49a5e50c6bf931af9fe105374f73c89f6e7f"
14
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "prompt_id": "stages.draft.article",
4
+ "version": "2.1.0",
5
+ "purpose": "Produce a first draft of one article section from a compiled requirement set.",
6
+ "system": "You are drafting one section of an article. Follow every hard requirement.",
7
+ "template": "SECTION: {{ section_title }}\n\nREQUIREMENTS\n{{ requirements_json }}\n\nCONTEXT\n{{ context }}\n\nWrite roughly {{ word_target }} words.",
8
+ "variables": {
9
+ "section_title": {
10
+ "type": "string",
11
+ "required": true,
12
+ "description": "Human-readable section title"
13
+ },
14
+ "requirements_json": {
15
+ "type": "string",
16
+ "required": true,
17
+ "description": "Canonical JSON of compiled requirements"
18
+ },
19
+ "context": {
20
+ "type": "string",
21
+ "required": false,
22
+ "default": "",
23
+ "description": "Bounded prior context"
24
+ },
25
+ "word_target": {
26
+ "type": "integer",
27
+ "required": false,
28
+ "default": 800,
29
+ "min": 100,
30
+ "max": 5000,
31
+ "description": "Target word count for the drafted section"
32
+ }
33
+ },
34
+ "task": "content.article_draft",
35
+ "capability": "creative_writing",
36
+ "response": {
37
+ "format": "text",
38
+ "json_schema_ref": null,
39
+ "expectations": ["No headings above H3", "No meta-commentary about being an AI"]
40
+ },
41
+ "model_requirements": {
42
+ "min_context_tokens": 8192,
43
+ "requires_capabilities": [],
44
+ "recommended_temperature": 0.7
45
+ },
46
+ "metadata": {
47
+ "author": "suite",
48
+ "created_at": "2026-08-21T00:00:00Z",
49
+ "changed_at": "2026-08-21T00:00:00Z",
50
+ "change_reason": "Tightened the requirement-compliance instruction after audit-pass regressions.",
51
+ "supersedes": "2.0.0",
52
+ "tags": ["content", "draft"]
53
+ }
54
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "prompt_id": "stages.notify.digest",
4
+ "version": "1.0.0",
5
+ "purpose": "Static prompt with no variables, exercising the record format's floor.",
6
+ "template": "Summarize today's queue in three bullet points.",
7
+ "metadata": {
8
+ "change_reason": "Initial version."
9
+ }
10
+ }