prizmkit 1.1.154 → 1.1.156

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 (164) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/codex/skill-adapter.js +1 -1
  3. package/bundled/adapters/pi/skill-adapter.js +2 -1
  4. package/bundled/dev-pipeline/.env.example +2 -6
  5. package/bundled/dev-pipeline/README.md +21 -27
  6. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +341 -15
  7. package/bundled/dev-pipeline/prizmkit_runtime/cli.py +0 -3
  8. package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +1 -15
  9. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +48 -14
  10. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +50 -2
  11. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -9
  12. package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +3 -3
  13. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +120 -22
  14. package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +482 -0
  15. package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +64 -11
  16. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +145 -367
  17. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +91 -62
  18. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +105 -69
  19. package/bundled/dev-pipeline/scripts/prompt_framework.py +36 -41
  20. package/bundled/dev-pipeline/scripts/update-bug-status.py +1 -4
  21. package/bundled/dev-pipeline/scripts/update-checkpoint.py +61 -13
  22. package/bundled/dev-pipeline/scripts/update-feature-status.py +1 -4
  23. package/bundled/dev-pipeline/scripts/update-refactor-status.py +1 -4
  24. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +22 -27
  25. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +71 -104
  26. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +59 -111
  27. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +37 -72
  28. package/bundled/dev-pipeline/templates/sections/artifact-manifest.md +39 -0
  29. package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +3 -1
  30. package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +8 -25
  31. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +3 -3
  32. package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +2 -2
  33. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
  34. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +2 -2
  35. package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +4 -4
  36. package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +0 -1
  37. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +7 -4
  38. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +14 -27
  39. package/bundled/dev-pipeline/templates/sections/feature-completion-summary.md +20 -0
  40. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -71
  41. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -7
  42. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +3 -3
  43. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
  44. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +5 -17
  45. package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +3 -1
  46. package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +10 -23
  47. package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +2 -2
  48. package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +2 -2
  49. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
  50. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +2 -2
  51. package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +7 -7
  52. package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +0 -1
  53. package/bundled/dev-pipeline/templates/sections/retrospective.md +12 -0
  54. package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +35 -0
  55. package/bundled/dev-pipeline/templates/session-status-schema.json +45 -62
  56. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +469 -18
  57. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +262 -175
  58. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +62 -23
  59. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +76 -28
  60. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +168 -18
  61. package/bundled/dev-pipeline/tests/test_recovery_workflow.py +111 -0
  62. package/bundled/dev-pipeline/tests/test_reset_preserve.py +0 -2
  63. package/bundled/dev-pipeline/tests/test_runtime_commit.py +608 -0
  64. package/bundled/dev-pipeline/tests/test_unified_cli.py +167 -25
  65. package/bundled/skills/_metadata.json +9 -9
  66. package/bundled/skills/app-planner/SKILL.md +3 -3
  67. package/bundled/skills/app-planner/references/generated-plan-review.md +2 -1
  68. package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +56 -64
  69. package/bundled/skills/app-planner/references/rules/backend/fixed-rules.md +85 -240
  70. package/bundled/skills/app-planner/references/rules/backend/question-bank.md +25 -25
  71. package/bundled/skills/app-planner/references/rules/backend/question-manifest.json +16 -7
  72. package/bundled/skills/app-planner/references/rules/backend/template.md +74 -92
  73. package/bundled/skills/app-planner/references/rules/database/derivation-rules.md +93 -93
  74. package/bundled/skills/app-planner/references/rules/database/fixed-rules.md +66 -173
  75. package/bundled/skills/app-planner/references/rules/database/question-bank.md +31 -26
  76. package/bundled/skills/app-planner/references/rules/database/question-manifest.json +13 -4
  77. package/bundled/skills/app-planner/references/rules/database/template.md +76 -98
  78. package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +68 -95
  79. package/bundled/skills/app-planner/references/rules/frontend/fixed-rules.md +93 -166
  80. package/bundled/skills/app-planner/references/rules/frontend/question-bank.md +46 -36
  81. package/bundled/skills/app-planner/references/rules/frontend/question-manifest.json +11 -2
  82. package/bundled/skills/app-planner/references/rules/frontend/template.md +79 -257
  83. package/bundled/skills/app-planner/references/rules/mobile/derivation-rules.md +91 -99
  84. package/bundled/skills/app-planner/references/rules/mobile/fixed-rules.md +73 -246
  85. package/bundled/skills/app-planner/references/rules/mobile/question-bank.md +17 -17
  86. package/bundled/skills/app-planner/references/rules/mobile/question-manifest.json +17 -2
  87. package/bundled/skills/app-planner/references/rules/mobile/template.md +73 -85
  88. package/bundled/skills/app-planner/references/rules-configuration.md +133 -65
  89. package/bundled/skills/app-planner/scripts/validate-rules-configuration.py +291 -0
  90. package/bundled/skills/prizmkit/SKILL.md +17 -18
  91. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +22 -18
  92. package/bundled/skills/prizmkit-code-review/SKILL.md +22 -58
  93. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +6 -6
  94. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +2 -2
  95. package/bundled/skills/prizmkit-committer/SKILL.md +82 -132
  96. package/bundled/skills/prizmkit-deploy/SKILL.md +27 -11
  97. package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +89 -0
  98. package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +44 -14
  99. package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +19 -3
  100. package/bundled/skills/prizmkit-deploy/references/database-setup.md +22 -12
  101. package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +3 -1
  102. package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +22 -6
  103. package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +49 -0
  104. package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +1 -1
  105. package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +164 -0
  106. package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +9 -8
  107. package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +6 -6
  108. package/bundled/skills/prizmkit-implement/SKILL.md +30 -66
  109. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +4 -4
  110. package/bundled/skills/prizmkit-init/SKILL.md +4 -0
  111. package/bundled/skills/prizmkit-plan/SKILL.md +21 -64
  112. package/bundled/skills/prizmkit-plan/references/artifact-identity.md +38 -0
  113. package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +4 -4
  114. package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +5 -5
  115. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +75 -11
  116. package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +191 -56
  117. package/bundled/skills/prizmkit-prizm-docs/references/op-init.md +18 -18
  118. package/bundled/skills/prizmkit-prizm-docs/references/op-rebuild.md +14 -8
  119. package/bundled/skills/prizmkit-prizm-docs/references/op-status.md +12 -5
  120. package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +23 -12
  121. package/bundled/skills/prizmkit-prizm-docs/references/op-validate.md +13 -9
  122. package/bundled/skills/prizmkit-retrospective/SKILL.md +94 -97
  123. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +79 -37
  124. package/bundled/skills/prizmkit-retrospective/references/retrospective-result-schema.json +138 -0
  125. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +111 -54
  126. package/bundled/skills/prizmkit-test/SKILL.md +64 -51
  127. package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +206 -0
  128. package/bundled/skills/prizmkit-test/references/independent-test-review.md +17 -9
  129. package/bundled/skills/prizmkit-test/references/test-coverage-model.md +45 -21
  130. package/bundled/skills/prizmkit-test/references/test-report-template.md +20 -13
  131. package/bundled/skills/prizmkit-workflow/SKILL.md +35 -37
  132. package/bundled/skills/prizmkit-workflow/references/artifact-identity.md +34 -0
  133. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +22 -18
  134. package/bundled/skills/recovery-workflow/SKILL.md +2 -1
  135. package/bundled/skills/recovery-workflow/references/detection.md +4 -3
  136. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +15 -5
  137. package/bundled/templates/hooks/validate-prizm-docs.py +124 -12
  138. package/package.json +1 -1
  139. package/src/scaffold.js +33 -22
  140. package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +0 -16
  141. package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +0 -31
  142. package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -7
  143. package/bundled/dev-pipeline/templates/sections/critical-paths-lite.md +0 -7
  144. package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +0 -9
  145. package/bundled/dev-pipeline/templates/sections/directory-convention-lite.md +0 -6
  146. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +0 -15
  147. package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -61
  148. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -24
  149. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -12
  150. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -44
  151. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -25
  152. package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +0 -20
  153. package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +0 -20
  154. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -20
  155. package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +0 -17
  156. package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +0 -28
  157. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +0 -52
  158. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +0 -52
  159. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +0 -174
  160. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +0 -174
  161. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +0 -174
  162. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +0 -174
  163. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +0 -174
  164. package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +0 -119
@@ -6,6 +6,8 @@ import json
6
6
  import subprocess
7
7
  import sys
8
8
 
9
+ import pytest
10
+
9
11
  from prizmkit_runtime.checkpoint_state import (
10
12
  VALID_CHECKPOINT_STATUSES,
11
13
  VALID_STAGE_RESULTS,
@@ -14,6 +16,7 @@ from prizmkit_runtime.checkpoint_state import (
14
16
  read_checkpoint_cursor,
15
17
  validate_checkpoint_data,
16
18
  )
19
+ from prizmkit_runtime.test_result import load_test_result
17
20
  from update_checkpoint import update_checkpoint
18
21
 
19
22
 
@@ -58,8 +61,6 @@ def _write_test_artifacts(root, slug="feature", *, result="TEST_PASS"):
58
61
  },
59
62
  "repair_rounds": 0,
60
63
  "production_changed": False,
61
- "review_required": False,
62
- "review_scope": None,
63
64
  "unresolved_items": [],
64
65
  }
65
66
  (artifact_dir / "test-result.json").write_text(
@@ -73,14 +74,16 @@ def _family_checkpoint(root=None, *, semantic=None, terminal=True):
73
74
  "prizmkit-implement",
74
75
  "prizmkit-code-review",
75
76
  "prizmkit-test",
77
+ "completion-summary",
76
78
  "prizmkit-retrospective",
77
79
  "prizmkit-committer",
78
- "completion-summary",
79
80
  ]
80
81
  steps = []
81
82
  previous = None
82
83
  for index, skill in enumerate(skills, start=1):
83
84
  step = _step(f"S{index:02d}", skill, skill, "completed", previous)
85
+ if skill == "prizmkit-implement":
86
+ step["stage_result"] = "IMPLEMENTED"
84
87
  if skill == "prizmkit-code-review":
85
88
  step["stage_result"] = "REVIEW_PASS"
86
89
  if skill == "prizmkit-test":
@@ -89,6 +92,10 @@ def _family_checkpoint(root=None, *, semantic=None, terminal=True):
89
92
  ".prizmkit/specs/feature/test-report.md",
90
93
  ".prizmkit/specs/feature/test-result.json",
91
94
  ]
95
+ if skill == "prizmkit-retrospective":
96
+ step["stage_result"] = "RETRO_COMPLETE"
97
+ if skill == "prizmkit-committer":
98
+ step["stage_result"] = "COMMITTED"
92
99
  steps.append(step)
93
100
  previous = step["id"]
94
101
  if root is not None:
@@ -97,7 +104,14 @@ def _family_checkpoint(root=None, *, semantic=None, terminal=True):
97
104
  "version": 1,
98
105
  "workflow_type": "feature-pipeline",
99
106
  "steps": steps,
100
- "feature_state": semantic or ({"terminal_status": "WORKFLOW_COMPLETED"} if terminal else {}),
107
+ "feature_state": semantic or (
108
+ {
109
+ "terminal_status": "WORKFLOW_COMPLETED",
110
+ "runtime_base_head": "a" * 40,
111
+ "runtime_commit_hash": "b" * 40,
112
+ }
113
+ if terminal else {}
114
+ ),
101
115
  }
102
116
 
103
117
 
@@ -201,6 +215,16 @@ def test_structural_validation_rejects_status_result_mismatch(tmp_path):
201
215
  assert state.error_code == "stage_result_status_mismatch"
202
216
 
203
217
 
218
+ def test_family_planning_wrappers_own_plan_results(tmp_path):
219
+ for skill in ("context-snapshot-and-plan", "bug-diagnosis-and-plan"):
220
+ step = _step("S01", skill, "Plan", "completed")
221
+ step["stage_result"] = "PLAN_READY"
222
+ state = validate_checkpoint_data(
223
+ _checkpoint([step]), path=tmp_path / f"{skill}.json",
224
+ )
225
+ assert state.valid is True
226
+
227
+
204
228
  def test_structural_validation_rejects_stage_result_owner_mismatch(tmp_path):
205
229
  for skill in ("prizmkit-test", "custom-stage"):
206
230
  step = _step("S01", skill, "Step", "completed")
@@ -391,7 +415,6 @@ def test_checkpoint_updater_supports_every_owned_stage_result(tmp_path):
391
415
  ("prizmkit-test", "TEST_BLOCKED", "failed"),
392
416
  ("prizmkit-retrospective", "RETRO_COMPLETE", "completed"),
393
417
  ("prizmkit-committer", "COMMIT_PENDING", "in_progress"),
394
- ("prizmkit-committer", "COMMITTED", "completed"),
395
418
  ]
396
419
 
397
420
  assert {stage_result for _skill, stage_result, _status in cases} <= VALID_STAGE_RESULTS
@@ -404,6 +427,19 @@ def test_checkpoint_updater_supports_every_owned_stage_result(tmp_path):
404
427
  payload["steps"].insert(0, _step("S01", skill, skill, "pending"))
405
428
  target_index = 0
406
429
  else:
430
+ payload["steps"].insert(
431
+ 0,
432
+ {
433
+ "id": "S00",
434
+ "skill": "context-snapshot-and-plan",
435
+ "name": "Plan",
436
+ "status": "completed",
437
+ "stage_result": "PLAN_READY",
438
+ "required_artifacts": [],
439
+ "depends_on": None,
440
+ },
441
+ )
442
+ payload["steps"][1]["depends_on"] = "S00"
407
443
  target_index = next(
408
444
  position
409
445
  for position, step in enumerate(payload["steps"])
@@ -511,11 +547,26 @@ def test_family_sequence_mismatch_cannot_complete(tmp_path):
511
547
  assert state.semantic.error_code == "invalid_stage_sequence"
512
548
 
513
549
 
514
- def test_test_needs_fixes_is_valid_domain_result_and_blocks_l4(tmp_path):
550
+ def test_test_needs_fixes_routes_to_implementation_and_rewinds_stale_gates(tmp_path):
515
551
  payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
516
- payload["steps"][2]["status"] = "in_progress"
517
- payload["steps"][2].pop("stage_result")
518
- for step in payload["steps"][3:]:
552
+ payload["steps"].insert(
553
+ 0,
554
+ {
555
+ "id": "S00",
556
+ "skill": "context-snapshot-and-plan",
557
+ "name": "Plan",
558
+ "status": "completed",
559
+ "stage_result": "PLAN_READY",
560
+ "required_artifacts": [],
561
+ "depends_on": None,
562
+ },
563
+ )
564
+ payload["steps"][1]["depends_on"] = "S00"
565
+ test_step = next(step for step in payload["steps"] if step["skill"] == "prizmkit-test")
566
+ test_index = payload["steps"].index(test_step)
567
+ test_step["status"] = "in_progress"
568
+ test_step.pop("stage_result")
569
+ for step in payload["steps"][test_index + 1:]:
519
570
  step["status"] = "pending"
520
571
  step.pop("stage_result", None)
521
572
  checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
@@ -524,16 +575,80 @@ def test_test_needs_fixes_is_valid_domain_result_and_blocks_l4(tmp_path):
524
575
  str(checkpoint),
525
576
  "prizmkit-test",
526
577
  "failed",
527
- semantic_update={"stage_result": "TEST_NEEDS_FIXES"},
578
+ semantic_update={
579
+ "stage_result": "TEST_NEEDS_FIXES",
580
+ "repair_scope": "production",
581
+ },
528
582
  )
529
- persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
583
+ routed = json.loads(checkpoint.read_text(encoding="utf-8"))
530
584
 
531
585
  assert result["ok"] is True
532
- assert persisted["steps"][2]["stage_result"] == "TEST_NEEDS_FIXES"
533
- assert persisted["feature_state"]["stage_result"] == "TEST_NEEDS_FIXES"
534
- assert persisted["feature_state"]["blocked_reason"] == "test_needs_fixes"
535
- assert persisted["feature_state"]["terminal_status"] == "WORKFLOW_BLOCKED"
536
- assert "authoritative_evidence_paths" not in persisted["steps"][2]
586
+ assert test_step["skill"] == "prizmkit-test"
587
+ persisted_test = next(
588
+ step for step in routed["steps"] if step["skill"] == "prizmkit-test"
589
+ )
590
+ assert persisted_test["stage_result"] == "TEST_NEEDS_FIXES"
591
+ assert routed["feature_state"]["stage_result"] == "TEST_NEEDS_FIXES"
592
+ assert routed["feature_state"]["next_stage"] == "prizmkit-implement"
593
+ assert routed["feature_state"]["repair_round"] == 1
594
+
595
+ restarted = update_checkpoint(
596
+ str(checkpoint), "prizmkit-implement", "in_progress"
597
+ )
598
+ rewound = json.loads(checkpoint.read_text(encoding="utf-8"))
599
+
600
+ assert restarted["ok"] is True
601
+ implementation = next(
602
+ step for step in rewound["steps"] if step["skill"] == "prizmkit-implement"
603
+ )
604
+ assert implementation["status"] == "in_progress"
605
+ for step in rewound["steps"][rewound["steps"].index(implementation) + 1:]:
606
+ assert step["status"] == "pending"
607
+ assert "stage_result" not in step
608
+
609
+
610
+ def test_terminal_atomic_blockers_become_workflow_blocked(tmp_path):
611
+ cases = [
612
+ ("context-snapshot-and-plan", "PLAN_BLOCKED"),
613
+ ("prizmkit-implement", "IMPLEMENT_BLOCKED"),
614
+ ("prizmkit-retrospective", "RETRO_BLOCKED"),
615
+ ("prizmkit-committer", "COMMIT_BLOCKED"),
616
+ ]
617
+ for index, (skill, stage_result) in enumerate(cases):
618
+ payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
619
+ if skill == "context-snapshot-and-plan":
620
+ payload["steps"].insert(
621
+ 0,
622
+ {
623
+ "id": "S00",
624
+ "skill": skill,
625
+ "name": "Plan",
626
+ "status": "pending",
627
+ "required_artifacts": [],
628
+ "depends_on": None,
629
+ },
630
+ )
631
+ payload["steps"][1]["depends_on"] = "S00"
632
+ target_index = 0
633
+ else:
634
+ target_index = next(
635
+ position for position, step in enumerate(payload["steps"])
636
+ if step["skill"] == skill
637
+ )
638
+ for step in payload["steps"][target_index:]:
639
+ step["status"] = "pending"
640
+ step.pop("stage_result", None)
641
+ checkpoint = _write(tmp_path / f"terminal-blocker-{index}.json", payload)
642
+
643
+ result = update_checkpoint(
644
+ str(checkpoint), skill, "failed",
645
+ semantic_update={"stage_result": stage_result},
646
+ )
647
+ persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
648
+
649
+ assert result["ok"] is True
650
+ assert persisted["feature_state"]["terminal_status"] == "WORKFLOW_BLOCKED"
651
+ assert persisted["feature_state"]["blocked_reason"]
537
652
 
538
653
 
539
654
  def test_test_blocked_preserves_l1_result_without_evidence_discovery(tmp_path):
@@ -602,6 +717,31 @@ def test_update_checkpoint_rejects_test_completion_for_non_pass(tmp_path):
602
717
  assert "requires TEST_PASS" in result["error"]
603
718
 
604
719
 
720
+ def test_prompt_checkpoint_helper_rejects_runtime_owned_committed_result(tmp_path):
721
+ payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
722
+ committer = next(
723
+ step for step in payload["steps"] if step["skill"] == "prizmkit-committer"
724
+ )
725
+ committer["status"] = "in_progress"
726
+ committer["stage_result"] = "COMMIT_PENDING"
727
+ checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
728
+
729
+ result = update_checkpoint(
730
+ str(checkpoint),
731
+ "prizmkit-committer",
732
+ "completed",
733
+ semantic_update={"stage_result": "COMMITTED"},
734
+ )
735
+
736
+ assert result["ok"] is False
737
+ assert "Runtime-owned" in result["error"]
738
+ without_result = update_checkpoint(
739
+ str(checkpoint), "prizmkit-committer", "completed"
740
+ )
741
+ assert without_result["ok"] is False
742
+ assert "Runtime-owned COMMITTED" in without_result["error"]
743
+
744
+
605
745
  def test_update_checkpoint_rejects_test_before_review_pass(tmp_path):
606
746
  payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
607
747
  payload["steps"][1]["status"] = "skipped"
@@ -623,7 +763,7 @@ def test_update_checkpoint_rejects_finalization_before_test_pass(tmp_path):
623
763
  payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
624
764
  payload["steps"][2]["status"] = "skipped"
625
765
  payload["steps"][2].pop("stage_result", None)
626
- payload["steps"][3]["status"] = "pending"
766
+ payload["steps"][3]["status"] = "completed"
627
767
  for step in payload["steps"][4:]:
628
768
  step["status"] = "pending"
629
769
  step.pop("stage_result", None)
@@ -635,9 +775,53 @@ def test_update_checkpoint_rejects_finalization_before_test_pass(tmp_path):
635
775
  assert "TEST_PASS" in result["error"]
636
776
 
637
777
 
778
+ def test_commit_pending_is_runtime_ready_but_not_workflow_complete(tmp_path):
779
+ payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
780
+ payload["steps"].insert(
781
+ 0,
782
+ {
783
+ "id": "S00",
784
+ "skill": "context-snapshot-and-plan",
785
+ "name": "Plan",
786
+ "status": "completed",
787
+ "stage_result": "PLAN_READY",
788
+ "required_artifacts": [],
789
+ "depends_on": None,
790
+ },
791
+ )
792
+ payload["steps"][1]["depends_on"] = "S00"
793
+ committer = next(
794
+ step for step in payload["steps"] if step["skill"] == "prizmkit-committer"
795
+ )
796
+ committer["status"] = "in_progress"
797
+ committer["stage_result"] = "COMMIT_PENDING"
798
+ committer["required_artifacts"] = [
799
+ ".prizmkit/specs/feature/runtime-commit-request.json"
800
+ ]
801
+ artifact_dir = tmp_path / ".prizmkit/specs/feature"
802
+ (artifact_dir / "runtime-commit-request.json").write_text("{}", encoding="utf-8")
803
+ payload["feature_state"] = {
804
+ "stage": "committer",
805
+ "status": "in_progress",
806
+ "stage_result": "COMMIT_PENDING",
807
+ "repair_round": 0,
808
+ }
809
+ checkpoint = _write(artifact_dir / "workflow-checkpoint.json", payload)
810
+
811
+ state = load_checkpoint_state(checkpoint, project_root=tmp_path)
812
+
813
+ assert state.valid is True
814
+ assert state.semantic_commit_ready is True
815
+ assert state.semantic_complete is False
816
+ assert state.semantic_snapshot["commit_ready"] is True
817
+
818
+
638
819
  def test_declared_final_artifact_must_exist_for_completion(tmp_path):
639
820
  payload = _family_checkpoint(tmp_path)
640
- payload["steps"][-1]["required_artifacts"] = [
821
+ summary_step = next(
822
+ step for step in payload["steps"] if step["skill"] == "completion-summary"
823
+ )
824
+ summary_step["required_artifacts"] = [
641
825
  ".prizmkit/specs/feature/completion-summary.json"
642
826
  ]
643
827
  checkpoint = _write(
@@ -655,6 +839,239 @@ def test_declared_final_artifact_must_exist_for_completion(tmp_path):
655
839
  assert present.semantic.artifacts_valid is True
656
840
 
657
841
 
842
+ def test_completion_requires_declared_review_report_with_terminal_pass(tmp_path):
843
+ payload = _family_checkpoint(tmp_path)
844
+ review = next(
845
+ step for step in payload["steps"]
846
+ if step["skill"] == "prizmkit-code-review"
847
+ )
848
+ review["required_artifacts"] = [
849
+ ".prizmkit/specs/feature/review-report.md"
850
+ ]
851
+ checkpoint = _write(
852
+ tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json", payload,
853
+ )
854
+
855
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is False
856
+ (checkpoint.parent / "review-report.md").write_text(
857
+ "# Review Report\n\n## Final Result\n\n- Verdict: NEEDS_FIXES\n",
858
+ encoding="utf-8",
859
+ )
860
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is False
861
+ (checkpoint.parent / "review-report.md").write_text(
862
+ "# Review Report\n\n## Final Result\n\n- Verdict: PASS\n",
863
+ encoding="utf-8",
864
+ )
865
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is True
866
+
867
+
868
+ def test_completion_requires_valid_declared_retrospective_result(tmp_path):
869
+ payload = _family_checkpoint(tmp_path)
870
+ retrospective = next(
871
+ step for step in payload["steps"]
872
+ if step["skill"] == "prizmkit-retrospective"
873
+ )
874
+ retrospective["required_artifacts"] = [
875
+ ".prizmkit/specs/feature/retrospective-result.json"
876
+ ]
877
+ checkpoint = _write(
878
+ tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json", payload,
879
+ )
880
+
881
+ (checkpoint.parent / "retrospective-result.json").write_text(
882
+ "{}\n", encoding="utf-8",
883
+ )
884
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is False
885
+ (checkpoint.parent / "retrospective-result.json").write_text(
886
+ json.dumps(
887
+ {
888
+ "schema_version": 1,
889
+ "outcome": "RETRO_COMPLETE",
890
+ "result": "NO_DOC_CHANGE",
891
+ "reason": "No durable change",
892
+ "change_paths": ["src/example.py"],
893
+ "documentation_paths": [],
894
+ "validation": [],
895
+ }
896
+ ),
897
+ encoding="utf-8",
898
+ )
899
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is True
900
+
901
+
902
+ @pytest.mark.parametrize(
903
+ "invalid_path",
904
+ [
905
+ ".prizmkit/prizm-docs/../escape.prizm",
906
+ ".prizmkit/prizm-docs/./module.prizm",
907
+ ".prizmkit/prizm-docs//module.prizm",
908
+ ".prizmkit\\prizm-docs\\module.prizm",
909
+ ".prizmkit/prizm-docs/module.md",
910
+ ],
911
+ )
912
+ def test_completion_rejects_unsafe_retrospective_documentation_path(
913
+ tmp_path, invalid_path,
914
+ ):
915
+ payload = _family_checkpoint(tmp_path)
916
+ retrospective = next(
917
+ step for step in payload["steps"]
918
+ if step["skill"] == "prizmkit-retrospective"
919
+ )
920
+ retrospective["required_artifacts"] = [
921
+ ".prizmkit/specs/feature/retrospective-result.json"
922
+ ]
923
+ checkpoint = _write(
924
+ tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json", payload,
925
+ )
926
+ (checkpoint.parent / "retrospective-result.json").write_text(
927
+ json.dumps(
928
+ {
929
+ "schema_version": 1,
930
+ "outcome": "RETRO_COMPLETE",
931
+ "result": "DOCS_UPDATED",
932
+ "reason": "Durable docs changed",
933
+ "change_paths": ["src/example.py"],
934
+ "documentation_paths": [invalid_path],
935
+ "validation": ["validate docs"],
936
+ }
937
+ ),
938
+ encoding="utf-8",
939
+ )
940
+
941
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is False
942
+
943
+
944
+ @pytest.mark.parametrize(
945
+ "invalid_path",
946
+ ["../escape.py", "./src/example.py", "src//example.py", "src\\example.py"],
947
+ )
948
+ def test_completion_rejects_unsafe_retrospective_change_path(tmp_path, invalid_path):
949
+ payload = _family_checkpoint(tmp_path)
950
+ retrospective = next(
951
+ step for step in payload["steps"]
952
+ if step["skill"] == "prizmkit-retrospective"
953
+ )
954
+ retrospective["required_artifacts"] = [
955
+ ".prizmkit/specs/feature/retrospective-result.json"
956
+ ]
957
+ checkpoint = _write(
958
+ tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json", payload,
959
+ )
960
+ (checkpoint.parent / "retrospective-result.json").write_text(
961
+ json.dumps(
962
+ {
963
+ "schema_version": 1,
964
+ "outcome": "RETRO_COMPLETE",
965
+ "result": "NO_DOC_CHANGE",
966
+ "reason": "No durable change",
967
+ "change_paths": [invalid_path],
968
+ "documentation_paths": [],
969
+ "validation": [],
970
+ }
971
+ ),
972
+ encoding="utf-8",
973
+ )
974
+
975
+ assert load_checkpoint_state(checkpoint, project_root=tmp_path).semantic_complete is False
976
+
977
+
978
+ def test_completion_rejects_configured_verification_after_test(tmp_path):
979
+ payload = _family_checkpoint(tmp_path)
980
+ test_index = next(
981
+ index for index, step in enumerate(payload["steps"])
982
+ if step["skill"] == "prizmkit-test"
983
+ )
984
+ test_step = payload["steps"][test_index]
985
+ following = payload["steps"][test_index + 1]
986
+ payload["steps"].insert(
987
+ test_index + 1,
988
+ {
989
+ "id": "S99",
990
+ "skill": "browser-verification",
991
+ "name": "Browser Verification",
992
+ "status": "completed",
993
+ "required_artifacts": [],
994
+ "depends_on": test_step["id"],
995
+ },
996
+ )
997
+ following["depends_on"] = "S99"
998
+
999
+ state = validate_checkpoint_data(payload, project_root=tmp_path)
1000
+
1001
+ assert state.semantic.sequence_valid is False
1002
+ assert "after review and before test" in state.semantic.error_message
1003
+
1004
+
1005
+ def test_configured_manual_verification_cannot_be_skipped_for_commit_readiness(tmp_path):
1006
+ payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
1007
+ payload["steps"].insert(
1008
+ 0,
1009
+ {
1010
+ "id": "S00",
1011
+ "skill": "context-snapshot-and-plan",
1012
+ "name": "Plan",
1013
+ "status": "completed",
1014
+ "stage_result": "PLAN_READY",
1015
+ "required_artifacts": [],
1016
+ "depends_on": None,
1017
+ },
1018
+ )
1019
+ payload["steps"][1]["depends_on"] = "S00"
1020
+ review = next(
1021
+ step for step in payload["steps"]
1022
+ if step["skill"] == "prizmkit-code-review"
1023
+ )
1024
+ test = next(
1025
+ step for step in payload["steps"] if step["skill"] == "prizmkit-test"
1026
+ )
1027
+ test_index = payload["steps"].index(test)
1028
+ payload["steps"].insert(
1029
+ test_index,
1030
+ {
1031
+ "id": "S99",
1032
+ "skill": "manual-verification",
1033
+ "name": "Manual Verification",
1034
+ "status": "skipped",
1035
+ "required_artifacts": [],
1036
+ "depends_on": review["id"],
1037
+ },
1038
+ )
1039
+ test["depends_on"] = "S99"
1040
+ committer = next(
1041
+ step for step in payload["steps"]
1042
+ if step["skill"] == "prizmkit-committer"
1043
+ )
1044
+ committer["status"] = "in_progress"
1045
+ committer["stage_result"] = "COMMIT_PENDING"
1046
+ committer["required_artifacts"] = [
1047
+ ".prizmkit/specs/feature/runtime-commit-request.json"
1048
+ ]
1049
+ artifact_dir = tmp_path / ".prizmkit/specs/feature"
1050
+ (artifact_dir / "runtime-commit-request.json").write_text("{}", encoding="utf-8")
1051
+ checkpoint = _write(artifact_dir / "workflow-checkpoint.json", payload)
1052
+
1053
+ skipped = load_checkpoint_state(checkpoint, project_root=tmp_path)
1054
+ payload["steps"][test_index]["status"] = "completed"
1055
+ checkpoint.write_text(json.dumps(payload), encoding="utf-8")
1056
+ completed = load_checkpoint_state(checkpoint, project_root=tmp_path)
1057
+
1058
+ assert skipped.semantic_commit_ready is False
1059
+ assert completed.semantic_commit_ready is True
1060
+
1061
+
1062
+ def test_nonpass_test_projection_requires_exact_unresolved_item(tmp_path):
1063
+ artifact_dir = _write_test_artifacts(
1064
+ tmp_path, slug="nonpass", result="TEST_NEEDS_FIXES"
1065
+ )
1066
+ result_path = artifact_dir / "test-result.json"
1067
+
1068
+ assert load_test_result(result_path) is None
1069
+ projection = json.loads(result_path.read_text(encoding="utf-8"))
1070
+ projection["unresolved_items"] = ["Public API correction remains"]
1071
+ result_path.write_text(json.dumps(projection), encoding="utf-8")
1072
+ assert load_test_result(result_path) is not None
1073
+
1074
+
658
1075
  def test_completion_rejects_invalid_test_result_projection(tmp_path):
659
1076
  payload = _family_checkpoint(tmp_path)
660
1077
  checkpoint = _write(
@@ -694,6 +1111,40 @@ def test_completion_rejects_inconsistent_independent_review_metadata(tmp_path):
694
1111
  assert state.semantic.artifacts_valid is False
695
1112
 
696
1113
 
1114
+ def test_legacy_completed_checkpoint_accepts_historical_test_projection_read_only(tmp_path):
1115
+ payload = _family_checkpoint(tmp_path)
1116
+ by_skill = {step["skill"]: step for step in payload["steps"]}
1117
+ legacy_order = [
1118
+ "prizmkit-implement",
1119
+ "prizmkit-code-review",
1120
+ "prizmkit-test",
1121
+ "prizmkit-retrospective",
1122
+ "prizmkit-committer",
1123
+ "completion-summary",
1124
+ ]
1125
+ payload["steps"] = [by_skill[skill] for skill in legacy_order]
1126
+ previous = None
1127
+ for index, step in enumerate(payload["steps"], start=1):
1128
+ step["id"] = f"S{index:02d}"
1129
+ step["depends_on"] = previous
1130
+ previous = step["id"]
1131
+ checkpoint = _write(
1132
+ tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json", payload,
1133
+ )
1134
+ result_path = checkpoint.parent / "test-result.json"
1135
+ projection = json.loads(result_path.read_text(encoding="utf-8"))
1136
+ projection["review_required"] = False
1137
+ projection["review_scope"] = None
1138
+ result_path.write_text(json.dumps(projection), encoding="utf-8")
1139
+ assert load_test_result(result_path) is None
1140
+
1141
+ state = load_checkpoint_state(checkpoint, project_root=tmp_path)
1142
+
1143
+ assert state.semantic_complete is True
1144
+ assert state.semantic.artifacts_valid is True
1145
+ assert state.semantic_commit_ready is False
1146
+
1147
+
697
1148
  def test_completion_accepts_legacy_terminal_result_report(tmp_path):
698
1149
  payload = _family_checkpoint(tmp_path)
699
1150
  checkpoint = _write(