prizmkit 1.1.154 → 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.
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/skill-adapter.js +1 -1
- package/bundled/adapters/pi/skill-adapter.js +2 -1
- package/bundled/dev-pipeline/.env.example +2 -6
- package/bundled/dev-pipeline/README.md +21 -27
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +341 -15
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +1 -15
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +48 -14
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +50 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -9
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +120 -22
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +482 -0
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +64 -11
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +145 -367
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +91 -62
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +105 -69
- package/bundled/dev-pipeline/scripts/prompt_framework.py +36 -41
- package/bundled/dev-pipeline/scripts/update-bug-status.py +1 -4
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +61 -13
- package/bundled/dev-pipeline/scripts/update-feature-status.py +1 -4
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +1 -4
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +22 -27
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +71 -104
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +59 -111
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +37 -72
- package/bundled/dev-pipeline/templates/sections/artifact-manifest.md +39 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +8 -25
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +4 -4
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +7 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +14 -27
- package/bundled/dev-pipeline/templates/sections/feature-completion-summary.md +20 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -71
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -7
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +5 -17
- package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +10 -23
- package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +7 -7
- package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/retrospective.md +12 -0
- package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +35 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +45 -62
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +469 -18
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +262 -175
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +62 -23
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +76 -28
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +168 -18
- package/bundled/dev-pipeline/tests/test_recovery_workflow.py +111 -0
- package/bundled/dev-pipeline/tests/test_reset_preserve.py +0 -2
- package/bundled/dev-pipeline/tests/test_runtime_commit.py +608 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +167 -25
- package/bundled/skills/_metadata.json +9 -9
- package/bundled/skills/app-planner/SKILL.md +3 -3
- package/bundled/skills/app-planner/references/generated-plan-review.md +2 -1
- package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +56 -64
- package/bundled/skills/app-planner/references/rules/backend/fixed-rules.md +85 -240
- package/bundled/skills/app-planner/references/rules/backend/question-bank.md +25 -25
- package/bundled/skills/app-planner/references/rules/backend/question-manifest.json +16 -7
- package/bundled/skills/app-planner/references/rules/backend/template.md +74 -92
- package/bundled/skills/app-planner/references/rules/database/derivation-rules.md +93 -93
- package/bundled/skills/app-planner/references/rules/database/fixed-rules.md +66 -173
- package/bundled/skills/app-planner/references/rules/database/question-bank.md +31 -26
- package/bundled/skills/app-planner/references/rules/database/question-manifest.json +13 -4
- package/bundled/skills/app-planner/references/rules/database/template.md +76 -98
- package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +68 -95
- package/bundled/skills/app-planner/references/rules/frontend/fixed-rules.md +93 -166
- package/bundled/skills/app-planner/references/rules/frontend/question-bank.md +46 -36
- package/bundled/skills/app-planner/references/rules/frontend/question-manifest.json +11 -2
- package/bundled/skills/app-planner/references/rules/frontend/template.md +79 -257
- package/bundled/skills/app-planner/references/rules/mobile/derivation-rules.md +91 -99
- package/bundled/skills/app-planner/references/rules/mobile/fixed-rules.md +73 -246
- package/bundled/skills/app-planner/references/rules/mobile/question-bank.md +17 -17
- package/bundled/skills/app-planner/references/rules/mobile/question-manifest.json +17 -2
- package/bundled/skills/app-planner/references/rules/mobile/template.md +73 -85
- package/bundled/skills/app-planner/references/rules-configuration.md +133 -65
- package/bundled/skills/app-planner/scripts/validate-rules-configuration.py +291 -0
- package/bundled/skills/prizmkit/SKILL.md +17 -18
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/prizmkit-code-review/SKILL.md +22 -58
- package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +6 -6
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +2 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +82 -132
- package/bundled/skills/prizmkit-deploy/SKILL.md +27 -11
- package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +89 -0
- package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +44 -14
- package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +19 -3
- package/bundled/skills/prizmkit-deploy/references/database-setup.md +22 -12
- package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +3 -1
- package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +22 -6
- package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +49 -0
- package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +1 -1
- package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +164 -0
- package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +9 -8
- package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +6 -6
- package/bundled/skills/prizmkit-implement/SKILL.md +30 -66
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +4 -4
- package/bundled/skills/prizmkit-init/SKILL.md +4 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +21 -64
- package/bundled/skills/prizmkit-plan/references/artifact-identity.md +38 -0
- package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +4 -4
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +5 -5
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +11 -1
- package/bundled/skills/prizmkit-retrospective/SKILL.md +63 -77
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -3
- package/bundled/skills/prizmkit-retrospective/references/retrospective-result-schema.json +138 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +17 -23
- package/bundled/skills/prizmkit-test/SKILL.md +64 -51
- package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +206 -0
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +17 -9
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +45 -21
- package/bundled/skills/prizmkit-test/references/test-report-template.md +20 -13
- package/bundled/skills/prizmkit-workflow/SKILL.md +35 -37
- package/bundled/skills/prizmkit-workflow/references/artifact-identity.md +34 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/recovery-workflow/SKILL.md +2 -1
- package/bundled/skills/recovery-workflow/references/detection.md +4 -3
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +15 -5
- package/package.json +1 -1
- package/src/scaffold.js +33 -22
- package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +0 -16
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +0 -31
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -7
- package/bundled/dev-pipeline/templates/sections/critical-paths-lite.md +0 -7
- package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +0 -9
- package/bundled/dev-pipeline/templates/sections/directory-convention-lite.md +0 -6
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +0 -15
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -61
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -24
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -12
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -44
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -25
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +0 -17
- package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +0 -28
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +0 -52
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +0 -52
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +0 -119
|
@@ -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
|
-
"""
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
|
585
|
-
"""
|
|
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(
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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
|
-
"
|
|
600
|
-
"Rebase then fast-forward
|
|
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
|
|
|
@@ -65,6 +65,17 @@ def classify_session(
|
|
|
65
65
|
progress_fingerprint=fingerprint,
|
|
66
66
|
checkpoint_state=checkpoint.semantic_snapshot,
|
|
67
67
|
)
|
|
68
|
+
if (
|
|
69
|
+
checkpoint is not None
|
|
70
|
+
and checkpoint.semantic_complete
|
|
71
|
+
and not _runtime_commit_receipt_matches(project_root, checkpoint)
|
|
72
|
+
):
|
|
73
|
+
return SessionClassification(
|
|
74
|
+
"commit_missing",
|
|
75
|
+
reason="runtime_commit_receipt_mismatch",
|
|
76
|
+
progress_fingerprint=fingerprint,
|
|
77
|
+
checkpoint_state=checkpoint.semantic_snapshot,
|
|
78
|
+
)
|
|
68
79
|
if _is_terminal_success(result) and checkpoint is not None and checkpoint.semantic_complete:
|
|
69
80
|
return SessionClassification(
|
|
70
81
|
"success",
|
|
@@ -172,6 +183,25 @@ def _is_terminal_success(result: AISessionResult) -> bool:
|
|
|
172
183
|
)
|
|
173
184
|
|
|
174
185
|
|
|
186
|
+
def _runtime_commit_receipt_matches(
|
|
187
|
+
project_root: Path,
|
|
188
|
+
checkpoint: CheckpointState,
|
|
189
|
+
) -> bool:
|
|
190
|
+
"""Verify canonical Runtime receipt identity against the current Git HEAD."""
|
|
191
|
+
commit_hash = checkpoint.semantic.runtime_commit_hash
|
|
192
|
+
base_head = checkpoint.semantic.runtime_base_head
|
|
193
|
+
if not commit_hash and not base_head:
|
|
194
|
+
return True
|
|
195
|
+
head = run_git_command(project_root, ("rev-parse", "HEAD"))
|
|
196
|
+
parent = run_git_command(project_root, ("rev-parse", f"{commit_hash}^"))
|
|
197
|
+
return bool(
|
|
198
|
+
head.return_code == 0
|
|
199
|
+
and parent.return_code == 0
|
|
200
|
+
and head.stdout.strip().lower() == commit_hash.lower()
|
|
201
|
+
and parent.stdout.strip().lower() == base_head.lower()
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
175
205
|
def _has_late_runtime_error_after_completion(
|
|
176
206
|
result: AISessionResult,
|
|
177
207
|
checkpoint: CheckpointState | None,
|
|
@@ -201,9 +231,27 @@ def has_branch_completion_evidence(
|
|
|
201
231
|
working_branch: str,
|
|
202
232
|
artifact_path: Path | None,
|
|
203
233
|
) -> bool:
|
|
204
|
-
"""
|
|
205
|
-
if not base_branch or not working_branch or
|
|
234
|
+
"""Require semantic completion and matching Runtime receipt on the task branch."""
|
|
235
|
+
if not base_branch or not working_branch or artifact_path is None:
|
|
236
|
+
return False
|
|
237
|
+
checkpoint_path = artifact_path / "workflow-checkpoint.json"
|
|
238
|
+
if not checkpoint_path.is_file():
|
|
239
|
+
return False
|
|
240
|
+
checkpoint = load_checkpoint_state(checkpoint_path, project_root=project_root)
|
|
241
|
+
if not checkpoint.semantic_complete:
|
|
206
242
|
return False
|
|
243
|
+
commit_hash = checkpoint.semantic.runtime_commit_hash
|
|
244
|
+
base_head = checkpoint.semantic.runtime_base_head
|
|
245
|
+
if commit_hash or base_head:
|
|
246
|
+
branch_head = run_git_command(project_root, ("rev-parse", working_branch))
|
|
247
|
+
parent = run_git_command(project_root, ("rev-parse", f"{commit_hash}^"))
|
|
248
|
+
if not (
|
|
249
|
+
branch_head.return_code == 0
|
|
250
|
+
and parent.return_code == 0
|
|
251
|
+
and branch_head.stdout.strip().lower() == commit_hash.lower()
|
|
252
|
+
and parent.stdout.strip().lower() == base_head.lower()
|
|
253
|
+
):
|
|
254
|
+
return False
|
|
207
255
|
return _has_commit_range(project_root, base_branch, working_branch)
|
|
208
256
|
|
|
209
257
|
|
|
@@ -79,7 +79,6 @@ class RunnerInvocation:
|
|
|
79
79
|
item_filter: str = ""
|
|
80
80
|
max_retries: int | None = None
|
|
81
81
|
max_infra_retries: int = 3
|
|
82
|
-
mode: str | None = None
|
|
83
82
|
dry_run: bool = False
|
|
84
83
|
clean: bool = False
|
|
85
84
|
no_reset: bool = False
|
|
@@ -95,7 +94,6 @@ class PromptGenerationResult:
|
|
|
95
94
|
|
|
96
95
|
output_path: Path
|
|
97
96
|
model: str = ""
|
|
98
|
-
pipeline_mode: str = ""
|
|
99
97
|
checkpoint_path: Path | None = None
|
|
100
98
|
artifact_path: Path | None = None
|
|
101
99
|
raw: Mapping[str, object] = field(default_factory=dict)
|
|
@@ -273,7 +271,6 @@ def parse_invocation(
|
|
|
273
271
|
item_filter = ""
|
|
274
272
|
max_retries: int | None = None
|
|
275
273
|
max_infra_retries = 3
|
|
276
|
-
mode: str | None = None
|
|
277
274
|
dry_run = False
|
|
278
275
|
clean = False
|
|
279
276
|
no_reset = False
|
|
@@ -308,11 +305,6 @@ def parse_invocation(
|
|
|
308
305
|
max_infra_retries = _safe_int(args[index], 3)
|
|
309
306
|
elif arg.startswith("--max-infra-retries="):
|
|
310
307
|
max_infra_retries = _safe_int(arg.split("=", 1)[1], 3)
|
|
311
|
-
elif arg == "--mode" and index + 1 < len(args):
|
|
312
|
-
index += 1
|
|
313
|
-
mode = args[index]
|
|
314
|
-
elif arg.startswith("--mode="):
|
|
315
|
-
mode = arg.split("=", 1)[1]
|
|
316
308
|
elif arg == "--resume-phase" and index + 1 < len(args):
|
|
317
309
|
index += 1
|
|
318
310
|
resume_phase = args[index]
|
|
@@ -339,7 +331,6 @@ def parse_invocation(
|
|
|
339
331
|
item_filter=item_filter,
|
|
340
332
|
max_retries=max_retries,
|
|
341
333
|
max_infra_retries=max_infra_retries,
|
|
342
|
-
mode=mode,
|
|
343
334
|
dry_run=dry_run,
|
|
344
335
|
clean=clean,
|
|
345
336
|
no_reset=no_reset,
|
|
@@ -22,6 +22,7 @@ def generate_prompt(
|
|
|
22
22
|
project_root: Path,
|
|
23
23
|
execution_root: Path | None = None,
|
|
24
24
|
continuation: dict[str, object] | None = None,
|
|
25
|
+
preview: bool = False,
|
|
25
26
|
) -> PromptGenerationResult:
|
|
26
27
|
"""Run the existing prompt generator and return its structured JSON result."""
|
|
27
28
|
visible_root = execution_root or project_root
|
|
@@ -47,8 +48,8 @@ def generate_prompt(
|
|
|
47
48
|
"--output",
|
|
48
49
|
str(session_paths.prompt_path),
|
|
49
50
|
]
|
|
50
|
-
if
|
|
51
|
-
command.
|
|
51
|
+
if preview:
|
|
52
|
+
command.append("--no-checkpoint-write")
|
|
52
53
|
if continuation:
|
|
53
54
|
command.extend(["--continuation-mode", "ai_error"])
|
|
54
55
|
for flag, key in (
|
|
@@ -95,7 +96,6 @@ def generate_prompt(
|
|
|
95
96
|
return PromptGenerationResult(
|
|
96
97
|
output_path=output_path,
|
|
97
98
|
model=str(data.get("model") or ""),
|
|
98
|
-
pipeline_mode=str(data.get("pipeline_mode") or ""),
|
|
99
99
|
checkpoint_path=Path(str(checkpoint)) if checkpoint else None,
|
|
100
100
|
artifact_path=_artifact_path(family, visible_root, item_id, data),
|
|
101
101
|
raw=data,
|
|
@@ -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,8 +43,17 @@ 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
|
|
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
|
|
56
|
+
from .runtime_commit import REQUEST_FILENAME, RuntimeCommitResult, finalize_runtime_commit
|
|
46
57
|
from .runner_status import get_next, start_item, status_text, update_item
|
|
47
58
|
from .sessions import AISessionConfig, AISessionLauncher, detect_stream_json_support
|
|
48
59
|
from .task_checkout import (
|
|
@@ -105,7 +116,7 @@ def run_pipeline_command(kind: str, action: str, legacy_args: tuple[str, ...], p
|
|
|
105
116
|
return CommandResult(1, f"Python {kind} runner failed", str(exc))
|
|
106
117
|
details = result.details + ((f"last_status: {result.last_status}",) if result.last_status else ())
|
|
107
118
|
return CommandResult(
|
|
108
|
-
0 if result.completed else 1,
|
|
119
|
+
0 if result.completed or result.last_status == "dry_run" else 1,
|
|
109
120
|
f"Python {kind} runner",
|
|
110
121
|
f"processed={result.processed} completed={result.completed} stopped_reason={result.stopped_reason or 'none'}",
|
|
111
122
|
details=details,
|
|
@@ -123,7 +134,12 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
|
|
|
123
134
|
except KeyboardInterrupt:
|
|
124
135
|
with shield_interruption_signals():
|
|
125
136
|
return PipelineRunResult(False, 1, "interrupted", "interrupted")
|
|
126
|
-
return PipelineRunResult(
|
|
137
|
+
return PipelineRunResult(
|
|
138
|
+
status == "completed",
|
|
139
|
+
1,
|
|
140
|
+
"dry_run" if status == "dry_run" else "single_item_done",
|
|
141
|
+
status,
|
|
142
|
+
)
|
|
127
143
|
|
|
128
144
|
while True:
|
|
129
145
|
next_result = get_next(family, invocation, paths.project_root)
|
|
@@ -178,6 +194,14 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
|
|
|
178
194
|
)
|
|
179
195
|
processed += 1
|
|
180
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
|
+
)
|
|
181
205
|
if final_status == "skipped":
|
|
182
206
|
return PipelineRunResult(
|
|
183
207
|
False,
|
|
@@ -412,21 +436,10 @@ def _child_log_activity_summary(progress: Mapping[str, object]) -> str:
|
|
|
412
436
|
return _bytes_text(child_bytes)
|
|
413
437
|
|
|
414
438
|
|
|
415
|
-
def _emit_prompt_summary(
|
|
416
|
-
if prompt.pipeline_mode:
|
|
417
|
-
_emit_info(f"Pipeline mode: {_pipeline_mode_label(prompt.pipeline_mode)}")
|
|
439
|
+
def _emit_prompt_summary(_prompt: PromptGenerationResult, session_id: str) -> None:
|
|
418
440
|
_emit_info(f"Spawning AI CLI session: {session_id}")
|
|
419
441
|
|
|
420
442
|
|
|
421
|
-
def _pipeline_mode_label(mode: str) -> str:
|
|
422
|
-
labels = {
|
|
423
|
-
"lite": "lite (Tier 1 — Scoped Guidance)",
|
|
424
|
-
"standard": "standard (Tier 2 — Review Gates)",
|
|
425
|
-
"full": "full (Full Guidance)",
|
|
426
|
-
}
|
|
427
|
-
return labels.get(mode, mode)
|
|
428
|
-
|
|
429
|
-
|
|
430
443
|
def _emit_session_summary(session_result, session_paths: SessionPaths, classification) -> None:
|
|
431
444
|
lines, size = _session_log_summary(session_paths.session_log)
|
|
432
445
|
_emit_info(f"Session log: {lines} lines, {size}")
|
|
@@ -471,6 +484,8 @@ def _process_item(
|
|
|
471
484
|
*,
|
|
472
485
|
initial_metadata: dict[str, object],
|
|
473
486
|
) -> str:
|
|
487
|
+
if invocation.dry_run:
|
|
488
|
+
return _preview_item(family, invocation, item_id, paths, initial_metadata)
|
|
474
489
|
try:
|
|
475
490
|
with task_checkout_guard(family.state_dir, item_id):
|
|
476
491
|
return _process_item_locked(family, invocation, item_id, paths, initial_metadata=initial_metadata)
|
|
@@ -478,6 +493,44 @@ def _process_item(
|
|
|
478
493
|
raise
|
|
479
494
|
|
|
480
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
|
+
|
|
481
534
|
def _process_item_locked(
|
|
482
535
|
family: RunnerFamily,
|
|
483
536
|
invocation: RunnerInvocation,
|
|
@@ -666,8 +719,6 @@ def _process_item_locked(
|
|
|
666
719
|
execution_root=execution_root,
|
|
667
720
|
continuation=continuation,
|
|
668
721
|
)
|
|
669
|
-
if invocation.dry_run:
|
|
670
|
-
return "dry_run"
|
|
671
722
|
_emit_prompt_summary(prompt, session_id)
|
|
672
723
|
stream_json = False
|
|
673
724
|
if getattr(config.ai_client, "launch_profile", None) is None:
|
|
@@ -696,6 +747,10 @@ def _process_item_locked(
|
|
|
696
747
|
suppress_stream_output=True,
|
|
697
748
|
)
|
|
698
749
|
).run()
|
|
750
|
+
runtime_commit = _finalize_prepared_runtime_commit(
|
|
751
|
+
execution_root,
|
|
752
|
+
prompt,
|
|
753
|
+
)
|
|
699
754
|
classification = classify_session(
|
|
700
755
|
session_result,
|
|
701
756
|
project_root=execution_root,
|
|
@@ -704,6 +759,13 @@ def _process_item_locked(
|
|
|
704
759
|
previous_fingerprint=previous_fingerprint,
|
|
705
760
|
previous_no_progress_count=previous_no_progress_count,
|
|
706
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
|
+
)
|
|
707
769
|
_emit_session_summary(session_result, session_paths, classification)
|
|
708
770
|
final_session_status = classification.session_status
|
|
709
771
|
summary_path = _continuation_summary_path(execution_root, family, item_id, prompt)
|
|
@@ -927,6 +989,34 @@ def _emit_interruption_cleanup_failure(result, message: str, recovery: str) -> N
|
|
|
927
989
|
_emit_warning(recovery)
|
|
928
990
|
|
|
929
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
|
+
|
|
930
1020
|
def _is_retryable_ai_outcome(session_status: str, new_status: str) -> bool:
|
|
931
1021
|
"""Return whether a persisted post-launch outcome should continue in-process."""
|
|
932
1022
|
if session_status in {
|
|
@@ -1058,7 +1148,17 @@ def _complete_success_merge(
|
|
|
1058
1148
|
return new_status
|
|
1059
1149
|
else:
|
|
1060
1150
|
_emit_info(f"Merging {branch_name} into {base_branch}...")
|
|
1061
|
-
merge = run_git_plan(
|
|
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
|
+
)
|
|
1062
1162
|
_emit_git_plan_output(merge)
|
|
1063
1163
|
if merge.ok:
|
|
1064
1164
|
checkout = load_task_checkout(family.state_dir, family.task_type, item_id)
|
|
@@ -1185,7 +1285,6 @@ def _invocation_for_execution_family(invocation: RunnerInvocation, family: Runne
|
|
|
1185
1285
|
item_filter=invocation.item_filter,
|
|
1186
1286
|
max_retries=invocation.max_retries,
|
|
1187
1287
|
max_infra_retries=invocation.max_infra_retries,
|
|
1188
|
-
mode=invocation.mode,
|
|
1189
1288
|
dry_run=invocation.dry_run,
|
|
1190
1289
|
clean=invocation.clean,
|
|
1191
1290
|
no_reset=invocation.no_reset,
|
|
@@ -1308,7 +1407,6 @@ def _resolve_invocation_paths(invocation: RunnerInvocation, project_root: Path)
|
|
|
1308
1407
|
item_filter=invocation.item_filter,
|
|
1309
1408
|
max_retries=invocation.max_retries,
|
|
1310
1409
|
max_infra_retries=invocation.max_infra_retries,
|
|
1311
|
-
mode=invocation.mode,
|
|
1312
1410
|
dry_run=invocation.dry_run,
|
|
1313
1411
|
clean=invocation.clean,
|
|
1314
1412
|
no_reset=invocation.no_reset,
|
|
@@ -1322,6 +1420,6 @@ def _resolve_invocation_paths(invocation: RunnerInvocation, project_root: Path)
|
|
|
1322
1420
|
def _help_text(family: RunnerFamily) -> str:
|
|
1323
1421
|
return (
|
|
1324
1422
|
f"Python foreground runner for {family.kind}.\n"
|
|
1325
|
-
"Supported
|
|
1326
|
-
"--max-retries, --max-infra-retries, --
|
|
1423
|
+
"Supported options include plan list path, item id, --features for feature, "
|
|
1424
|
+
"--max-retries, --max-infra-retries, --resume-phase, and --dry-run."
|
|
1327
1425
|
)
|