prizmkit 1.1.130 → 1.1.134

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 (119) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/claude/command-adapter.js +0 -2
  3. package/bundled/dev-pipeline/.env.example +1 -1
  4. package/bundled/dev-pipeline/assets/skill-subagent-integration.md +11 -6
  5. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
  6. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -9
  7. package/bundled/dev-pipeline/prizmkit_runtime/processes.py +21 -1
  8. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
  9. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
  10. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
  11. package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
  12. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +119 -18
  13. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +3 -2
  14. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +411 -17
  15. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +57 -14
  16. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +5 -4
  17. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +55 -2
  18. package/bundled/dev-pipeline/scripts/prompt_framework.py +152 -1
  19. package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
  20. package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
  21. package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
  22. package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
  23. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +5 -5
  24. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +28 -22
  25. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +24 -10
  26. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +20 -2
  27. package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +2 -2
  28. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
  29. package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +5 -10
  30. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +9 -19
  31. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
  32. package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +1 -1
  33. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
  34. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
  35. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +15 -0
  36. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +2 -2
  37. package/bundled/dev-pipeline/templates/sections/phase-commit.md +2 -2
  38. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +26 -23
  39. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +5 -5
  40. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +15 -11
  41. package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +1 -1
  42. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +6 -6
  43. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
  44. package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
  45. package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +1 -1
  46. package/bundled/dev-pipeline/templates/sections/task-contract.md +4 -0
  47. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
  48. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +349 -48
  49. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +129 -0
  50. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
  51. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +567 -23
  52. package/bundled/dev-pipeline/tests/test_unified_cli.py +37 -10
  53. package/bundled/skills/_metadata.json +63 -26
  54. package/bundled/skills/app-planner/SKILL.md +4 -3
  55. package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
  56. package/bundled/skills/bug-planner/SKILL.md +12 -27
  57. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
  58. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
  59. package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
  60. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
  61. package/bundled/skills/feature-planner/SKILL.md +27 -22
  62. package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
  63. package/bundled/skills/feature-workflow/SKILL.md +94 -121
  64. package/bundled/skills/prizmkit/SKILL.md +130 -94
  65. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
  66. package/bundled/skills/prizmkit-code-review/SKILL.md +132 -86
  67. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +243 -0
  68. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +42 -1
  69. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
  70. package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +93 -1
  71. package/bundled/skills/prizmkit-committer/SKILL.md +125 -48
  72. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
  73. package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
  74. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
  75. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
  76. package/bundled/skills/prizmkit-init/SKILL.md +5 -5
  77. package/bundled/skills/prizmkit-plan/SKILL.md +146 -105
  78. package/bundled/skills/prizmkit-plan/assets/plan-template.md +18 -0
  79. package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +242 -0
  80. package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +4 -4
  81. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
  82. package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
  83. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
  84. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
  85. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
  86. package/bundled/skills/prizmkit-test/SKILL.md +52 -6
  87. package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +63 -12
  88. package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +1 -1
  89. package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +13 -0
  90. package/bundled/skills/prizmkit-test/references/evidence-protocol.md +6 -1
  91. package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +8 -1
  92. package/bundled/skills/prizmkit-test/references/examples.md +1 -1
  93. package/bundled/skills/prizmkit-test/references/test-generation-steps.md +4 -1
  94. package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
  95. package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +15 -0
  96. package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
  97. package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +744 -23
  98. package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +194 -15
  99. package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
  100. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
  101. package/bundled/skills/recovery-workflow/SKILL.md +62 -241
  102. package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
  103. package/bundled/skills/recovery-workflow/references/detection.md +28 -52
  104. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
  105. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
  106. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
  107. package/bundled/skills/refactor-planner/SKILL.md +12 -57
  108. package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
  109. package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
  110. package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
  111. package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
  112. package/bundled/skills/refactor-workflow/SKILL.md +98 -139
  113. package/bundled/templates/project-memory-template.md +4 -4
  114. package/package.json +1 -1
  115. package/src/index.js +4 -0
  116. package/src/prompts.js +10 -3
  117. package/src/scaffold.js +1 -1
  118. package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
  119. package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
@@ -1,6 +1,7 @@
1
1
  """Tests for generate-bootstrap-prompt.py core functions."""
2
2
 
3
3
  import json
4
+ import hashlib
4
5
  import importlib.util
5
6
  import os
6
7
  import re
@@ -27,6 +28,9 @@ from generate_bootstrap_prompt import (
27
28
  merge_checkpoint_state,
28
29
  load_active_agent_prompts,
29
30
  validate_rendered,
31
+ validate_feature_test_evidence,
32
+ route_feature_repair,
33
+ feature_checkpoint_metadata,
30
34
  main as generate_bootstrap_main,
31
35
  )
32
36
  from continuation import append_continuation_handoff, checkpoint_cursor
@@ -47,9 +51,9 @@ FORBIDDEN_THREE_STRIKE = "3-" + "strike"
47
51
  FORBIDDEN_TWENTY_STEP = "20-" + "step window"
48
52
 
49
53
  REVIEW_PROMPT_REQUIRED = (
50
- "The current Main Agent is the only Code Review executor",
51
- "Do not invoke another review skill or review workflow",
52
- "finder, verifier, audit, compatibility review, verification, or gap sweep",
54
+ "mandatory Main-Agent review",
55
+ "strict structural capability gate",
56
+ "skill-owned independent Reviewer",
53
57
  "`review-report.md` is the only persisted review artifact",
54
58
  )
55
59
 
@@ -157,7 +161,7 @@ def scoped_report_text(verdict="PASS", boundary_missing=0,
157
161
 
158
162
 
159
163
  def write_fresh_scoped_report(project_root, report_path, report_text=None):
160
- """Write marker, in-scope files, and a fresh scoped report."""
164
+ """Write marker, in-scope files, and a fresh derived report."""
161
165
  source = project_root / "src" / "feature.js"
162
166
  test = project_root / "tests" / "feature.test.js"
163
167
  marker = project_root / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
@@ -173,6 +177,135 @@ def write_fresh_scoped_report(project_root, report_path, report_text=None):
173
177
  return source, test, marker
174
178
 
175
179
 
180
+ def write_fresh_structured_evidence(project_root):
181
+ """Create the smallest protocol-shaped, validator-attested pass package."""
182
+ source, test, marker = write_fresh_scoped_report(
183
+ project_root,
184
+ project_root / ".prizmkit" / "test" / "unused" / "test-report.md",
185
+ )
186
+ evidence_base = project_root / ".prizmkit" / "test" / "evidence"
187
+ environment = {}
188
+ plan_inputs = {"feature": "123-scope-test"}
189
+ categories = {
190
+ "source": [{"path": "src/feature.js", "sha256": hashlib.sha256(source.read_bytes()).hexdigest()}],
191
+ "tests": [{"path": "tests/feature.test.js", "sha256": hashlib.sha256(test.read_bytes()).hexdigest()}],
192
+ "contracts": [],
193
+ "lockfiles": [],
194
+ }
195
+ target_hashes = {
196
+ category: hashlib.sha256(json.dumps(
197
+ sorted(records, key=lambda item: item["path"]),
198
+ sort_keys=True, separators=(",", ":"),
199
+ ).encode()).hexdigest()
200
+ for category, records in categories.items()
201
+ }
202
+ target_hashes["environment"] = hashlib.sha256(b"{}").hexdigest()
203
+ target_hashes["plan"] = hashlib.sha256(json.dumps(
204
+ plan_inputs, sort_keys=True, separators=(",", ":"),
205
+ ).encode()).hexdigest()
206
+ inventory = {
207
+ "categories": categories,
208
+ "plan_inputs": plan_inputs,
209
+ }
210
+ scope = {"feature": "123-scope-test"}
211
+ zero_hash = "0" * 64
212
+ identity = {
213
+ "baseline_commit": "baseline",
214
+ "working_diff_sha256": zero_hash,
215
+ "scope_sha256": hashlib.sha256(json.dumps(
216
+ scope, sort_keys=True, separators=(",", ":"),
217
+ ).encode()).hexdigest(),
218
+ }
219
+ evidence_id = hashlib.sha256(json.dumps(
220
+ identity, sort_keys=True, separators=(",", ":"),
221
+ ).encode()).hexdigest()
222
+ evidence_root = evidence_base / evidence_id
223
+ evidence_root.mkdir(parents=True, exist_ok=True)
224
+ (evidence_root / "environment.json").write_text("{}\n", encoding="utf-8")
225
+ (evidence_root / "target-inventory.json").write_text(
226
+ json.dumps(inventory), encoding="utf-8",
227
+ )
228
+ (evidence_root / "scope.json").write_text(json.dumps(scope), encoding="utf-8")
229
+ verdict = {
230
+ "verdict": "TEST_PASS",
231
+ "testing_domain_only": True,
232
+ "authorizes_commit_or_release": False,
233
+ "real_environment_validated": False,
234
+ "repairs_business_defects": False,
235
+ "code_evidence_replayable": True,
236
+ "blockers": [],
237
+ "reproduced_failures": [],
238
+ }
239
+ (evidence_root / "verdict.json").write_text(json.dumps(verdict), encoding="utf-8")
240
+ report = scoped_report_text()
241
+ (evidence_root / "test-report.md").write_text(report, encoding="utf-8")
242
+ stages = []
243
+ stage_names = [
244
+ "CHANGE_CLASSIFY", "SCOPE_DISCOVER", "CONTRACT_MODEL", "TEST_PLAN",
245
+ "INFRA_READY", "TEST_BUILD", "EXECUTE_PROVE", "EVIDENCE_PACKAGE",
246
+ "EVIDENCE_VALIDATE",
247
+ ]
248
+ for name in stage_names:
249
+ stages.append({"name": name, "status": "complete", "input_sha256": zero_hash, "outputs": []})
250
+ manifest = {
251
+ "protocol_version": "1.0",
252
+ "evidence_id": evidence_id,
253
+ "evidence_id_inputs": identity,
254
+ "sensitivity": "project-controlled",
255
+ "environment_claim": "mocked-code-level-only",
256
+ "compatibility": "legacy-test-report-interface-not-supported",
257
+ "change_class": "lightweight",
258
+ "baseline_commit": "baseline",
259
+ "working_diff_sha256": zero_hash,
260
+ "target_hashes": target_hashes,
261
+ "stages": stages,
262
+ "files": [],
263
+ "final_verdict": "TEST_PASS",
264
+ }
265
+ file_records = []
266
+ for path in sorted(evidence_root.rglob("*")):
267
+ if path.is_file():
268
+ relative = path.relative_to(evidence_root).as_posix()
269
+ file_records.append({
270
+ "path": relative,
271
+ "sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
272
+ "produced_by": "EVIDENCE_PACKAGE",
273
+ })
274
+ payload = sorted(
275
+ ({"path": item["path"], "sha256": item["sha256"]}
276
+ for item in file_records
277
+ if item["path"] not in {"verdict.json", "test-report.md"}),
278
+ key=lambda item: item["path"],
279
+ )
280
+ validation = {
281
+ "validator": "validate_test_evidence.py",
282
+ "validator_sha256": zero_hash,
283
+ "payload_sha256": hashlib.sha256(json.dumps(
284
+ payload, sort_keys=True, separators=(",", ":"),
285
+ ).encode()).hexdigest(),
286
+ "result": "passed",
287
+ "verdict": "TEST_PASS",
288
+ "semantics": "integrity-and-protocol-validation-not-hostile-producer-proof",
289
+ "replay_receipt_ids": [],
290
+ }
291
+ (evidence_root / "validation.json").write_text(json.dumps(validation), encoding="utf-8")
292
+ file_records.append({
293
+ "path": "validation.json",
294
+ "sha256": hashlib.sha256((evidence_root / "validation.json").read_bytes()).hexdigest(),
295
+ "produced_by": "EVIDENCE_VALIDATE",
296
+ })
297
+ manifest["files"] = file_records
298
+ (evidence_root / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
299
+ pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
300
+ pointer.parent.mkdir(parents=True, exist_ok=True)
301
+ pointer.write_text(str(evidence_root / "test-report.md"), encoding="utf-8")
302
+ report_path = evidence_root / "test-report.md"
303
+ now = time.time()
304
+ os.utime(marker, (now, now))
305
+ os.utime(report_path, (now + 1, now + 1))
306
+ return evidence_root
307
+
308
+
176
309
  # ---------------------------------------------------------------------------
177
310
  # compute_feature_slug
178
311
  # ---------------------------------------------------------------------------
@@ -469,6 +602,13 @@ class TestProcessModeBlocks:
469
602
  # ---------------------------------------------------------------------------
470
603
 
471
604
  class TestScopedFeatureTestGate:
605
+ @staticmethod
606
+ def _trust_fixture_validator(monkeypatch):
607
+ monkeypatch.setattr(
608
+ "generate_bootstrap_prompt._evidence_validator_passes",
609
+ lambda _project_root, _evidence_root, _validation: True,
610
+ )
611
+
472
612
  def test_standard_sections_insert_prizmkit_test_after_review(self):
473
613
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
474
614
  sections = assemble_sections(
@@ -561,7 +701,9 @@ class TestScopedFeatureTestGate:
561
701
  test_step = next(step for step in checkpoint["steps"] if step["skill"] == "prizmkit-test")
562
702
  assert test_step["required_artifacts"] == [
563
703
  ".prizmkit/specs/123-scope-test/test-report-path.txt",
564
- ".prizmkit/test/*/test-report.md",
704
+ ".prizmkit/test/evidence/*/manifest.json",
705
+ ".prizmkit/test/evidence/*/verdict.json",
706
+ ".prizmkit/test/evidence/*/validation.json",
565
707
  ]
566
708
 
567
709
  def test_merge_resets_completed_prizmkit_test_without_report_artifact(self, tmp_path):
@@ -596,19 +738,19 @@ class TestScopedFeatureTestGate:
596
738
 
597
739
  assert [step["status"] for step in merged["steps"]] == ["pending", "pending"]
598
740
 
599
- def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path):
600
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
601
- write_fresh_scoped_report(tmp_path, report)
741
+ def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path, monkeypatch):
742
+ self._trust_fixture_validator(monkeypatch)
743
+ evidence_root = write_fresh_structured_evidence(tmp_path)
602
744
  pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
603
745
  pointer.parent.mkdir(parents=True, exist_ok=True)
604
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
746
+ pointer.write_text(str(evidence_root / "test-report.md") + "\n", encoding="utf-8")
605
747
  existing = {
606
748
  "steps": [
607
- {"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
749
+ {"id": "S01", "skill": "prizmkit-test", "status": "completed", "stage_result": "TEST_PASS", "required_artifacts": [
608
750
  ".prizmkit/specs/123-scope-test/test-report-path.txt",
609
751
  ".prizmkit/test/*/test-report.md",
610
752
  ]},
611
- {"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "required_artifacts": []},
753
+ {"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "stage_result": "REVIEW_PASS", "required_artifacts": []},
612
754
  ],
613
755
  }
614
756
  fresh = {
@@ -834,25 +976,171 @@ class TestScopedFeatureTestGate:
834
976
 
835
977
  assert merged["steps"][0]["status"] == "pending"
836
978
 
837
- def test_merge_resets_completed_prizmkit_test_without_in_scope_sources(self, tmp_path):
979
+ def test_structured_evidence_accepts_current_matching_package(self, tmp_path, monkeypatch):
980
+ self._trust_fixture_validator(monkeypatch)
981
+ evidence_root = write_fresh_structured_evidence(tmp_path)
982
+
983
+ evidence = validate_feature_test_evidence(
984
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
985
+ )
986
+
987
+ assert evidence is not None
988
+ assert evidence["verdict"] == "TEST_PASS"
989
+ assert evidence["manifest_path"] == str(
990
+ (evidence_root / "manifest.json").relative_to(tmp_path),
991
+ )
992
+ assert evidence["validation_path"].endswith("/validation.json")
993
+
994
+ def test_structured_evidence_requires_authoritative_validator(self, tmp_path, monkeypatch):
995
+ write_fresh_structured_evidence(tmp_path)
996
+ monkeypatch.setattr(
997
+ "generate_bootstrap_prompt._evidence_validator_passes",
998
+ lambda _project_root, _evidence_root, _validation: False,
999
+ )
1000
+
1001
+ assert validate_feature_test_evidence(
1002
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1003
+ ) is None
1004
+
1005
+ def test_structured_evidence_rejects_legacy_markdown_only_pass(self, tmp_path):
838
1006
  report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
839
- report_text = scoped_report_text().replace("- In-Scope Source Files:\n - src/feature.js\n", "- In-Scope Source Files:\n")
840
- write_fresh_scoped_report(tmp_path, report, report_text)
1007
+ write_fresh_scoped_report(tmp_path, report)
841
1008
  pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
842
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
843
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
844
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
845
- ".prizmkit/test/*/test-report.md",
846
- ]}]}
847
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
848
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
849
- ".prizmkit/test/*/test-report.md",
850
- ]}]}
1009
+ pointer.write_text(str(report), encoding="utf-8")
1010
+
1011
+ assert validate_feature_test_evidence(
1012
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1013
+ ) is None
1014
+
1015
+ def test_structured_evidence_rejects_missing_validation(self, tmp_path):
1016
+ evidence_root = write_fresh_structured_evidence(tmp_path)
1017
+ (evidence_root / "validation.json").unlink()
1018
+
1019
+ assert validate_feature_test_evidence(
1020
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1021
+ ) is None
1022
+
1023
+ def test_structured_evidence_rejects_tampered_verdict(self, tmp_path, monkeypatch):
1024
+ self._trust_fixture_validator(monkeypatch)
1025
+ evidence_root = write_fresh_structured_evidence(tmp_path)
1026
+ verdict_path = evidence_root / "verdict.json"
1027
+ verdict = json.loads(verdict_path.read_text(encoding="utf-8"))
1028
+ verdict["verdict"] = "TEST_BLOCKED"
1029
+ verdict_path.write_text(json.dumps(verdict), encoding="utf-8")
1030
+
1031
+ assert validate_feature_test_evidence(
1032
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1033
+ ) is None
1034
+
1035
+ def test_structured_evidence_rejects_wrong_artifact_scope(self, tmp_path, monkeypatch):
1036
+ self._trust_fixture_validator(monkeypatch)
1037
+ evidence_root = write_fresh_structured_evidence(tmp_path)
1038
+ report_path = evidence_root / "test-report.md"
1039
+ report_path.write_text(
1040
+ scoped_report_text().replace(
1041
+ ".prizmkit/specs/123-scope-test/",
1042
+ ".prizmkit/specs/999-other/",
1043
+ ),
1044
+ encoding="utf-8",
1045
+ )
851
1046
 
852
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
1047
+ assert validate_feature_test_evidence(
1048
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1049
+ ) is None
853
1050
 
854
- assert merged["steps"][0]["status"] == "pending"
1051
+ def test_structured_evidence_rejects_stale_report(self, tmp_path, monkeypatch):
1052
+ self._trust_fixture_validator(monkeypatch)
1053
+ evidence_root = write_fresh_structured_evidence(tmp_path)
1054
+ report = evidence_root / "test-report.md"
1055
+ marker = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
1056
+ now = time.time()
1057
+ os.utime(report, (now, now))
1058
+ os.utime(marker, (now + 10, now + 10))
1059
+
1060
+ assert validate_feature_test_evidence(
1061
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1062
+ ) is None
1063
+
1064
+ def test_structured_evidence_rejects_live_inventory_drift(self, tmp_path, monkeypatch):
1065
+ self._trust_fixture_validator(monkeypatch)
1066
+ write_fresh_structured_evidence(tmp_path)
1067
+ source = tmp_path / "src" / "feature.js"
1068
+ source.write_text("export const feature = false;\n", encoding="utf-8")
855
1069
 
1070
+ assert validate_feature_test_evidence(
1071
+ str(tmp_path), ".prizmkit/specs/123-scope-test",
1072
+ ) is None
1073
+
1074
+ def test_feature_repair_routing_preserves_review_and_test_boundaries(self):
1075
+ review = route_feature_repair("REVIEW_NEEDS_FIXES", repair_round=0)
1076
+ test_infra = route_feature_repair(
1077
+ "TEST_FAIL", "test-infrastructure", repair_round=1,
1078
+ )
1079
+ production = route_feature_repair("TEST_FAIL", "production", repair_round=1)
1080
+
1081
+ assert review["route"] == [
1082
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
1083
+ ]
1084
+ assert test_infra["route"] == ["prizmkit-implement", "prizmkit-test"]
1085
+ assert production["route"] == [
1086
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
1087
+ ]
1088
+ assert review["repair_round"] == 1
1089
+
1090
+ def test_feature_repair_routing_blocks_unsafe_and_exhausted_routes(self):
1091
+ unknown = route_feature_repair("TEST_FAIL", "unknown", repair_round=1)
1092
+ blocked = route_feature_repair("TEST_BLOCKED", "production", repair_round=1)
1093
+ exhausted = route_feature_repair("TEST_FAIL", "production", repair_round=3)
1094
+
1095
+ assert unknown["blocked"] is True
1096
+ assert unknown["route"] == []
1097
+ assert blocked["blocked"] is True
1098
+ assert blocked["resumable"] is False
1099
+ assert blocked["next_stage"] is None
1100
+ assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
1101
+ assert blocked["blocked_reason"] == "test_blocked"
1102
+ assert exhausted["blocked"] is True
1103
+ assert exhausted["repair_round"] == 3
1104
+
1105
+ def test_feature_checkpoint_metadata_records_l1_handoff_fields(self):
1106
+ evidence = {
1107
+ "manifest_path": ".prizmkit/test/evidence/id/manifest.json",
1108
+ "verdict_path": ".prizmkit/test/evidence/id/verdict.json",
1109
+ "validation_path": ".prizmkit/test/evidence/id/validation.json",
1110
+ }
1111
+
1112
+ metadata = feature_checkpoint_metadata(
1113
+ ".prizmkit/specs/123-scope-test",
1114
+ evidence=evidence,
1115
+ stage="test",
1116
+ status="TEST_FAIL",
1117
+ stage_result="TEST_FAIL",
1118
+ repair_scope="test-infrastructure",
1119
+ repair_round=2,
1120
+ next_stage="prizmkit-implement",
1121
+ resume_from="prizmkit-implement",
1122
+ )
1123
+
1124
+ assert metadata == {
1125
+ "schema_version": 1,
1126
+ "artifact_dir": ".prizmkit/specs/123-scope-test",
1127
+ "orchestrator": "prizmkit-l4",
1128
+ "stage": "test",
1129
+ "current_stage": "test",
1130
+ "status": "TEST_FAIL",
1131
+ "stage_result": "TEST_FAIL",
1132
+ "repair_scope": "test-infrastructure",
1133
+ "repair_round": 2,
1134
+ "authoritative_evidence_paths": {
1135
+ "manifest": ".prizmkit/test/evidence/id/manifest.json",
1136
+ "verdict": ".prizmkit/test/evidence/id/verdict.json",
1137
+ "validation": ".prizmkit/test/evidence/id/validation.json",
1138
+ },
1139
+ "next_stage": "prizmkit-implement",
1140
+ "resume_from": "prizmkit-implement",
1141
+ "blocked_reason": None,
1142
+ "terminal_status": None,
1143
+ }
856
1144
 
857
1145
  # ---------------------------------------------------------------------------
858
1146
  # Active compaction harness rollback
@@ -947,10 +1235,10 @@ class TestDirectOrchestratorImplementationPrompts:
947
1235
  assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
948
1236
  assert FORBIDDEN_THREE_STRIKE not in rendered
949
1237
  assert FORBIDDEN_TWENTY_STEP not in rendered
950
- assert "Main-Agent review loop" in rendered
1238
+ assert "mandatory Main-Agent review" in rendered
951
1239
  assert "up to ten completed rounds" in rendered
952
- assert "The current Main Agent is the only Code Review executor" in rendered
953
- assert "Do not invoke another review skill or review workflow" in rendered
1240
+ assert "strict structural capability gate" in rendered
1241
+ assert "skill-owned independent Reviewer" in rendered
954
1242
  assert "low=0" not in rendered
955
1243
  assert "Reviewer 3" not in rendered
956
1244
 
@@ -1078,9 +1366,19 @@ class TestContinuationHandoff:
1078
1366
  assert checkpoint_cursor(checkpoint)["first_cursor"] == "S02 (active)"
1079
1367
 
1080
1368
 
1081
- # ---------------------------------------------------------------------------
1082
- # F-033 headless prompt cleanup and platform-aware paths
1083
- # ---------------------------------------------------------------------------
1369
+ def test_modular_feature_prompt_renders_user_context(tmp_path):
1370
+ prompt = _render_feature_prompt(
1371
+ tmp_path / "user-context",
1372
+ mode="standard",
1373
+ user_context=["Use the existing event envelope", "Keep the public API backward compatible"],
1374
+ )
1375
+
1376
+ assert "Use the existing event envelope" in prompt
1377
+ assert "Keep the public API backward compatible" in prompt
1378
+ assert "These materials were provided by the user" in prompt
1379
+ assert "{{USER_CONTEXT}}" not in prompt
1380
+
1381
+
1084
1382
 
1085
1383
  HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
1086
1384
  "### Interactive Claude Code Note",
@@ -1101,7 +1399,7 @@ def _write_feature_list(path, feature):
1101
1399
  path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
1102
1400
 
1103
1401
 
1104
- def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None, complexity=None):
1402
+ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None, complexity=None, user_context=None):
1105
1403
  project = tmp_path / "project"
1106
1404
  project.mkdir(parents=True)
1107
1405
  (project / ".prizmkit" / "plans").mkdir(parents=True)
@@ -1115,6 +1413,8 @@ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="c
1115
1413
  "estimated_complexity": complexity or {"lite": "low", "standard": "medium", "full": "high"}[mode],
1116
1414
  "acceptance_criteria": ["Prompts are clean"],
1117
1415
  }
1416
+ if user_context is not None:
1417
+ feature["user_context"] = user_context
1118
1418
  if browser_interaction is not None:
1119
1419
  feature["browser_interaction"] = browser_interaction
1120
1420
  feature_list = project / ".prizmkit" / "plans" / "feature-list.json"
@@ -1513,28 +1813,29 @@ class TestBrowserOptInPromptPolicy:
1513
1813
  assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "bugfix", "bugfix")
1514
1814
  assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "refactor", "refactor")
1515
1815
 
1516
- def test_fresh_manual_bugfix_prompt_stops_before_commit_phase(self, tmp_path):
1816
+ def test_manual_bugfix_prompt_remains_headless_and_committable(self, tmp_path):
1517
1817
  prompt = _render_bugfix_prompt(
1518
- tmp_path / "fresh-manual-bugfix",
1818
+ tmp_path / "manual-bugfix",
1519
1819
  bug_overrides={"verification_type": "manual"},
1520
1820
  )
1521
1821
 
1522
1822
  assert "Manual / Hybrid Verification Gate" in prompt
1523
- assert "Stop before `/prizmkit-retrospective` and `/prizmkit-committer`" in prompt
1524
- assert "### Phase 4: Commit & Learn" not in prompt
1525
- assert "Run `/prizmkit-committer` with commit prefix" not in prompt
1823
+ assert "must complete manual/hybrid verification automatically" in prompt
1824
+ assert "Stop before `/prizmkit-retrospective` and `/prizmkit-committer`" not in prompt
1825
+ assert "### Headless Commit Authorization" in prompt
1826
+ assert "status=\"partial\"" not in prompt
1526
1827
 
1527
- def test_recovery_manual_bugfix_prompt_includes_commit_phase_with_substitution_policy(self, tmp_path):
1828
+ def test_recovery_manual_bugfix_uses_same_automatic_policy(self, tmp_path):
1528
1829
  prompt = _render_bugfix_prompt(
1529
1830
  tmp_path / "recovery-manual-bugfix",
1530
1831
  bug_overrides={"verification_type": "manual"},
1531
1832
  retry_count="1",
1532
1833
  )
1533
1834
 
1534
- assert "Manual / Hybrid Verification Gate" not in prompt
1535
- assert "Recovery-class session: automated verification may substitute for manual/hybrid UAT" in prompt
1536
- assert "### Phase 4: Commit & Learn" in prompt
1537
- assert "Run `/prizmkit-committer` with commit prefix" in prompt
1835
+ assert "Manual / Hybrid Verification Gate" in prompt
1836
+ assert "must complete manual/hybrid verification automatically" in prompt
1837
+ assert "Recovery-class session: automated verification may substitute" not in prompt
1838
+ assert "### Headless Commit Authorization" in prompt
1538
1839
 
1539
1840
 
1540
1841
  class TestHeadlessPromptCleanupF033:
@@ -1630,9 +1931,9 @@ class TestHeadlessPromptCleanupF033:
1630
1931
  refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-mixed", platform="claude")
1631
1932
 
1632
1933
  assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
1633
- assert "Main-Agent review loop" in refactor_prompt
1634
- assert "The current Main Agent is the only Code Review executor" in refactor_prompt
1635
- assert "Do not invoke another review skill or review workflow" in refactor_prompt
1934
+ assert "mandatory Main-Agent review" in refactor_prompt
1935
+ assert "strict structural capability gate" in refactor_prompt
1936
+ assert "skill-owned independent Reviewer" in refactor_prompt
1636
1937
  assert "Reviewer 3" not in refactor_prompt
1637
1938
 
1638
1939
  def test_bugfix_refactor_explicit_codex_override(self, tmp_path, monkeypatch):
@@ -1654,9 +1955,9 @@ class TestHeadlessPromptCleanupF033:
1654
1955
  refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-codex", platform="claude", explicit_platform="codex")
1655
1956
 
1656
1957
  assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
1657
- assert "Main-Agent review loop" in refactor_prompt
1658
- assert "The current Main Agent is the only Code Review executor" in refactor_prompt
1659
- assert "Do not invoke another review skill or review workflow" in refactor_prompt
1958
+ assert "mandatory Main-Agent review" in refactor_prompt
1959
+ assert "strict structural capability gate" in refactor_prompt
1960
+ assert "skill-owned independent Reviewer" in refactor_prompt
1660
1961
  assert "Reviewer 3" not in refactor_prompt
1661
1962
 
1662
1963