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
- """Python foreground runner dispatch for feature, bugfix, refactor, and recovery."""
1
+ """Python foreground runner dispatch for feature, bugfix, and refactor pipelines."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -6,6 +6,7 @@ import json
6
6
  import os
7
7
  import signal
8
8
  import sys
9
+ import tempfile
9
10
  import threading
10
11
  import time
11
12
  from collections.abc import Iterator, Mapping
@@ -19,6 +20,7 @@ from .gitops import (
19
20
  WorktreePolicy,
20
21
  branch_create_plan,
21
22
  branch_ensure_return,
23
+ branch_fast_forward_possible,
22
24
  branch_merge_plan,
23
25
  branch_save_wip,
24
26
  current_branch,
@@ -41,10 +43,18 @@ from .runner_bookkeeping import (
41
43
  commit_wip_changes,
42
44
  )
43
45
  from .runner_classification import classify_session, has_branch_completion_evidence, has_completed_artifact_path
44
- from .runner_models import RunnerEnvironment, RunnerFamily, RunnerInvocation, SessionPaths, family_for, parse_invocation
46
+ from .runner_models import (
47
+ RunnerEnvironment,
48
+ RunnerFamily,
49
+ RunnerInvocation,
50
+ SessionClassification,
51
+ SessionPaths,
52
+ family_for,
53
+ parse_invocation,
54
+ )
45
55
  from .runner_prompts import PromptGenerationResult, generate_prompt
46
- from .runner_recovery import run_recovery, run_recovery_detect
47
- from .runner_status import get_next, run_status_action, start_item, status_text, update_item
56
+ from .runtime_commit import REQUEST_FILENAME, RuntimeCommitResult, finalize_runtime_commit
57
+ from .runner_status import get_next, start_item, status_text, update_item
48
58
  from .sessions import AISessionConfig, AISessionLauncher, detect_stream_json_support
49
59
  from .task_checkout import (
50
60
  CHECKOUT_MODE_BRANCH,
@@ -95,21 +105,6 @@ def run_pipeline_command(kind: str, action: str, legacy_args: tuple[str, ...], p
95
105
  if action == "status":
96
106
  status = status_text(family, invocation, paths.project_root)
97
107
  return CommandResult(status.return_code, f"Python {kind} status", status.stdout.strip() or status.stderr.strip())
98
- if action == "unskip":
99
- status = run_status_action(
100
- family,
101
- "unskip",
102
- invocation,
103
- item_id=invocation.item_id,
104
- project_root=paths.project_root,
105
- )
106
- return CommandResult(
107
- status.return_code,
108
- f"Python {kind} unskip",
109
- status.stdout.strip() or status.stderr.strip(),
110
- )
111
- if action == "test-cli":
112
- return _test_cli(kind, paths)
113
108
  if action != "run":
114
109
  return CommandResult(1, "Unsupported Python runner action", f"Unsupported action: {kind} {action}")
115
110
  if not invocation.list_path.is_file():
@@ -121,24 +116,13 @@ def run_pipeline_command(kind: str, action: str, legacy_args: tuple[str, ...], p
121
116
  return CommandResult(1, f"Python {kind} runner failed", str(exc))
122
117
  details = result.details + ((f"last_status: {result.last_status}",) if result.last_status else ())
123
118
  return CommandResult(
124
- 0 if result.completed else 1,
119
+ 0 if result.completed or result.last_status == "dry_run" else 1,
125
120
  f"Python {kind} runner",
126
121
  f"processed={result.processed} completed={result.completed} stopped_reason={result.stopped_reason or 'none'}",
127
122
  details=details,
128
123
  )
129
124
 
130
125
 
131
- def run_recovery_command(action: str, legacy_args: tuple[str, ...], paths) -> CommandResult:
132
- """Dispatch a Python recovery command."""
133
- if action == "detect":
134
- code, stdout, stderr, _data = run_recovery_detect(paths)
135
- return CommandResult(code, "Python recovery detect", stdout.strip() or stderr.strip())
136
- if action == "run":
137
- code, stdout, stderr = run_recovery(paths, legacy_args)
138
- return CommandResult(code, "Python recovery run", stdout.strip() or stderr.strip())
139
- return CommandResult(1, "Unsupported Python recovery action", f"Unsupported action: {action}")
140
-
141
-
142
126
  def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) -> "PipelineRunResult":
143
127
  from .runner_models import PipelineRunResult
144
128
 
@@ -150,7 +134,12 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
150
134
  except KeyboardInterrupt:
151
135
  with shield_interruption_signals():
152
136
  return PipelineRunResult(False, 1, "interrupted", "interrupted")
153
- return PipelineRunResult(status == "completed", 1, "single_item_done", status)
137
+ return PipelineRunResult(
138
+ status == "completed",
139
+ 1,
140
+ "dry_run" if status == "dry_run" else "single_item_done",
141
+ status,
142
+ )
154
143
 
155
144
  while True:
156
145
  next_result = get_next(family, invocation, paths.project_root)
@@ -182,6 +171,14 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
182
171
  item_id = str(next_result.data.get(f"{family.kind}_id") or next_result.data.get("feature_id") or next_result.data.get("bug_id") or next_result.data.get("refactor_id") or "")
183
172
  if not item_id:
184
173
  return PipelineRunResult(False, processed, "missing_item_id", details=(marker[:500],))
174
+ if item_id in item_statuses:
175
+ return PipelineRunResult(
176
+ False,
177
+ processed,
178
+ "reselected_item",
179
+ item_statuses[item_id],
180
+ _render_item_statuses(item_statuses),
181
+ )
185
182
  try:
186
183
  final_status = _process_item(family, invocation, item_id, paths, initial_metadata=next_result.data)
187
184
  except KeyboardInterrupt:
@@ -197,6 +194,22 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
197
194
  )
198
195
  processed += 1
199
196
  item_statuses[item_id] = final_status
197
+ if final_status == "dry_run":
198
+ return PipelineRunResult(
199
+ False,
200
+ processed,
201
+ "dry_run",
202
+ final_status,
203
+ _render_item_statuses(item_statuses),
204
+ )
205
+ if final_status == "skipped":
206
+ return PipelineRunResult(
207
+ False,
208
+ processed,
209
+ "item_skipped",
210
+ final_status,
211
+ _render_item_statuses(item_statuses),
212
+ )
200
213
  if final_status in _completion_compatible_statuses(family):
201
214
  if final_status == "completed":
202
215
  _emit_info(f"Pausing 5s before next {family.kind}...")
@@ -210,7 +223,7 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
210
223
  return PipelineRunResult(False, processed, "item_failed", final_status, details)
211
224
 
212
225
 
213
- _COMPLETION_COMPATIBLE_STATUSES = {"completed", "skipped", "auto_skipped"}
226
+ _COMPLETION_COMPATIBLE_STATUSES = {"completed", "auto_skipped"}
214
227
 
215
228
 
216
229
  def _completion_compatible_statuses(family: RunnerFamily) -> set[str]:
@@ -423,21 +436,10 @@ def _child_log_activity_summary(progress: Mapping[str, object]) -> str:
423
436
  return _bytes_text(child_bytes)
424
437
 
425
438
 
426
- def _emit_prompt_summary(prompt: PromptGenerationResult, session_id: str) -> None:
427
- if prompt.pipeline_mode:
428
- _emit_info(f"Pipeline mode: {_pipeline_mode_label(prompt.pipeline_mode)}")
439
+ def _emit_prompt_summary(_prompt: PromptGenerationResult, session_id: str) -> None:
429
440
  _emit_info(f"Spawning AI CLI session: {session_id}")
430
441
 
431
442
 
432
- def _pipeline_mode_label(mode: str) -> str:
433
- labels = {
434
- "lite": "lite (Tier 1 — Scoped Guidance)",
435
- "standard": "standard (Tier 2 — Review Gates)",
436
- "full": "full (Full Guidance)",
437
- }
438
- return labels.get(mode, mode)
439
-
440
-
441
443
  def _emit_session_summary(session_result, session_paths: SessionPaths, classification) -> None:
442
444
  lines, size = _session_log_summary(session_paths.session_log)
443
445
  _emit_info(f"Session log: {lines} lines, {size}")
@@ -482,6 +484,8 @@ def _process_item(
482
484
  *,
483
485
  initial_metadata: dict[str, object],
484
486
  ) -> str:
487
+ if invocation.dry_run:
488
+ return _preview_item(family, invocation, item_id, paths, initial_metadata)
485
489
  try:
486
490
  with task_checkout_guard(family.state_dir, item_id):
487
491
  return _process_item_locked(family, invocation, item_id, paths, initial_metadata=initial_metadata)
@@ -489,6 +493,44 @@ def _process_item(
489
493
  raise
490
494
 
491
495
 
496
+ def _preview_item(
497
+ family: RunnerFamily,
498
+ invocation: RunnerInvocation,
499
+ item_id: str,
500
+ paths,
501
+ initial_metadata: dict[str, object],
502
+ ) -> str:
503
+ """Render one disposable Prompt preview without mutating task or Git state."""
504
+ config = load_runtime_config(paths)
505
+ if not config.ai_client.command:
506
+ raise RuntimeError("No supported AI CLI command was found")
507
+ metadata = _with_recovery_metadata(family, item_id, initial_metadata)
508
+ retry_count = int(metadata.get("retry_count") or 0)
509
+ session_id = f"{item_id}-dry-run"
510
+ _emit_item_header(family, invocation, item_id, metadata)
511
+ with tempfile.TemporaryDirectory(prefix="prizmkit-dry-run-") as temporary:
512
+ session_paths = SessionPaths.for_item(Path(temporary), item_id, session_id)
513
+ prompt = generate_prompt(
514
+ family,
515
+ invocation,
516
+ item_id=item_id,
517
+ session_id=session_id,
518
+ run_id="dry-run",
519
+ retry_count=retry_count,
520
+ session_paths=session_paths,
521
+ project_root=paths.project_root,
522
+ execution_root=paths.project_root,
523
+ preview=True,
524
+ )
525
+ prompt_size = prompt.output_path.stat().st_size
526
+ model = prompt.model or config.model or "default"
527
+ _emit_info(
528
+ f"Dry-run Prompt validated for {item_id}: model={model}, bytes={prompt_size}; "
529
+ "no checkout, checkpoint, task status, or AI session was created"
530
+ )
531
+ return "dry_run"
532
+
533
+
492
534
  def _process_item_locked(
493
535
  family: RunnerFamily,
494
536
  invocation: RunnerInvocation,
@@ -677,8 +719,6 @@ def _process_item_locked(
677
719
  execution_root=execution_root,
678
720
  continuation=continuation,
679
721
  )
680
- if invocation.dry_run:
681
- return "dry_run"
682
722
  _emit_prompt_summary(prompt, session_id)
683
723
  stream_json = False
684
724
  if getattr(config.ai_client, "launch_profile", None) is None:
@@ -707,6 +747,10 @@ def _process_item_locked(
707
747
  suppress_stream_output=True,
708
748
  )
709
749
  ).run()
750
+ runtime_commit = _finalize_prepared_runtime_commit(
751
+ execution_root,
752
+ prompt,
753
+ )
710
754
  classification = classify_session(
711
755
  session_result,
712
756
  project_root=execution_root,
@@ -715,6 +759,13 @@ def _process_item_locked(
715
759
  previous_fingerprint=previous_fingerprint,
716
760
  previous_no_progress_count=previous_no_progress_count,
717
761
  )
762
+ if runtime_commit.status == "failed":
763
+ classification = SessionClassification(
764
+ "commit_missing",
765
+ reason=runtime_commit.reason,
766
+ progress_fingerprint=classification.progress_fingerprint,
767
+ checkpoint_state=classification.checkpoint_state,
768
+ )
718
769
  _emit_session_summary(session_result, session_paths, classification)
719
770
  final_session_status = classification.session_status
720
771
  summary_path = _continuation_summary_path(execution_root, family, item_id, prompt)
@@ -938,6 +989,34 @@ def _emit_interruption_cleanup_failure(result, message: str, recovery: str) -> N
938
989
  _emit_warning(recovery)
939
990
 
940
991
 
992
+ def _finalize_prepared_runtime_commit(
993
+ execution_root: Path,
994
+ prompt: PromptGenerationResult,
995
+ ) -> RuntimeCommitResult:
996
+ """Execute a prepared commit only when the checkpoint proves readiness."""
997
+ checkpoint = prompt.checkpoint_path
998
+ artifact = prompt.artifact_path
999
+ if checkpoint is None or artifact is None or not checkpoint.is_file():
1000
+ return RuntimeCommitResult("not_ready", "commit_handoff_unavailable")
1001
+ from .checkpoint_state import load_checkpoint_state
1002
+
1003
+ state = load_checkpoint_state(checkpoint, project_root=execution_root)
1004
+ request_path = artifact / REQUEST_FILENAME
1005
+ cleanup_recovery = state.semantic_complete and request_path.is_file()
1006
+ if not state.semantic_commit_ready and not cleanup_recovery:
1007
+ return RuntimeCommitResult("not_ready", "checkpoint_not_commit_ready")
1008
+ result = finalize_runtime_commit(
1009
+ execution_root,
1010
+ artifact_dir=artifact,
1011
+ checkpoint_path=checkpoint,
1012
+ )
1013
+ if result.committed:
1014
+ _emit_success(f"Runtime committed prepared task change: {result.commit_hash}")
1015
+ else:
1016
+ _emit_warning(f"Runtime commit preparation failed: {result.reason}")
1017
+ return result
1018
+
1019
+
941
1020
  def _is_retryable_ai_outcome(session_status: str, new_status: str) -> bool:
942
1021
  """Return whether a persisted post-launch outcome should continue in-process."""
943
1022
  if session_status in {
@@ -1069,7 +1148,17 @@ def _complete_success_merge(
1069
1148
  return new_status
1070
1149
  else:
1071
1150
  _emit_info(f"Merging {branch_name} into {base_branch}...")
1072
- merge = run_git_plan(paths.project_root, branch_merge_plan(branch_context, auto_push=env.auto_push))
1151
+ merge = run_git_plan(
1152
+ paths.project_root,
1153
+ branch_merge_plan(
1154
+ branch_context,
1155
+ auto_push=env.auto_push,
1156
+ rebase=not branch_fast_forward_possible(
1157
+ paths.project_root,
1158
+ branch_context,
1159
+ ),
1160
+ ),
1161
+ )
1073
1162
  _emit_git_plan_output(merge)
1074
1163
  if merge.ok:
1075
1164
  checkout = load_task_checkout(family.state_dir, family.task_type, item_id)
@@ -1165,28 +1254,6 @@ def _record_setup_failure(
1165
1254
  branch_ensure_return(project_root, base_branch, branch_name)
1166
1255
  return new_status
1167
1256
 
1168
- def _test_cli(kind: str, paths) -> CommandResult:
1169
- """Report the AI CLI resolution used by Python runner commands."""
1170
- config = load_runtime_config(paths)
1171
- cli = config.ai_client.command or "(not found)"
1172
- platform = config.ai_client.platform or "unresolved"
1173
- details = [
1174
- f"detected_cli: {cli}",
1175
- f"platform: {platform}",
1176
- f"source: {config.ai_client.source}",
1177
- f"runtime_mode: {config.runtime_mode}",
1178
- ]
1179
- if config.model:
1180
- details.append(f"model: {config.model}")
1181
- details.extend(f"note: {note}" for note in config.ai_client.notes)
1182
- return CommandResult(
1183
- 0 if config.ai_client.command else 1,
1184
- f"Python {kind} AI CLI test",
1185
- "AI CLI resolution for the canonical Python runtime.",
1186
- tuple(details),
1187
- )
1188
-
1189
-
1190
1257
  def _use_worktree_for_family(env: RunnerEnvironment, family: RunnerFamily) -> bool:
1191
1258
  return env.use_worktree and family.kind in {"feature", "bugfix"}
1192
1259
 
@@ -1218,7 +1285,6 @@ def _invocation_for_execution_family(invocation: RunnerInvocation, family: Runne
1218
1285
  item_filter=invocation.item_filter,
1219
1286
  max_retries=invocation.max_retries,
1220
1287
  max_infra_retries=invocation.max_infra_retries,
1221
- mode=invocation.mode,
1222
1288
  dry_run=invocation.dry_run,
1223
1289
  clean=invocation.clean,
1224
1290
  no_reset=invocation.no_reset,
@@ -1341,7 +1407,6 @@ def _resolve_invocation_paths(invocation: RunnerInvocation, project_root: Path)
1341
1407
  item_filter=invocation.item_filter,
1342
1408
  max_retries=invocation.max_retries,
1343
1409
  max_infra_retries=invocation.max_infra_retries,
1344
- mode=invocation.mode,
1345
1410
  dry_run=invocation.dry_run,
1346
1411
  clean=invocation.clean,
1347
1412
  no_reset=invocation.no_reset,
@@ -1355,6 +1420,6 @@ def _resolve_invocation_paths(invocation: RunnerInvocation, project_root: Path)
1355
1420
  def _help_text(family: RunnerFamily) -> str:
1356
1421
  return (
1357
1422
  f"Python foreground runner for {family.kind}.\n"
1358
- "Supported legacy options include plan list path, item id, --features for feature, "
1359
- "--max-retries, --max-infra-retries, --mode, --resume-phase, and --dry-run."
1423
+ "Supported options include plan list path, item id, --features for feature, "
1424
+ "--max-retries, --max-infra-retries, --resume-phase, and --dry-run."
1360
1425
  )