prizmkit 1.1.129 → 1.1.131

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 (88) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/dev-pipeline/.env.example +1 -1
  3. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
  4. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
  5. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
  6. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
  7. package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
  8. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +15 -1
  9. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +398 -6
  10. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +61 -3
  11. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +64 -4
  12. package/bundled/dev-pipeline/scripts/prompt_framework.py +156 -7
  13. package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
  14. package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
  15. package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
  16. package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
  17. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
  18. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +20 -14
  19. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +21 -4
  20. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +18 -0
  21. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
  22. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +5 -15
  23. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
  24. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
  25. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +16 -0
  26. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +26 -23
  27. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +12 -8
  28. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +1 -1
  29. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
  30. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
  31. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +404 -24
  32. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +132 -0
  33. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
  34. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +411 -18
  35. package/bundled/dev-pipeline/tests/test_unified_cli.py +3 -3
  36. package/bundled/skills/_metadata.json +62 -25
  37. package/bundled/skills/app-planner/SKILL.md +4 -3
  38. package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
  39. package/bundled/skills/bug-planner/SKILL.md +12 -27
  40. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
  41. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
  42. package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
  43. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
  44. package/bundled/skills/feature-planner/SKILL.md +27 -22
  45. package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
  46. package/bundled/skills/feature-workflow/SKILL.md +94 -121
  47. package/bundled/skills/prizmkit/SKILL.md +130 -94
  48. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
  49. package/bundled/skills/prizmkit-code-review/SKILL.md +114 -84
  50. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +1 -1
  51. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
  52. package/bundled/skills/prizmkit-committer/SKILL.md +131 -47
  53. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
  54. package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
  55. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
  56. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
  57. package/bundled/skills/prizmkit-init/SKILL.md +5 -5
  58. package/bundled/skills/prizmkit-plan/SKILL.md +132 -105
  59. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
  60. package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
  61. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
  62. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
  63. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
  64. package/bundled/skills/prizmkit-test/SKILL.md +28 -6
  65. package/bundled/skills/prizmkit-test/references/examples.md +1 -1
  66. package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
  67. package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
  68. package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
  69. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
  70. package/bundled/skills/recovery-workflow/SKILL.md +62 -241
  71. package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
  72. package/bundled/skills/recovery-workflow/references/detection.md +28 -52
  73. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
  74. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
  75. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
  76. package/bundled/skills/refactor-planner/SKILL.md +12 -57
  77. package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
  78. package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
  79. package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
  80. package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
  81. package/bundled/skills/refactor-workflow/SKILL.md +98 -139
  82. package/bundled/templates/project-memory-template.md +4 -4
  83. package/package.json +1 -1
  84. package/src/index.js +4 -0
  85. package/src/prompts.js +10 -3
  86. package/src/scaffold.js +1 -1
  87. package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
  88. package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
@@ -22,9 +22,11 @@ from prompt_framework import (
22
22
  merge_checkpoint_state,
23
23
  mode_agent_count,
24
24
  normalize_mode,
25
+ pipeline_checkpoint_metadata,
25
26
  read_text_file,
26
27
  render_from_sections,
27
28
  replace_placeholders,
29
+ route_pipeline_repair,
28
30
  validate_rendered,
29
31
  write_output,
30
32
  )
@@ -34,6 +36,30 @@ from utils import enrich_global_context, helper_replacements, load_json_file, re
34
36
  LOGGER = setup_logging("generate-refactor-prompt")
35
37
 
36
38
 
39
+ def route_refactor_repair(stage_result, repair_scope=None, repair_round=0):
40
+ """Map Refactor review/test outcomes to the shared safe route contract."""
41
+ return route_pipeline_repair(stage_result, repair_scope, repair_round)
42
+
43
+
44
+ def refactor_checkpoint_metadata(artifact_dir, evidence=None, stage="plan",
45
+ status="PLAN_READY", stage_result=None,
46
+ repair_scope=None, repair_round=0,
47
+ next_stage="prizmkit-implement",
48
+ resume_from="prizmkit-implement"):
49
+ """Build Refactor L4 handoff metadata while preserving Refactor L1 state."""
50
+ return pipeline_checkpoint_metadata(
51
+ artifact_dir,
52
+ evidence=evidence,
53
+ stage=stage,
54
+ status=status,
55
+ stage_result=stage_result,
56
+ repair_scope=repair_scope,
57
+ repair_round=repair_round,
58
+ next_stage=next_stage,
59
+ resume_from=resume_from,
60
+ )
61
+
62
+
37
63
  REFACTOR_SECTION_TO_SKILL = {
38
64
  "refactor-phase-init": checkpoint_step("prizmkit-init", "Initialize", [".prizmkit/refactor/{slug}"]),
39
65
  "refactor-phase-plan": checkpoint_step(
@@ -48,6 +74,14 @@ REFACTOR_SECTION_TO_SKILL = {
48
74
  "prizmkit-code-review", "Review — Code Review & Behavior Verification",
49
75
  [".prizmkit/refactor/{slug}/review-report.md"],
50
76
  ),
77
+ "refactor-phase-test": checkpoint_step(
78
+ "prizmkit-test", "Post-Review Test Gate",
79
+ [
80
+ ".prizmkit/test/evidence/*/manifest.json",
81
+ ".prizmkit/test/evidence/*/verdict.json",
82
+ ".prizmkit/test/evidence/*/validation.json",
83
+ ],
84
+ ),
51
85
  "phase-browser": checkpoint_step("browser-verification", "Browser Verification", []),
52
86
  "refactor-phase-commit-report": checkpoint_group([
53
87
  ("prizmkit-retrospective", "Retrospective", []),
@@ -383,7 +417,7 @@ def _refactor_header(pipeline_mode):
383
417
  return title + "\n" + desc + "\n"
384
418
 
385
419
 
386
- def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled):
420
+ def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled, browser_tool="auto"):
387
421
  sections = [
388
422
  ("header", _refactor_header(pipeline_mode)),
389
423
  ("refactor-session-context", load_section(sections_dir, "refactor-session-context.md")),
@@ -399,10 +433,16 @@ def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled):
399
433
  sections.extend([
400
434
  ("refactor-phase-implement", load_section(sections_dir, "refactor-phase-implement.md")),
401
435
  ("refactor-phase-review", load_section(sections_dir, "refactor-phase-review.md")),
436
+ ("refactor-phase-test", load_section(sections_dir, "refactor-phase-test.md")),
402
437
  ])
403
438
  if browser_enabled:
404
- sections.append(("phase-browser", load_section(sections_dir, "phase-browser-verification-auto.md")))
439
+ browser_section_file = {
440
+ "playwright-cli": "phase-browser-verification.md",
441
+ "opencli": "phase-browser-verification-opencli.md",
442
+ }.get(browser_tool, "phase-browser-verification-auto.md")
443
+ sections.append(("phase-browser", load_section(sections_dir, browser_section_file)))
405
444
  sections.extend([
445
+ ("headless-commit-authorization", load_section(sections_dir, "headless-commit-authorization.md")),
406
446
  ("refactor-phase-commit-report", load_section(sections_dir, "refactor-phase-commit-report.md")),
407
447
  ("refactor-session-status", load_section(sections_dir, "refactor-session-status.md")),
408
448
  ("refactor-critical-paths", load_section(sections_dir, "refactor-critical-paths.md")),
@@ -419,6 +459,7 @@ def generate_refactor_checkpoint(refactor_id, session_id, pipeline_mode="standar
419
459
  ("refactor-phase-plan", ""),
420
460
  ("refactor-phase-implement", ""),
421
461
  ("refactor-phase-review", ""),
462
+ ("refactor-phase-test", ""),
422
463
  ("refactor-phase-commit-report", ""),
423
464
  ]
424
465
  return generate_checkpoint_from_sections(
@@ -434,7 +475,11 @@ def generate_refactor_checkpoint(refactor_id, session_id, pipeline_mode="standar
434
475
 
435
476
 
436
477
  def merge_refactor_checkpoint_state(existing, fresh, project_root):
437
- return merge_checkpoint_state(existing, fresh, project_root, logger=LOGGER)
478
+ merged = merge_checkpoint_state(existing, fresh, project_root, logger=LOGGER)
479
+ existing_state = existing.get("refactor_state")
480
+ if isinstance(existing_state, dict):
481
+ merged["refactor_state"] = existing_state
482
+ return merged
438
483
 
439
484
 
440
485
  def emit_failure(message):
@@ -469,7 +514,10 @@ def main():
469
514
  sections = []
470
515
  if use_sections:
471
516
  try:
472
- sections = assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled)
517
+ sections = assemble_refactor_sections(
518
+ pipeline_mode, sections_dir, browser_enabled,
519
+ browser_tool=replacements["{{BROWSER_TOOL}}"],
520
+ )
473
521
  rendered = render_from_sections(sections, replacements)
474
522
  except FileNotFoundError as exc:
475
523
  LOGGER.warning("Refactor section assembly failed (%s), falling back to legacy template", exc)
@@ -504,6 +552,18 @@ def main():
504
552
  checkpoint = generate_refactor_checkpoint(
505
553
  args.refactor_id, args.session_id, pipeline_mode, sections if use_sections else None,
506
554
  )
555
+ if not any(step.get("skill") == "prizmkit-test" for step in checkpoint["steps"]):
556
+ emit_failure("Refactor checkpoint is missing the mandatory prizmkit-test stage")
557
+ checkpoint["refactor_state"] = refactor_checkpoint_metadata(
558
+ replacements["{{ARTIFACT_DIR}}"],
559
+ stage="plan",
560
+ status="PLAN_READY",
561
+ stage_result=None,
562
+ repair_scope=None,
563
+ repair_round=0,
564
+ next_stage="prizmkit-implement",
565
+ resume_from="prizmkit-implement",
566
+ )
507
567
 
508
568
  if (args.resume_phase != "null" or is_continuation(args)) and os.path.exists(checkpoint_path):
509
569
  try:
@@ -10,6 +10,137 @@ import os
10
10
  import re
11
11
 
12
12
 
13
+ MAX_OUTER_REPAIR_ROUNDS = 3
14
+ REPAIR_SCOPES_REQUIRING_REVIEW = {
15
+ "production",
16
+ "runtime",
17
+ "schema",
18
+ "dependency",
19
+ "public-interface",
20
+ }
21
+
22
+
23
+ def route_pipeline_repair(stage_result, repair_scope=None, repair_round=0):
24
+ """Map review/test outcomes to bounded, safe L4 continuation routes."""
25
+ try:
26
+ current_round = int(repair_round)
27
+ except (TypeError, ValueError):
28
+ current_round = MAX_OUTER_REPAIR_ROUNDS
29
+ if current_round < 0:
30
+ current_round = MAX_OUTER_REPAIR_ROUNDS
31
+
32
+ result = {
33
+ "stage_result": stage_result,
34
+ "repair_scope": repair_scope,
35
+ "repair_round": current_round,
36
+ "blocked": False,
37
+ "resumable": False,
38
+ "next_stage": None,
39
+ "resume_from": None,
40
+ "route": [],
41
+ "reason": "",
42
+ "blocked_reason": None,
43
+ "terminal_status": None,
44
+ }
45
+ if stage_result == "TEST_PASS":
46
+ result.update(
47
+ next_stage="prizmkit-retrospective",
48
+ resume_from="prizmkit-retrospective",
49
+ reason="authoritative test evidence passed",
50
+ )
51
+ return result
52
+ if stage_result == "TEST_BLOCKED":
53
+ result.update(
54
+ blocked=True,
55
+ resumable=False,
56
+ reason="bounded automatic recovery cannot safely edit production",
57
+ blocked_reason="test_blocked",
58
+ terminal_status="WORKFLOW_BLOCKED",
59
+ )
60
+ return result
61
+ if stage_result not in {"REVIEW_NEEDS_FIXES", "TEST_FAIL"}:
62
+ result.update(
63
+ blocked=True,
64
+ reason="unknown stage result is unsafe",
65
+ blocked_reason="unknown_stage_result",
66
+ terminal_status="WORKFLOW_BLOCKED",
67
+ )
68
+ return result
69
+ if current_round >= MAX_OUTER_REPAIR_ROUNDS:
70
+ result.update(
71
+ blocked=True,
72
+ reason="maximum outer repair rounds reached",
73
+ blocked_reason="repair_round_limit",
74
+ terminal_status="WORKFLOW_BLOCKED",
75
+ )
76
+ return result
77
+
78
+ result["repair_round"] = current_round + 1
79
+ if stage_result == "REVIEW_NEEDS_FIXES":
80
+ result.update(
81
+ next_stage="prizmkit-implement",
82
+ resume_from="prizmkit-implement",
83
+ route=["prizmkit-implement", "prizmkit-code-review", "prizmkit-test"],
84
+ reason="review findings require implementation and fresh review",
85
+ )
86
+ return result
87
+ if repair_scope == "test-infrastructure":
88
+ result.update(
89
+ next_stage="prizmkit-implement",
90
+ resume_from="prizmkit-implement",
91
+ route=["prizmkit-implement", "prizmkit-test"],
92
+ reason="test infrastructure failure must not edit production speculatively",
93
+ )
94
+ return result
95
+ if repair_scope in REPAIR_SCOPES_REQUIRING_REVIEW:
96
+ result.update(
97
+ next_stage="prizmkit-implement",
98
+ resume_from="prizmkit-implement",
99
+ route=["prizmkit-implement", "prizmkit-code-review", "prizmkit-test"],
100
+ reason="production-facing failure requires review before retesting",
101
+ )
102
+ return result
103
+ result.update(
104
+ blocked=True,
105
+ reason="unknown repair scope has no safe automatic route",
106
+ blocked_reason="unsafe_repair_scope",
107
+ terminal_status="WORKFLOW_BLOCKED",
108
+ )
109
+ return result
110
+
111
+
112
+ def pipeline_checkpoint_metadata(artifact_dir, evidence=None, stage="plan",
113
+ status="PLAN_READY", stage_result=None,
114
+ repair_scope=None, repair_round=0,
115
+ next_stage="prizmkit-implement",
116
+ resume_from="prizmkit-implement",
117
+ blocked_reason=None, terminal_status=None):
118
+ """Build family-neutral semantic L4 metadata without replacing L1 state."""
119
+ evidence = evidence or {}
120
+ current_stage = None if terminal_status else stage
121
+ semantic_next_stage = None if terminal_status else next_stage
122
+ return {
123
+ "schema_version": 1,
124
+ "artifact_dir": artifact_dir,
125
+ "orchestrator": "prizmkit-l4",
126
+ "stage": stage,
127
+ "current_stage": current_stage,
128
+ "status": status,
129
+ "stage_result": stage_result,
130
+ "repair_scope": repair_scope,
131
+ "repair_round": repair_round,
132
+ "authoritative_evidence_paths": {
133
+ "manifest": evidence.get("manifest_path"),
134
+ "verdict": evidence.get("verdict_path"),
135
+ "validation": evidence.get("validation_path"),
136
+ },
137
+ "next_stage": semantic_next_stage,
138
+ "resume_from": resume_from,
139
+ "blocked_reason": blocked_reason,
140
+ "terminal_status": terminal_status,
141
+ }
142
+
143
+
13
144
  def read_text_file(path):
14
145
  """Read and return the text content of a file, plus an error string."""
15
146
  abs_path = os.path.abspath(path)
@@ -257,8 +388,19 @@ def merge_checkpoint_state(existing, fresh, project_root, validator=None, logger
257
388
  previous_status = previous.get("status", "pending") if isinstance(previous, dict) else "pending"
258
389
  if previous_status == "completed":
259
390
  artifacts = step.get("required_artifacts", [])
260
- if validator(step.get("skill", ""), artifacts):
391
+ skill = step.get("skill", "")
392
+ stage_result = previous.get("stage_result") if isinstance(previous, dict) else None
393
+ verdict_valid = (
394
+ (skill != "prizmkit-code-review" or stage_result == "REVIEW_PASS")
395
+ and (skill != "prizmkit-test" or stage_result == "TEST_PASS")
396
+ )
397
+ if verdict_valid and validator(skill, artifacts):
261
398
  step["status"] = "completed"
399
+ if stage_result:
400
+ step["stage_result"] = stage_result
401
+ evidence = previous.get("authoritative_evidence_paths")
402
+ if isinstance(evidence, dict):
403
+ step["authoritative_evidence_paths"] = evidence
262
404
  continue
263
405
  if logger is not None:
264
406
  logger.warning(
@@ -271,6 +413,15 @@ def merge_checkpoint_state(existing, fresh, project_root, validator=None, logger
271
413
  chain_broken = True
272
414
  step["status"] = "pending"
273
415
 
416
+ workflow_type = fresh.get("workflow_type", "")
417
+ state_key = {
418
+ "feature-pipeline": "feature_state",
419
+ "bugfix-pipeline": "bugfix_state",
420
+ "refactor-pipeline": "refactor_state",
421
+ }.get(workflow_type)
422
+ if state_key and isinstance(existing.get(state_key), dict):
423
+ fresh[state_key] = dict(existing[state_key])
424
+
274
425
  return fresh
275
426
 
276
427
 
@@ -327,21 +478,19 @@ def detect_dev_server_port(project_root):
327
478
 
328
479
 
329
480
  def browser_enabled_from_item(item):
330
- """Return browser enabled/tool/steps with mandatory-attempt defaults."""
481
+ """Return browser enabled/tool/steps with explicit opt-in semantics."""
331
482
  browser_interaction = item.get("browser_interaction") if isinstance(item, dict) else None
332
483
  env_value = os.environ.get("BROWSER_VERIFY", "").lower()
333
484
  if env_value == "false":
334
485
  return False, "auto", "", False
335
486
 
336
- enabled = True
487
+ enabled = False
337
488
  tool = "auto"
338
489
  blocking = False
339
490
  steps = []
340
491
 
341
- if isinstance(browser_interaction, bool):
342
- enabled = browser_interaction
343
- elif isinstance(browser_interaction, dict):
344
- enabled = browser_interaction.get("enabled", True)
492
+ if isinstance(browser_interaction, dict):
493
+ enabled = True
345
494
  tool = browser_interaction.get("tool", "auto")
346
495
  blocking = bool(browser_interaction.get("blocking", False))
347
496
  steps = browser_interaction.get("verify_steps", []) or []
@@ -46,6 +46,7 @@ SESSION_STATUS_VALUES = [
46
46
  "infra_error",
47
47
  "context_overflow",
48
48
  "stalled_context_continuation",
49
+ "workflow_blocked",
49
50
  "commit_missing",
50
51
  "docs_missing",
51
52
  "merge_conflict",
@@ -88,6 +89,7 @@ def parse_args():
88
89
  parser.add_argument("--continuation-summary-path", default=None, help="Optional continuation summary artifact path.")
89
90
  parser.add_argument("--no-progress-count", type=int, default=None, help="Consecutive context-overflow sessions without objective progress.")
90
91
  parser.add_argument("--progress-fingerprint", default=None, help="JSON progress fingerprint captured after the session.")
92
+ parser.add_argument("--checkpoint-state", default=None, help="JSON semantic L4 checkpoint snapshot captured after the session.")
91
93
  return parser.parse_args()
92
94
 
93
95
 
@@ -253,6 +255,37 @@ def _load_progress_fingerprint(raw):
253
255
  return {"unparsed": str(raw)}
254
256
 
255
257
 
258
+ SEMANTIC_CHECKPOINT_FIELDS = (
259
+ "current_stage",
260
+ "next_stage",
261
+ "stage_result",
262
+ "repair_scope",
263
+ "repair_round",
264
+ "authoritative_evidence_paths",
265
+ "blocked_reason",
266
+ "terminal_status",
267
+ )
268
+
269
+
270
+ def _load_checkpoint_state(raw):
271
+ if not raw:
272
+ return {}
273
+ try:
274
+ data = json.loads(raw)
275
+ except (TypeError, ValueError):
276
+ return {}
277
+ if not isinstance(data, dict):
278
+ return {}
279
+ return {key: data.get(key) for key in SEMANTIC_CHECKPOINT_FIELDS}
280
+
281
+
282
+ def _sync_semantic_checkpoint(status_data, args):
283
+ semantic = _load_checkpoint_state(getattr(args, "checkpoint_state", None))
284
+ if semantic:
285
+ status_data.update(semantic)
286
+ return {key: status_data.get(key) for key in SEMANTIC_CHECKPOINT_FIELDS}
287
+
288
+
256
289
  def _set_continuation_common(status_data, args, session_id):
257
290
  if args.active_dev_branch:
258
291
  status_data["active_dev_branch"] = args.active_dev_branch
@@ -494,6 +527,7 @@ def action_update(args, bug_list_path, state_dir):
494
527
  fingerprint = _load_progress_fingerprint(args.progress_fingerprint)
495
528
  if fingerprint is not None:
496
529
  bs["last_progress_fingerprint"] = fingerprint
530
+ semantic_checkpoint = _sync_semantic_checkpoint(bs, args)
497
531
 
498
532
 
499
533
  if session_status == "success":
@@ -560,6 +594,19 @@ def action_update(args, bug_list_path, state_dir):
560
594
  if err:
561
595
  error_out("Failed to update .prizmkit/plans/bug-fix-list.json: {}".format(err))
562
596
  return
597
+ elif session_status == "workflow_blocked":
598
+ # Semantic terminal state: preserve partial work without consuming any retry budget.
599
+ new_status = "needs_info"
600
+ bs["continuation_pending"] = False
601
+ bs["needs_attention"] = True
602
+ bs["terminal_status"] = "WORKFLOW_BLOCKED"
603
+ bs["blocked_reason"] = (
604
+ semantic_checkpoint.get("blocked_reason") or "workflow_blocked"
605
+ )
606
+ err = update_bug_in_list(bug_list_path, bug_id, new_status)
607
+ if err:
608
+ error_out("Failed to update .prizmkit/plans/bug-fix-list.json: {}".format(err))
609
+ return
563
610
  elif session_status == "stalled_context_continuation":
564
611
  # No-progress context-overflow stall pauses automation without consuming retry budgets.
565
612
  set_stalled_context_continuation_metadata(bs, args, session_id)
@@ -616,6 +663,7 @@ def action_update(args, bug_list_path, state_dir):
616
663
  "infra_error_count": bs.get("infra_error_count", 0),
617
664
  "max_infra_retries": max_infra_retries,
618
665
  "resume_from_phase": bs.get("resume_from_phase"),
666
+ "checkpoint_state": semantic_checkpoint,
619
667
  "updated_at": bs["updated_at"],
620
668
  }
621
669
  if session_status in ("commit_missing", "docs_missing", "merge_conflict"):
@@ -630,6 +678,11 @@ def action_update(args, bug_list_path, state_dir):
630
678
  summary["restart_policy"] = "context_overflow_continuation"
631
679
  summary.update(continuation_metadata_summary(bs))
632
680
  summary["artifacts_preserved"] = True
681
+ elif session_status == "workflow_blocked":
682
+ summary["restart_policy"] = "terminal_blocked"
683
+ summary.update(continuation_metadata_summary(bs))
684
+ summary["checkpoint_state"] = semantic_checkpoint
685
+ summary["artifacts_preserved"] = True
633
686
  elif session_status == "stalled_context_continuation":
634
687
  summary["restart_policy"] = "needs_attention"
635
688
  summary.update(continuation_metadata_summary(bs))