prizmkit 1.1.98 → 1.1.100

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 (79) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/dev-pipeline/README.md +5 -5
  3. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +1 -1
  4. package/bundled/dev-pipeline/launch-feature-daemon.sh +1 -1
  5. package/bundled/dev-pipeline/launch-refactor-daemon.sh +1 -1
  6. package/bundled/dev-pipeline/lib/common.sh +244 -25
  7. package/bundled/dev-pipeline/reset-bug.sh +32 -2
  8. package/bundled/dev-pipeline/reset-feature.sh +32 -2
  9. package/bundled/dev-pipeline/reset-refactor.sh +32 -2
  10. package/bundled/dev-pipeline/run-bugfix.sh +133 -18
  11. package/bundled/dev-pipeline/run-feature.sh +134 -18
  12. package/bundled/dev-pipeline/run-refactor.sh +133 -18
  13. package/bundled/dev-pipeline/scripts/check-session-status.py +74 -1
  14. package/bundled/dev-pipeline/scripts/continuation.py +374 -0
  15. package/bundled/dev-pipeline/scripts/detect-stuck.py +5 -5
  16. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +29 -35
  17. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +19 -25
  18. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +19 -25
  19. package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +1 -1
  20. package/bundled/dev-pipeline/scripts/init-pipeline.py +1 -1
  21. package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +1 -1
  22. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +146 -16
  23. package/bundled/dev-pipeline/scripts/update-bug-status.py +220 -13
  24. package/bundled/dev-pipeline/scripts/update-feature-status.py +244 -18
  25. package/bundled/dev-pipeline/scripts/update-refactor-status.py +220 -13
  26. package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +1 -1
  27. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +2 -2
  28. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +3 -3
  29. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +3 -3
  30. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +1 -1
  31. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +3 -3
  32. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +31 -66
  33. package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
  34. package/bundled/dev-pipeline/tests/conftest.py +1 -0
  35. package/bundled/dev-pipeline/tests/test_auto_skip.py +518 -8
  36. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +103 -0
  37. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +60 -0
  38. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +43 -0
  39. package/bundled/dev-pipeline-windows/lib/common.ps1 +172 -10
  40. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +95 -11
  41. package/bundled/dev-pipeline-windows/lib/reset.ps1 +34 -0
  42. package/bundled/dev-pipeline-windows/reset-bug.ps1 +1 -0
  43. package/bundled/dev-pipeline-windows/reset-feature.ps1 +1 -0
  44. package/bundled/dev-pipeline-windows/reset-refactor.ps1 +1 -0
  45. package/bundled/dev-pipeline-windows/scripts/check-session-status.py +74 -1
  46. package/bundled/dev-pipeline-windows/scripts/continuation.py +374 -0
  47. package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +5 -5
  48. package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +29 -35
  49. package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +20 -25
  50. package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +20 -25
  51. package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +1 -1
  52. package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +1 -1
  53. package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +1 -1
  54. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +146 -16
  55. package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +220 -13
  56. package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +244 -18
  57. package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +220 -13
  58. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +1 -1
  59. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +2 -2
  60. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +3 -3
  61. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +3 -3
  62. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +1 -1
  63. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +3 -3
  64. package/bundled/dev-pipeline-windows/templates/sections/log-size-awareness.md +31 -68
  65. package/bundled/dev-pipeline-windows/templates/session-status-schema.json +1 -1
  66. package/bundled/skills/_metadata.json +1 -1
  67. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +2 -2
  68. package/bundled/skills/feature-pipeline-launcher/SKILL.md +2 -2
  69. package/bundled/skills/recovery-workflow/SKILL.md +2 -2
  70. package/bundled/skills/recovery-workflow/evals/evals.json +2 -2
  71. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +2 -2
  72. package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +2 -2
  73. package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +2 -2
  74. package/bundled/skills-windows/recovery-workflow/SKILL.md +2 -2
  75. package/bundled/skills-windows/recovery-workflow/evals/evals.json +2 -2
  76. package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +2 -2
  77. package/package.json +1 -1
  78. package/bundled/dev-pipeline/scripts/monitor-log.sh +0 -104
  79. package/bundled/dev-pipeline-windows/scripts/monitor-log.ps1 +0 -102
@@ -43,6 +43,8 @@ SESSION_STATUS_VALUES = [
43
43
  "crashed",
44
44
  "timed_out",
45
45
  "infra_error",
46
+ "context_overflow",
47
+ "stalled_context_continuation",
46
48
  "commit_missing",
47
49
  "docs_missing",
48
50
  "merge_conflict",
@@ -90,6 +92,12 @@ def parse_args():
90
92
  parser.add_argument("--max-retries", type=int, default=3, help="Maximum code retry count (default: 3)")
91
93
  parser.add_argument("--max-infra-retries", type=int, default=3, help="Maximum infrastructure retry count (default: 3)")
92
94
  parser.add_argument("--project-root", default=None, help="Project root directory. Required for 'clean' action.")
95
+ parser.add_argument("--active-dev-branch", default=None, help="Development branch preserved for continuation after context overflow.")
96
+ parser.add_argument("--base-branch", default=None, help="Original/base branch for continuation branch reuse.")
97
+ parser.add_argument("--last-fatal-error-code", default=None, help="Fatal runtime error code associated with continuation.")
98
+ parser.add_argument("--continuation-summary-path", default=None, help="Optional continuation summary artifact path.")
99
+ parser.add_argument("--no-progress-count", type=int, default=None, help="Consecutive context-overflow sessions without objective progress.")
100
+ parser.add_argument("--progress-fingerprint", default=None, help="JSON progress fingerprint captured after the session.")
93
101
  return parser.parse_args()
94
102
 
95
103
 
@@ -122,7 +130,7 @@ def load_refactor_status(state_dir, refactor_id):
122
130
  The 'status' field is NOT included — status lives exclusively
123
131
  in refactor-list.json.
124
132
  """
125
- status_path = os.path.join(state_dir, "refactors", refactor_id, "status.json")
133
+ status_path = os.path.join(state_dir, refactor_id, "status.json")
126
134
  if not os.path.isfile(status_path):
127
135
  return _default_status(refactor_id)
128
136
  data, err = load_json_file(status_path)
@@ -137,7 +145,7 @@ def save_refactor_status(state_dir, refactor_id, status_data):
137
145
  """Write the status.json for a refactor (runtime fields only)."""
138
146
  # Defensively strip status — it belongs in refactor-list.json
139
147
  status_data.pop("status", None)
140
- status_path = os.path.join(state_dir, "refactors", refactor_id, "status.json")
148
+ status_path = os.path.join(state_dir, refactor_id, "status.json")
141
149
  return write_json_file(status_path, status_data)
142
150
 
143
151
 
@@ -168,6 +176,128 @@ def update_refactor_in_list(refactor_list_path, refactor_id, new_status):
168
176
  return write_json_file(refactor_list_path, data)
169
177
 
170
178
 
179
+ CONTINUATION_PENDING_FIELDS = [
180
+ "active_dev_branch",
181
+ "base_branch",
182
+ "continuation_pending",
183
+ "continuation_reason",
184
+ "last_context_overflow_session_id",
185
+ "previous_session_id",
186
+ "last_fatal_error_code",
187
+ "continuation_summary_path",
188
+ "no_progress_count",
189
+ "last_progress_fingerprint",
190
+ "needs_attention",
191
+ "stalled_context_continuation",
192
+ "stalled_reason",
193
+ ]
194
+
195
+ CONTINUATION_COUNTER_FIELDS = [
196
+ "context_overflow_count",
197
+ "continuation_count",
198
+ ]
199
+
200
+
201
+ def has_pending_continuation_metadata(status_data):
202
+ """Return True when runtime state still has pending continuation metadata."""
203
+ return any(key in status_data for key in CONTINUATION_PENDING_FIELDS)
204
+
205
+
206
+ def clear_pending_continuation_metadata(status_data):
207
+ """Clear branch/pending continuation fields while preserving observability counters."""
208
+ for key in CONTINUATION_PENDING_FIELDS:
209
+ status_data.pop(key, None)
210
+
211
+
212
+ def reset_continuation_metadata(status_data):
213
+ """Clear all continuation runtime metadata, including manual-reset counters."""
214
+ clear_pending_continuation_metadata(status_data)
215
+ for key in CONTINUATION_COUNTER_FIELDS:
216
+ status_data.pop(key, None)
217
+
218
+
219
+ STALL_REASON = "context_overflow_without_git_checkpoint_or_artifact_progress"
220
+
221
+
222
+ def _load_progress_fingerprint(raw):
223
+ if not raw:
224
+ return None
225
+ try:
226
+ return json.loads(raw)
227
+ except (TypeError, ValueError):
228
+ return {"unparsed": str(raw)}
229
+
230
+
231
+ def _set_continuation_common(status_data, args, session_id):
232
+ if args.active_dev_branch:
233
+ status_data["active_dev_branch"] = args.active_dev_branch
234
+ if args.base_branch:
235
+ status_data["base_branch"] = args.base_branch
236
+ status_data["last_context_overflow_session_id"] = session_id
237
+ status_data["previous_session_id"] = session_id
238
+ status_data["last_fatal_error_code"] = args.last_fatal_error_code or "context_overflow"
239
+ if args.continuation_summary_path:
240
+ status_data["continuation_summary_path"] = args.continuation_summary_path
241
+ else:
242
+ status_data.pop("continuation_summary_path", None)
243
+ if args.no_progress_count is not None:
244
+ status_data["no_progress_count"] = max(0, args.no_progress_count)
245
+ fingerprint = _load_progress_fingerprint(args.progress_fingerprint)
246
+ if fingerprint is not None:
247
+ status_data["last_progress_fingerprint"] = fingerprint
248
+ status_data["last_infra_error_session_id"] = None
249
+ status_data["resume_from_phase"] = None
250
+ if session_id:
251
+ status_data["last_session_id"] = session_id
252
+
253
+
254
+ def set_context_overflow_continuation_metadata(status_data, args, session_id):
255
+ """Record continuation metadata for a context-overflow outcome in status.json."""
256
+ status_data["context_overflow_count"] = status_data.get("context_overflow_count", 0) + 1
257
+ status_data["continuation_count"] = status_data.get("continuation_count", 0) + 1
258
+ status_data["continuation_pending"] = True
259
+ status_data["continuation_reason"] = "context_overflow"
260
+ status_data.pop("needs_attention", None)
261
+ status_data.pop("stalled_context_continuation", None)
262
+ status_data.pop("stalled_reason", None)
263
+ _set_continuation_common(status_data, args, session_id)
264
+
265
+
266
+ def set_stalled_context_continuation_metadata(status_data, args, session_id):
267
+ """Record no-progress context-overflow stall without consuming retry budgets."""
268
+ status_data["context_overflow_count"] = status_data.get("context_overflow_count", 0) + 1
269
+ status_data["continuation_count"] = status_data.get("continuation_count", 0) + 1
270
+ _set_continuation_common(status_data, args, session_id)
271
+ status_data["continuation_pending"] = False
272
+ status_data["continuation_reason"] = STALL_REASON
273
+ status_data["needs_attention"] = True
274
+ status_data["stalled_context_continuation"] = True
275
+ status_data["stalled_reason"] = STALL_REASON
276
+ if args.no_progress_count is None or args.no_progress_count < 2:
277
+ status_data["no_progress_count"] = 2
278
+
279
+
280
+ def continuation_metadata_summary(status_data):
281
+ """Return additive continuation fields for CLI JSON outputs."""
282
+ return {
283
+ "active_dev_branch": status_data.get("active_dev_branch"),
284
+ "base_branch": status_data.get("base_branch"),
285
+ "continuation_pending": status_data.get("continuation_pending", False),
286
+ "continuation_reason": status_data.get("continuation_reason"),
287
+ "last_context_overflow_session_id": status_data.get("last_context_overflow_session_id"),
288
+ "last_fatal_error_code": status_data.get("last_fatal_error_code"),
289
+ "previous_session_id": status_data.get("previous_session_id"),
290
+ "context_overflow_count": status_data.get("context_overflow_count", 0),
291
+ "continuation_count": status_data.get("continuation_count", 0),
292
+ "no_progress_count": status_data.get("no_progress_count", 0),
293
+ "last_progress_fingerprint": status_data.get("last_progress_fingerprint"),
294
+ "needs_attention": status_data.get("needs_attention", False),
295
+ "stalled_context_continuation": status_data.get("stalled_context_continuation", False),
296
+ "stalled_reason": status_data.get("stalled_reason"),
297
+ "continuation_summary_path": status_data.get("continuation_summary_path"),
298
+ }
299
+
300
+
171
301
  # ---------------------------------------------------------------------------
172
302
  # Action: get_next
173
303
  # ---------------------------------------------------------------------------
@@ -275,6 +405,7 @@ def action_get_next(refactor_list_data, state_dir):
275
405
  "infra_error_count": chosen_status_data.get("infra_error_count", 0),
276
406
  "resume_from_phase": chosen_status_data.get("resume_from_phase", None),
277
407
  }
408
+ result.update(continuation_metadata_summary(chosen_status_data))
278
409
  print(json.dumps(result, indent=2, ensure_ascii=False))
279
410
 
280
411
 
@@ -298,12 +429,15 @@ def action_update(args, refactor_list_path, state_dir):
298
429
 
299
430
  rs = load_refactor_status(state_dir, refactor_id)
300
431
 
432
+ current_list_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
433
+
301
434
  # Track what status we write to refactor-list.json
302
- new_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
435
+ new_status = current_list_status
303
436
 
304
437
  if session_status == "success":
305
438
  rs["infra_error_count"] = 0
306
439
  rs["last_infra_error_session_id"] = None
440
+ clear_pending_continuation_metadata(rs)
307
441
  new_status = "completed"
308
442
  rs["resume_from_phase"] = None
309
443
  err = update_refactor_in_list(refactor_list_path, refactor_id, "completed")
@@ -335,7 +469,7 @@ def action_update(args, refactor_list_path, state_dir):
335
469
  rs["infra_error_count"] = infra_error_count
336
470
  rs["last_infra_error_session_id"] = session_id
337
471
  rs["max_infra_retries"] = max_infra_retries
338
- rs["degraded_reason"] = "infra_error"
472
+ rs["degraded_reason"] = session_status
339
473
  if session_id:
340
474
  rs["last_session_id"] = session_id
341
475
  rs["resume_from_phase"] = None
@@ -347,6 +481,23 @@ def action_update(args, refactor_list_path, state_dir):
347
481
  else:
348
482
  new_status = "pending"
349
483
 
484
+ err = update_refactor_in_list(refactor_list_path, refactor_id, new_status)
485
+ if err:
486
+ error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
487
+ return
488
+ elif session_status == "context_overflow":
489
+ # Context overflow is continuation state, not code or infra retry state.
490
+ set_context_overflow_continuation_metadata(rs, args, session_id)
491
+ new_status = "in_progress"
492
+ if current_list_status != "in_progress":
493
+ err = update_refactor_in_list(refactor_list_path, refactor_id, "in_progress")
494
+ if err:
495
+ error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
496
+ return
497
+ elif session_status == "stalled_context_continuation":
498
+ # No-progress context-overflow stall pauses automation without consuming retry budgets.
499
+ set_stalled_context_continuation_metadata(rs, args, session_id)
500
+ new_status = "failed"
350
501
  err = update_refactor_in_list(refactor_list_path, refactor_id, new_status)
351
502
  if err:
352
503
  error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
@@ -415,6 +566,14 @@ def action_update(args, refactor_list_path, state_dir):
415
566
  summary["restart_policy"] = "infra_retry"
416
567
  summary["infra_error_count"] = rs.get("infra_error_count", 0)
417
568
  summary["artifacts_preserved"] = True
569
+ elif session_status == "context_overflow":
570
+ summary["restart_policy"] = "context_overflow_continuation"
571
+ summary.update(continuation_metadata_summary(rs))
572
+ summary["artifacts_preserved"] = True
573
+ elif session_status == "stalled_context_continuation":
574
+ summary["restart_policy"] = "needs_attention"
575
+ summary.update(continuation_metadata_summary(rs))
576
+ summary["artifacts_preserved"] = True
418
577
  elif session_status != "success":
419
578
  summary["restart_policy"] = "full_restart"
420
579
  summary["cleanup_performed"] = cleaned
@@ -442,7 +601,7 @@ def cleanup_refactor_artifacts(state_dir, refactor_id, project_root=None):
442
601
  cleaned = []
443
602
 
444
603
  # 1) Remove all session history
445
- sessions_dir = os.path.join(state_dir, "refactors", refactor_id, "sessions")
604
+ sessions_dir = os.path.join(state_dir, refactor_id, "sessions")
446
605
  sessions_deleted = 0
447
606
  if os.path.isdir(sessions_dir):
448
607
  for entry in os.listdir(sessions_dir):
@@ -453,7 +612,7 @@ def cleanup_refactor_artifacts(state_dir, refactor_id, project_root=None):
453
612
  cleaned.append("Deleted {} session(s) from {}".format(sessions_deleted, sessions_dir))
454
613
 
455
614
  # 2) Remove transient files under refactor dir (keep status.json)
456
- refactor_dir = os.path.join(state_dir, "refactors", refactor_id)
615
+ refactor_dir = os.path.join(state_dir, refactor_id)
457
616
  if os.path.isdir(refactor_dir):
458
617
  for entry in os.listdir(refactor_dir):
459
618
  if entry == "status.json" or entry == "sessions":
@@ -481,9 +640,47 @@ def cleanup_refactor_artifacts(state_dir, refactor_id, project_root=None):
481
640
  return cleaned
482
641
 
483
642
 
643
+ def _resolve_project_file(project_root, path):
644
+ if not path:
645
+ return None
646
+ if os.path.isabs(path):
647
+ target = os.path.abspath(path)
648
+ else:
649
+ target = os.path.abspath(os.path.join(project_root, path))
650
+ root = os.path.abspath(project_root)
651
+ try:
652
+ rel = os.path.relpath(target, root)
653
+ except ValueError:
654
+ return None
655
+ if rel == os.pardir or rel.startswith(os.pardir + os.sep):
656
+ return None
657
+ return target
658
+
659
+
660
+ def cleanup_continuation_artifacts(project_root, status_data, candidate_paths=None):
661
+ """Delete durable continuation handoff files for a manual clean reset."""
662
+ candidates = []
663
+ if isinstance(status_data, dict):
664
+ candidates.append(status_data.get("continuation_summary_path"))
665
+ candidates.extend(candidate_paths or [])
666
+
667
+ cleaned = []
668
+ seen = set()
669
+ for candidate in candidates:
670
+ target = _resolve_project_file(project_root, candidate)
671
+ if not target or target in seen:
672
+ continue
673
+ seen.add(target)
674
+ if os.path.basename(target) != "continuation-summary.md":
675
+ continue
676
+ if os.path.isfile(target):
677
+ os.remove(target)
678
+ cleaned.append("Deleted continuation artifact {}".format(target))
679
+ return cleaned
680
+
681
+
484
682
  def load_session_status(state_dir, refactor_id, session_id):
485
- session_status_path = os.path.join(
486
- state_dir, "refactors", refactor_id, "sessions",
683
+ session_status_path = os.path.join(state_dir, refactor_id, "sessions",
487
684
  session_id, "session-status.json"
488
685
  )
489
686
  data, err = load_json_file(session_status_path)
@@ -757,6 +954,7 @@ def action_reset(args, refactor_list_path, state_dir):
757
954
  rs["sessions"] = []
758
955
  rs["last_session_id"] = None
759
956
  rs["resume_from_phase"] = None
957
+ reset_continuation_metadata(rs)
760
958
  rs["updated_at"] = now_iso()
761
959
 
762
960
  err = save_refactor_status(state_dir, refactor_id, rs)
@@ -797,7 +995,7 @@ def action_clean(args, refactor_list_path, state_dir):
797
995
  cleaned = []
798
996
 
799
997
  # 1. Delete session history
800
- sessions_dir = os.path.join(state_dir, "refactors", refactor_id, "sessions")
998
+ sessions_dir = os.path.join(state_dir, refactor_id, "sessions")
801
999
  sessions_deleted = 0
802
1000
  if os.path.isdir(sessions_dir):
803
1001
  for entry in os.listdir(sessions_dir):
@@ -824,12 +1022,19 @@ def action_clean(args, refactor_list_path, state_dir):
824
1022
  old_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
825
1023
  old_retry = rs.get("retry_count", 0)
826
1024
 
1025
+ continuation_candidates = [
1026
+ os.path.join(".prizmkit", "refactor", refactor_id, "continuation-summary.md"),
1027
+ os.path.join(".prizmkit", "specs", refactor_id, "continuation-summary.md"),
1028
+ ]
1029
+ cleaned.extend(cleanup_continuation_artifacts(project_root, rs, continuation_candidates))
1030
+
827
1031
  rs["retry_count"] = 0
828
1032
  rs["infra_error_count"] = 0
829
1033
  rs["last_infra_error_session_id"] = None
830
1034
  rs["sessions"] = []
831
1035
  rs["last_session_id"] = None
832
1036
  rs["resume_from_phase"] = None
1037
+ reset_continuation_metadata(rs)
833
1038
  rs["updated_at"] = now_iso()
834
1039
 
835
1040
  err = save_refactor_status(state_dir, refactor_id, rs)
@@ -905,10 +1110,11 @@ def action_start(args, refactor_list_path, state_dir):
905
1110
  error_out("Failed to save refactor status: {}".format(err))
906
1111
  return
907
1112
 
908
- err = update_refactor_in_list(refactor_list_path, refactor_id, "in_progress")
909
- if err:
910
- error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
911
- return
1113
+ if old_status != "in_progress":
1114
+ err = update_refactor_in_list(refactor_list_path, refactor_id, "in_progress")
1115
+ if err:
1116
+ error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
1117
+ return
912
1118
 
913
1119
  result = {
914
1120
  "action": "start",
@@ -1060,6 +1266,7 @@ def action_unskip(args, refactor_list_path, state_dir):
1060
1266
  rs["sessions"] = []
1061
1267
  rs["last_session_id"] = None
1062
1268
  rs["resume_from_phase"] = None
1269
+ reset_continuation_metadata(rs)
1063
1270
  rs["updated_at"] = now_iso()
1064
1271
  save_refactor_status(state_dir, rid, rs)
1065
1272
 
@@ -35,7 +35,7 @@ Test command:
35
35
  Known baseline failures:
36
36
  `{{BASELINE_FAILURES}}`
37
37
 
38
- If plan.md has more than 5 tasks, run `/compact` after completing every 3 tasks to manage context budget. If `/compact` is unavailable, continue without it.
38
+ If plan.md has more than 5 tasks, update durable checkpoints/artifacts after every 3 tasks, minimize output, and rely on runner continuation if context overflow occurs. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
39
39
 
40
40
  ## Required Outputs
41
41
 
@@ -12,7 +12,7 @@ You are the **session orchestrator**. Implement Feature {{FEATURE_ID}}: "{{FEATU
12
12
 
13
13
  **CRITICAL**: You MUST NOT exit until ALL work is complete and committed.
14
14
 
15
- **Tier 1 — Single Agent**: You handle everything directly. No normal work subagents; the log-size monitor is the only allowed persistent background subagent. No TeamCreate.
15
+ **Tier 1 — Single Agent**: You handle everything directly. No normal work subagents. No TeamCreate.
16
16
 
17
17
  ### Feature Description
18
18
 
@@ -145,7 +145,7 @@ You know this project's tech stack. Identify ALL test commands that apply (e.g.,
145
145
  - Runs tests using `TEST_CMD` after each task
146
146
  - Writes '## Implementation Log' to `context-snapshot.md`
147
147
 
148
- **3b-compact.** Context management — if plan.md has more than 5 tasks, run `/compact` after completing every 3 tasks during implementation. This prevents context window exhaustion in long sessions. If `/compact` is not available (non-Claude CLI), skip this step.
148
+ **3b-checkpoint.** Context management — if plan.md has more than 5 tasks, update durable checkpoints/artifacts after every 3 tasks, minimize output, and continue. If context overflow occurs in a headless run, the runner starts a fresh continuation session from those checkpoints and artifacts. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
149
149
 
150
150
  **3c.** After implement completes, verify:
151
151
  1. All tasks in plan.md are `[x]`
@@ -10,7 +10,7 @@
10
10
 
11
11
  You are the **session orchestrator**. Implement Feature {{FEATURE_ID}}: "{{FEATURE_TITLE}}".
12
12
 
13
- **CRITICAL**: You MUST NOT exit until ALL work is complete and committed. When you spawn normal work subagents, wait for each to finish (run_in_background=false). The log-size monitor is the only allowed persistent background subagent.
13
+ **CRITICAL**: You MUST NOT exit until ALL work is complete and committed. When you spawn normal work subagents, wait for each to finish (run_in_background=false).
14
14
 
15
15
  **Tier 2 — Dual Agent**: You handle context + planning directly. Then spawn Dev and Reviewer subagents. Spawn Dev and Reviewer agents via the Agent tool.
16
16
 
@@ -276,7 +276,7 @@ Prompt:
276
276
  > ⚠️ DO NOT re-read source files already listed in Section 4 File Manifest unless you need implementation detail beyond the interface summary.
277
277
  > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` for full context.
278
278
  > 2. Run `/prizmkit-implement` to execute the tasks in plan.md. Run tests with: `{{TEST_CMD}}`. Known baseline failures (pre-existing, not your fault): `{{BASELINE_FAILURES}}`.
279
- > 3. If plan.md has more than 5 tasks: run `/compact` after completing every 3 tasks to manage context budget. If `/compact` is unavailable, continue without it.
279
+ > 3. If plan.md has more than 5 tasks: update durable checkpoints/artifacts after every 3 tasks, minimize output, and rely on runner continuation if context overflow occurs. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
280
280
  > 4. After implement completes, verify the '## Implementation Log' section was written to context-snapshot.md.
281
281
  > 5. Do NOT execute any git commands (no git add/commit/reset/push).
282
282
  > Do NOT exit until all tasks are [x] and the '## Implementation Log' section is written in context-snapshot.md."
@@ -604,7 +604,7 @@ If you encounter an unrecoverable error, context overflow, or are about to exit
604
604
  - Build context-snapshot.md FIRST; all subagents read it instead of re-reading source files
605
605
  - context-snapshot.md is append-only: orchestrator writes Sections 1-4, Dev appends Implementation Log, Reviewer appends Review Notes
606
606
  - Gate checks enforce Implementation Log and Review Notes are written before proceeding
607
- - Do NOT use `run_in_background=true` when spawning normal work subagents; the log-size monitor is the only allowed persistent background subagent
607
+ - Do NOT use `run_in_background=true` when spawning normal work subagents
608
608
  - **NEVER delete, modify, or touch any file under `.prizmkit/state/`** — those are pipeline runtime files managed by the runner
609
609
  - NEVER run `rm -rf`, `git clean`, or any destructive filesystem operations
610
610
  - `/prizmkit-committer` is mandatory, and must not be replaced with manual git commit commands
@@ -10,7 +10,7 @@
10
10
 
11
11
  You are the **session orchestrator**. Implement Feature {{FEATURE_ID}}: "{{FEATURE_TITLE}}".
12
12
 
13
- **CRITICAL**: You MUST NOT exit until ALL work is complete and committed. When you spawn normal work subagents, wait for each to finish (run_in_background=false). The log-size monitor is the only allowed persistent background subagent. Do NOT spawn work agents in background and exit — that kills the session.
13
+ **CRITICAL**: You MUST NOT exit until ALL work is complete and committed. When you spawn normal work subagents, wait for each to finish (run_in_background=false). Do NOT spawn work agents in background and exit — that kills the session.
14
14
 
15
15
  **Tier 3 — Full Team**: For complex features, use the full pipeline (Phase 0–6) with Dev + Reviewer agents spawned via the Agent tool.
16
16
 
@@ -284,7 +284,7 @@ Prompt:
284
284
  > ⚠️ DO NOT re-read source files already listed in Section 4 File Manifest unless you need implementation detail beyond the interface summary.
285
285
  > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` for full context.
286
286
  > 2. Run `/prizmkit-implement` to execute the tasks in plan.md. Run tests with: `{{TEST_CMD}}`. Known baseline failures (pre-existing, not your fault): `{{BASELINE_FAILURES}}`.
287
- > 3. If plan.md has more than 5 tasks: run `/compact` after completing every 3 tasks to manage context budget. If `/compact` is unavailable, continue without it.
287
+ > 3. If plan.md has more than 5 tasks: update durable checkpoints/artifacts after every 3 tasks, minimize output, and rely on runner continuation if context overflow occurs. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
288
288
  > 4. After implement completes, verify the '## Implementation Log' section was written to context-snapshot.md.
289
289
  > 5. Do NOT execute any git commands (no git add/commit/reset/push).
290
290
  > Do NOT exit until all tasks are [x] and the '## Implementation Log' section is written in context-snapshot.md."
@@ -638,7 +638,7 @@ If you encounter an unrecoverable error, context overflow, or are about to exit
638
638
  - Tier 3: full team — Dev (implementation) → Reviewer (review) — spawn agents directly via Agent tool
639
639
  - context-snapshot.md is append-only: orchestrator writes Sections 1-4, Dev appends Implementation Log, Reviewer appends Review Notes
640
640
  - Gate checks enforce Implementation Log and Review Notes are written before proceeding
641
- - Do NOT use `run_in_background=true` when spawning normal work agents; the log-size monitor is the only allowed persistent background subagent
641
+ - Do NOT use `run_in_background=true` when spawning normal work agents
642
642
  - Commit phase must use `/prizmkit-committer`; do NOT replace with manual git commit commands
643
643
  - **NEVER delete, modify, or touch any file under `.prizmkit/state/`** — those are pipeline runtime files managed by the runner
644
644
  - NEVER run `rm -rf`, `git clean`, or any destructive filesystem operations
@@ -13,7 +13,7 @@ You are the **bug fix session agent**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}".
13
13
 
14
14
  **CRITICAL**: You MUST NOT exit until ALL work is complete and committed.
15
15
 
16
- **SUBAGENT LIFECYCLE**: The log-size monitor is the only allowed persistent background subagent. Any normal work subagent must be awaited with `run_in_background=false`.
16
+ **SUBAGENT LIFECYCLE**: Any normal work subagent must be awaited with `run_in_background=false`. Do not spawn persistent background subagents for headless recovery.
17
17
 
18
18
  **NON-INTERACTIVE MODE**: There is NO human on the other end. NEVER ask for user confirmation, NEVER wait for user input. Make decisions autonomously and move forward.
19
19
 
@@ -12,7 +12,7 @@
12
12
 
13
13
  You are the **refactor session orchestrator**. Execute Refactor {{REFACTOR_ID}}: "{{REFACTOR_TITLE}}".
14
14
 
15
- **CRITICAL SESSION LIFECYCLE RULE**: You MUST NOT exit until ALL work is complete and session-status.json is written. When you spawn normal work subagents, you MUST **wait for each to finish** (run_in_background=false) before proceeding. The log-size monitor is the only allowed persistent background subagent. Do NOT spawn a work agent in the background and exit — that kills the session.
15
+ **CRITICAL SESSION LIFECYCLE RULE**: You MUST NOT exit until ALL work is complete and session-status.json is written. When you spawn normal work subagents, you MUST **wait for each to finish** (run_in_background=false) before proceeding. Do NOT spawn a work agent in the background and exit — that kills the session.
16
16
 
17
17
  **NON-INTERACTIVE MODE**: You are running in headless non-interactive mode. There is NO human on the other end. NEVER ask for user confirmation, NEVER wait for user input, NEVER use interactive prompts (e.g. "Would you like me to…"). If a skill has an interactive step (e.g. offer remediation, ask for approval), skip it and proceed autonomously. Make decisions based on the data available and move forward.
18
18
 
@@ -185,7 +185,7 @@ Evidence must cover affected UI render, primary user interactions, behavior-sens
185
185
 
186
186
  {{END_IF_BROWSER_INTERACTION}}
187
187
 
188
- 7. If the refactor involves multiple files: run `/compact` after completing half the tasks to free context budget. If `/compact` is unavailable, continue without it.
188
+ 7. If the refactor involves multiple files: update durable checkpoints/artifacts at the midpoint, minimize output, and rely on runner continuation if context overflow occurs. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
189
189
  8. After all tasks complete, run the full test suite one final time
190
190
  "
191
191
  - **Wait for Dev to return**
@@ -337,4 +337,4 @@ Write to: `{{SESSION_STATUS_PATH}}`
337
337
  - **Commit with** `refactor(<scope>):` prefix, NOT `feat:` or `fix:`
338
338
  - **Run full retrospective** (Job 1 + Job 2) — refactoring changes code structure
339
339
  - ALWAYS write session-status.json before exiting
340
- - Do NOT use `run_in_background=true` when spawning normal work agents; the log-size monitor is the only allowed persistent background subagent
340
+ - Do NOT use `run_in_background=true` when spawning normal work agents
@@ -1,76 +1,41 @@
1
- ## Session Log Budget Monitoring (PROACTIVE)
1
+ ## Session Log Budget Awareness (CHECKPOINT GUIDANCE)
2
2
 
3
- Your session log grows with every tool call and subagent spawn.
4
- To prevent context exhaustion, **spawn a background monitoring subagent at session start**.
3
+ Your session log grows with every tool call and agent interaction. Use this
4
+ section as passive checkpoint guidance only; do not spawn a background helper for
5
+ log-size checks.
5
6
 
6
- ### How to Set Up the Monitor
7
+ ### Checkpoint Before Large Work Batches
7
8
 
8
- 1. **AS THE VERY FIRST ACTION** (before Phase 0), spawn a monitoring subagent
9
- with `run_in_background: true` and this task:
9
+ - `MAX_LOG_SIZE` controls whether this advisory section is included. It is not a
10
+ runtime recovery mechanism.
11
+ - Before major phases or large task batches, update durable artifacts that a
12
+ continuation session can trust: plan/task checkboxes, context snapshots,
13
+ implementation or review notes, workflow checkpoints, session status, and test
14
+ reports where applicable.
15
+ - Keep command output small. Capture large outputs to temporary files and read
16
+ only filtered summaries needed for the current decision.
17
+ - Prefer already-built context snapshots and manifests over broad source-file
18
+ re-reads.
10
19
 
11
- ```
12
- Run this command and keep it running:
13
- bash {{LOG_MONITOR_SCRIPT}} {{SESSION_LOG_PATH}} {{MAX_LOG_SIZE}} {{LOG_MONITOR_POLL_INTERVAL}} {{LOG_MONITOR_STATUS_FILE}} {{LOG_MONITOR_BASELINE_FILE}}
14
- ```
20
+ ### Headless Recovery Source of Truth
15
21
 
16
- The subagent MUST use `run_in_background: true`. It writes a JSON status
17
- file every {{LOG_MONITOR_POLL_INTERVAL}} seconds.
22
+ - Headless recovery must rely on durable checkpoints and artifacts, not an
23
+ interactive slash command.
24
+ - If context overflow still occurs, the runner detects the overflow and starts a
25
+ fresh continuation session from checkpoint files and durable artifacts.
26
+ - Durable checkpoint files and artifacts are the source of truth for what was
27
+ completed, what remains, and the tests or review gates already run.
18
28
 
19
- 2. **BEFORE EACH MAJOR PHASE** (Phase 0, 1, 2, ...), read the status:
29
+ ### Interactive Claude Code Note
20
30
 
21
- ```bash
22
- cat {{LOG_MONITOR_STATUS_FILE}} 2>/dev/null || echo '{"status":"OK"}'
23
- ```
24
-
25
- The status file contains structured diagnostics:
26
-
27
- ```json
28
- {
29
- "status": "OK | COMPACT_NEEDED",
30
- "total_bytes": <current session.log size>,
31
- "baseline_bytes": <size after last /compact>,
32
- "effective_bytes": <total - baseline, incremental growth>,
33
- "threshold_bytes": <{{MAX_LOG_SIZE}}>,
34
- "overage_bytes": <effective - threshold, 0 when OK>,
35
- "usage_pct": <effective / threshold * 100>
36
- }
37
- ```
38
-
39
- Extract fields for decision-making:
40
- ```bash
41
- status=$(jq -r '.status' {{LOG_MONITOR_STATUS_FILE}})
42
- effective=$(jq -r '.effective_bytes' {{LOG_MONITOR_STATUS_FILE}})
43
- overage=$(jq -r '.overage_bytes' {{LOG_MONITOR_STATUS_FILE}})
44
- usage=$(jq -r '.usage_pct' {{LOG_MONITOR_STATUS_FILE}})
45
- ```
46
-
47
- 3. **On COMPACT_NEEDED** (effective_bytes > {{MAX_LOG_SIZE}} bytes, {{MAX_LOG_SIZE_HUMAN}}):
48
- - Check `overage_bytes` to decide urgency:
49
- - **Minor overage** (< 512KB): complete the current sub-step, update durable workflow checkpoint artifacts, write baseline, `/compact`
50
- - **Major overage** (>= 512KB): update durable workflow checkpoint artifacts, write baseline, `/compact` — do NOT start new work
51
- - Write baseline before `/compact`:
52
- ```bash
53
- wc -c < {{SESSION_LOG_PATH}} > {{LOG_MONITOR_BASELINE_FILE}}
54
- ```
55
- - Execute `/compact`
56
- - Continue the workflow after `/compact`; re-read this status file before the next major phase
57
-
58
- ### How the Baseline Prevents Infinite Compaction Loops
59
-
60
- - Before the first `/compact`: baseline = 0, effective = full log size → triggers when log exceeds threshold
61
- - After `/compact`: baseline = current log size, effective ≈ 0 → status resets to OK
62
- - The monitor only triggers again when **new** log growth since the last compact exceeds the threshold
63
- - Each `/compact` resets the baseline, breaking the infinite loop
31
+ - `/compact` may help when a human is manually operating an interactive Claude
32
+ Code session.
33
+ - `/compact` is optional interactive convenience only. It must not be required
34
+ for headless recovery and must not be delegated to a background helper.
64
35
 
65
36
  ### Log Growth Reduction Tips
66
37
 
67
- - **Re-read context-snapshot.md instead of source files** already in context
68
- - **Filter command output** pipe to `tail -20` or `grep`, never full output
69
- - **Limit subagent prompts** shorter, focused prompts = shorter transcripts
70
- - **Checkpoint often** update durable workflow checkpoint artifacts before `/compact`
71
-
72
- ### What Survives
73
-
74
- - All git commits are permanent
75
- - The workflow checkpoint is preserved and auto-resumed
76
- - `/compact` compresses context without losing progress
38
+ - Reuse context-snapshot.md instead of re-reading files already summarized there.
39
+ - Filter command output before loading it into context.
40
+ - Keep agent prompts focused and avoid unnecessary transcript expansion.
41
+ - Checkpoint often so automatic continuation can resume from durable state.
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "status": {
17
17
  "type": "string",
18
- "enum": ["success", "partial", "failed", "commit_missing", "docs_missing", "merge_conflict"]
18
+ "enum": ["success", "partial", "failed", "commit_missing", "docs_missing", "merge_conflict", "context_overflow"]
19
19
  },
20
20
  "completed_phases": {
21
21
  "type": "array",
@@ -24,5 +24,6 @@ def _load_hyphenated_module(module_name, filename):
24
24
  # Register hyphenated script files so tests can import them with underscores.
25
25
  _load_hyphenated_module("generate_bootstrap_prompt", "generate-bootstrap-prompt.py")
26
26
  _load_hyphenated_module("generate_bugfix_prompt", "generate-bugfix-prompt.py")
27
+ _load_hyphenated_module("generate_refactor_prompt", "generate-refactor-prompt.py")
27
28
  _load_hyphenated_module("prizmkit_test_gate", "prizmkit-test-gate.py")
28
29
  _load_hyphenated_module("update_feature_status", "update-feature-status.py")