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,15 +6,19 @@ import copy
6
6
  import json
7
7
  import re
8
8
  from dataclasses import dataclass, field
9
- from pathlib import Path
9
+ from pathlib import Path, PurePosixPath
10
10
  from typing import Any, Mapping
11
11
 
12
- from .test_result import valid_test_artifact_pair
12
+ from .test_result import (
13
+ valid_legacy_test_artifact_pair,
14
+ valid_test_artifact_pair,
15
+ )
13
16
 
14
17
  VALID_CHECKPOINT_STATUSES = {"pending", "in_progress", "completed", "skipped", "failed"}
15
18
  TERMINAL_STEP_STATUSES = {"completed", "skipped"}
16
19
  STEP_ID_PATTERN = re.compile(r"^S[0-9]+$")
17
20
  MAX_OUTER_REPAIR_ROUNDS = 3
21
+ _COMMIT_HASH_PATTERN = re.compile(r"^[0-9a-fA-F]{40,64}$")
18
22
 
19
23
  REVIEW_RESULTS = {"REVIEW_PASS", "REVIEW_NEEDS_FIXES"}
20
24
  TEST_RESULTS = {"TEST_PASS", "TEST_NEEDS_FIXES", "TEST_BLOCKED"}
@@ -32,6 +36,8 @@ VALID_STAGE_RESULTS = (
32
36
  )
33
37
  STAGE_RESULT_OWNERS = {
34
38
  "prizmkit-plan": PLAN_RESULTS,
39
+ "context-snapshot-and-plan": PLAN_RESULTS,
40
+ "bug-diagnosis-and-plan": PLAN_RESULTS,
35
41
  "prizmkit-implement": IMPLEMENT_RESULTS,
36
42
  "prizmkit-code-review": REVIEW_RESULTS,
37
43
  "prizmkit-test": TEST_RESULTS,
@@ -49,6 +55,15 @@ WORKFLOW_STAGE_SKILLS = {
49
55
  SKILL_WORKFLOW_STAGES = {
50
56
  skill: stage for stage, skill in WORKFLOW_STAGE_SKILLS.items()
51
57
  }
58
+ SKILL_WORKFLOW_STAGES.update({
59
+ "context-snapshot-and-plan": "plan",
60
+ "bug-diagnosis-and-plan": "plan",
61
+ })
62
+ FAMILY_PLAN_SKILLS = {
63
+ "feature-pipeline": "context-snapshot-and-plan",
64
+ "bugfix-pipeline": "bug-diagnosis-and-plan",
65
+ "refactor-pipeline": "prizmkit-plan",
66
+ }
52
67
  VALID_TERMINAL_STATUSES = {"WORKFLOW_COMPLETED", "WORKFLOW_BLOCKED"}
53
68
  VALID_REPAIR_SCOPES = {
54
69
  "production",
@@ -63,7 +78,7 @@ SEMANTIC_STATE_KEYS = {
63
78
  "bugfix-pipeline": "bugfix_state",
64
79
  "refactor-pipeline": "refactor_state",
65
80
  }
66
- FAMILY_REQUIRED_STAGES = {
81
+ LEGACY_FAMILY_REQUIRED_STAGES = {
67
82
  "feature-pipeline": (
68
83
  "prizmkit-implement",
69
84
  "prizmkit-code-review",
@@ -91,6 +106,34 @@ FAMILY_REQUIRED_STAGES = {
91
106
  ),
92
107
  }
93
108
 
109
+ FAMILY_REQUIRED_STAGES = {
110
+ "feature-pipeline": (
111
+ "prizmkit-implement",
112
+ "prizmkit-code-review",
113
+ "prizmkit-test",
114
+ "completion-summary",
115
+ "prizmkit-retrospective",
116
+ "prizmkit-committer",
117
+ ),
118
+ "bugfix-pipeline": (
119
+ "prizmkit-implement",
120
+ "prizmkit-code-review",
121
+ "prizmkit-test",
122
+ "bug-report",
123
+ "prizmkit-retrospective",
124
+ "prizmkit-committer",
125
+ ),
126
+ "refactor-pipeline": (
127
+ "prizmkit-implement",
128
+ "prizmkit-code-review",
129
+ "prizmkit-test",
130
+ "refactor-report",
131
+ "completion-summary",
132
+ "prizmkit-retrospective",
133
+ "prizmkit-committer",
134
+ ),
135
+ }
136
+
94
137
 
95
138
  @dataclass(frozen=True)
96
139
  class CheckpointStep:
@@ -128,10 +171,13 @@ class SemanticCheckpoint:
128
171
  repair_round: int = 0
129
172
  blocked_reason: str | None = None
130
173
  terminal_status: str | None = None
174
+ runtime_base_head: str = ""
175
+ runtime_commit_hash: str = ""
131
176
  sequence_valid: bool = False
132
177
  test_result_valid: bool = False
133
178
  artifacts_valid: bool = False
134
179
  completion_ready: bool = False
180
+ commit_ready: bool = False
135
181
  complete: bool = False
136
182
  blocked: bool = False
137
183
  error_code: str = ""
@@ -147,6 +193,9 @@ class SemanticCheckpoint:
147
193
  "repair_round": self.repair_round,
148
194
  "blocked_reason": self.blocked_reason,
149
195
  "terminal_status": self.terminal_status,
196
+ "runtime_base_head": self.runtime_base_head,
197
+ "runtime_commit_hash": self.runtime_commit_hash,
198
+ "commit_ready": self.commit_ready,
150
199
  }
151
200
 
152
201
 
@@ -200,6 +249,11 @@ class CheckpointState:
200
249
  """Return whether this checkpoint authoritatively proves task completion."""
201
250
  return self.valid and self.semantic.complete
202
251
 
252
+ @property
253
+ def semantic_commit_ready(self) -> bool:
254
+ """Return whether the runtime may execute the prepared local commit."""
255
+ return self.valid and self.semantic.commit_ready
256
+
203
257
  @property
204
258
  def semantic_blocked(self) -> bool:
205
259
  """Return whether this checkpoint has a deterministic blocked terminal state."""
@@ -591,8 +645,21 @@ def _derive_semantic_state(
591
645
  return _semantic_error(workflow_type, current_step, "invalid_semantic_state", f"{state_key} must be an object")
592
646
 
593
647
  required = FAMILY_REQUIRED_STAGES.get(workflow_type)
594
- sequence_error = _required_sequence_error(steps, required) if required else "unsupported workflow family"
648
+ canonical_sequence_error = (
649
+ _required_sequence_error(steps, required)
650
+ if required else "unsupported workflow family"
651
+ )
652
+ if not canonical_sequence_error:
653
+ canonical_sequence_error = _configured_verification_sequence_error(steps)
654
+ legacy_required = LEGACY_FAMILY_REQUIRED_STAGES.get(workflow_type)
655
+ legacy_sequence_valid = bool(
656
+ canonical_sequence_error
657
+ and legacy_required
658
+ and not _required_sequence_error(steps, legacy_required)
659
+ )
660
+ sequence_error = "" if legacy_sequence_valid else canonical_sequence_error
595
661
  sequence_valid = not sequence_error
662
+ canonical_sequence_valid = not canonical_sequence_error
596
663
 
597
664
  repair_round = raw_state.get("repair_round", 0)
598
665
  if isinstance(repair_round, bool) or not isinstance(repair_round, int) or not 0 <= repair_round <= MAX_OUTER_REPAIR_ROUNDS:
@@ -629,6 +696,20 @@ def _derive_semantic_state(
629
696
  terminal_status = raw_state.get("terminal_status")
630
697
  if terminal_status is not None and terminal_status not in VALID_TERMINAL_STATUSES:
631
698
  return _semantic_error(workflow_type, current_step, "invalid_terminal_status", "terminal_status is not recognized")
699
+ runtime_base_head = str(raw_state.get("runtime_base_head") or "").strip()
700
+ runtime_commit_hash = str(raw_state.get("runtime_commit_hash") or "").strip()
701
+ runtime_receipt_valid = bool(
702
+ _COMMIT_HASH_PATTERN.fullmatch(runtime_base_head)
703
+ and _COMMIT_HASH_PATTERN.fullmatch(runtime_commit_hash)
704
+ and runtime_base_head.lower() != runtime_commit_hash.lower()
705
+ )
706
+ if (runtime_base_head or runtime_commit_hash) and not runtime_receipt_valid:
707
+ return _semantic_error(
708
+ workflow_type,
709
+ current_step,
710
+ "invalid_runtime_commit_receipt",
711
+ "Runtime commit receipt requires distinct valid base and commit hashes",
712
+ )
632
713
  blocked_reason = raw_state.get("blocked_reason")
633
714
  if blocked_reason is not None and (not isinstance(blocked_reason, str) or not blocked_reason.strip()):
634
715
  return _semantic_error(workflow_type, current_step, "invalid_blocked_reason", "blocked_reason must be a non-empty string")
@@ -651,6 +732,25 @@ def _derive_semantic_state(
651
732
 
652
733
  review_step = _single_step(steps, "prizmkit-code-review")
653
734
  test_step = _single_step(steps, "prizmkit-test")
735
+ plan_step = _single_step(steps, FAMILY_PLAN_SKILLS.get(workflow_type, ""))
736
+ implement_step = _single_step(steps, "prizmkit-implement")
737
+ plan_valid = bool(
738
+ plan_step
739
+ and implement_step
740
+ and plan_step.index < implement_step.index
741
+ and plan_step.status == "completed"
742
+ and plan_step.stage_result == "PLAN_READY"
743
+ )
744
+ configured_manual_valid = all(
745
+ step.status == "completed"
746
+ for step in steps
747
+ if step.skill == "manual-verification"
748
+ )
749
+ implementation_valid = bool(
750
+ implement_step
751
+ and implement_step.status == "completed"
752
+ and implement_step.stage_result == "IMPLEMENTED"
753
+ )
654
754
  review_valid = bool(
655
755
  review_step
656
756
  and review_step.status == "completed"
@@ -661,18 +761,65 @@ def _derive_semantic_state(
661
761
  and test_step.status == "completed"
662
762
  and test_step.stage_result == "TEST_PASS"
663
763
  )
664
- all_required_completed = bool(required) and all(
764
+ effective_required = legacy_required if legacy_sequence_valid else required
765
+ all_required_completed = bool(effective_required) and all(
665
766
  (step := _single_step(steps, skill)) is not None and step.status == "completed"
666
- for skill in required
767
+ for skill in effective_required
667
768
  )
668
769
  artifacts_valid = all(
669
770
  _required_step_artifacts_valid(step, checkpoint_path, project_root)
670
771
  for step in steps
671
772
  if step.status == "completed"
672
773
  )
774
+ completion_artifacts_valid = artifacts_valid or (
775
+ legacy_sequence_valid
776
+ and all(
777
+ _required_step_artifacts_valid(
778
+ step,
779
+ checkpoint_path,
780
+ project_root,
781
+ allow_legacy_test=True,
782
+ )
783
+ for step in steps
784
+ if step.status == "completed"
785
+ )
786
+ )
787
+ retrospective_step = _single_step(steps, "prizmkit-retrospective")
788
+ retrospective_valid = bool(
789
+ retrospective_step
790
+ and retrospective_step.status == "completed"
791
+ and retrospective_step.stage_result == "RETRO_COMPLETE"
792
+ )
793
+ committer_step = _single_step(steps, "prizmkit-committer")
794
+ committed_valid = bool(
795
+ committer_step
796
+ and committer_step.status == "completed"
797
+ and committer_step.stage_result == "COMMITTED"
798
+ )
799
+ precommit_required = tuple(
800
+ skill for skill in (required or ()) if skill != "prizmkit-committer"
801
+ )
802
+ all_precommit_completed = bool(precommit_required) and all(
803
+ (step := _single_step(steps, skill)) is not None and step.status == "completed"
804
+ for skill in precommit_required
805
+ )
806
+ commit_request_valid = bool(
807
+ committer_step
808
+ and committer_step.status == "in_progress"
809
+ and committer_step.stage_result == "COMMIT_PENDING"
810
+ and _required_step_artifacts_valid(committer_step, checkpoint_path, project_root)
811
+ )
812
+ commit_ready = (
813
+ canonical_sequence_valid and plan_valid and configured_manual_valid
814
+ and all_precommit_completed and implementation_valid and review_valid
815
+ and test_result_valid and retrospective_valid and artifacts_valid
816
+ and commit_request_valid and terminal_status is None
817
+ )
673
818
  completion_ready = (
674
- sequence_valid and all_required_completed and review_valid
675
- and test_result_valid and artifacts_valid
819
+ sequence_valid and all_required_completed and implementation_valid
820
+ and review_valid and test_result_valid and retrospective_valid
821
+ and committed_valid and completion_artifacts_valid
822
+ and (legacy_sequence_valid or runtime_receipt_valid)
676
823
  )
677
824
  blocked = terminal_status == "WORKFLOW_BLOCKED" and bool(blocked_reason)
678
825
  complete = completion_ready and terminal_status == "WORKFLOW_COMPLETED" and not blocked
@@ -719,10 +866,13 @@ def _derive_semantic_state(
719
866
  repair_round=repair_round,
720
867
  blocked_reason=blocked_reason,
721
868
  terminal_status=terminal_status,
869
+ runtime_base_head=runtime_base_head,
870
+ runtime_commit_hash=runtime_commit_hash,
722
871
  sequence_valid=sequence_valid,
723
872
  test_result_valid=test_result_valid,
724
- artifacts_valid=artifacts_valid,
873
+ artifacts_valid=completion_artifacts_valid,
725
874
  completion_ready=completion_ready,
875
+ commit_ready=commit_ready,
726
876
  complete=complete,
727
877
  blocked=blocked,
728
878
  error_code=error_code,
@@ -747,16 +897,34 @@ def _required_sequence_error(
747
897
  return ""
748
898
 
749
899
 
900
+ def _configured_verification_sequence_error(
901
+ steps: tuple[CheckpointStep, ...],
902
+ ) -> str:
903
+ """Require configured Browser/Manual checks between Review and Test."""
904
+ review = _single_step(steps, "prizmkit-code-review")
905
+ test = _single_step(steps, "prizmkit-test")
906
+ if review is None or test is None:
907
+ return ""
908
+ for skill in ("browser-verification", "manual-verification"):
909
+ matches = [step for step in steps if step.skill == skill]
910
+ if len(matches) > 1:
911
+ return f"Expected at most one configured stage {skill}"
912
+ if matches and not (review.index < matches[0].index < test.index):
913
+ return f"Configured stage {skill} must run after review and before test"
914
+ return ""
915
+
916
+
750
917
  def _single_step(steps: tuple[CheckpointStep, ...], skill: str) -> CheckpointStep | None:
751
918
  matches = [step for step in steps if step.skill == skill]
752
919
  return matches[0] if len(matches) == 1 else None
753
920
 
754
921
 
755
-
756
922
  def _required_step_artifacts_valid(
757
923
  step: CheckpointStep,
758
924
  checkpoint_path: Path | None,
759
925
  project_root: Path | None,
926
+ *,
927
+ allow_legacy_test: bool = False,
760
928
  ) -> bool:
761
929
  """Validate declared artifacts, including semantic test terminal output."""
762
930
  if step.skill == "prizmkit-test":
@@ -769,11 +937,38 @@ def _required_step_artifacts_valid(
769
937
  result_path = (root / results[0]).resolve()
770
938
  if not _is_within_root(report_path, root) or not _is_within_root(result_path, root):
771
939
  return False
772
- return valid_test_artifact_pair(
940
+ validator = (
941
+ valid_legacy_test_artifact_pair
942
+ if allow_legacy_test
943
+ else valid_test_artifact_pair
944
+ )
945
+ return validator(
773
946
  report_path,
774
947
  result_path,
775
948
  required_result="TEST_PASS",
776
949
  )
950
+ if step.skill == "prizmkit-code-review":
951
+ return _valid_review_artifact(step, checkpoint_path, project_root)
952
+ if step.skill == "prizmkit-retrospective":
953
+ return _valid_retrospective_artifact(step, checkpoint_path, project_root)
954
+ if (
955
+ step.skill == "prizmkit-committer"
956
+ and step.status == "completed"
957
+ and step.stage_result == "COMMITTED"
958
+ ):
959
+ requests = tuple(
960
+ artifact
961
+ for artifact in step.required_artifacts
962
+ if artifact.endswith("/runtime-commit-request.json")
963
+ )
964
+ if not step.required_artifacts:
965
+ return True
966
+ if len(requests) != 1:
967
+ return False
968
+ durable = tuple(
969
+ artifact for artifact in step.required_artifacts if artifact not in requests
970
+ )
971
+ return _required_artifact_paths_exist(durable, checkpoint_path, project_root)
777
972
  return _required_artifacts_exist(step, checkpoint_path, project_root)
778
973
 
779
974
 
@@ -782,23 +977,154 @@ def _required_artifacts_exist(
782
977
  checkpoint_path: Path | None,
783
978
  project_root: Path | None,
784
979
  ) -> bool:
785
- if not step.required_artifacts:
980
+ return _required_artifact_paths_exist(
981
+ step.required_artifacts,
982
+ checkpoint_path,
983
+ project_root,
984
+ )
985
+
986
+
987
+ def _required_artifact_paths_exist(
988
+ artifacts: tuple[str, ...],
989
+ checkpoint_path: Path | None,
990
+ project_root: Path | None,
991
+ ) -> bool:
992
+ if not artifacts:
786
993
  return True
787
994
  import glob
788
995
 
789
996
  root = (project_root or _infer_project_root(checkpoint_path)).resolve()
790
- for artifact in step.required_artifacts:
997
+ for artifact in artifacts:
791
998
  candidate = (root / artifact).resolve()
792
999
  if not _is_within_root(candidate, root):
793
1000
  return False
794
1001
  if glob.has_magic(str(candidate)):
795
1002
  if not glob.glob(str(candidate)):
796
1003
  return False
797
- elif not candidate.exists():
798
- return False
1004
+ else:
1005
+ try:
1006
+ if not candidate.exists():
1007
+ return False
1008
+ if candidate.is_file() and candidate.stat().st_size == 0:
1009
+ return False
1010
+ except OSError:
1011
+ return False
799
1012
  return True
800
1013
 
801
1014
 
1015
+ def _valid_review_artifact(
1016
+ step: CheckpointStep,
1017
+ checkpoint_path: Path | None,
1018
+ project_root: Path | None,
1019
+ ) -> bool:
1020
+ reports = [item for item in step.required_artifacts if item.endswith("/review-report.md")]
1021
+ if not reports:
1022
+ return _required_artifacts_exist(step, checkpoint_path, project_root)
1023
+ if len(reports) != 1 or not _required_artifacts_exist(step, checkpoint_path, project_root):
1024
+ return False
1025
+ root = (project_root or _infer_project_root(checkpoint_path)).resolve()
1026
+ report = (root / reports[0]).resolve()
1027
+ if not _is_within_root(report, root):
1028
+ return False
1029
+ try:
1030
+ text = report.read_text(encoding="utf-8")
1031
+ except (OSError, UnicodeError):
1032
+ return False
1033
+ final_sections = re.findall(
1034
+ r"(?ms)^## Final Result\s*$\n(?P<body>.*?)(?=^## |\Z)",
1035
+ text,
1036
+ )
1037
+ if len(final_sections) != 1:
1038
+ return False
1039
+ verdicts = re.findall(r"(?m)^- Verdict:\s*(PASS|NEEDS_FIXES)\s*$", final_sections[0])
1040
+ return verdicts == ["PASS"]
1041
+
1042
+
1043
+ def _valid_retrospective_artifact(
1044
+ step: CheckpointStep,
1045
+ checkpoint_path: Path | None,
1046
+ project_root: Path | None,
1047
+ ) -> bool:
1048
+ results = [
1049
+ item for item in step.required_artifacts
1050
+ if item.endswith("/retrospective-result.json")
1051
+ ]
1052
+ if not results:
1053
+ return _required_artifacts_exist(step, checkpoint_path, project_root)
1054
+ if len(results) != 1 or not _required_artifacts_exist(step, checkpoint_path, project_root):
1055
+ return False
1056
+ root = (project_root or _infer_project_root(checkpoint_path)).resolve()
1057
+ result_path = (root / results[0]).resolve()
1058
+ if not _is_within_root(result_path, root):
1059
+ return False
1060
+ try:
1061
+ value = json.loads(result_path.read_text(encoding="utf-8"))
1062
+ except (OSError, UnicodeError, json.JSONDecodeError):
1063
+ return False
1064
+ expected_keys = {
1065
+ "schema_version",
1066
+ "outcome",
1067
+ "result",
1068
+ "reason",
1069
+ "change_paths",
1070
+ "documentation_paths",
1071
+ "validation",
1072
+ }
1073
+ return bool(
1074
+ isinstance(value, dict)
1075
+ and set(value) == expected_keys
1076
+ and value.get("schema_version") == 1
1077
+ and value.get("outcome") == "RETRO_COMPLETE"
1078
+ and value.get("result") in {"DOCS_UPDATED", "NO_DOC_CHANGE"}
1079
+ and isinstance(value.get("reason"), str)
1080
+ and value["reason"].strip()
1081
+ and isinstance(value.get("change_paths"), list)
1082
+ and all(_valid_retrospective_change_path(path) for path in value["change_paths"])
1083
+ and isinstance(value.get("documentation_paths"), list)
1084
+ and all(
1085
+ _valid_retrospective_documentation_path(path)
1086
+ for path in value["documentation_paths"]
1087
+ )
1088
+ and isinstance(value.get("validation"), list)
1089
+ and all(isinstance(item, str) and item.strip() for item in value["validation"])
1090
+ and (
1091
+ value.get("result") != "DOCS_UPDATED"
1092
+ or (
1093
+ bool(value["documentation_paths"])
1094
+ and bool(value["validation"])
1095
+ )
1096
+ )
1097
+ )
1098
+
1099
+
1100
+ def _valid_retrospective_documentation_path(value: object) -> bool:
1101
+ if not isinstance(value, str) or not value.strip() or "\\" in value:
1102
+ return False
1103
+ raw_parts = value.split("/")
1104
+ if any(part in {"", ".", ".."} for part in raw_parts):
1105
+ return False
1106
+ path = PurePosixPath(value)
1107
+ return bool(
1108
+ not path.is_absolute()
1109
+ and len(raw_parts) >= 3
1110
+ and raw_parts[:2] == [".prizmkit", "prizm-docs"]
1111
+ and path.suffix == ".prizm"
1112
+ )
1113
+
1114
+
1115
+ def _valid_retrospective_change_path(value: object) -> bool:
1116
+ if not isinstance(value, str) or not value.strip() or "\\" in value:
1117
+ return False
1118
+ raw_parts = value.split("/")
1119
+ if any(part in {"", ".", ".."} for part in raw_parts):
1120
+ return False
1121
+ path = PurePosixPath(value)
1122
+ return bool(
1123
+ not path.is_absolute()
1124
+ and raw_parts[0] != ".prizmkit"
1125
+ )
1126
+
1127
+
802
1128
  def _is_within_root(path: Path, root: Path) -> bool:
803
1129
  try:
804
1130
  path.relative_to(root)
@@ -24,7 +24,6 @@ _LEAF_OPTIONS_WITH_VALUES = {
24
24
  "-n",
25
25
  "--max-retries",
26
26
  "--max-infra-retries",
27
- "--mode",
28
27
  "--resume-phase",
29
28
  }
30
29
 
@@ -55,7 +54,6 @@ def _add_runner_arguments(parser: argparse.ArgumentParser, family: str) -> None:
55
54
  parser.add_argument("--features", help="Feature ID, range, or comma-separated selection.")
56
55
  parser.add_argument("--max-retries", type=_non_negative_int)
57
56
  parser.add_argument("--max-infra-retries", type=_non_negative_int)
58
- parser.add_argument("--mode")
59
57
  parser.add_argument("--resume-phase")
60
58
  parser.add_argument("--dry-run", action="store_true")
61
59
  parser.add_argument("--clean", action="store_true")
@@ -112,7 +110,6 @@ def _add_reset_subparsers(
112
110
  def _add_daemon_start_arguments(parser: argparse.ArgumentParser, family: str) -> None:
113
111
  parser.add_argument("list_path", nargs="?", help="Optional family plan path.")
114
112
  parser.add_argument("--env", help="Quoted KEY=VALUE daemon environment overrides.")
115
- parser.add_argument("--mode", choices=("lite", "standard", "full"))
116
113
  if family == "feature":
117
114
  parser.add_argument("--features", help="Feature ID, range, or comma-separated selection.")
118
115
 
@@ -37,7 +37,6 @@ class DaemonStartOptions:
37
37
 
38
38
  list_path: Path
39
39
  env_overrides: str = ""
40
- mode: str = ""
41
40
  features_filter: str = ""
42
41
  help_requested: bool = False
43
42
 
@@ -242,7 +241,6 @@ def _logs(family: RunnerFamily, daemon_paths: DaemonPaths, legacy_args: tuple[st
242
241
  def _parse_start_args(family: RunnerFamily, legacy_args: tuple[str, ...]) -> DaemonStartOptions:
243
242
  list_path = family.plan_path
244
243
  env_overrides = ""
245
- mode = ""
246
244
  features_filter = ""
247
245
  help_requested = False
248
246
  positional: list[str] = []
@@ -254,11 +252,6 @@ def _parse_start_args(family: RunnerFamily, legacy_args: tuple[str, ...]) -> Dae
254
252
  elif arg == "--env" and index + 1 < len(legacy_args):
255
253
  index += 1
256
254
  env_overrides = legacy_args[index]
257
- elif arg == "--mode" and index + 1 < len(legacy_args):
258
- index += 1
259
- mode = legacy_args[index]
260
- if mode not in {"lite", "standard", "full"}:
261
- raise ValueError(f"Invalid mode: {mode} (must be lite, standard, or full)")
262
255
  elif arg == "--features" and index + 1 < len(legacy_args):
263
256
  index += 1
264
257
  features_filter = legacy_args[index]
@@ -269,7 +262,7 @@ def _parse_start_args(family: RunnerFamily, legacy_args: tuple[str, ...]) -> Dae
269
262
  index += 1
270
263
  if positional:
271
264
  list_path = Path(positional[0]).expanduser()
272
- return DaemonStartOptions(list_path=list_path, env_overrides=env_overrides, mode=mode, features_filter=features_filter, help_requested=help_requested)
265
+ return DaemonStartOptions(list_path=list_path, env_overrides=env_overrides, features_filter=features_filter, help_requested=help_requested)
273
266
 
274
267
 
275
268
  def _parse_log_args(legacy_args: tuple[str, ...]) -> DaemonLogOptions:
@@ -304,8 +297,6 @@ def _payload_command(family: RunnerFamily, paths, list_path: Path, options: Daem
304
297
  ]
305
298
  if family.kind == "feature" and options.features_filter:
306
299
  command.extend(["--features", options.features_filter])
307
- if options.mode:
308
- command.extend(["--mode", options.mode])
309
300
  return command
310
301
 
311
302
 
@@ -316,8 +307,6 @@ def _daemon_env(options: DaemonStartOptions) -> dict[str, str]:
316
307
  key, value = token.split("=", 1)
317
308
  if key:
318
309
  env[key] = value
319
- if options.mode:
320
- env["PIPELINE_MODE"] = options.mode
321
310
  return env
322
311
 
323
312
 
@@ -348,7 +337,6 @@ def _write_meta(family: RunnerFamily, path: Path, pid: int, started_at: str, lis
348
337
  "started_at": started_at,
349
338
  LIST_META_KEY_BY_KIND[family.kind]: str(list_path),
350
339
  "env_overrides": options.env_overrides,
351
- "mode": options.mode,
352
340
  "log_file": str(log_file),
353
341
  }
354
342
  tmp = path.with_suffix(path.suffix + ".tmp")
@@ -405,8 +393,6 @@ def _write_log_separator(family: RunnerFamily, log_file: Path, list_path: Path,
405
393
  ]
406
394
  if options.features_filter:
407
395
  lines.append(f" Features filter: {options.features_filter}")
408
- if options.mode:
409
- lines.append(f" Mode: {options.mode}")
410
396
  if options.env_overrides:
411
397
  lines.append(f" Environment: {options.env_overrides}")
412
398
  lines.extend(["================================================================", ""])