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
@@ -0,0 +1,374 @@
1
+ """Continuation prompt and durable handoff helpers for pipeline prompt generators."""
2
+
3
+ import os
4
+
5
+ SUPPORTED_CONTINUATION_MODES = {"context_overflow"}
6
+ TASK_TYPES = {"feature", "bugfix", "refactor"}
7
+
8
+
9
+ def add_continuation_args(parser, default_task_type):
10
+ """Add continuation-mode CLI arguments to a prompt generator parser."""
11
+ parser.add_argument(
12
+ "--continuation-mode",
13
+ choices=sorted(SUPPORTED_CONTINUATION_MODES),
14
+ default="",
15
+ help="Continuation recovery mode, currently context_overflow.",
16
+ )
17
+ parser.add_argument(
18
+ "--previous-session-id",
19
+ default="",
20
+ help="Session ID that triggered this continuation.",
21
+ )
22
+ parser.add_argument(
23
+ "--continuation-count",
24
+ default="0",
25
+ help="Cumulative continuation count for this task.",
26
+ )
27
+ parser.add_argument(
28
+ "--context-overflow-count",
29
+ default="0",
30
+ help="Cumulative context overflow count for this task.",
31
+ )
32
+ parser.add_argument(
33
+ "--task-type",
34
+ choices=sorted(TASK_TYPES),
35
+ default=default_task_type,
36
+ help="Task type for continuation handoff metadata.",
37
+ )
38
+ parser.add_argument(
39
+ "--active-dev-branch",
40
+ default="",
41
+ help="Development branch being reused by the continuation.",
42
+ )
43
+ parser.add_argument(
44
+ "--base-branch",
45
+ default="",
46
+ help="Base branch preserved across continuation sessions.",
47
+ )
48
+ parser.add_argument(
49
+ "--continuation-summary-path",
50
+ default="",
51
+ help="Durable continuation summary artifact path.",
52
+ )
53
+
54
+
55
+ def is_continuation(args):
56
+ """Return True when continuation mode is active."""
57
+ return bool(getattr(args, "continuation_mode", ""))
58
+
59
+
60
+ def _safe_int(value, default=0):
61
+ try:
62
+ parsed = int(value)
63
+ except (TypeError, ValueError):
64
+ return default
65
+ return parsed if parsed >= 0 else default
66
+
67
+
68
+ def _norm(path):
69
+ return str(path).replace(os.sep, "/")
70
+
71
+
72
+ def _rel_path(project_root, path):
73
+ """Return a stable project-relative path when possible."""
74
+ if not path:
75
+ return "(not available)"
76
+ text = str(path)
77
+ if text.startswith(".prizmkit/") or text.startswith(".prizmkit\\"):
78
+ return _norm(text)
79
+ try:
80
+ abs_path = os.path.abspath(text)
81
+ root = os.path.abspath(project_root)
82
+ rel = os.path.relpath(abs_path, root)
83
+ if not rel.startswith(".."): # keep paths under the project concise
84
+ return _norm(rel)
85
+ except ValueError:
86
+ pass
87
+ return _norm(text)
88
+
89
+
90
+ def _dedupe(items):
91
+ result = []
92
+ seen = set()
93
+ for item in items:
94
+ if not item:
95
+ continue
96
+ key = _norm(item)
97
+ if key in seen:
98
+ continue
99
+ seen.add(key)
100
+ result.append(item)
101
+ return result
102
+
103
+
104
+ def continuation_summary_paths(task_type, task_id, task_slug, override_path=""):
105
+ """Return all durable continuation summary paths to write."""
106
+ if task_type == "feature":
107
+ default_path = os.path.join(
108
+ ".prizmkit", "specs", task_slug, "continuation-summary.md",
109
+ )
110
+ return _dedupe([override_path, default_path])
111
+
112
+ task_dir = "bugfix" if task_type == "bugfix" else "refactor"
113
+ task_local = os.path.join(
114
+ ".prizmkit", task_dir, task_id, "continuation-summary.md",
115
+ )
116
+ specs_path = os.path.join(
117
+ ".prizmkit", "specs", task_slug or task_id, "continuation-summary.md",
118
+ )
119
+ return _dedupe([override_path, specs_path, task_local])
120
+
121
+
122
+ def durable_paths(task_type, task_id, task_slug):
123
+ """Return stable durable artifact paths for a task."""
124
+ if task_type == "feature":
125
+ artifact_dir = os.path.join(".prizmkit", "specs", task_slug)
126
+ elif task_type == "bugfix":
127
+ artifact_dir = os.path.join(".prizmkit", "bugfix", task_id)
128
+ else:
129
+ artifact_dir = os.path.join(".prizmkit", "refactor", task_id)
130
+
131
+ return {
132
+ "artifact_dir": artifact_dir,
133
+ "checkpoint": os.path.join(artifact_dir, "workflow-checkpoint.json"),
134
+ "plan": os.path.join(artifact_dir, "plan.md"),
135
+ "context_snapshot": os.path.join(artifact_dir, "context-snapshot.md"),
136
+ "failure_log": os.path.join(artifact_dir, "failure-log.md"),
137
+ }
138
+
139
+
140
+ def runtime_paths(args, project_root, task_type, task_id, previous_session_id):
141
+ """Return fresh runtime paths for the new session and previous log pointer."""
142
+ output_path = getattr(args, "output", "")
143
+ session_dir = os.path.dirname(os.path.abspath(output_path)) if output_path else ""
144
+ logs_dir = os.path.join(session_dir, "logs") if session_dir else ""
145
+ session_status = os.path.join(session_dir, "session-status.json") if session_dir else ""
146
+
147
+ state_dir = getattr(args, "state_dir", "") or os.path.join(
148
+ ".prizmkit", "state", "features" if task_type == "feature" else task_type,
149
+ )
150
+ previous_log = "(unknown)"
151
+ if previous_session_id:
152
+ previous_log = os.path.join(
153
+ state_dir, task_id, "sessions", previous_session_id, "logs", "session.log",
154
+ )
155
+
156
+ return {
157
+ "session_dir": _rel_path(project_root, session_dir),
158
+ "bootstrap_prompt": _rel_path(project_root, output_path),
159
+ "session_log": _rel_path(project_root, os.path.join(logs_dir, "session.log")),
160
+ "progress_json": _rel_path(project_root, os.path.join(logs_dir, "progress.json")),
161
+ "log_monitor_status": _rel_path(project_root, os.path.join(logs_dir, "log-monitor-status.txt")),
162
+ "log_monitor_baseline": _rel_path(project_root, os.path.join(logs_dir, "log-monitor-baseline.txt")),
163
+ "pid_file": _rel_path(project_root, os.path.join(logs_dir, "ai.pid")),
164
+ "session_status": _rel_path(project_root, session_status),
165
+ "previous_session_log": _rel_path(project_root, previous_log),
166
+ }
167
+
168
+
169
+ def checkpoint_cursor(checkpoint):
170
+ """Summarize current checkpoint state and cursor."""
171
+ steps = checkpoint.get("steps", []) if isinstance(checkpoint, dict) else []
172
+ completed = []
173
+ skipped = []
174
+ in_progress = []
175
+ pending = []
176
+ failed = []
177
+ first_cursor = None
178
+
179
+ for step in steps:
180
+ skill = step.get("skill", "unknown")
181
+ label = "{} ({})".format(step.get("id", "?"), skill)
182
+ status = step.get("status", "pending")
183
+ if status == "completed":
184
+ completed.append(label)
185
+ elif status == "skipped":
186
+ skipped.append(label)
187
+ elif status == "in_progress":
188
+ in_progress.append(label)
189
+ if first_cursor is None:
190
+ first_cursor = label
191
+ elif status == "failed":
192
+ failed.append(label)
193
+ if first_cursor is None:
194
+ first_cursor = label
195
+ else:
196
+ pending.append(label)
197
+ if first_cursor is None:
198
+ first_cursor = label
199
+
200
+ if first_cursor is None:
201
+ first_cursor = "(all checkpoint steps completed or skipped)"
202
+
203
+ return {
204
+ "completed": completed,
205
+ "skipped": skipped,
206
+ "in_progress": in_progress,
207
+ "pending": pending,
208
+ "failed": failed,
209
+ "first_cursor": first_cursor,
210
+ }
211
+
212
+
213
+ def _list_or_none(values):
214
+ if not values:
215
+ return "- (none)"
216
+ return "\n".join("- {}".format(value) for value in values)
217
+
218
+
219
+ def _build_summary(context):
220
+ cursor = context["cursor"]
221
+ durable = context["durable"]
222
+ runtime = context["runtime"]
223
+ summary_paths = context["summary_paths"]
224
+ lines = [
225
+ "# Continuation Summary",
226
+ "",
227
+ "## Reason",
228
+ "",
229
+ "- Continuation mode: {}".format(context["mode"]),
230
+ "- This continuation was started after context overflow.",
231
+ "- This artifact is the durable handoff summary; do not rely on the previous full transcript.",
232
+ "",
233
+ "## Previous Session",
234
+ "",
235
+ "- Previous session id: {}".format(context["previous_session_id"] or "(unknown)"),
236
+ "- Previous session log: {}".format(runtime["previous_session_log"]),
237
+ "- Previous full session.log should only be read if synthesized artifacts are insufficient.",
238
+ "",
239
+ "## Task Identity",
240
+ "",
241
+ "- Task type: {}".format(context["task_type"]),
242
+ "- Task id: {}".format(context["task_id"]),
243
+ "- Task slug: {}".format(context["task_slug"]),
244
+ "- Active dev branch: {}".format(context["active_dev_branch"] or "(preserve current worktree branch)"),
245
+ "- Base branch: {}".format(context["base_branch"] or "(unknown)"),
246
+ "- Continuation count: {}".format(context["continuation_count"]),
247
+ "- Context overflow count: {}".format(context["context_overflow_count"]),
248
+ "",
249
+ "## Observed Partial Progress",
250
+ "",
251
+ "- First pending/in-progress checkpoint step: {}".format(cursor["first_cursor"]),
252
+ "- Completed checkpoint steps:",
253
+ _list_or_none(cursor["completed"]),
254
+ "- Skipped checkpoint steps:",
255
+ _list_or_none(cursor["skipped"]),
256
+ "- In-progress checkpoint steps:",
257
+ _list_or_none(cursor["in_progress"]),
258
+ "- Pending checkpoint steps:",
259
+ _list_or_none(cursor["pending"]),
260
+ "- Failed checkpoint steps:",
261
+ _list_or_none(cursor["failed"]),
262
+ "",
263
+ "## Durable Pointers",
264
+ "",
265
+ "- Start with current worktree state: `git status` and `git diff`.",
266
+ "- Durable task artifact directory: {}".format(_norm(durable["artifact_dir"])),
267
+ "- Workflow checkpoint: {}".format(_norm(durable["checkpoint"])),
268
+ "- Context snapshot: {}".format(_norm(durable["context_snapshot"])),
269
+ "- Plan: {}".format(_norm(durable["plan"])),
270
+ "- Failure log: {}".format(_norm(durable["failure_log"])),
271
+ "- Continuation summaries:",
272
+ _list_or_none([_norm(path) for path in summary_paths]),
273
+ "",
274
+ ]
275
+ return "\n".join(lines)
276
+
277
+
278
+ def _build_addendum(context):
279
+ cursor = context["cursor"]
280
+ durable = context["durable"]
281
+ runtime = context["runtime"]
282
+ summary_paths = context["summary_paths"]
283
+ lines = [
284
+ "## Continuation Addendum — Context Overflow Recovery",
285
+ "",
286
+ "This is an automatic continuation after context overflow.",
287
+ "This is a fresh headless AI CLI session for the same task.",
288
+ "Continue on the current branch/worktree.",
289
+ "Do not reset, clean, switch branches, or discard work.",
290
+ "",
291
+ "### Required First Actions",
292
+ "",
293
+ "- Start with `git status` and `git diff`.",
294
+ "- Reuse the same durable task artifact directory: `{}`.".format(_norm(durable["artifact_dir"])),
295
+ "- Treat completed/skipped checkpoint steps as already done.",
296
+ "- Continue from the first pending/in-progress checkpoint step: `{}`.".format(cursor["first_cursor"]),
297
+ "- Do not read the previous full `session.log` unless explicitly necessary.",
298
+ "- If previous runtime context is needed, prefer synthesized artifacts and summaries over logs.",
299
+ "- Do not attempt `/compact` as the recovery mechanism.",
300
+ "",
301
+ "### Preserved Durable Context",
302
+ "",
303
+ "- Task type: `{}`".format(context["task_type"]),
304
+ "- Task id: `{}`".format(context["task_id"]),
305
+ "- Task slug: `{}`".format(context["task_slug"]),
306
+ "- Dev branch: `{}`".format(context["active_dev_branch"] or "(current branch)"),
307
+ "- Base branch: `{}`".format(context["base_branch"] or "(unknown)"),
308
+ "- Checkpoint path: `{}`".format(_norm(durable["checkpoint"])),
309
+ "- Plan path: `{}`".format(_norm(durable["plan"])),
310
+ "- Context snapshot path: `{}`".format(_norm(durable["context_snapshot"])),
311
+ "- Failure log path: `{}`".format(_norm(durable["failure_log"])),
312
+ "- Continuation summary path(s): `{}`".format(", ".join(_norm(p) for p in summary_paths)),
313
+ "- Continuation count: `{}`".format(context["continuation_count"]),
314
+ "- Context overflow count: `{}`".format(context["context_overflow_count"]),
315
+ "- Previous session id: `{}`".format(context["previous_session_id"] or "(unknown)"),
316
+ "- Previous session log reference: `{}`".format(runtime["previous_session_log"]),
317
+ "",
318
+ "### Fresh Session Runtime Paths",
319
+ "",
320
+ "- Fresh session directory: `{}`".format(runtime["session_dir"]),
321
+ "- Generated bootstrap prompt path: `{}`".format(runtime["bootstrap_prompt"]),
322
+ "- Session log path: `{}`".format(runtime["session_log"]),
323
+ "- Progress JSON path: `{}`".format(runtime["progress_json"]),
324
+ "- Log monitor status path: `{}`".format(runtime["log_monitor_status"]),
325
+ "- Log monitor baseline path: `{}`".format(runtime["log_monitor_baseline"]),
326
+ "- PID file: `{}`".format(runtime["pid_file"]),
327
+ "- Session status file: `{}`".format(runtime["session_status"]),
328
+ "",
329
+ ]
330
+ return "\n".join(lines)
331
+
332
+
333
+ def append_continuation_handoff(rendered_prompt, args, project_root, task_type,
334
+ task_id, task_slug, checkpoint,
335
+ summary_paths=None):
336
+ """Write continuation summaries and append the addendum to a prompt."""
337
+ if not is_continuation(args):
338
+ return rendered_prompt
339
+
340
+ mode = getattr(args, "continuation_mode", "")
341
+ if mode not in SUPPORTED_CONTINUATION_MODES:
342
+ raise ValueError("Unsupported continuation mode: {}".format(mode))
343
+
344
+ durable = durable_paths(task_type, task_id, task_slug)
345
+ paths = summary_paths or continuation_summary_paths(
346
+ task_type, task_id, task_slug, getattr(args, "continuation_summary_path", ""),
347
+ )
348
+ paths = [_rel_path(project_root, path) for path in paths]
349
+ previous_session_id = getattr(args, "previous_session_id", "")
350
+ context = {
351
+ "mode": mode,
352
+ "task_type": task_type,
353
+ "task_id": task_id,
354
+ "task_slug": task_slug,
355
+ "previous_session_id": previous_session_id,
356
+ "continuation_count": _safe_int(getattr(args, "continuation_count", 0)),
357
+ "context_overflow_count": _safe_int(getattr(args, "context_overflow_count", 0)),
358
+ "active_dev_branch": getattr(args, "active_dev_branch", ""),
359
+ "base_branch": getattr(args, "base_branch", ""),
360
+ "durable": durable,
361
+ "runtime": runtime_paths(args, project_root, task_type, task_id, previous_session_id),
362
+ "cursor": checkpoint_cursor(checkpoint or {}),
363
+ "summary_paths": paths,
364
+ }
365
+
366
+ summary_text = _build_summary(context)
367
+ for summary_path in paths:
368
+ abs_summary = os.path.join(project_root, summary_path)
369
+ os.makedirs(os.path.dirname(abs_summary), exist_ok=True)
370
+ with open(abs_summary, "w", encoding="utf-8") as f:
371
+ f.write(summary_text)
372
+
373
+ addendum = _build_addendum(context)
374
+ return rendered_prompt.rstrip() + "\n\n" + addendum
@@ -113,8 +113,8 @@ def load_json(path):
113
113
 
114
114
 
115
115
  def discover_item_ids(state_dir, subdir):
116
- """Return a sorted list of item IDs found in state/{subdir}/."""
117
- items_dir = os.path.join(state_dir, subdir)
116
+ """Return a sorted list of item IDs found directly under the typed state dir."""
117
+ items_dir = os.path.join(state_dir, subdir) if subdir else state_dir
118
118
  if not os.path.isdir(items_dir):
119
119
  return []
120
120
  ids = []
@@ -363,9 +363,9 @@ def find_task_list(state_dir):
363
363
 
364
364
  # Pipeline type configurations
365
365
  PIPELINE_CONFIG = {
366
- "feature": {"subdir": "features", "items_key": "features", "id_label": "feature_id"},
367
- "bugfix": {"subdir": "bugs", "items_key": "bugs", "id_label": "bug_id"},
368
- "refactor": {"subdir": "refactors", "items_key": "refactors", "id_label": "refactor_id"},
366
+ "feature": {"subdir": "", "items_key": "features", "id_label": "feature_id"},
367
+ "bugfix": {"subdir": "", "items_key": "bugs", "id_label": "bug_id"},
368
+ "refactor": {"subdir": "", "items_key": "refactors", "id_label": "refactor_id"},
369
369
  }
370
370
 
371
371
 
@@ -28,11 +28,11 @@ import re
28
28
  import sys
29
29
 
30
30
  from utils import enrich_global_context, load_json_file, read_platform_conventions, setup_logging
31
+ from continuation import add_continuation_args, append_continuation_handoff, is_continuation
31
32
 
32
33
 
33
34
  DEFAULT_MAX_RETRIES = 3
34
35
  DEFAULT_MAX_LOG_SIZE = 2097152
35
- DEFAULT_LOG_MONITOR_POLL_INTERVAL = 30
36
36
 
37
37
  LOGGER = setup_logging("generate-bootstrap-prompt")
38
38
 
@@ -115,6 +115,8 @@ def parse_args():
115
115
  help="Do not write workflow-checkpoint.json (used by pipeline dry-run)",
116
116
  )
117
117
 
118
+ add_continuation_args(parser, "feature")
119
+
118
120
  return parser.parse_args()
119
121
 
120
122
 
@@ -430,9 +432,7 @@ def get_prev_session_status(state_dir, feature_id):
430
432
  return "N/A (first run)"
431
433
 
432
434
  # Try to read the feature status file to find the last session
433
- feature_status_path = os.path.join(
434
- state_dir, "features", feature_id, "status.json"
435
- )
435
+ feature_status_path = os.path.join(state_dir, feature_id, "status.json")
436
436
  if not os.path.isfile(feature_status_path):
437
437
  return "N/A (first run)"
438
438
 
@@ -447,8 +447,7 @@ def get_prev_session_status(state_dir, feature_id):
447
447
  return "N/A (first run)"
448
448
 
449
449
  # Try to read the last session's session-status.json
450
- session_status_path = os.path.join(
451
- state_dir, "features", feature_id, "sessions",
450
+ session_status_path = os.path.join(state_dir, feature_id, "sessions",
452
451
  last_session_id, "session-status.json"
453
452
  )
454
453
  if not os.path.isfile(session_status_path):
@@ -1360,8 +1359,7 @@ def _tier_header(pipeline_mode):
1360
1359
  "lite": (
1361
1360
  "# Dev-Pipeline Session Bootstrap — Tier 1 (Single Agent)\n",
1362
1361
  "**Tier 1 — Single Agent**: You handle everything directly. "
1363
- "No normal work subagents; the log-size monitor is the only "
1364
- "allowed persistent background subagent. No TeamCreate.\n",
1362
+ "No normal work subagents. No TeamCreate.\n",
1365
1363
  ),
1366
1364
  "standard": (
1367
1365
  "# Dev-Pipeline Session Bootstrap — Tier 2 (Dual Agent)\n",
@@ -1399,8 +1397,7 @@ def _tier_reminders(pipeline_mode, critic_enabled=False):
1399
1397
  if pipeline_mode == "lite":
1400
1398
  specific = [
1401
1399
  "- Tier 1: you handle everything directly — no normal work "
1402
- "subagents needed; the log-size monitor is the only allowed "
1403
- "persistent background subagent",
1400
+ "subagents needed",
1404
1401
  ]
1405
1402
  elif pipeline_mode == "standard":
1406
1403
  specific = [
@@ -1413,8 +1410,7 @@ def _tier_reminders(pipeline_mode, critic_enabled=False):
1413
1410
  "- Gate checks enforce Implementation Log and Review Notes are "
1414
1411
  "written before proceeding",
1415
1412
  "- Do NOT use `run_in_background=true` when spawning normal work "
1416
- "subagents; the log-size monitor is the only allowed persistent "
1417
- "background subagent",
1413
+ "subagents",
1418
1414
  "- On timeout: check snapshot + git diff HEAD → model:lite → "
1419
1415
  "remaining steps only → max 2 retries per phase → "
1420
1416
  "orchestrator fallback",
@@ -1429,8 +1425,7 @@ def _tier_reminders(pipeline_mode, critic_enabled=False):
1429
1425
  "- Gate checks enforce Implementation Log and Review Notes are "
1430
1426
  "written before proceeding",
1431
1427
  "- Do NOT use `run_in_background=true` when spawning normal work "
1432
- "agents; the log-size monitor is the only allowed persistent "
1433
- "background subagent",
1428
+ "agents",
1434
1429
  "- On timeout: check snapshot → model:lite → remaining steps "
1435
1430
  "only → max 2 retries → orchestrator fallback",
1436
1431
  ]
@@ -1470,8 +1465,7 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
1470
1465
  if pipeline_mode != "lite":
1471
1466
  mission += (
1472
1467
  " When you spawn normal work subagents, wait for each to finish "
1473
- "(run_in_background=false). The log-size monitor is the only "
1474
- "allowed persistent background subagent."
1468
+ "(run_in_background=false)."
1475
1469
  )
1476
1470
  if pipeline_mode == "full":
1477
1471
  mission += (
@@ -1972,12 +1966,6 @@ def build_replacements(args, feature, features, global_context, script_dir):
1972
1966
  dev_url = f"http://localhost:{dev_port}"
1973
1967
 
1974
1968
  max_log_size = _parse_non_negative_int_env("MAX_LOG_SIZE", DEFAULT_MAX_LOG_SIZE)
1975
- if max_log_size == 0:
1976
- poll_interval = DEFAULT_LOG_MONITOR_POLL_INTERVAL
1977
- else:
1978
- poll_interval = _parse_positive_int_env(
1979
- "LOG_MONITOR_POLL_INTERVAL", DEFAULT_LOG_MONITOR_POLL_INTERVAL,
1980
- )
1981
1969
 
1982
1970
  replacements = {
1983
1971
  "{{LOG_SIZE_AWARENESS}}": load_log_size_section(script_dir),
@@ -2028,22 +2016,10 @@ def build_replacements(args, feature, features, global_context, script_dir):
2028
2016
  "{{DEV_URL}}": dev_url,
2029
2017
  "{{MAX_LOG_SIZE}}": str(max_log_size),
2030
2018
  "{{MAX_LOG_SIZE_HUMAN}}": _format_bytes(max_log_size),
2031
- "{{LOG_MONITOR_POLL_INTERVAL}}": str(poll_interval),
2032
- "{{LOG_MONITOR_STATUS_FILE}}": os.path.join(
2033
- ".prizmkit", "state", "features", args.feature_id,
2034
- "sessions", args.session_id, "logs", "log-monitor-status.txt",
2035
- ),
2036
- "{{LOG_MONITOR_BASELINE_FILE}}": os.path.join(
2037
- ".prizmkit", "state", "features", args.feature_id,
2038
- "sessions", args.session_id, "logs", "log-monitor-baseline.txt",
2039
- ),
2040
2019
  "{{SESSION_LOG_PATH}}": os.path.join(
2041
2020
  ".prizmkit", "state", "features", args.feature_id,
2042
2021
  "sessions", args.session_id, "logs", "session.log",
2043
2022
  ),
2044
- "{{LOG_MONITOR_SCRIPT}}": os.path.join(
2045
- ".prizmkit", "dev-pipeline", "scripts", "monitor-log.ps1",
2046
- ),
2047
2023
  }
2048
2024
 
2049
2025
  return replacements, effective_resume, browser_enabled, browser_tool
@@ -2130,6 +2106,9 @@ def main():
2130
2106
  agent_prompt_replacements = load_agent_prompts(templates_dir)
2131
2107
  replacements.update(agent_prompt_replacements)
2132
2108
 
2109
+ if is_continuation(args):
2110
+ replacements["{{LOG_SIZE_AWARENESS}}"] = ""
2111
+
2133
2112
  # Extract state needed for assembly
2134
2113
  pipeline_mode = replacements.get("{{PIPELINE_MODE}}", "lite")
2135
2114
  init_done = replacements.get("{{INIT_DONE}}", "false") == "true"
@@ -2149,6 +2128,11 @@ def main():
2149
2128
  retry_count=int(args.retry_count),
2150
2129
  browser_tool=browser_tool,
2151
2130
  )
2131
+ if is_continuation(args):
2132
+ sections = [
2133
+ (name, content) for name, content in sections
2134
+ if name != "log-size-awareness"
2135
+ ]
2152
2136
  rendered = render_from_sections(sections, replacements)
2153
2137
  except FileNotFoundError as exc:
2154
2138
  LOGGER.warning(
@@ -2210,6 +2194,7 @@ def main():
2210
2194
  )
2211
2195
  feature_slug = replacements.get("{{FEATURE_SLUG}}", "")
2212
2196
  checkpoint_path = ""
2197
+ checkpoint = {}
2213
2198
 
2214
2199
  if not args.no_checkpoint and use_sections and feature_slug:
2215
2200
  checkpoint = generate_checkpoint_definition(
@@ -2231,7 +2216,7 @@ def main():
2231
2216
  )
2232
2217
 
2233
2218
  # On resume, merge existing completed state (with artifact validation)
2234
- if is_resume and os.path.exists(checkpoint_path):
2219
+ if (is_resume or is_continuation(args)) and os.path.exists(checkpoint_path):
2235
2220
  try:
2236
2221
  with open(checkpoint_path, "r", encoding="utf-8") as f:
2237
2222
  existing = json.load(f)
@@ -2250,6 +2235,15 @@ def main():
2250
2235
  json.dump(checkpoint, f, indent=2, ensure_ascii=False)
2251
2236
  LOGGER.info("Wrote checkpoint to %s", checkpoint_path)
2252
2237
 
2238
+ if is_continuation(args):
2239
+ rendered = append_continuation_handoff(
2240
+ rendered, args, project_root, args.task_type, args.feature_id,
2241
+ feature_slug, checkpoint,
2242
+ )
2243
+ err = write_output(args.output, rendered)
2244
+ if err:
2245
+ emit_failure(err)
2246
+
2253
2247
  # ── Success JSON ───────────────────────────────────────────────────
2254
2248
  feature_model = feature.get("model", "")
2255
2249
  mode_agent_counts = {"lite": 1, "standard": 3, "full": 3}