prizmkit 1.1.130 → 1.1.134
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/claude/command-adapter.js +0 -2
- package/bundled/dev-pipeline/.env.example +1 -1
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +11 -6
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -9
- package/bundled/dev-pipeline/prizmkit_runtime/processes.py +21 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
- package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +119 -18
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +3 -2
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +411 -17
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +57 -14
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +5 -4
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +55 -2
- package/bundled/dev-pipeline/scripts/prompt_framework.py +152 -1
- package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
- package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +5 -5
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +28 -22
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +24 -10
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +20 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +5 -10
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +9 -19
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +1 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +15 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +26 -23
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +5 -5
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +15 -11
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +6 -6
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +1 -1
- package/bundled/dev-pipeline/templates/sections/task-contract.md +4 -0
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +349 -48
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +129 -0
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +567 -23
- package/bundled/dev-pipeline/tests/test_unified_cli.py +37 -10
- package/bundled/skills/_metadata.json +63 -26
- package/bundled/skills/app-planner/SKILL.md +4 -3
- package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
- package/bundled/skills/bug-planner/SKILL.md +12 -27
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
- package/bundled/skills/feature-planner/SKILL.md +27 -22
- package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
- package/bundled/skills/feature-workflow/SKILL.md +94 -121
- package/bundled/skills/prizmkit/SKILL.md +130 -94
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-code-review/SKILL.md +132 -86
- package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +243 -0
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +42 -1
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +93 -1
- package/bundled/skills/prizmkit-committer/SKILL.md +125 -48
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-init/SKILL.md +5 -5
- package/bundled/skills/prizmkit-plan/SKILL.md +146 -105
- package/bundled/skills/prizmkit-plan/assets/plan-template.md +18 -0
- package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +242 -0
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +4 -4
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-test/SKILL.md +52 -6
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +63 -12
- package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +1 -1
- package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +13 -0
- package/bundled/skills/prizmkit-test/references/evidence-protocol.md +6 -1
- package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +8 -1
- package/bundled/skills/prizmkit-test/references/examples.md +1 -1
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +4 -1
- package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
- package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +15 -0
- package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +744 -23
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +194 -15
- package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/recovery-workflow/SKILL.md +62 -241
- package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
- package/bundled/skills/recovery-workflow/references/detection.md +28 -52
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
- package/bundled/skills/refactor-planner/SKILL.md +12 -57
- package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
- package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
- package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
- package/bundled/skills/refactor-workflow/SKILL.md +98 -139
- package/bundled/templates/project-memory-template.md +4 -4
- package/package.json +1 -1
- package/src/index.js +4 -0
- package/src/prompts.js +10 -3
- package/src/scaffold.js +1 -1
- package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
- package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import signal
|
|
6
7
|
import sys
|
|
8
|
+
import threading
|
|
7
9
|
import time
|
|
8
|
-
from collections.abc import Mapping
|
|
10
|
+
from collections.abc import Iterator, Mapping
|
|
11
|
+
from contextlib import contextmanager
|
|
9
12
|
from pathlib import Path
|
|
10
13
|
|
|
11
14
|
from .commands import CommandResult # type: ignore # imported only at runtime by commands.py
|
|
@@ -27,6 +30,7 @@ from .gitops import (
|
|
|
27
30
|
worktree_runtime_context,
|
|
28
31
|
worktree_save_wip,
|
|
29
32
|
)
|
|
33
|
+
from .processes import shield_interruption_signals
|
|
30
34
|
from .runner_bookkeeping import (
|
|
31
35
|
commit_final_bookkeeping,
|
|
32
36
|
commit_pre_branch_bookkeeping,
|
|
@@ -51,6 +55,26 @@ from .task_checkout import (
|
|
|
51
55
|
)
|
|
52
56
|
|
|
53
57
|
|
|
58
|
+
def _interrupt_from_sigterm(_signal_number, _frame) -> None:
|
|
59
|
+
"""Translate daemon SIGTERM into the existing fail-closed interrupt path."""
|
|
60
|
+
raise KeyboardInterrupt
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@contextmanager
|
|
64
|
+
def _bridge_sigterm_to_interrupt() -> Iterator[None]:
|
|
65
|
+
"""Install a scoped SIGTERM bridge when running on the main thread."""
|
|
66
|
+
if threading.current_thread() is not threading.main_thread():
|
|
67
|
+
yield
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
previous_handler = signal.getsignal(signal.SIGTERM)
|
|
71
|
+
try:
|
|
72
|
+
signal.signal(signal.SIGTERM, _interrupt_from_sigterm)
|
|
73
|
+
yield
|
|
74
|
+
finally:
|
|
75
|
+
signal.signal(signal.SIGTERM, previous_handler)
|
|
76
|
+
|
|
77
|
+
|
|
54
78
|
def run_pipeline_command(kind: str, action: str, legacy_args: tuple[str, ...], paths) -> CommandResult:
|
|
55
79
|
"""Dispatch a feature/bugfix/refactor Python foreground command."""
|
|
56
80
|
family = family_for(kind, paths)
|
|
@@ -89,7 +113,8 @@ def run_pipeline_command(kind: str, action: str, legacy_args: tuple[str, ...], p
|
|
|
89
113
|
if not invocation.list_path.is_file():
|
|
90
114
|
return CommandResult(1, f"Python {kind} runner", f"Plan list not found: {invocation.list_path}")
|
|
91
115
|
try:
|
|
92
|
-
|
|
116
|
+
with _bridge_sigterm_to_interrupt():
|
|
117
|
+
result = _run_pipeline(family, invocation, paths)
|
|
93
118
|
except Exception as exc: # keep CLI boundary traceback-free
|
|
94
119
|
return CommandResult(1, f"Python {kind} runner failed", str(exc))
|
|
95
120
|
details = result.details + ((f"last_status: {result.last_status}",) if result.last_status else ())
|
|
@@ -121,7 +146,8 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
|
|
|
121
146
|
try:
|
|
122
147
|
status = _process_item(family, invocation, invocation.item_id, paths, initial_metadata={})
|
|
123
148
|
except KeyboardInterrupt:
|
|
124
|
-
|
|
149
|
+
with shield_interruption_signals():
|
|
150
|
+
return PipelineRunResult(False, 1, "interrupted", "interrupted")
|
|
125
151
|
return PipelineRunResult(status == "completed", 1, "single_item_done", status)
|
|
126
152
|
|
|
127
153
|
while True:
|
|
@@ -157,15 +183,16 @@ def _run_pipeline(family: RunnerFamily, invocation: RunnerInvocation, paths) ->
|
|
|
157
183
|
try:
|
|
158
184
|
final_status = _process_item(family, invocation, item_id, paths, initial_metadata=next_result.data)
|
|
159
185
|
except KeyboardInterrupt:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
186
|
+
with shield_interruption_signals():
|
|
187
|
+
processed += 1
|
|
188
|
+
item_statuses[item_id] = "interrupted"
|
|
189
|
+
return PipelineRunResult(
|
|
190
|
+
False,
|
|
191
|
+
processed,
|
|
192
|
+
"interrupted",
|
|
193
|
+
"interrupted",
|
|
194
|
+
_render_item_statuses(item_statuses),
|
|
195
|
+
)
|
|
169
196
|
processed += 1
|
|
170
197
|
item_statuses[item_id] = final_status
|
|
171
198
|
if final_status in _completion_compatible_statuses(family):
|
|
@@ -621,6 +648,15 @@ def _process_item_locked(
|
|
|
621
648
|
"max_infra_retries": invocation.max_infra_retries,
|
|
622
649
|
"no_progress_count": previous_no_progress_count,
|
|
623
650
|
"progress_fingerprint": previous_fingerprint or {},
|
|
651
|
+
"checkpoint_state": {
|
|
652
|
+
key: metadata.get(key)
|
|
653
|
+
for key in (
|
|
654
|
+
"current_stage", "next_stage", "stage_result",
|
|
655
|
+
"repair_scope", "repair_round",
|
|
656
|
+
"authoritative_evidence_paths", "blocked_reason",
|
|
657
|
+
"terminal_status",
|
|
658
|
+
)
|
|
659
|
+
},
|
|
624
660
|
"task_type": family.task_type,
|
|
625
661
|
"active_dev_branch": branch_name,
|
|
626
662
|
"base_branch": base_branch,
|
|
@@ -691,6 +727,7 @@ def _process_item_locked(
|
|
|
691
727
|
worktree_path=worktree_runtime.worktree_path if worktree_runtime is not None else None,
|
|
692
728
|
reason=classification.reason,
|
|
693
729
|
fatal_error_code=classification.fatal_error_code,
|
|
730
|
+
checkpoint_state=classification.checkpoint_state,
|
|
694
731
|
)
|
|
695
732
|
|
|
696
733
|
update = update_item(
|
|
@@ -706,6 +743,7 @@ def _process_item_locked(
|
|
|
706
743
|
continuation_summary_path=summary_path,
|
|
707
744
|
no_progress_count=classification.no_progress_count,
|
|
708
745
|
progress_fingerprint=classification.progress_fingerprint,
|
|
746
|
+
checkpoint_state=classification.checkpoint_state,
|
|
709
747
|
)
|
|
710
748
|
if not update.ok:
|
|
711
749
|
raise RuntimeError(update.text)
|
|
@@ -757,17 +795,77 @@ def _process_item_locked(
|
|
|
757
795
|
classification_fatal_error_code=classification.fatal_error_code,
|
|
758
796
|
)
|
|
759
797
|
except KeyboardInterrupt:
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
798
|
+
with shield_interruption_signals():
|
|
799
|
+
_cleanup_interrupted_task(
|
|
800
|
+
item_id=item_id,
|
|
801
|
+
paths=paths,
|
|
802
|
+
base_branch=base_branch,
|
|
803
|
+
branch_name=branch_name,
|
|
804
|
+
use_worktree=use_worktree,
|
|
805
|
+
worktree_runtime=worktree_runtime,
|
|
806
|
+
)
|
|
765
807
|
raise
|
|
766
808
|
|
|
767
809
|
|
|
810
|
+
def _cleanup_interrupted_task(
|
|
811
|
+
*,
|
|
812
|
+
item_id: str,
|
|
813
|
+
paths,
|
|
814
|
+
base_branch: str,
|
|
815
|
+
branch_name: str,
|
|
816
|
+
use_worktree: bool,
|
|
817
|
+
worktree_runtime,
|
|
818
|
+
) -> None:
|
|
819
|
+
"""Preserve interrupted task work and report the verified checkout state."""
|
|
820
|
+
_emit_warning(f"Interrupted while processing {item_id}; preserving work safely")
|
|
821
|
+
if use_worktree and worktree_runtime is not None:
|
|
822
|
+
preserved = worktree_save_wip(worktree_runtime)
|
|
823
|
+
if getattr(preserved, "ok", True):
|
|
824
|
+
_emit_warning(
|
|
825
|
+
f"Interrupted task {item_id}: work preserved in {worktree_runtime.worktree_path}; "
|
|
826
|
+
"task worktree remains active"
|
|
827
|
+
)
|
|
828
|
+
return
|
|
829
|
+
_emit_interruption_cleanup_failure(
|
|
830
|
+
preserved,
|
|
831
|
+
f"Could not preserve interrupted work for {item_id}; leaving task worktree active",
|
|
832
|
+
f"Inspect the task worktree: {worktree_runtime.worktree_path}",
|
|
833
|
+
)
|
|
834
|
+
return
|
|
835
|
+
|
|
836
|
+
returned = branch_ensure_return(paths.project_root, base_branch, branch_name)
|
|
837
|
+
if getattr(returned, "ok", True) and current_branch(paths.project_root) == base_branch:
|
|
838
|
+
_emit_warning(f"Interrupted task {item_id}: work preserved and returned to {base_branch}")
|
|
839
|
+
return
|
|
840
|
+
_emit_interruption_cleanup_failure(
|
|
841
|
+
returned,
|
|
842
|
+
f"Could not safely return to {base_branch}; leaving {branch_name} active",
|
|
843
|
+
f"After resolving the Git blocker, run: git checkout {base_branch}",
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def _emit_interruption_cleanup_failure(result, message: str, recovery: str) -> None:
|
|
848
|
+
"""Emit a concise failed Git step and a non-destructive recovery action."""
|
|
849
|
+
_emit_warning(message)
|
|
850
|
+
commands = tuple(getattr(getattr(result, "plan", None), "commands", ()))
|
|
851
|
+
results = tuple(getattr(result, "results", ()))
|
|
852
|
+
for index, command_result in enumerate(results):
|
|
853
|
+
if getattr(command_result, "return_code", 0) == 0:
|
|
854
|
+
continue
|
|
855
|
+
command = commands[index] if index < len(commands) else None
|
|
856
|
+
if command is not None and getattr(command, "allow_failure", False):
|
|
857
|
+
continue
|
|
858
|
+
args = " ".join(map(str, getattr(command_result, "args", ())))
|
|
859
|
+
detail = str(getattr(command_result, "stderr", "") or getattr(command_result, "stdout", "")).strip()
|
|
860
|
+
suffix = f": {detail.splitlines()[0][:500]}" if detail else ""
|
|
861
|
+
_emit_warning(f"git {args} failed{suffix}")
|
|
862
|
+
break
|
|
863
|
+
_emit_warning(recovery)
|
|
864
|
+
|
|
865
|
+
|
|
768
866
|
def _is_retryable_ai_outcome(session_status: str, new_status: str) -> bool:
|
|
769
867
|
"""Return whether a persisted post-launch outcome should continue in-process."""
|
|
770
|
-
if session_status
|
|
868
|
+
if session_status in {"success", "stalled_context_continuation", "workflow_blocked"}:
|
|
771
869
|
return False
|
|
772
870
|
return new_status in {"in_progress", "pending"} and session_status in {
|
|
773
871
|
"context_overflow",
|
|
@@ -1079,6 +1177,7 @@ def _write_session_status(
|
|
|
1079
1177
|
worktree_path: Path | None = None,
|
|
1080
1178
|
reason: str = "",
|
|
1081
1179
|
fatal_error_code: str = "",
|
|
1180
|
+
checkpoint_state: object | None = None,
|
|
1082
1181
|
) -> None:
|
|
1083
1182
|
payload = {
|
|
1084
1183
|
"status": status,
|
|
@@ -1094,6 +1193,8 @@ def _write_session_status(
|
|
|
1094
1193
|
payload["reason"] = reason
|
|
1095
1194
|
if fatal_error_code:
|
|
1096
1195
|
payload["fatal_error_code"] = fatal_error_code
|
|
1196
|
+
if isinstance(checkpoint_state, dict):
|
|
1197
|
+
payload["checkpoint_state"] = checkpoint_state
|
|
1097
1198
|
session_paths.session_status_json.parent.mkdir(parents=True, exist_ok=True)
|
|
1098
1199
|
session_paths.session_status_json.write_text(
|
|
1099
1200
|
json.dumps(payload, indent=2, sort_keys=True) + "\n",
|
|
@@ -20,7 +20,7 @@ from pathlib import Path
|
|
|
20
20
|
from .checkpoint_state import load_checkpoint_state
|
|
21
21
|
from .heartbeat import HeartbeatConfig, HeartbeatMonitor, HeartbeatState, observe_heartbeat_state, write_stale_marker
|
|
22
22
|
from .launch_profiles import HeadlessLaunchProfile
|
|
23
|
-
from .processes import ProcessHandle, ProcessSpec, launch_process, terminate_process_tree
|
|
23
|
+
from .processes import ProcessHandle, ProcessSpec, launch_process, shield_interruption_signals, terminate_process_tree
|
|
24
24
|
from .status import ProgressSummary
|
|
25
25
|
|
|
26
26
|
|
|
@@ -868,7 +868,8 @@ class AISessionLauncher:
|
|
|
868
868
|
)
|
|
869
869
|
last_progress_emit = now
|
|
870
870
|
except KeyboardInterrupt:
|
|
871
|
-
|
|
871
|
+
with shield_interruption_signals():
|
|
872
|
+
terminate_process_tree(handle, "interrupted", grace_seconds=self.config.stale_kill_grace_seconds)
|
|
872
873
|
raise
|
|
873
874
|
if self.config.live_output and (not self.config.verbose or self.config.suppress_stream_output):
|
|
874
875
|
final_heartbeat_state = _display_heartbeat_state(
|