prizmkit 1.1.153 → 1.1.155

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 (178) 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 +120 -113
  6. package/bundled/dev-pipeline/assets/skill-subagent-integration.md +1 -1
  7. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +355 -15
  8. package/bundled/dev-pipeline/prizmkit_runtime/cli.py +189 -110
  9. package/bundled/dev-pipeline/prizmkit_runtime/commands.py +146 -111
  10. package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +4 -20
  11. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +48 -14
  12. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +0 -1
  13. package/bundled/dev-pipeline/prizmkit_runtime/paths.py +0 -3
  14. package/bundled/dev-pipeline/prizmkit_runtime/reset.py +122 -15
  15. package/bundled/dev-pipeline/prizmkit_runtime/reset_preserve.py +1 -1
  16. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +50 -2
  17. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +1 -10
  18. package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +3 -3
  19. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +139 -74
  20. package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +482 -0
  21. package/bundled/dev-pipeline/prizmkit_runtime/status.py +106 -1
  22. package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +64 -11
  23. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +145 -367
  24. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +91 -62
  25. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +105 -69
  26. package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +2 -2
  27. package/bundled/dev-pipeline/scripts/prompt_framework.py +36 -41
  28. package/bundled/dev-pipeline/scripts/update-bug-status.py +197 -71
  29. package/bundled/dev-pipeline/scripts/update-checkpoint.py +77 -16
  30. package/bundled/dev-pipeline/scripts/update-feature-status.py +46 -121
  31. package/bundled/dev-pipeline/scripts/update-refactor-status.py +46 -123
  32. package/bundled/dev-pipeline/scripts/utils.py +119 -0
  33. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +22 -27
  34. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +71 -104
  35. package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +3 -2
  36. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +59 -111
  37. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +37 -72
  38. package/bundled/dev-pipeline/templates/sections/artifact-manifest.md +39 -0
  39. package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +3 -1
  40. package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +8 -25
  41. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +3 -3
  42. package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +2 -2
  43. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
  44. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +2 -2
  45. package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +4 -4
  46. package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +0 -1
  47. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +7 -4
  48. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +14 -27
  49. package/bundled/dev-pipeline/templates/sections/feature-completion-summary.md +20 -0
  50. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -71
  51. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -7
  52. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +3 -3
  53. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
  54. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +5 -17
  55. package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +3 -1
  56. package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +10 -23
  57. package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +2 -2
  58. package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +2 -2
  59. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
  60. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +2 -2
  61. package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +7 -7
  62. package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +0 -1
  63. package/bundled/dev-pipeline/templates/sections/retrospective.md +12 -0
  64. package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +35 -0
  65. package/bundled/dev-pipeline/templates/session-status-schema.json +45 -62
  66. package/bundled/dev-pipeline/tests/test_auto_skip.py +111 -32
  67. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +617 -30
  68. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +262 -194
  69. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +62 -23
  70. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +76 -28
  71. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +281 -206
  72. package/bundled/dev-pipeline/tests/test_recovery_workflow.py +322 -0
  73. package/bundled/dev-pipeline/tests/test_reset_modes.py +935 -0
  74. package/bundled/dev-pipeline/tests/test_reset_preserve.py +5 -7
  75. package/bundled/dev-pipeline/tests/test_runtime_commit.py +608 -0
  76. package/bundled/dev-pipeline/tests/test_unified_cli.py +617 -188
  77. package/bundled/skills/_metadata.json +9 -9
  78. package/bundled/skills/app-planner/SKILL.md +3 -3
  79. package/bundled/skills/app-planner/references/generated-plan-review.md +2 -1
  80. package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +56 -64
  81. package/bundled/skills/app-planner/references/rules/backend/fixed-rules.md +85 -240
  82. package/bundled/skills/app-planner/references/rules/backend/question-bank.md +25 -25
  83. package/bundled/skills/app-planner/references/rules/backend/question-manifest.json +16 -7
  84. package/bundled/skills/app-planner/references/rules/backend/template.md +74 -92
  85. package/bundled/skills/app-planner/references/rules/database/derivation-rules.md +93 -93
  86. package/bundled/skills/app-planner/references/rules/database/fixed-rules.md +66 -173
  87. package/bundled/skills/app-planner/references/rules/database/question-bank.md +31 -26
  88. package/bundled/skills/app-planner/references/rules/database/question-manifest.json +13 -4
  89. package/bundled/skills/app-planner/references/rules/database/template.md +76 -98
  90. package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +68 -95
  91. package/bundled/skills/app-planner/references/rules/frontend/fixed-rules.md +93 -166
  92. package/bundled/skills/app-planner/references/rules/frontend/question-bank.md +46 -36
  93. package/bundled/skills/app-planner/references/rules/frontend/question-manifest.json +11 -2
  94. package/bundled/skills/app-planner/references/rules/frontend/template.md +79 -257
  95. package/bundled/skills/app-planner/references/rules/mobile/derivation-rules.md +91 -99
  96. package/bundled/skills/app-planner/references/rules/mobile/fixed-rules.md +73 -246
  97. package/bundled/skills/app-planner/references/rules/mobile/question-bank.md +17 -17
  98. package/bundled/skills/app-planner/references/rules/mobile/question-manifest.json +17 -2
  99. package/bundled/skills/app-planner/references/rules/mobile/template.md +73 -85
  100. package/bundled/skills/app-planner/references/rules-configuration.md +133 -65
  101. package/bundled/skills/app-planner/scripts/validate-rules-configuration.py +291 -0
  102. package/bundled/skills/bug-planner/references/schema-validation.md +1 -1
  103. package/bundled/skills/bug-planner/scripts/validate-bug-list.py +1 -1
  104. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +21 -13
  105. package/bundled/skills/feature-pipeline-launcher/SKILL.md +22 -14
  106. package/bundled/skills/prizmkit/SKILL.md +17 -18
  107. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +22 -18
  108. package/bundled/skills/prizmkit-code-review/SKILL.md +22 -58
  109. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +6 -6
  110. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +2 -2
  111. package/bundled/skills/prizmkit-committer/SKILL.md +82 -132
  112. package/bundled/skills/prizmkit-deploy/SKILL.md +27 -11
  113. package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +89 -0
  114. package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +44 -14
  115. package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +19 -3
  116. package/bundled/skills/prizmkit-deploy/references/database-setup.md +22 -12
  117. package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +3 -1
  118. package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +22 -6
  119. package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +49 -0
  120. package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +1 -1
  121. package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +164 -0
  122. package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +9 -8
  123. package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +6 -6
  124. package/bundled/skills/prizmkit-implement/SKILL.md +30 -66
  125. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +4 -4
  126. package/bundled/skills/prizmkit-init/SKILL.md +4 -0
  127. package/bundled/skills/prizmkit-plan/SKILL.md +21 -64
  128. package/bundled/skills/prizmkit-plan/references/artifact-identity.md +38 -0
  129. package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +4 -4
  130. package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +5 -5
  131. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +11 -1
  132. package/bundled/skills/prizmkit-retrospective/SKILL.md +63 -77
  133. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -3
  134. package/bundled/skills/prizmkit-retrospective/references/retrospective-result-schema.json +138 -0
  135. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +17 -23
  136. package/bundled/skills/prizmkit-test/SKILL.md +64 -51
  137. package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +206 -0
  138. package/bundled/skills/prizmkit-test/references/independent-test-review.md +17 -9
  139. package/bundled/skills/prizmkit-test/references/test-coverage-model.md +45 -21
  140. package/bundled/skills/prizmkit-test/references/test-report-template.md +20 -13
  141. package/bundled/skills/prizmkit-workflow/SKILL.md +35 -37
  142. package/bundled/skills/prizmkit-workflow/references/artifact-identity.md +34 -0
  143. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +22 -18
  144. package/bundled/skills/recovery-workflow/SKILL.md +9 -6
  145. package/bundled/skills/recovery-workflow/evals/evals.json +2 -2
  146. package/bundled/skills/recovery-workflow/references/detection.md +7 -6
  147. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +16 -6
  148. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +21 -13
  149. package/bundled/templates/project-memory-template.md +19 -11
  150. package/package.json +1 -1
  151. package/src/scaffold.js +33 -22
  152. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +0 -228
  153. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +0 -767
  154. package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +0 -16
  155. package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +0 -31
  156. package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -7
  157. package/bundled/dev-pipeline/templates/sections/critical-paths-lite.md +0 -7
  158. package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +0 -9
  159. package/bundled/dev-pipeline/templates/sections/directory-convention-lite.md +0 -6
  160. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +0 -15
  161. package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -61
  162. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -24
  163. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -12
  164. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -44
  165. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -25
  166. package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +0 -20
  167. package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +0 -20
  168. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -20
  169. package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +0 -17
  170. package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +0 -28
  171. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +0 -52
  172. package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +0 -52
  173. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +0 -174
  174. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +0 -174
  175. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +0 -174
  176. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +0 -174
  177. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +0 -174
  178. package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +0 -119
@@ -1,4 +1,4 @@
1
- """Command contracts for the canonical PrizmKit Python runtime."""
1
+ """Executing command registry for the canonical operation-first runtime."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -11,18 +11,19 @@ from .paths import RuntimePaths, resolve_runtime_paths
11
11
  RUNTIME_STATUS = "python-runtime-canonical"
12
12
  CANONICAL_RUNTIME_NOTE = "The Python CLI and prizmkit_runtime modules are the canonical runtime."
13
13
  PYTHON_CLI = "dev-pipeline/cli.py"
14
+ _FAMILIES = ("feature", "bugfix", "refactor")
14
15
 
15
16
 
16
17
  @dataclass(frozen=True)
17
18
  class RuntimeCommand:
18
- """Unified CLI command mapped to the Python behavior provider."""
19
+ """One public CLI command mapped to an executing or diagnostic handler."""
19
20
 
20
21
  group: str
21
22
  action: str
22
23
  target: str | None
23
- current_entrypoint: str | None
24
- entrypoint_kind: str | None
25
- entrypoint_name: str | None
24
+ current_entrypoint: str
25
+ entrypoint_kind: str = "python-cli"
26
+ entrypoint_name: str = ""
26
27
  transition_status: str = RUNTIME_STATUS
27
28
  notes: tuple[str, ...] = (CANONICAL_RUNTIME_NOTE,)
28
29
 
@@ -54,12 +55,16 @@ class CommandResult:
54
55
 
55
56
  def _runtime_invocation(group: str, action: str, target: str | None = None) -> str:
56
57
  if group == "daemon" and target is not None:
57
- return f"python3 {PYTHON_CLI} daemon {target} {action}"
58
- if group == "reset" and target is not None and action == "reset":
59
- return f"python3 {PYTHON_CLI} reset {target}"
60
- if group == "status" and target is not None:
61
- return f"python3 {PYTHON_CLI} status {target}"
62
- return f"python3 {PYTHON_CLI} {group} {action}"
58
+ command = f"daemon {target} {action}"
59
+ elif group in {"run", "status", "reset"} and target is not None:
60
+ command = f"{group} {target}"
61
+ elif group == "diagnostics":
62
+ command = f"diagnostics {action}"
63
+ elif group == "helper":
64
+ command = "helper"
65
+ else:
66
+ command = f"{group} {action}"
67
+ return f"python3 {PYTHON_CLI} {command}"
63
68
 
64
69
 
65
70
  def _command(
@@ -74,74 +79,24 @@ def _command(
74
79
  target=target,
75
80
  action=action,
76
81
  current_entrypoint=_runtime_invocation(group, action, target),
77
- entrypoint_kind="python-cli",
78
82
  entrypoint_name=group if target is None else f"{group}:{target}",
79
83
  notes=notes,
80
84
  )
81
85
 
82
86
 
83
- def _pipeline_commands() -> tuple[RuntimeCommand, ...]:
84
- commands: list[RuntimeCommand] = []
85
- action_matrix = {
86
- "feature": ("run", "status", "reset", "unskip", "test-cli", "describe"),
87
- "bugfix": ("run", "status", "reset", "unskip", "describe"),
88
- "refactor": ("run", "status", "reset", "unskip", "describe"),
89
- "recovery": ("run", "detect", "describe"),
90
- }
91
- for group, actions in action_matrix.items():
92
- for action in actions:
93
- commands.append(
94
- _command(
95
- group,
96
- action,
97
- notes=(
98
- CANONICAL_RUNTIME_NOTE,
99
- "Foreground and recovery behavior is implemented in Python runtime modules.",
100
- ),
101
- )
102
- )
103
- return tuple(commands)
104
-
105
-
106
- def _reset_commands() -> tuple[RuntimeCommand, ...]:
107
- commands = [
87
+ def _run_commands() -> tuple[RuntimeCommand, ...]:
88
+ return tuple(
108
89
  _command(
109
- "reset",
110
- "reset",
111
- target=target,
90
+ "run",
91
+ "run",
92
+ target=family,
112
93
  notes=(
113
94
  CANONICAL_RUNTIME_NOTE,
114
- "Reset actions are explicit manual operations and are not part of automatic continuation recovery.",
95
+ "Foreground pipeline behavior is implemented in Python runtime modules.",
115
96
  ),
116
97
  )
117
- for target in ("feature", "bugfix", "refactor")
118
- ]
119
- commands.append(
120
- _command(
121
- "reset",
122
- "describe",
123
- notes=(CANONICAL_RUNTIME_NOTE, "Lists destructive Python reset commands without running them."),
124
- )
98
+ for family in _FAMILIES
125
99
  )
126
- return tuple(commands)
127
-
128
-
129
- def _daemon_commands() -> tuple[RuntimeCommand, ...]:
130
- commands: list[RuntimeCommand] = []
131
- for target in ("feature", "bugfix", "refactor"):
132
- for action in ("start", "stop", "status", "logs", "restart", "describe"):
133
- commands.append(
134
- _command(
135
- "daemon",
136
- action,
137
- target=target,
138
- notes=(
139
- CANONICAL_RUNTIME_NOTE,
140
- "Daemon lifecycle behavior is implemented by the Python daemon runtime.",
141
- ),
142
- )
143
- )
144
- return tuple(commands)
145
100
 
146
101
 
147
102
  def _status_commands() -> tuple[RuntimeCommand, ...]:
@@ -149,44 +104,80 @@ def _status_commands() -> tuple[RuntimeCommand, ...]:
149
104
  _command(
150
105
  "status",
151
106
  "status",
152
- target=target,
153
- notes=(CANONICAL_RUNTIME_NOTE, "Status is resolved by the Python foreground runtime."),
107
+ target=family,
108
+ notes=(CANONICAL_RUNTIME_NOTE, "Status is resolved by the canonical family status handler."),
154
109
  )
155
- for target in ("feature", "bugfix", "refactor")
110
+ for family in _FAMILIES
156
111
  ]
157
112
  commands.append(
158
113
  _command(
159
114
  "status",
160
115
  "status",
161
116
  target="all",
162
- notes=(CANONICAL_RUNTIME_NOTE, "Aggregated status is represented at the Python CLI boundary."),
117
+ notes=(CANONICAL_RUNTIME_NOTE, "Aggregates configured family status handlers without mutation."),
163
118
  )
164
119
  )
165
120
  return tuple(commands)
166
121
 
167
122
 
123
+ def _reset_commands() -> tuple[RuntimeCommand, ...]:
124
+ return tuple(
125
+ _command(
126
+ "reset",
127
+ "reset",
128
+ target=family,
129
+ notes=(
130
+ CANONICAL_RUNTIME_NOTE,
131
+ "Reset requires one explicit mode and never starts execution.",
132
+ ),
133
+ )
134
+ for family in _FAMILIES
135
+ )
136
+
137
+
138
+ def _daemon_commands() -> tuple[RuntimeCommand, ...]:
139
+ return tuple(
140
+ _command(
141
+ "daemon",
142
+ action,
143
+ target=family,
144
+ notes=(
145
+ CANONICAL_RUNTIME_NOTE,
146
+ "Daemon lifecycle behavior is implemented by the Python daemon runtime.",
147
+ ),
148
+ )
149
+ for family in _FAMILIES
150
+ for action in ("start", "stop", "status", "logs", "restart")
151
+ )
152
+
153
+
168
154
  def _diagnostic_commands() -> tuple[RuntimeCommand, ...]:
169
155
  return tuple(
170
156
  _command(
171
157
  "diagnostics",
172
158
  action,
173
- notes=(CANONICAL_RUNTIME_NOTE, "Diagnostic commands are implemented in the Python runtime."),
159
+ notes=(CANONICAL_RUNTIME_NOTE, "Diagnostic behavior is implemented in the Python runtime."),
174
160
  )
175
- for action in ("paths", "entrypoints", "version")
161
+ for action in ("paths", "entrypoints", "version", "ai-cli")
176
162
  )
177
163
 
178
164
 
179
165
  COMMAND_REGISTRY: tuple[RuntimeCommand, ...] = (
180
- *_pipeline_commands(),
166
+ *_run_commands(),
167
+ *_status_commands(),
181
168
  *_reset_commands(),
182
169
  *_daemon_commands(),
183
- *_status_commands(),
184
170
  *_diagnostic_commands(),
171
+ _command(
172
+ "helper",
173
+ "run",
174
+ notes=(CANONICAL_RUNTIME_NOTE, "Low-level helper behavior is implemented by runtime_helper.py."),
175
+ ),
185
176
  )
186
177
 
187
178
 
188
179
  def list_runtime_commands() -> tuple[RuntimeCommand, ...]:
189
- """Return the unified CLI command inventory."""
180
+ """Return the public executing/diagnostic command inventory."""
190
181
  return COMMAND_REGISTRY
191
182
 
192
183
 
@@ -199,23 +190,6 @@ def find_runtime_command(group: str, action: str, target: str | None = None) ->
199
190
  raise KeyError(f"Unknown runtime command: group={group}{target_text} action={action}")
200
191
 
201
192
 
202
- def describe_command(command: RuntimeCommand, paths: RuntimePaths | None = None) -> CommandResult:
203
- """Describe a Python runtime command contract without running behavior."""
204
- _ = paths or resolve_runtime_paths()
205
- details = [
206
- f"command: {command.group} {command.target or ''} {command.action}".replace(" ", " ").strip(),
207
- f"runtime status: {command.transition_status}",
208
- f"current entrypoint: {command.current_entrypoint or PYTHON_CLI}",
209
- ]
210
- details.extend(f"note: {note}" for note in command.notes)
211
- return CommandResult(
212
- exit_code=0,
213
- title="Python runtime command contract",
214
- message="This command is available in the canonical PrizmKit Python runtime.",
215
- details=tuple(details),
216
- )
217
-
218
-
219
193
  def handle_runtime_command(
220
194
  group: str,
221
195
  action: str,
@@ -224,33 +198,60 @@ def handle_runtime_command(
224
198
  paths: RuntimePaths | None = None,
225
199
  legacy_args: tuple[str, ...] = (),
226
200
  ) -> CommandResult:
227
- """Handle runtime commands, dispatching ported Python runtime behavior where available."""
201
+ """Dispatch one registered public runtime command to real behavior."""
202
+ find_runtime_command(group, action, target)
228
203
  runtime_paths = paths or resolve_runtime_paths()
229
- if group in {"feature", "bugfix", "refactor"} and action in {"run", "status", "unskip", "test-cli"}:
204
+
205
+ if group == "run" and target in _FAMILIES and action == "run":
230
206
  from .runners import run_pipeline_command
231
207
 
232
- return run_pipeline_command(group, action, legacy_args, runtime_paths)
233
- if group == "status" and target in {"feature", "bugfix", "refactor"} and action == "status":
208
+ return run_pipeline_command(target, "run", legacy_args, runtime_paths)
209
+ if group == "status" and target in _FAMILIES and action == "status":
234
210
  from .runners import run_pipeline_command
235
211
 
236
- return run_pipeline_command(target, action, legacy_args, runtime_paths)
237
- if group == "recovery" and action in {"run", "detect"}:
238
- from .runners import run_recovery_command
212
+ return run_pipeline_command(target, "status", legacy_args, runtime_paths)
213
+ if group == "status" and target == "all" and action == "status":
214
+ if legacy_args:
215
+ return CommandResult(
216
+ 2,
217
+ "Python aggregate status",
218
+ "status all does not accept a plan path or family-specific arguments",
219
+ )
220
+ from .runners import run_pipeline_command
221
+ from .status import aggregate_family_status
239
222
 
240
- return run_recovery_command(action, legacy_args, runtime_paths)
241
- if group == "reset" and target in {"feature", "bugfix", "refactor"} and action == "reset":
223
+ aggregate = aggregate_family_status(
224
+ runtime_paths,
225
+ lambda family: run_pipeline_command(family, "status", (), runtime_paths),
226
+ )
227
+ return CommandResult(
228
+ aggregate.exit_code,
229
+ "Python aggregate status",
230
+ "Read-only status for all pipeline families.",
231
+ stdout=aggregate.output,
232
+ )
233
+ if group == "reset" and target in _FAMILIES and action == "reset":
242
234
  from .reset import run_reset_command
243
235
 
244
236
  return run_reset_command(target, legacy_args, runtime_paths)
245
- if group == "daemon" and target in {"feature", "bugfix", "refactor"} and action in {"start", "stop", "status", "logs", "restart", "describe"}:
237
+ if group == "daemon" and target in _FAMILIES and action in {"start", "stop", "status", "logs", "restart"}:
246
238
  from .daemon import run_daemon_command
247
239
 
248
240
  try:
249
241
  return run_daemon_command(target, action, legacy_args, runtime_paths)
250
242
  except ValueError as exc:
251
243
  return CommandResult(1, f"Python {target} daemon", str(exc), stdout="", stderr=str(exc))
252
- command = find_runtime_command(group, action, target)
253
- return describe_command(command, runtime_paths)
244
+ if group == "helper" and action == "run" and target is None:
245
+ from .runtime_helper import render_result, run_helper_command
246
+
247
+ helper_result = run_helper_command(legacy_args)
248
+ return CommandResult(
249
+ helper_result.exit_code,
250
+ "Python runtime helper",
251
+ "Cross-platform prompt runtime helper result.",
252
+ stdout=render_result(helper_result, json_output="--json" in legacy_args),
253
+ )
254
+ raise RuntimeError(f"Registered command has no handler: {group} {target or ''} {action}".strip())
254
255
 
255
256
 
256
257
  def diagnostics_paths(paths: RuntimePaths | None = None) -> CommandResult:
@@ -275,20 +276,27 @@ def diagnostics_paths(paths: RuntimePaths | None = None) -> CommandResult:
275
276
 
276
277
 
277
278
  def diagnostics_entrypoints(paths: RuntimePaths | None = None) -> CommandResult:
278
- """Return an inventory of canonical Python runtime and utility entrypoints."""
279
+ """Return canonical runtime, command, and utility entrypoints."""
279
280
  runtime_paths = paths or resolve_runtime_paths()
280
281
  details: list[str] = [
281
282
  f"runtime:cli: {runtime_paths.pipeline_root / 'cli.py'}",
282
283
  f"runtime:module: {runtime_paths.pipeline_root / 'prizmkit_runtime'}",
283
284
  f"runtime:helper-module: {runtime_paths.pipeline_root / 'prizmkit_runtime' / 'runtime_helper.py'}",
284
285
  ]
286
+ details.extend(
287
+ f"command:{command.group}:{command.target or command.action}: {command.current_entrypoint}"
288
+ for command in COMMAND_REGISTRY
289
+ )
285
290
  for name, relative_path in sorted(UTILITY_ENTRYPOINTS.items()):
286
291
  invocation = describe_existing_entrypoint("utility", name, paths=runtime_paths)
287
- details.append(f"utility:{name}: {invocation.script_path if invocation else runtime_paths.pipeline_root / relative_path}")
292
+ details.append(
293
+ f"utility:{name}: "
294
+ f"{invocation.script_path if invocation else runtime_paths.pipeline_root / relative_path}"
295
+ )
288
296
  return CommandResult(
289
297
  exit_code=0,
290
298
  title="Python runtime entrypoint inventory",
291
- message="Inventory for canonical Python runtime and utility scripts.",
299
+ message="Inventory for canonical Python runtime commands and utility scripts.",
292
300
  details=tuple(details),
293
301
  )
294
302
 
@@ -303,12 +311,39 @@ def diagnostics_version() -> CommandResult:
303
311
  )
304
312
 
305
313
 
314
+ def diagnostics_ai_cli(paths: RuntimePaths | None = None) -> CommandResult:
315
+ """Report the family-neutral AI CLI resolution used by runtime commands."""
316
+ from .config import load_runtime_config
317
+
318
+ runtime_paths = paths or resolve_runtime_paths()
319
+ config = load_runtime_config(runtime_paths)
320
+ cli = config.ai_client.command or "(not found)"
321
+ details = [
322
+ f"detected_cli: {cli}",
323
+ f"platform: {config.ai_client.platform or 'unresolved'}",
324
+ f"source: {config.ai_client.source}",
325
+ f"runtime_mode: {config.runtime_mode}",
326
+ ]
327
+ if config.model:
328
+ details.append(f"model: {config.model}")
329
+ details.extend(f"note: {note}" for note in config.ai_client.notes)
330
+ return CommandResult(
331
+ 0 if config.ai_client.command else 1,
332
+ "Python runtime AI CLI diagnostics",
333
+ "AI CLI resolution for the canonical Python runtime.",
334
+ tuple(details),
335
+ )
336
+
337
+
306
338
  def handle_diagnostics(action: str, paths: RuntimePaths | None = None) -> CommandResult:
307
- """Dispatch diagnostics actions."""
339
+ """Dispatch one registered diagnostics action."""
340
+ find_runtime_command("diagnostics", action)
308
341
  if action == "paths":
309
342
  return diagnostics_paths(paths)
310
343
  if action == "entrypoints":
311
344
  return diagnostics_entrypoints(paths)
312
345
  if action == "version":
313
346
  return diagnostics_version()
314
- raise KeyError(f"Unknown diagnostics action: {action}")
347
+ if action == "ai-cli":
348
+ return diagnostics_ai_cli(paths)
349
+ raise RuntimeError(f"Registered diagnostics action has no handler: {action}")
@@ -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
 
@@ -70,7 +69,7 @@ ID_KEY_BY_KIND = {
70
69
 
71
70
  PROGRESS_COUNTS_BY_KIND = {
72
71
  "feature": ("completed", "in_progress", "failed", "pending", "skipped", "auto_skipped"),
73
- "bugfix": ("completed", "in_progress", "failed", "pending", "needs_info", "skipped"),
72
+ "bugfix": ("completed", "in_progress", "failed", "pending", "needs_info", "skipped", "auto_skipped"),
74
73
  "refactor": ("completed", "in_progress", "failed", "pending", "skipped", "auto_skipped"),
75
74
  }
76
75
 
@@ -79,8 +78,6 @@ def run_daemon_command(kind: str, action: str, legacy_args: tuple[str, ...], pat
79
78
  """Run a daemon lifecycle command for feature, bugfix, or refactor."""
80
79
  family = family_for(kind, paths)
81
80
  daemon_paths = _daemon_paths(family)
82
- if action == "describe":
83
- return CommandResult(0, f"Python {kind} daemon", _help_text(family))
84
81
  if action == "start":
85
82
  return _start(family, daemon_paths, legacy_args, paths)
86
83
  if action == "status":
@@ -244,23 +241,17 @@ def _logs(family: RunnerFamily, daemon_paths: DaemonPaths, legacy_args: tuple[st
244
241
  def _parse_start_args(family: RunnerFamily, legacy_args: tuple[str, ...]) -> DaemonStartOptions:
245
242
  list_path = family.plan_path
246
243
  env_overrides = ""
247
- mode = ""
248
244
  features_filter = ""
249
245
  help_requested = False
250
246
  positional: list[str] = []
251
247
  index = 0
252
248
  while index < len(legacy_args):
253
249
  arg = legacy_args[index]
254
- if arg in {"--help", "-h", "help"}:
250
+ if arg in {"--help", "-h"}:
255
251
  help_requested = True
256
252
  elif arg == "--env" and index + 1 < len(legacy_args):
257
253
  index += 1
258
254
  env_overrides = legacy_args[index]
259
- elif arg == "--mode" and index + 1 < len(legacy_args):
260
- index += 1
261
- mode = legacy_args[index]
262
- if mode not in {"lite", "standard", "full"}:
263
- raise ValueError(f"Invalid mode: {mode} (must be lite, standard, or full)")
264
255
  elif arg == "--features" and index + 1 < len(legacy_args):
265
256
  index += 1
266
257
  features_filter = legacy_args[index]
@@ -271,7 +262,7 @@ def _parse_start_args(family: RunnerFamily, legacy_args: tuple[str, ...]) -> Dae
271
262
  index += 1
272
263
  if positional:
273
264
  list_path = Path(positional[0]).expanduser()
274
- 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)
275
266
 
276
267
 
277
268
  def _parse_log_args(legacy_args: tuple[str, ...]) -> DaemonLogOptions:
@@ -300,14 +291,12 @@ def _payload_command(family: RunnerFamily, paths, list_path: Path, options: Daem
300
291
  str(paths.project_root),
301
292
  "--pipeline-root",
302
293
  str(paths.pipeline_root),
303
- family.kind,
304
294
  "run",
295
+ family.kind,
305
296
  str(list_path),
306
297
  ]
307
298
  if family.kind == "feature" and options.features_filter:
308
299
  command.extend(["--features", options.features_filter])
309
- if options.mode:
310
- command.extend(["--mode", options.mode])
311
300
  return command
312
301
 
313
302
 
@@ -318,8 +307,6 @@ def _daemon_env(options: DaemonStartOptions) -> dict[str, str]:
318
307
  key, value = token.split("=", 1)
319
308
  if key:
320
309
  env[key] = value
321
- if options.mode:
322
- env["PIPELINE_MODE"] = options.mode
323
310
  return env
324
311
 
325
312
 
@@ -350,7 +337,6 @@ def _write_meta(family: RunnerFamily, path: Path, pid: int, started_at: str, lis
350
337
  "started_at": started_at,
351
338
  LIST_META_KEY_BY_KIND[family.kind]: str(list_path),
352
339
  "env_overrides": options.env_overrides,
353
- "mode": options.mode,
354
340
  "log_file": str(log_file),
355
341
  }
356
342
  tmp = path.with_suffix(path.suffix + ".tmp")
@@ -407,8 +393,6 @@ def _write_log_separator(family: RunnerFamily, log_file: Path, list_path: Path,
407
393
  ]
408
394
  if options.features_filter:
409
395
  lines.append(f" Features filter: {options.features_filter}")
410
- if options.mode:
411
- lines.append(f" Mode: {options.mode}")
412
396
  if options.env_overrides:
413
397
  lines.append(f" Environment: {options.env_overrides}")
414
398
  lines.extend(["================================================================", ""])
@@ -442,7 +442,7 @@ def ensure_linked_worktree(runtime: WorktreeRuntimeContext) -> WorktreeOperation
442
442
 
443
443
 
444
444
  def merge_linked_worktree(runtime: WorktreeRuntimeContext, *, auto_push: bool = False) -> WorktreeOperationResult:
445
- """Rebase a clean linked-worktree branch and fast-forward merge it into base."""
445
+ """Fast-forward a committed linked-worktree branch, rebasing only when needed."""
446
446
  results: list[GitCommandResult] = []
447
447
  clean = run_git_command(
448
448
  runtime.worktree_path,
@@ -459,11 +459,12 @@ def merge_linked_worktree(runtime: WorktreeRuntimeContext, *, auto_push: bool =
459
459
  if clean.return_code != 0:
460
460
  return WorktreeOperationResult(False, "dirty_task_checkout", runtime, tuple(results))
461
461
 
462
- rebase = run_git_command(runtime.worktree_path, ("rebase", runtime.context.base_branch))
463
- results.append(rebase)
464
- if rebase.return_code != 0:
465
- results.append(run_git_command(runtime.worktree_path, ("rebase", "--abort")))
466
- return WorktreeOperationResult(False, "merge_conflict", runtime, tuple(results))
462
+ if not branch_fast_forward_possible(runtime.context.project_root, runtime.context):
463
+ rebase = run_git_command(runtime.worktree_path, ("rebase", runtime.context.base_branch))
464
+ results.append(rebase)
465
+ if rebase.return_code != 0:
466
+ results.append(run_git_command(runtime.worktree_path, ("rebase", "--abort")))
467
+ return WorktreeOperationResult(False, "merge_conflict", runtime, tuple(results))
467
468
 
468
469
  checkout = run_git_command(runtime.context.project_root, ("checkout", runtime.context.base_branch))
469
470
  results.append(checkout)
@@ -581,14 +582,47 @@ def branch_return_plan(context: BranchContext) -> GitCommandPlan:
581
582
  )
582
583
 
583
584
 
584
- def branch_merge_plan(context: BranchContext, *, auto_push: bool = False) -> GitCommandPlan:
585
- """Plan a merge that consumes only a clean, committed task branch."""
585
+ def branch_fast_forward_possible(project_root: Path, context: BranchContext) -> bool:
586
+ """Return whether the base branch is an ancestor of the committed task branch."""
587
+ return run_git_command(
588
+ project_root,
589
+ ("merge-base", "--is-ancestor", context.base_branch, context.working_branch),
590
+ ).return_code == 0
591
+
592
+
593
+ def branch_merge_plan(
594
+ context: BranchContext,
595
+ *,
596
+ auto_push: bool = False,
597
+ rebase: bool = True,
598
+ ) -> GitCommandPlan:
599
+ """Plan a merge that consumes only task-visible committed branch state."""
586
600
  commands = [
587
- _git("status", "--porcelain", "--untracked-files=normal", description="require a clean task checkout"),
588
- _git("rebase", context.base_branch, context.working_branch, description="rebase dev onto original"),
589
- _git("checkout", context.base_branch, description="checkout original for fast-forward"),
590
- _git("merge", "--ff-only", context.working_branch, description="fast-forward original"),
601
+ _git(
602
+ "status",
603
+ "--porcelain",
604
+ "--untracked-files=normal",
605
+ "--",
606
+ ".",
607
+ *HIDDEN_TOOL_WORKTREE_EXCLUDES,
608
+ description="require a clean task checkout",
609
+ ),
591
610
  ]
611
+ if rebase:
612
+ commands.append(
613
+ _git(
614
+ "rebase",
615
+ context.base_branch,
616
+ context.working_branch,
617
+ description="rebase dev onto original",
618
+ )
619
+ )
620
+ commands.extend(
621
+ [
622
+ _git("checkout", context.base_branch, description="checkout original for fast-forward"),
623
+ _git("merge", "--ff-only", context.working_branch, description="fast-forward original"),
624
+ ]
625
+ )
592
626
  if auto_push:
593
627
  commands.append(_git("push", allow_failure=True, description="optional auto-push"))
594
628
  commands.append(_git("branch", "-d", context.working_branch, allow_failure=True, description="delete merged dev branch"))
@@ -596,8 +630,8 @@ def branch_merge_plan(context: BranchContext, *, auto_push: bool = False) -> Git
596
630
  name="branch_merge",
597
631
  commands=tuple(commands),
598
632
  notes=(
599
- "The task checkout must be clean because committed branch state is the only merge input.",
600
- "Rebase then fast-forward preserves merge history without pipeline-owned stashes.",
633
+ "Task-visible checkout state must be clean; recognized internal/support dirt remains outside merge input.",
634
+ "Rebase only when base ancestry requires it, then fast-forward without pipeline-owned stashes.",
601
635
  ),
602
636
  )
603
637
 
@@ -16,7 +16,6 @@ UTILITY_ENTRYPOINTS = {
16
16
  "detect-stuck": "scripts/detect-stuck.py",
17
17
  "generate-bootstrap-prompt": "scripts/generate-bootstrap-prompt.py",
18
18
  "generate-bugfix-prompt": "scripts/generate-bugfix-prompt.py",
19
- "generate-recovery-prompt": "scripts/generate-recovery-prompt.py",
20
19
  "generate-refactor-prompt": "scripts/generate-refactor-prompt.py",
21
20
  "init-bugfix-pipeline": "scripts/init-bugfix-pipeline.py",
22
21
  "init-change-artifact": "scripts/init-change-artifact.py",
@@ -23,7 +23,6 @@ class RuntimePaths:
23
23
  feature_state_dir: Path
24
24
  bugfix_state_dir: Path
25
25
  refactor_state_dir: Path
26
- recovery_state_dir: Path
27
26
  layout: str
28
27
 
29
28
  def path_values(self) -> tuple[Path, ...]:
@@ -42,7 +41,6 @@ class RuntimePaths:
42
41
  self.feature_state_dir,
43
42
  self.bugfix_state_dir,
44
43
  self.refactor_state_dir,
45
- self.recovery_state_dir,
46
44
  )
47
45
 
48
46
 
@@ -105,6 +103,5 @@ def resolve_runtime_paths(
105
103
  feature_state_dir=state_dir / "features",
106
104
  bugfix_state_dir=state_dir / "bugfix",
107
105
  refactor_state_dir=state_dir / "refactor",
108
- recovery_state_dir=state_dir / "recovery",
109
106
  layout=layout,
110
107
  )