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.
Files changed (119) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/claude/command-adapter.js +0 -2
  3. package/bundled/dev-pipeline/.env.example +1 -1
  4. package/bundled/dev-pipeline/assets/skill-subagent-integration.md +11 -6
  5. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
  6. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -9
  7. package/bundled/dev-pipeline/prizmkit_runtime/processes.py +21 -1
  8. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
  9. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
  10. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
  11. package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
  12. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +119 -18
  13. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +3 -2
  14. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +411 -17
  15. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +57 -14
  16. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +5 -4
  17. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +55 -2
  18. package/bundled/dev-pipeline/scripts/prompt_framework.py +152 -1
  19. package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
  20. package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
  21. package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
  22. package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
  23. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +5 -5
  24. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +28 -22
  25. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +24 -10
  26. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +20 -2
  27. package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +2 -2
  28. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
  29. package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +5 -10
  30. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +9 -19
  31. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
  32. package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +1 -1
  33. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
  34. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
  35. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +15 -0
  36. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +2 -2
  37. package/bundled/dev-pipeline/templates/sections/phase-commit.md +2 -2
  38. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +26 -23
  39. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +5 -5
  40. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +15 -11
  41. package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +1 -1
  42. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +6 -6
  43. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
  44. package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
  45. package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +1 -1
  46. package/bundled/dev-pipeline/templates/sections/task-contract.md +4 -0
  47. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
  48. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +349 -48
  49. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +129 -0
  50. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
  51. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +567 -23
  52. package/bundled/dev-pipeline/tests/test_unified_cli.py +37 -10
  53. package/bundled/skills/_metadata.json +63 -26
  54. package/bundled/skills/app-planner/SKILL.md +4 -3
  55. package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
  56. package/bundled/skills/bug-planner/SKILL.md +12 -27
  57. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
  58. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
  59. package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
  60. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
  61. package/bundled/skills/feature-planner/SKILL.md +27 -22
  62. package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
  63. package/bundled/skills/feature-workflow/SKILL.md +94 -121
  64. package/bundled/skills/prizmkit/SKILL.md +130 -94
  65. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
  66. package/bundled/skills/prizmkit-code-review/SKILL.md +132 -86
  67. package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +243 -0
  68. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +42 -1
  69. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
  70. package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +93 -1
  71. package/bundled/skills/prizmkit-committer/SKILL.md +125 -48
  72. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
  73. package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
  74. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
  75. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
  76. package/bundled/skills/prizmkit-init/SKILL.md +5 -5
  77. package/bundled/skills/prizmkit-plan/SKILL.md +146 -105
  78. package/bundled/skills/prizmkit-plan/assets/plan-template.md +18 -0
  79. package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +242 -0
  80. package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +4 -4
  81. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
  82. package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
  83. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
  84. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
  85. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
  86. package/bundled/skills/prizmkit-test/SKILL.md +52 -6
  87. package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +63 -12
  88. package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +1 -1
  89. package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +13 -0
  90. package/bundled/skills/prizmkit-test/references/evidence-protocol.md +6 -1
  91. package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +8 -1
  92. package/bundled/skills/prizmkit-test/references/examples.md +1 -1
  93. package/bundled/skills/prizmkit-test/references/test-generation-steps.md +4 -1
  94. package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
  95. package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +15 -0
  96. package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
  97. package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +744 -23
  98. package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +194 -15
  99. package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
  100. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
  101. package/bundled/skills/recovery-workflow/SKILL.md +62 -241
  102. package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
  103. package/bundled/skills/recovery-workflow/references/detection.md +28 -52
  104. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
  105. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
  106. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
  107. package/bundled/skills/refactor-planner/SKILL.md +12 -57
  108. package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
  109. package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
  110. package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
  111. package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
  112. package/bundled/skills/refactor-workflow/SKILL.md +98 -139
  113. package/bundled/templates/project-memory-template.md +4 -4
  114. package/package.json +1 -1
  115. package/src/index.js +4 -0
  116. package/src/prompts.js +10 -3
  117. package/src/scaffold.js +1 -1
  118. package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
  119. 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
- result = _run_pipeline(family, invocation, paths)
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
- return PipelineRunResult(False, 1, "interrupted", "interrupted")
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
- processed += 1
161
- item_statuses[item_id] = "interrupted"
162
- return PipelineRunResult(
163
- False,
164
- processed,
165
- "interrupted",
166
- "interrupted",
167
- _render_item_statuses(item_statuses),
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
- _emit_warning(f"Interrupted while processing {item_id}; preserving work and returning to {base_branch}")
761
- if use_worktree and worktree_runtime is not None:
762
- worktree_save_wip(worktree_runtime)
763
- else:
764
- branch_ensure_return(paths.project_root, base_branch, branch_name)
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 == "success" or session_status == "stalled_context_continuation":
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
- terminate_process_tree(handle, "interrupted", grace_seconds=self.config.stale_kill_grace_seconds)
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(