prizmkit 1.1.68 → 1.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/lib/heartbeat.sh +5 -5
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +217 -18
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +19 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +19 -1
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +22 -1
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +10 -0
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +10 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +12 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +12 -0
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +5 -1
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +5 -1
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +217 -18
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +22 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +5 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +5 -1
- package/bundled/skills/_metadata.json +1 -1
- package/package.json +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -90,8 +90,8 @@ PY
|
|
|
90
90
|
fi
|
|
91
91
|
prev_child_activity_signature="$child_activity_signature"
|
|
92
92
|
|
|
93
|
-
# Track progress staleness.
|
|
94
|
-
# while child transcripts keep growing, so child activity counts.
|
|
93
|
+
# Track progress staleness. Parent sessions can sit in a wait/polling
|
|
94
|
+
# tool while child transcripts keep growing, so child activity counts.
|
|
95
95
|
if [[ $growth -eq 0 && $child_growth -eq 0 ]]; then
|
|
96
96
|
stale_seconds=$((stale_seconds + heartbeat_interval))
|
|
97
97
|
else
|
|
@@ -174,9 +174,9 @@ PY
|
|
|
174
174
|
fi
|
|
175
175
|
|
|
176
176
|
# Stale-kill: auto-terminate process if no progress for too long.
|
|
177
|
-
#
|
|
178
|
-
#
|
|
179
|
-
# stale window
|
|
177
|
+
# Parent sessions can wait on spawned work; child transcript growth
|
|
178
|
+
# counts as progress above, while silent waits still use the active
|
|
179
|
+
# stale window to surface stuck agents promptly.
|
|
180
180
|
if [[ $effective_stale_kill_threshold -gt 0 && $stale_seconds -ge $effective_stale_kill_threshold ]]; then
|
|
181
181
|
local stale_mins=$((stale_seconds / 60))
|
|
182
182
|
echo -e " ${RED}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} | ${RED}STALE-KILL: no progress for ${stale_mins}m (threshold: ${effective_stale_kill_threshold}s)${NC}"
|
|
@@ -63,7 +63,8 @@ PHASE_KEYWORDS = {
|
|
|
63
63
|
class ProgressTracker:
|
|
64
64
|
"""Tracks progress state from stream-json events."""
|
|
65
65
|
|
|
66
|
-
def __init__(self):
|
|
66
|
+
def __init__(self, session_log=None):
|
|
67
|
+
self.session_log_path = Path(session_log).expanduser() if session_log else None
|
|
67
68
|
self.message_count = 0
|
|
68
69
|
self.current_tool = None
|
|
69
70
|
self.current_tool_input_summary = ""
|
|
@@ -78,12 +79,19 @@ class ProgressTracker:
|
|
|
78
79
|
self.active_subagent_count = 0
|
|
79
80
|
self.subagent_status_counts = Counter()
|
|
80
81
|
self.codex_child_thread_ids = set()
|
|
82
|
+
self.claude_session_id = ""
|
|
83
|
+
self.claude_cwd = ""
|
|
84
|
+
self.claude_task_states = {}
|
|
81
85
|
self.child_session_files = []
|
|
82
86
|
self.child_total_bytes = 0
|
|
83
87
|
self.child_activity_signature = ""
|
|
84
88
|
self.last_child_activity_at = ""
|
|
85
89
|
self._codex_child_session_paths = {}
|
|
90
|
+
self._claude_child_session_files = []
|
|
86
91
|
self._last_child_scan_at = 0.0
|
|
92
|
+
self._last_claude_fallback_scan_at = 0.0
|
|
93
|
+
self._last_claude_fallback_scan_key = ""
|
|
94
|
+
self._claude_fallback_scan_interval_seconds = 10.0
|
|
87
95
|
self._text_buffer = ""
|
|
88
96
|
self._in_tool_use = False
|
|
89
97
|
self._current_tool_input_parts = []
|
|
@@ -195,11 +203,76 @@ class ProgressTracker:
|
|
|
195
203
|
self.is_active = True
|
|
196
204
|
|
|
197
205
|
elif event_type == "system":
|
|
198
|
-
# System events (hooks, init, etc.) — track but don't count as messages
|
|
206
|
+
# System events (hooks, init, task notifications, etc.) — track but don't count as messages.
|
|
199
207
|
self.event_format = self.event_format or "stream-json"
|
|
200
208
|
subtype = event.get("subtype", "")
|
|
201
209
|
if subtype == "init":
|
|
202
210
|
self.is_active = True
|
|
211
|
+
session_id = event.get("session_id")
|
|
212
|
+
if isinstance(session_id, str) and session_id.strip():
|
|
213
|
+
self.claude_session_id = session_id.strip()
|
|
214
|
+
cwd = event.get("cwd")
|
|
215
|
+
if isinstance(cwd, str) and cwd.strip():
|
|
216
|
+
self.claude_cwd = cwd.strip()
|
|
217
|
+
elif subtype == "task_started":
|
|
218
|
+
task_id = event.get("task_id")
|
|
219
|
+
if isinstance(task_id, str) and task_id.strip():
|
|
220
|
+
self.claude_task_states[task_id.strip()] = {
|
|
221
|
+
"status": "running",
|
|
222
|
+
"summary": str(event.get("description") or "")[:120],
|
|
223
|
+
"tool_use_id": str(event.get("tool_use_id") or ""),
|
|
224
|
+
"task_type": str(event.get("task_type") or ""),
|
|
225
|
+
"subagent_type": str(event.get("subagent_type") or ""),
|
|
226
|
+
}
|
|
227
|
+
self._update_claude_subagent_status_counts()
|
|
228
|
+
elif subtype in ("task_updated", "task_progress"):
|
|
229
|
+
task_id = event.get("task_id")
|
|
230
|
+
if isinstance(task_id, str) and task_id.strip():
|
|
231
|
+
state = self.claude_task_states.setdefault(task_id.strip(), {})
|
|
232
|
+
patch = event.get("patch") if isinstance(event.get("patch"), dict) else {}
|
|
233
|
+
status = patch.get("status") or event.get("status")
|
|
234
|
+
if status:
|
|
235
|
+
state["status"] = str(status)
|
|
236
|
+
summary = patch.get("summary") or patch.get("description") or event.get("summary") or event.get("description")
|
|
237
|
+
if summary:
|
|
238
|
+
state["summary"] = str(summary)[:120]
|
|
239
|
+
else:
|
|
240
|
+
state.setdefault("summary", "")
|
|
241
|
+
tool_use_id = patch.get("tool_use_id") or event.get("tool_use_id")
|
|
242
|
+
if tool_use_id:
|
|
243
|
+
state["tool_use_id"] = str(tool_use_id)
|
|
244
|
+
else:
|
|
245
|
+
state.setdefault("tool_use_id", "")
|
|
246
|
+
task_type = patch.get("task_type") or event.get("task_type")
|
|
247
|
+
if task_type:
|
|
248
|
+
state["task_type"] = str(task_type)
|
|
249
|
+
else:
|
|
250
|
+
state.setdefault("task_type", "")
|
|
251
|
+
subagent_type = patch.get("subagent_type") or event.get("subagent_type")
|
|
252
|
+
if subagent_type:
|
|
253
|
+
state["subagent_type"] = str(subagent_type)
|
|
254
|
+
else:
|
|
255
|
+
state.setdefault("subagent_type", "")
|
|
256
|
+
self._update_claude_subagent_status_counts()
|
|
257
|
+
elif subtype == "task_notification":
|
|
258
|
+
task_id = event.get("task_id")
|
|
259
|
+
if isinstance(task_id, str) and task_id.strip():
|
|
260
|
+
state = self.claude_task_states.setdefault(task_id.strip(), {})
|
|
261
|
+
status = event.get("status") or "completed"
|
|
262
|
+
state["status"] = str(status)
|
|
263
|
+
state["summary"] = str(event.get("summary") or state.get("summary") or "")[:120]
|
|
264
|
+
state.setdefault("tool_use_id", str(event.get("tool_use_id") or ""))
|
|
265
|
+
task_type = event.get("task_type")
|
|
266
|
+
if task_type:
|
|
267
|
+
state["task_type"] = str(task_type)
|
|
268
|
+
else:
|
|
269
|
+
state.setdefault("task_type", "")
|
|
270
|
+
subagent_type = event.get("subagent_type")
|
|
271
|
+
if subagent_type:
|
|
272
|
+
state["subagent_type"] = str(subagent_type)
|
|
273
|
+
else:
|
|
274
|
+
state.setdefault("subagent_type", "")
|
|
275
|
+
self._update_claude_subagent_status_counts()
|
|
203
276
|
|
|
204
277
|
# ── Claude API raw stream format ────────────────────────────
|
|
205
278
|
elif event_type == "message_start":
|
|
@@ -391,16 +464,135 @@ class ProgressTracker:
|
|
|
391
464
|
pass
|
|
392
465
|
return str(matches[0])
|
|
393
466
|
|
|
467
|
+
def _is_tracked_claude_subagent_state(self, state):
|
|
468
|
+
"""Return true for Claude Code task events representing in-process agents."""
|
|
469
|
+
if not isinstance(state, dict):
|
|
470
|
+
return False
|
|
471
|
+
task_type = str(state.get("task_type") or "")
|
|
472
|
+
task_type_lower = task_type.lower()
|
|
473
|
+
subagent_type = str(state.get("subagent_type") or "")
|
|
474
|
+
if task_type_lower == "local_bash":
|
|
475
|
+
return False
|
|
476
|
+
tracked_types = {"in_process_teammate", "subagent", "agent", "teammate"}
|
|
477
|
+
if task_type_lower in tracked_types:
|
|
478
|
+
return True
|
|
479
|
+
if task_type_lower == "local_agent" and subagent_type:
|
|
480
|
+
return True
|
|
481
|
+
summary = str(state.get("summary") or "")
|
|
482
|
+
return bool(
|
|
483
|
+
not task_type
|
|
484
|
+
and summary.lower().startswith(("dev:", "critic:", "reviewer:", "agent:"))
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def _has_tracked_claude_subagent_task(self):
|
|
488
|
+
"""Return true once a Claude Code local-agent/subagent task has been observed."""
|
|
489
|
+
return any(
|
|
490
|
+
self._is_tracked_claude_subagent_state(state)
|
|
491
|
+
for state in self.claude_task_states.values()
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
def _update_claude_subagent_status_counts(self):
|
|
495
|
+
"""Track Claude Code in-process teammate task state counts."""
|
|
496
|
+
counts = Counter()
|
|
497
|
+
active = 0
|
|
498
|
+
inactive_statuses = {
|
|
499
|
+
"completed",
|
|
500
|
+
"failed",
|
|
501
|
+
"cancelled",
|
|
502
|
+
"canceled",
|
|
503
|
+
"killed",
|
|
504
|
+
"stopped",
|
|
505
|
+
"success",
|
|
506
|
+
"error",
|
|
507
|
+
}
|
|
508
|
+
for state in self.claude_task_states.values():
|
|
509
|
+
if not self._is_tracked_claude_subagent_state(state):
|
|
510
|
+
continue
|
|
511
|
+
status = str(state.get("status") or "unknown")
|
|
512
|
+
counts[status] += 1
|
|
513
|
+
if status.lower() not in inactive_statuses:
|
|
514
|
+
active += 1
|
|
515
|
+
summary = state.get("summary") or state.get("subagent_type")
|
|
516
|
+
if summary:
|
|
517
|
+
self.last_text_snippet = str(summary).strip()[:120]
|
|
518
|
+
self._detect_phase(str(summary))
|
|
519
|
+
self.subagent_status_counts = counts
|
|
520
|
+
self.active_subagent_count = active
|
|
521
|
+
|
|
522
|
+
def _claude_projects_dir(self):
|
|
523
|
+
"""Return the Claude Code projects directory for transcript lookup."""
|
|
524
|
+
projects_dir = os.environ.get("CLAUDE_PROJECTS_DIR")
|
|
525
|
+
if projects_dir:
|
|
526
|
+
return Path(projects_dir).expanduser()
|
|
527
|
+
claude_config_dir = os.environ.get("CLAUDE_CONFIG_DIR")
|
|
528
|
+
if claude_config_dir:
|
|
529
|
+
return Path(claude_config_dir).expanduser() / "projects"
|
|
530
|
+
claude_home = os.environ.get("CLAUDE_HOME")
|
|
531
|
+
if claude_home:
|
|
532
|
+
return Path(claude_home).expanduser() / "projects"
|
|
533
|
+
return Path.home() / ".claude" / "projects"
|
|
534
|
+
|
|
535
|
+
def _claude_project_key(self):
|
|
536
|
+
"""Encode cwd the same way Claude Code stores project transcript dirs."""
|
|
537
|
+
cwd = self.claude_cwd
|
|
538
|
+
if not cwd:
|
|
539
|
+
return ""
|
|
540
|
+
return cwd.replace("\\", "-").replace("/", "-").replace(":", "")
|
|
541
|
+
|
|
542
|
+
def _find_claude_child_session_files(self):
|
|
543
|
+
"""Find Claude Code subagent transcripts for this parent session."""
|
|
544
|
+
if not self.claude_session_id:
|
|
545
|
+
return []
|
|
546
|
+
|
|
547
|
+
projects_dir = self._claude_projects_dir()
|
|
548
|
+
if not projects_dir.exists():
|
|
549
|
+
return []
|
|
550
|
+
|
|
551
|
+
candidates = []
|
|
552
|
+
project_key = self._claude_project_key()
|
|
553
|
+
if project_key:
|
|
554
|
+
candidates.append(
|
|
555
|
+
projects_dir / project_key / self.claude_session_id / "subagents"
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
for candidate in candidates:
|
|
559
|
+
if candidate.exists():
|
|
560
|
+
try:
|
|
561
|
+
return sorted(candidate.glob("*.jsonl"))
|
|
562
|
+
except OSError:
|
|
563
|
+
return []
|
|
564
|
+
|
|
565
|
+
# Fallback for non-standard cwd encoding or custom Claude homes. Avoid
|
|
566
|
+
# repeatedly walking every stored transcript before any Agent task exists.
|
|
567
|
+
if not self._has_tracked_claude_subagent_task():
|
|
568
|
+
return []
|
|
569
|
+
|
|
570
|
+
fallback_scan_key = f"{projects_dir}:{self.claude_session_id}"
|
|
571
|
+
now = time.monotonic()
|
|
572
|
+
if (
|
|
573
|
+
self._last_claude_fallback_scan_key == fallback_scan_key
|
|
574
|
+
and now - self._last_claude_fallback_scan_at < self._claude_fallback_scan_interval_seconds
|
|
575
|
+
):
|
|
576
|
+
return self._claude_child_session_files
|
|
577
|
+
self._last_claude_fallback_scan_key = fallback_scan_key
|
|
578
|
+
self._last_claude_fallback_scan_at = now
|
|
579
|
+
try:
|
|
580
|
+
matches = sorted(projects_dir.rglob(f"{self.claude_session_id}/subagents/*.jsonl"))
|
|
581
|
+
except OSError:
|
|
582
|
+
return []
|
|
583
|
+
return matches
|
|
584
|
+
|
|
394
585
|
def refresh_child_session_activity(self, force=False):
|
|
395
|
-
"""Refresh
|
|
586
|
+
"""Refresh child transcript file stats.
|
|
396
587
|
|
|
397
588
|
The heartbeat monitor uses this activity signature to treat subagent
|
|
398
|
-
transcript growth as real progress while the parent
|
|
399
|
-
|
|
589
|
+
transcript growth as real progress while the parent session is blocked
|
|
590
|
+
waiting for a child agent/tool result. Supports Codex child threads and
|
|
591
|
+
Claude Code in-process teammate transcripts.
|
|
400
592
|
"""
|
|
401
593
|
previous_signature = self.child_activity_signature
|
|
402
594
|
|
|
403
|
-
if not self.codex_child_thread_ids:
|
|
595
|
+
if not self.codex_child_thread_ids and not self.claude_session_id:
|
|
404
596
|
self.child_session_files = []
|
|
405
597
|
self.child_total_bytes = 0
|
|
406
598
|
self.child_activity_signature = ""
|
|
@@ -420,6 +612,7 @@ class ProgressTracker:
|
|
|
420
612
|
found = self._find_codex_child_session_file(thread_id)
|
|
421
613
|
if found:
|
|
422
614
|
self._codex_child_session_paths[thread_id] = found
|
|
615
|
+
self._claude_child_session_files = self._find_claude_child_session_files()
|
|
423
616
|
self._last_child_scan_at = now
|
|
424
617
|
|
|
425
618
|
files = []
|
|
@@ -427,24 +620,22 @@ class ProgressTracker:
|
|
|
427
620
|
total_bytes = 0
|
|
428
621
|
max_mtime = 0.0
|
|
429
622
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if not path:
|
|
433
|
-
continue
|
|
623
|
+
def add_file(kind, identifier, path):
|
|
624
|
+
nonlocal total_bytes, max_mtime
|
|
434
625
|
try:
|
|
435
626
|
stat = os.stat(path)
|
|
436
627
|
except OSError:
|
|
437
|
-
|
|
438
|
-
|
|
628
|
+
return
|
|
629
|
+
path_str = str(path)
|
|
439
630
|
total_bytes += stat.st_size
|
|
440
631
|
max_mtime = max(max_mtime, stat.st_mtime)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
)
|
|
632
|
+
mtime_ns = getattr(stat, "st_mtime_ns", int(stat.st_mtime * 1_000_000_000))
|
|
633
|
+
signature_parts.append(f"{kind}:{identifier}:{stat.st_size}:{mtime_ns}")
|
|
444
634
|
files.append(
|
|
445
635
|
{
|
|
446
|
-
"
|
|
447
|
-
"
|
|
636
|
+
"kind": kind,
|
|
637
|
+
"thread_id": identifier,
|
|
638
|
+
"path": path_str,
|
|
448
639
|
"size": stat.st_size,
|
|
449
640
|
"mtime": datetime.fromtimestamp(
|
|
450
641
|
stat.st_mtime, timezone.utc
|
|
@@ -452,6 +643,14 @@ class ProgressTracker:
|
|
|
452
643
|
}
|
|
453
644
|
)
|
|
454
645
|
|
|
646
|
+
for thread_id in sorted(self.codex_child_thread_ids):
|
|
647
|
+
path = self._codex_child_session_paths.get(thread_id)
|
|
648
|
+
if path:
|
|
649
|
+
add_file("codex", thread_id, path)
|
|
650
|
+
|
|
651
|
+
for path in self._claude_child_session_files:
|
|
652
|
+
add_file("claude", path.stem, path)
|
|
653
|
+
|
|
455
654
|
self.child_session_files = files
|
|
456
655
|
self.child_total_bytes = total_bytes
|
|
457
656
|
self.child_activity_signature = "|".join(signature_parts)
|
|
@@ -519,7 +718,7 @@ def atomic_write_json(data, filepath):
|
|
|
519
718
|
|
|
520
719
|
def tail_and_parse(session_log, progress_file, poll_interval=0.5):
|
|
521
720
|
"""Tail session log and parse stream-json events."""
|
|
522
|
-
tracker = ProgressTracker()
|
|
721
|
+
tracker = ProgressTracker(session_log)
|
|
523
722
|
last_write_state = None
|
|
524
723
|
|
|
525
724
|
def state_key(state):
|
|
@@ -14,6 +14,12 @@ You are the **session orchestrator**. Implement Feature {{FEATURE_ID}}: "{{FEATU
|
|
|
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
|
|
|
17
|
+
**Agent spawn failure policy (all Agent tool calls)**:
|
|
18
|
+
- If spawning Dev, Reviewer, or Critic fails with team/config/lock errors, retry at most once.
|
|
19
|
+
- If the second attempt fails, do not keep spawning variants and do not enter artifact polling for Implementation Log, challenge report, or review report markers.
|
|
20
|
+
- Use the documented inline/recovery fallback for that phase: write the required report yourself where possible, complete remaining Dev work directly in the orchestrator when safe, or write `failure-log.md` with the spawn error and last observable state before stopping for recovery.
|
|
21
|
+
- Apply the same cap to any re-spawn for report repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
22
|
+
|
|
17
23
|
### Feature Description
|
|
18
24
|
|
|
19
25
|
{{FEATURE_DESCRIPTION}}
|
|
@@ -163,6 +169,8 @@ Before proceeding past CP-1, verify:
|
|
|
163
169
|
|
|
164
170
|
Spawn Reviewer agent (Agent tool, subagent_type="prizm-dev-team-reviewer", mode="plan", run_in_background=false).
|
|
165
171
|
|
|
172
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Reviewer spawn. If the second attempt fails, do not poll for report artifacts; fix/check the plan inline or write `failure-log.md` before stopping for recovery.
|
|
173
|
+
|
|
166
174
|
Prompt:
|
|
167
175
|
> "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
168
176
|
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has project context, Section 4 has file manifest.
|
|
@@ -186,6 +194,8 @@ If CRITIC:MISSING — skip Phase 3.5 entirely and proceed to Phase 4. Log: "Crit
|
|
|
186
194
|
|
|
187
195
|
Spawn Critic agent (Agent tool, subagent_type="prizm-dev-team-critic", mode="plan", run_in_background=false).
|
|
188
196
|
|
|
197
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Critic spawn. If the second attempt fails, do not poll for `challenge-report.md`; perform the plan challenge inline and record the fallback.
|
|
198
|
+
|
|
189
199
|
Prompt:
|
|
190
200
|
> "Read {{CRITIC_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
191
201
|
> **MODE: Plan Challenge**
|
|
@@ -208,6 +218,8 @@ Wait for Critic to return.
|
|
|
208
218
|
|
|
209
219
|
Spawn Dev subagent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false).
|
|
210
220
|
|
|
221
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Dev spawn. If the second attempt fails, do not poll for `## Implementation Log`; write `failure-log.md` and either implement remaining tasks directly in the orchestrator or stop for recovery.
|
|
222
|
+
|
|
211
223
|
Prompt:
|
|
212
224
|
> "Read {{DEV_SUBAGENT_PATH}}. Implement feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}).
|
|
213
225
|
> **IMPORTANT**: Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has Prizm Context (TRAPS/RULES), Section 4 has File Manifest with paths and interfaces.
|
|
@@ -232,6 +244,8 @@ If GATE:MISSING — send message to Dev (re-spawn if needed): "Write the '## Imp
|
|
|
232
244
|
|
|
233
245
|
Spawn Reviewer subagent (Agent tool, subagent_type="prizm-dev-team-reviewer", run_in_background=false).
|
|
234
246
|
|
|
247
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Reviewer spawn. If the second attempt fails, do not poll for `review-report.md`; write `failure-log.md` with the spawn error and last observable state before stopping or performing an inline fallback.
|
|
248
|
+
|
|
235
249
|
Prompt:
|
|
236
250
|
> "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
237
251
|
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/spec.md` for goals and acceptance criteria
|
|
@@ -248,7 +262,11 @@ After Reviewer agent returns, verify the review report was written:
|
|
|
248
262
|
```bash
|
|
249
263
|
grep -q "## Verdict" .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md && echo "GATE:PASS" || echo "GATE:MISSING"
|
|
250
264
|
```
|
|
251
|
-
If GATE:MISSING
|
|
265
|
+
If GATE:MISSING:
|
|
266
|
+
- Do not re-spawn Reviewer or re-run `/prizmkit-code-review` in an unbounded report-repair loop.
|
|
267
|
+
- Perform one bounded status check; retry at most once: inspect Reviewer output, code-review skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
|
|
268
|
+
- If the missing report is caused by team/config/lock errors from Reviewer or the internal code-review loop, write `failure-log.md` with the spawn/skill error and last observable state.
|
|
269
|
+
- If the report is still missing after that single check/retry, either perform a safe inline fallback review and write `review-report.md` with `## Verdict`, or stop with a clear recovery failure.
|
|
252
270
|
|
|
253
271
|
Read `review-report.md` and check the Verdict:
|
|
254
272
|
- `PASS` → proceed to next phase
|
|
@@ -14,6 +14,12 @@ You are the **session orchestrator**. Implement Feature {{FEATURE_ID}}: "{{FEATU
|
|
|
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
|
|
|
17
|
+
**Agent spawn failure policy (all Agent tool calls)**:
|
|
18
|
+
- If spawning Dev, Reviewer, or Critic fails with team/config/lock errors, retry at most once.
|
|
19
|
+
- If the second attempt fails, do not keep spawning variants and do not enter artifact polling for Implementation Log, challenge report, or review report markers.
|
|
20
|
+
- Use the documented inline/recovery fallback for that phase: write the required report yourself where possible, complete remaining Dev work directly in the orchestrator when safe, or write `failure-log.md` with the spawn error and last observable state before stopping for recovery.
|
|
21
|
+
- Apply the same cap to any re-spawn for report repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
22
|
+
|
|
17
23
|
### Feature Description
|
|
18
24
|
|
|
19
25
|
{{FEATURE_DESCRIPTION}}
|
|
@@ -190,6 +196,8 @@ Before proceeding past CP-1, verify:
|
|
|
190
196
|
|
|
191
197
|
Spawn Reviewer agent (Agent tool, subagent_type="prizm-dev-team-reviewer", mode="plan", run_in_background=false).
|
|
192
198
|
|
|
199
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Reviewer spawn. If the second attempt fails, do not poll for report artifacts; fix/check the plan inline or write `failure-log.md` before stopping for recovery.
|
|
200
|
+
|
|
193
201
|
Prompt:
|
|
194
202
|
> "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
195
203
|
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has project context, Section 4 has file manifest.
|
|
@@ -217,6 +225,8 @@ If CRITIC:MISSING — skip Phase 3.5 entirely and proceed to Phase 4. Log: "Crit
|
|
|
217
225
|
|
|
218
226
|
Spawn Critic agent (Agent tool, subagent_type="prizm-dev-team-critic", mode="plan", run_in_background=false).
|
|
219
227
|
|
|
228
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Critic spawn. If the second attempt fails, do not poll for challenge reports; perform the plan challenge inline and record the fallback.
|
|
229
|
+
|
|
220
230
|
Prompt:
|
|
221
231
|
> "Read {{CRITIC_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
222
232
|
> **MODE: Plan Challenge**
|
|
@@ -263,6 +273,8 @@ grep -c '^\- \[ \]' .prizmkit/specs/{{FEATURE_SLUG}}/plan.md 2>/dev/null || true
|
|
|
263
273
|
|
|
264
274
|
Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false).
|
|
265
275
|
|
|
276
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Dev spawn. If the second attempt fails, do not poll for `## Implementation Log`; write `failure-log.md` and either implement remaining tasks directly in the orchestrator or stop for recovery.
|
|
277
|
+
|
|
266
278
|
Prompt:
|
|
267
279
|
> "Read {{DEV_SUBAGENT_PATH}}. Implement feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}).
|
|
268
280
|
> **IMPORTANT**: Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has Prizm Context (TRAPS/RULES), Section 4 has File Manifest with paths and interfaces.
|
|
@@ -297,6 +309,8 @@ All tasks `[x]`, tests pass.
|
|
|
297
309
|
|
|
298
310
|
Spawn Reviewer agent (Agent tool, subagent_type="prizm-dev-team-reviewer", run_in_background=false).
|
|
299
311
|
|
|
312
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Reviewer spawn. If the second attempt fails, do not poll for `review-report.md`; write `failure-log.md` with the spawn error and last observable state before stopping or performing an inline fallback.
|
|
313
|
+
|
|
300
314
|
Prompt:
|
|
301
315
|
> "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
|
|
302
316
|
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/spec.md` for goals and acceptance criteria
|
|
@@ -313,7 +327,11 @@ After Reviewer agent returns, verify the review report was written:
|
|
|
313
327
|
```bash
|
|
314
328
|
grep -q "## Verdict" .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md && echo "GATE:PASS" || echo "GATE:MISSING"
|
|
315
329
|
```
|
|
316
|
-
If GATE:MISSING
|
|
330
|
+
If GATE:MISSING:
|
|
331
|
+
- Do not re-spawn Reviewer or re-run `/prizmkit-code-review` in an unbounded report-repair loop.
|
|
332
|
+
- Perform one bounded status check; retry at most once: inspect Reviewer output, code-review skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
|
|
333
|
+
- If the missing report is caused by team/config/lock errors from Reviewer or the internal code-review loop, write `failure-log.md` with the spawn/skill error and last observable state.
|
|
334
|
+
- If the report is still missing after that single check/retry, either perform a safe inline fallback review and write `review-report.md` with `## Verdict`, or stop with a clear recovery failure.
|
|
317
335
|
|
|
318
336
|
Read `review-report.md` and check the Verdict:
|
|
319
337
|
- `PASS` → proceed to next phase
|
|
@@ -80,6 +80,12 @@ You are the **refactor session orchestrator**. Execute Refactor {{REFACTOR_ID}}:
|
|
|
80
80
|
|
|
81
81
|
**YOU are the orchestrator. Execute each phase by spawning the appropriate team agent with run_in_background=false.**
|
|
82
82
|
|
|
83
|
+
**Agent spawn failure policy (all Agent tool calls)**:
|
|
84
|
+
- If spawning Dev or Reviewer fails with team/config/lock errors, retry at most once.
|
|
85
|
+
- If the second attempt fails, do not keep spawning variants and do not enter artifact polling for Implementation Log, review-report, or refactor-report markers.
|
|
86
|
+
- Use the documented inline/recovery fallback for that phase: complete remaining refactor work directly in the orchestrator when safe, write the required report yourself where possible, or write `failure-log.md` with the spawn error and last observable state before stopping for recovery.
|
|
87
|
+
- Apply the same cap to any re-spawn for report repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
88
|
+
|
|
83
89
|
## Workflow Checkpoint System
|
|
84
90
|
|
|
85
91
|
A checkpoint file tracks your progress through this workflow:
|
|
@@ -164,6 +170,7 @@ Include browser verification approach in plan.md:
|
|
|
164
170
|
**Goal**: Execute all tasks from plan.md while preserving existing behavior.
|
|
165
171
|
|
|
166
172
|
- Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false)
|
|
173
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Dev spawn. If the second attempt fails, do not poll for `## Implementation Log`; write `failure-log.md` and either complete remaining refactor work directly in the orchestrator or stop for recovery.
|
|
167
174
|
Prompt: "Read {{DEV_SUBAGENT_PATH}}. For refactor {{REFACTOR_ID}} ('{{REFACTOR_TITLE}}'):
|
|
168
175
|
1. Read `.prizmkit/refactor/{{REFACTOR_ID}}/spec.md` and `.prizmkit/refactor/{{REFACTOR_ID}}/plan.md`
|
|
169
176
|
2. Read `.prizmkit/prizm-docs/` for affected modules (TRAPS, RULES, PATTERNS)
|
|
@@ -201,6 +208,7 @@ Include browser verification approach in plan.md:
|
|
|
201
208
|
**Goal**: Verify refactoring quality and behavior preservation.
|
|
202
209
|
|
|
203
210
|
- Spawn Reviewer agent (Agent tool, subagent_type="prizm-dev-team-reviewer", run_in_background=false)
|
|
211
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Reviewer spawn. If the second attempt fails, do not poll for `review-report.md`; write `failure-log.md` with the spawn error and last observable state before stopping or performing an inline fallback.
|
|
204
212
|
Prompt: "Read {{REVIEWER_SUBAGENT_PATH}}. For refactor {{REFACTOR_ID}}:
|
|
205
213
|
1. Read `.prizmkit/refactor/{{REFACTOR_ID}}/spec.md` for goals and behavior preservation contracts
|
|
206
214
|
2. Read `.prizmkit/refactor/{{REFACTOR_ID}}/plan.md` for architecture decisions and completed tasks
|
|
@@ -221,7 +229,20 @@ Include browser verification approach in plan.md:
|
|
|
221
229
|
7. Report: verdict (PASS/NEEDS_FIXES), number of rounds, findings fixed/rejected
|
|
222
230
|
"
|
|
223
231
|
- **Wait for Reviewer to return**
|
|
224
|
-
-
|
|
232
|
+
- **Gate Check — Review Report**:
|
|
233
|
+
After Reviewer returns, verify the review report contains a verdict:
|
|
234
|
+
```bash
|
|
235
|
+
grep -q "## Verdict" .prizmkit/refactor/{{REFACTOR_ID}}/review-report.md && echo "GATE:PASS" || echo "GATE:MISSING"
|
|
236
|
+
```
|
|
237
|
+
If GATE:MISSING:
|
|
238
|
+
- Do not enter an unbounded report-repair loop and do not repeatedly re-spawn Reviewer.
|
|
239
|
+
- Perform one bounded status check; retry at most once: inspect the Reviewer output, `review-report.md` path, and any internal Reviewer/Dev spawn messages from `/prizmkit-code-review`.
|
|
240
|
+
- If the missing report is caused by team/config/lock errors from the Reviewer or internal Reviewer/Dev agent spawn, retry the Reviewer agent at most once only if it appears transient.
|
|
241
|
+
- If the report is still missing after that single check/retry, write `.prizmkit/refactor/{{REFACTOR_ID}}/failure-log.md` with the spawn/skill error and last observable state, then either perform a safe inline fallback review (spec/plan/diff/tests → write `review-report.md` with `## Verdict`) or stop with a clear recovery failure.
|
|
242
|
+
|
|
243
|
+
Read `review-report.md` and check the Verdict:
|
|
244
|
+
- `PASS` → proceed to next phase
|
|
245
|
+
- `NEEDS_FIXES` → log remaining findings and proceed (do not retry externally)
|
|
225
246
|
- **CP-RF-3**: Code review complete, tests pass, behavior preserved
|
|
226
247
|
- **Checkpoint update**: set step `prizmkit-code-review` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
227
248
|
|
|
@@ -8,6 +8,16 @@ If CRITIC:MISSING — skip this phase entirely and proceed. Log: "Critic agent n
|
|
|
8
8
|
|
|
9
9
|
**Choose ONE path based on `{{CRITIC_COUNT}}`:**
|
|
10
10
|
|
|
11
|
+
**Agent spawn failure policy**:
|
|
12
|
+
- If spawning Critic fails with team/config/lock errors, retry at most once.
|
|
13
|
+
- If the second attempt fails, do not keep spawning variants. Either create the required team once (when team tooling is available) or perform the plan challenge inline and write the required challenge report yourself.
|
|
14
|
+
- Record the fallback in the report; do not burn multiple minutes on repeated identical spawn failures.
|
|
15
|
+
|
|
16
|
+
**No silent report polling**:
|
|
17
|
+
- Do NOT run a long no-output loop waiting for `challenge-report*.md`.
|
|
18
|
+
- If you need to wait for a report file, use a short bounded check (≤120s) that prints elapsed time and reports present on every iteration.
|
|
19
|
+
- If reports are still missing after the bounded check, request one status update; if still missing, perform the missing challenge lens inline and continue.
|
|
20
|
+
|
|
11
21
|
**If {{CRITIC_COUNT}} = 1 → Single Critic** (skip to CP-2.5 after this):
|
|
12
22
|
|
|
13
23
|
**Spawn Agent**:
|
|
@@ -16,6 +16,16 @@ If CRITIC:MISSING — skip this phase entirely and proceed. Log: "Critic agent n
|
|
|
16
16
|
**Prompt**:
|
|
17
17
|
> {{AGENT_PROMPT_CRITIC_PLAN_CHALLENGE}}
|
|
18
18
|
|
|
19
|
+
**Agent spawn failure policy**:
|
|
20
|
+
- If spawning Critic fails with team/config/lock errors, retry at most once.
|
|
21
|
+
- If the second attempt fails, do not keep spawning variants. Either create the required team once (when team tooling is available) or perform the plan challenge inline and write `challenge-report.md` yourself.
|
|
22
|
+
- Record the fallback in the report; do not burn multiple minutes on repeated identical spawn failures.
|
|
23
|
+
|
|
24
|
+
**No silent report polling**:
|
|
25
|
+
- Do NOT run a long no-output loop waiting for `challenge-report.md`.
|
|
26
|
+
- If you need to wait for the report file, use a short bounded check (≤120s) that prints elapsed time and whether the report exists on every iteration.
|
|
27
|
+
- If the report is still missing after the bounded check, request one status update; if still missing, perform the challenge inline and continue.
|
|
28
|
+
|
|
19
29
|
Wait for Critic to return.
|
|
20
30
|
- Read challenge-report.md. For items marked CRITICAL/HIGH: decide whether to adjust plan.md or document why the plan stands.
|
|
21
31
|
- Max 1 plan revision round.
|
|
@@ -15,11 +15,23 @@
|
|
|
15
15
|
| subagent_type | prizm-dev-team-dev |
|
|
16
16
|
| run_in_background | false |
|
|
17
17
|
|
|
18
|
+
**Agent spawn failure policy**:
|
|
19
|
+
- If spawning Dev fails with team/config/lock errors, retry at most once.
|
|
20
|
+
- If the second attempt fails, do not enter Implementation Log polling or repeated recovery spawn loops.
|
|
21
|
+
- Use the documented inline/recovery fallback: write `failure-log.md` with the spawn error and last observable state, then either complete remaining tasks directly in the orchestrator or stop with a clear failure for recovery.
|
|
22
|
+
- Apply the same cap to Dev re-spawns for Implementation Log repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
23
|
+
|
|
18
24
|
**Prompt**:
|
|
19
25
|
> {{AGENT_PROMPT_DEV_IMPLEMENT}}
|
|
20
26
|
|
|
21
27
|
Wait for Dev to return. All tasks must be `[x]`, tests pass.
|
|
22
28
|
|
|
29
|
+
**No silent artifact polling**:
|
|
30
|
+
- Do NOT run a long no-output loop that only waits for `## Implementation Log` or any other file marker.
|
|
31
|
+
- If you must wait for Dev after spawning or sending a status request, use short bounded checks (≤120s) that print a heartbeat line each iteration with: elapsed time, remaining unchecked task count, whether `## Implementation Log` exists, and whether `git diff --stat` changed.
|
|
32
|
+
- If Dev has no transcript/file/diff progress for one bounded check, send one status request. If there is still no progress on the next bounded check, stop waiting, write `failure-log.md` with the last observable state, and follow Subagent Timeout Recovery.
|
|
33
|
+
- Prefer the Agent tool's completion notification or Dev's `COMPLETION_SIGNAL`; file presence alone is not a liveness signal.
|
|
34
|
+
|
|
23
35
|
**Gate Check — Implementation Log**:
|
|
24
36
|
After Dev agent returns, verify the Implementation Log was written:
|
|
25
37
|
```bash
|
|
@@ -22,9 +22,21 @@ grep -c '^\- \[ \]' .prizmkit/specs/{{FEATURE_SLUG}}/plan.md 2>/dev/null || true
|
|
|
22
22
|
| subagent_type | prizm-dev-team-dev |
|
|
23
23
|
| run_in_background | false |
|
|
24
24
|
|
|
25
|
+
**Agent spawn failure policy**:
|
|
26
|
+
- If spawning Dev fails with team/config/lock errors, retry at most once.
|
|
27
|
+
- If the second attempt fails, do not enter Implementation Log polling or repeated recovery spawn loops.
|
|
28
|
+
- Use the documented inline/recovery fallback: write `failure-log.md` with the spawn error and last observable state, then either complete remaining tasks directly in the orchestrator or stop with a clear failure for recovery.
|
|
29
|
+
- Apply the same cap to Dev re-spawns for Implementation Log repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
30
|
+
|
|
25
31
|
**Prompt**:
|
|
26
32
|
> {{AGENT_PROMPT_DEV_IMPLEMENT}}
|
|
27
33
|
|
|
34
|
+
**No silent artifact polling**:
|
|
35
|
+
- Do NOT run a long no-output loop that only waits for `## Implementation Log` or any other file marker.
|
|
36
|
+
- If you must wait for Dev after spawning or sending a status request, use short bounded checks (≤120s) that print a heartbeat line each iteration with: elapsed time, remaining unchecked task count, whether `## Implementation Log` exists, and whether `git diff --stat` changed.
|
|
37
|
+
- If Dev has no transcript/file/diff progress for one bounded check, send one status request. If there is still no progress on the next bounded check, stop waiting, write `failure-log.md` with the last observable state, and follow Subagent Timeout Recovery.
|
|
38
|
+
- Prefer the Agent tool's completion notification or Dev's `COMPLETION_SIGNAL`; file presence alone is not a liveness signal.
|
|
39
|
+
|
|
28
40
|
**Gate Check — Implementation Log**:
|
|
29
41
|
After Dev agent returns, verify the Implementation Log was written:
|
|
30
42
|
```bash
|
|
@@ -9,7 +9,11 @@ After `/prizmkit-code-review` returns, verify the review report:
|
|
|
9
9
|
```bash
|
|
10
10
|
grep -q "## Verdict" .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md && echo "GATE:PASS" || echo "GATE:MISSING"
|
|
11
11
|
```
|
|
12
|
-
If GATE:MISSING
|
|
12
|
+
If GATE:MISSING:
|
|
13
|
+
- Do not re-run `/prizmkit-code-review` in an unbounded report-repair loop.
|
|
14
|
+
- Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
|
|
15
|
+
- If the missing report is caused by team/config/lock errors from the internal Reviewer/Dev agent spawn, retry `/prizmkit-code-review` at most once only if it appears transient.
|
|
16
|
+
- If the report is still missing after that single check/retry, write `failure-log.md` with the spawn/skill error and last observable state, then either perform a safe inline fallback review (spec/plan/diff/tests → write `review-report.md` with `## Verdict`) or stop with a clear recovery failure.
|
|
13
17
|
|
|
14
18
|
Read `review-report.md` and check the Verdict:
|
|
15
19
|
- `PASS` → proceed to next phase
|
|
@@ -9,7 +9,11 @@ After `/prizmkit-code-review` returns, verify the review report:
|
|
|
9
9
|
```bash
|
|
10
10
|
grep -q "## Verdict" .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md && echo "GATE:PASS" || echo "GATE:MISSING"
|
|
11
11
|
```
|
|
12
|
-
If GATE:MISSING
|
|
12
|
+
If GATE:MISSING:
|
|
13
|
+
- Do not re-run `/prizmkit-code-review` in an unbounded report-repair loop.
|
|
14
|
+
- Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
|
|
15
|
+
- If the missing report is caused by team/config/lock errors from the internal Reviewer/Dev agent spawn, retry `/prizmkit-code-review` at most once only if it appears transient.
|
|
16
|
+
- If the report is still missing after that single check/retry, write `failure-log.md` with the spawn/skill error and last observable state, then either perform a safe inline fallback review (spec/plan/diff/tests → write `review-report.md` with `## Verdict`) or stop with a clear recovery failure.
|
|
13
17
|
|
|
14
18
|
Read `review-report.md` and check the Verdict:
|
|
15
19
|
- `PASS` → proceed to next phase
|
|
@@ -63,7 +63,8 @@ PHASE_KEYWORDS = {
|
|
|
63
63
|
class ProgressTracker:
|
|
64
64
|
"""Tracks progress state from stream-json events."""
|
|
65
65
|
|
|
66
|
-
def __init__(self):
|
|
66
|
+
def __init__(self, session_log=None):
|
|
67
|
+
self.session_log_path = Path(session_log).expanduser() if session_log else None
|
|
67
68
|
self.message_count = 0
|
|
68
69
|
self.current_tool = None
|
|
69
70
|
self.current_tool_input_summary = ""
|
|
@@ -78,12 +79,19 @@ class ProgressTracker:
|
|
|
78
79
|
self.active_subagent_count = 0
|
|
79
80
|
self.subagent_status_counts = Counter()
|
|
80
81
|
self.codex_child_thread_ids = set()
|
|
82
|
+
self.claude_session_id = ""
|
|
83
|
+
self.claude_cwd = ""
|
|
84
|
+
self.claude_task_states = {}
|
|
81
85
|
self.child_session_files = []
|
|
82
86
|
self.child_total_bytes = 0
|
|
83
87
|
self.child_activity_signature = ""
|
|
84
88
|
self.last_child_activity_at = ""
|
|
85
89
|
self._codex_child_session_paths = {}
|
|
90
|
+
self._claude_child_session_files = []
|
|
86
91
|
self._last_child_scan_at = 0.0
|
|
92
|
+
self._last_claude_fallback_scan_at = 0.0
|
|
93
|
+
self._last_claude_fallback_scan_key = ""
|
|
94
|
+
self._claude_fallback_scan_interval_seconds = 10.0
|
|
87
95
|
self._text_buffer = ""
|
|
88
96
|
self._in_tool_use = False
|
|
89
97
|
self._current_tool_input_parts = []
|
|
@@ -195,11 +203,76 @@ class ProgressTracker:
|
|
|
195
203
|
self.is_active = True
|
|
196
204
|
|
|
197
205
|
elif event_type == "system":
|
|
198
|
-
# System events (hooks, init, etc.) — track but don't count as messages
|
|
206
|
+
# System events (hooks, init, task notifications, etc.) — track but don't count as messages.
|
|
199
207
|
self.event_format = self.event_format or "stream-json"
|
|
200
208
|
subtype = event.get("subtype", "")
|
|
201
209
|
if subtype == "init":
|
|
202
210
|
self.is_active = True
|
|
211
|
+
session_id = event.get("session_id")
|
|
212
|
+
if isinstance(session_id, str) and session_id.strip():
|
|
213
|
+
self.claude_session_id = session_id.strip()
|
|
214
|
+
cwd = event.get("cwd")
|
|
215
|
+
if isinstance(cwd, str) and cwd.strip():
|
|
216
|
+
self.claude_cwd = cwd.strip()
|
|
217
|
+
elif subtype == "task_started":
|
|
218
|
+
task_id = event.get("task_id")
|
|
219
|
+
if isinstance(task_id, str) and task_id.strip():
|
|
220
|
+
self.claude_task_states[task_id.strip()] = {
|
|
221
|
+
"status": "running",
|
|
222
|
+
"summary": str(event.get("description") or "")[:120],
|
|
223
|
+
"tool_use_id": str(event.get("tool_use_id") or ""),
|
|
224
|
+
"task_type": str(event.get("task_type") or ""),
|
|
225
|
+
"subagent_type": str(event.get("subagent_type") or ""),
|
|
226
|
+
}
|
|
227
|
+
self._update_claude_subagent_status_counts()
|
|
228
|
+
elif subtype in ("task_updated", "task_progress"):
|
|
229
|
+
task_id = event.get("task_id")
|
|
230
|
+
if isinstance(task_id, str) and task_id.strip():
|
|
231
|
+
state = self.claude_task_states.setdefault(task_id.strip(), {})
|
|
232
|
+
patch = event.get("patch") if isinstance(event.get("patch"), dict) else {}
|
|
233
|
+
status = patch.get("status") or event.get("status")
|
|
234
|
+
if status:
|
|
235
|
+
state["status"] = str(status)
|
|
236
|
+
summary = patch.get("summary") or patch.get("description") or event.get("summary") or event.get("description")
|
|
237
|
+
if summary:
|
|
238
|
+
state["summary"] = str(summary)[:120]
|
|
239
|
+
else:
|
|
240
|
+
state.setdefault("summary", "")
|
|
241
|
+
tool_use_id = patch.get("tool_use_id") or event.get("tool_use_id")
|
|
242
|
+
if tool_use_id:
|
|
243
|
+
state["tool_use_id"] = str(tool_use_id)
|
|
244
|
+
else:
|
|
245
|
+
state.setdefault("tool_use_id", "")
|
|
246
|
+
task_type = patch.get("task_type") or event.get("task_type")
|
|
247
|
+
if task_type:
|
|
248
|
+
state["task_type"] = str(task_type)
|
|
249
|
+
else:
|
|
250
|
+
state.setdefault("task_type", "")
|
|
251
|
+
subagent_type = patch.get("subagent_type") or event.get("subagent_type")
|
|
252
|
+
if subagent_type:
|
|
253
|
+
state["subagent_type"] = str(subagent_type)
|
|
254
|
+
else:
|
|
255
|
+
state.setdefault("subagent_type", "")
|
|
256
|
+
self._update_claude_subagent_status_counts()
|
|
257
|
+
elif subtype == "task_notification":
|
|
258
|
+
task_id = event.get("task_id")
|
|
259
|
+
if isinstance(task_id, str) and task_id.strip():
|
|
260
|
+
state = self.claude_task_states.setdefault(task_id.strip(), {})
|
|
261
|
+
status = event.get("status") or "completed"
|
|
262
|
+
state["status"] = str(status)
|
|
263
|
+
state["summary"] = str(event.get("summary") or state.get("summary") or "")[:120]
|
|
264
|
+
state.setdefault("tool_use_id", str(event.get("tool_use_id") or ""))
|
|
265
|
+
task_type = event.get("task_type")
|
|
266
|
+
if task_type:
|
|
267
|
+
state["task_type"] = str(task_type)
|
|
268
|
+
else:
|
|
269
|
+
state.setdefault("task_type", "")
|
|
270
|
+
subagent_type = event.get("subagent_type")
|
|
271
|
+
if subagent_type:
|
|
272
|
+
state["subagent_type"] = str(subagent_type)
|
|
273
|
+
else:
|
|
274
|
+
state.setdefault("subagent_type", "")
|
|
275
|
+
self._update_claude_subagent_status_counts()
|
|
203
276
|
|
|
204
277
|
# ── Claude API raw stream format ────────────────────────────
|
|
205
278
|
elif event_type == "message_start":
|
|
@@ -391,16 +464,135 @@ class ProgressTracker:
|
|
|
391
464
|
pass
|
|
392
465
|
return str(matches[0])
|
|
393
466
|
|
|
467
|
+
def _is_tracked_claude_subagent_state(self, state):
|
|
468
|
+
"""Return true for Claude Code task events representing in-process agents."""
|
|
469
|
+
if not isinstance(state, dict):
|
|
470
|
+
return False
|
|
471
|
+
task_type = str(state.get("task_type") or "")
|
|
472
|
+
task_type_lower = task_type.lower()
|
|
473
|
+
subagent_type = str(state.get("subagent_type") or "")
|
|
474
|
+
if task_type_lower == "local_bash":
|
|
475
|
+
return False
|
|
476
|
+
tracked_types = {"in_process_teammate", "subagent", "agent", "teammate"}
|
|
477
|
+
if task_type_lower in tracked_types:
|
|
478
|
+
return True
|
|
479
|
+
if task_type_lower == "local_agent" and subagent_type:
|
|
480
|
+
return True
|
|
481
|
+
summary = str(state.get("summary") or "")
|
|
482
|
+
return bool(
|
|
483
|
+
not task_type
|
|
484
|
+
and summary.lower().startswith(("dev:", "critic:", "reviewer:", "agent:"))
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def _has_tracked_claude_subagent_task(self):
|
|
488
|
+
"""Return true once a Claude Code local-agent/subagent task has been observed."""
|
|
489
|
+
return any(
|
|
490
|
+
self._is_tracked_claude_subagent_state(state)
|
|
491
|
+
for state in self.claude_task_states.values()
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
def _update_claude_subagent_status_counts(self):
|
|
495
|
+
"""Track Claude Code in-process teammate task state counts."""
|
|
496
|
+
counts = Counter()
|
|
497
|
+
active = 0
|
|
498
|
+
inactive_statuses = {
|
|
499
|
+
"completed",
|
|
500
|
+
"failed",
|
|
501
|
+
"cancelled",
|
|
502
|
+
"canceled",
|
|
503
|
+
"killed",
|
|
504
|
+
"stopped",
|
|
505
|
+
"success",
|
|
506
|
+
"error",
|
|
507
|
+
}
|
|
508
|
+
for state in self.claude_task_states.values():
|
|
509
|
+
if not self._is_tracked_claude_subagent_state(state):
|
|
510
|
+
continue
|
|
511
|
+
status = str(state.get("status") or "unknown")
|
|
512
|
+
counts[status] += 1
|
|
513
|
+
if status.lower() not in inactive_statuses:
|
|
514
|
+
active += 1
|
|
515
|
+
summary = state.get("summary") or state.get("subagent_type")
|
|
516
|
+
if summary:
|
|
517
|
+
self.last_text_snippet = str(summary).strip()[:120]
|
|
518
|
+
self._detect_phase(str(summary))
|
|
519
|
+
self.subagent_status_counts = counts
|
|
520
|
+
self.active_subagent_count = active
|
|
521
|
+
|
|
522
|
+
def _claude_projects_dir(self):
|
|
523
|
+
"""Return the Claude Code projects directory for transcript lookup."""
|
|
524
|
+
projects_dir = os.environ.get("CLAUDE_PROJECTS_DIR")
|
|
525
|
+
if projects_dir:
|
|
526
|
+
return Path(projects_dir).expanduser()
|
|
527
|
+
claude_config_dir = os.environ.get("CLAUDE_CONFIG_DIR")
|
|
528
|
+
if claude_config_dir:
|
|
529
|
+
return Path(claude_config_dir).expanduser() / "projects"
|
|
530
|
+
claude_home = os.environ.get("CLAUDE_HOME")
|
|
531
|
+
if claude_home:
|
|
532
|
+
return Path(claude_home).expanduser() / "projects"
|
|
533
|
+
return Path.home() / ".claude" / "projects"
|
|
534
|
+
|
|
535
|
+
def _claude_project_key(self):
|
|
536
|
+
"""Encode cwd the same way Claude Code stores project transcript dirs."""
|
|
537
|
+
cwd = self.claude_cwd
|
|
538
|
+
if not cwd:
|
|
539
|
+
return ""
|
|
540
|
+
return cwd.replace("\\", "-").replace("/", "-").replace(":", "")
|
|
541
|
+
|
|
542
|
+
def _find_claude_child_session_files(self):
|
|
543
|
+
"""Find Claude Code subagent transcripts for this parent session."""
|
|
544
|
+
if not self.claude_session_id:
|
|
545
|
+
return []
|
|
546
|
+
|
|
547
|
+
projects_dir = self._claude_projects_dir()
|
|
548
|
+
if not projects_dir.exists():
|
|
549
|
+
return []
|
|
550
|
+
|
|
551
|
+
candidates = []
|
|
552
|
+
project_key = self._claude_project_key()
|
|
553
|
+
if project_key:
|
|
554
|
+
candidates.append(
|
|
555
|
+
projects_dir / project_key / self.claude_session_id / "subagents"
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
for candidate in candidates:
|
|
559
|
+
if candidate.exists():
|
|
560
|
+
try:
|
|
561
|
+
return sorted(candidate.glob("*.jsonl"))
|
|
562
|
+
except OSError:
|
|
563
|
+
return []
|
|
564
|
+
|
|
565
|
+
# Fallback for non-standard cwd encoding or custom Claude homes. Avoid
|
|
566
|
+
# repeatedly walking every stored transcript before any Agent task exists.
|
|
567
|
+
if not self._has_tracked_claude_subagent_task():
|
|
568
|
+
return []
|
|
569
|
+
|
|
570
|
+
fallback_scan_key = f"{projects_dir}:{self.claude_session_id}"
|
|
571
|
+
now = time.monotonic()
|
|
572
|
+
if (
|
|
573
|
+
self._last_claude_fallback_scan_key == fallback_scan_key
|
|
574
|
+
and now - self._last_claude_fallback_scan_at < self._claude_fallback_scan_interval_seconds
|
|
575
|
+
):
|
|
576
|
+
return self._claude_child_session_files
|
|
577
|
+
self._last_claude_fallback_scan_key = fallback_scan_key
|
|
578
|
+
self._last_claude_fallback_scan_at = now
|
|
579
|
+
try:
|
|
580
|
+
matches = sorted(projects_dir.rglob(f"{self.claude_session_id}/subagents/*.jsonl"))
|
|
581
|
+
except OSError:
|
|
582
|
+
return []
|
|
583
|
+
return matches
|
|
584
|
+
|
|
394
585
|
def refresh_child_session_activity(self, force=False):
|
|
395
|
-
"""Refresh
|
|
586
|
+
"""Refresh child transcript file stats.
|
|
396
587
|
|
|
397
588
|
The heartbeat monitor uses this activity signature to treat subagent
|
|
398
|
-
transcript growth as real progress while the parent
|
|
399
|
-
|
|
589
|
+
transcript growth as real progress while the parent session is blocked
|
|
590
|
+
waiting for a child agent/tool result. Supports Codex child threads and
|
|
591
|
+
Claude Code in-process teammate transcripts.
|
|
400
592
|
"""
|
|
401
593
|
previous_signature = self.child_activity_signature
|
|
402
594
|
|
|
403
|
-
if not self.codex_child_thread_ids:
|
|
595
|
+
if not self.codex_child_thread_ids and not self.claude_session_id:
|
|
404
596
|
self.child_session_files = []
|
|
405
597
|
self.child_total_bytes = 0
|
|
406
598
|
self.child_activity_signature = ""
|
|
@@ -420,6 +612,7 @@ class ProgressTracker:
|
|
|
420
612
|
found = self._find_codex_child_session_file(thread_id)
|
|
421
613
|
if found:
|
|
422
614
|
self._codex_child_session_paths[thread_id] = found
|
|
615
|
+
self._claude_child_session_files = self._find_claude_child_session_files()
|
|
423
616
|
self._last_child_scan_at = now
|
|
424
617
|
|
|
425
618
|
files = []
|
|
@@ -427,24 +620,22 @@ class ProgressTracker:
|
|
|
427
620
|
total_bytes = 0
|
|
428
621
|
max_mtime = 0.0
|
|
429
622
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if not path:
|
|
433
|
-
continue
|
|
623
|
+
def add_file(kind, identifier, path):
|
|
624
|
+
nonlocal total_bytes, max_mtime
|
|
434
625
|
try:
|
|
435
626
|
stat = os.stat(path)
|
|
436
627
|
except OSError:
|
|
437
|
-
|
|
438
|
-
|
|
628
|
+
return
|
|
629
|
+
path_str = str(path)
|
|
439
630
|
total_bytes += stat.st_size
|
|
440
631
|
max_mtime = max(max_mtime, stat.st_mtime)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
)
|
|
632
|
+
mtime_ns = getattr(stat, "st_mtime_ns", int(stat.st_mtime * 1_000_000_000))
|
|
633
|
+
signature_parts.append(f"{kind}:{identifier}:{stat.st_size}:{mtime_ns}")
|
|
444
634
|
files.append(
|
|
445
635
|
{
|
|
446
|
-
"
|
|
447
|
-
"
|
|
636
|
+
"kind": kind,
|
|
637
|
+
"thread_id": identifier,
|
|
638
|
+
"path": path_str,
|
|
448
639
|
"size": stat.st_size,
|
|
449
640
|
"mtime": datetime.fromtimestamp(
|
|
450
641
|
stat.st_mtime, timezone.utc
|
|
@@ -452,6 +643,14 @@ class ProgressTracker:
|
|
|
452
643
|
}
|
|
453
644
|
)
|
|
454
645
|
|
|
646
|
+
for thread_id in sorted(self.codex_child_thread_ids):
|
|
647
|
+
path = self._codex_child_session_paths.get(thread_id)
|
|
648
|
+
if path:
|
|
649
|
+
add_file("codex", thread_id, path)
|
|
650
|
+
|
|
651
|
+
for path in self._claude_child_session_files:
|
|
652
|
+
add_file("claude", path.stem, path)
|
|
653
|
+
|
|
455
654
|
self.child_session_files = files
|
|
456
655
|
self.child_total_bytes = total_bytes
|
|
457
656
|
self.child_activity_signature = "|".join(signature_parts)
|
|
@@ -519,7 +718,7 @@ def atomic_write_json(data, filepath):
|
|
|
519
718
|
|
|
520
719
|
def tail_and_parse(session_log, progress_file, poll_interval=0.5):
|
|
521
720
|
"""Tail session log and parse stream-json events."""
|
|
522
|
-
tracker = ProgressTracker()
|
|
721
|
+
tracker = ProgressTracker(session_log)
|
|
523
722
|
last_write_state = None
|
|
524
723
|
|
|
525
724
|
def state_key(state):
|
|
@@ -80,6 +80,12 @@ You are the **refactor session orchestrator**. Execute Refactor {{REFACTOR_ID}}:
|
|
|
80
80
|
|
|
81
81
|
**YOU are the orchestrator. Execute each phase by spawning the appropriate team agent with run_in_background=false.**
|
|
82
82
|
|
|
83
|
+
**Agent spawn failure policy (all Agent tool calls)**:
|
|
84
|
+
- If spawning Dev or Reviewer fails with team/config/lock errors, retry at most once.
|
|
85
|
+
- If the second attempt fails, do not keep spawning variants and do not enter artifact polling for Implementation Log, review-report, or refactor-report markers.
|
|
86
|
+
- Use the documented inline/recovery fallback for that phase: complete remaining refactor work directly in the orchestrator when safe, write the required report yourself where possible, or write `failure-log.md` with the spawn error and last observable state before stopping for recovery.
|
|
87
|
+
- Apply the same cap to any re-spawn for report repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
88
|
+
|
|
83
89
|
## Workflow Checkpoint System
|
|
84
90
|
|
|
85
91
|
A checkpoint file tracks your progress through this workflow:
|
|
@@ -164,6 +170,7 @@ Include browser verification approach in plan.md:
|
|
|
164
170
|
**Goal**: Execute all tasks from plan.md while preserving existing behavior.
|
|
165
171
|
|
|
166
172
|
- Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false)
|
|
173
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Dev spawn. If the second attempt fails, do not poll for `## Implementation Log`; write `failure-log.md` and either complete remaining refactor work directly in the orchestrator or stop for recovery.
|
|
167
174
|
Prompt: "Read {{DEV_SUBAGENT_PATH}}. For refactor {{REFACTOR_ID}} ('{{REFACTOR_TITLE}}'):
|
|
168
175
|
1. Read `.prizmkit/refactor/{{REFACTOR_ID}}/spec.md` and `.prizmkit/refactor/{{REFACTOR_ID}}/plan.md`
|
|
169
176
|
2. Read `.prizmkit/prizm-docs/` for affected modules (TRAPS, RULES, PATTERNS)
|
|
@@ -201,6 +208,7 @@ Include browser verification approach in plan.md:
|
|
|
201
208
|
**Goal**: Verify refactoring quality and behavior preservation.
|
|
202
209
|
|
|
203
210
|
- Spawn Reviewer agent (Agent tool, subagent_type="prizm-dev-team-reviewer", run_in_background=false)
|
|
211
|
+
Spawn failure cap: for team/config/lock errors, retry at most once for this Reviewer spawn. If the second attempt fails, do not poll for `review-report.md`; write `failure-log.md` with the spawn error and last observable state before stopping or performing an inline fallback.
|
|
204
212
|
Prompt: "Read {{REVIEWER_SUBAGENT_PATH}}. For refactor {{REFACTOR_ID}}:
|
|
205
213
|
1. Read `.prizmkit/refactor/{{REFACTOR_ID}}/spec.md` for goals and behavior preservation contracts
|
|
206
214
|
2. Read `.prizmkit/refactor/{{REFACTOR_ID}}/plan.md` for architecture decisions and completed tasks
|
|
@@ -221,7 +229,20 @@ Include browser verification approach in plan.md:
|
|
|
221
229
|
7. Report: verdict (PASS/NEEDS_FIXES), number of rounds, findings fixed/rejected
|
|
222
230
|
"
|
|
223
231
|
- **Wait for Reviewer to return**
|
|
224
|
-
-
|
|
232
|
+
- **Gate Check — Review Report**:
|
|
233
|
+
After Reviewer returns, verify the review report contains a verdict:
|
|
234
|
+
```powershell
|
|
235
|
+
if (Select-String -Path .prizmkit/refactor/{{REFACTOR_ID}}/review-report.md -Pattern "## Verdict" -Quiet) { "GATE:PASS" } else { "GATE:MISSING" }
|
|
236
|
+
```
|
|
237
|
+
If GATE:MISSING:
|
|
238
|
+
- Do not enter an unbounded report-repair loop and do not repeatedly re-spawn Reviewer.
|
|
239
|
+
- Perform one bounded status check; retry at most once: inspect the Reviewer output, `review-report.md` path, and any internal Reviewer/Dev spawn messages from `/prizmkit-code-review`.
|
|
240
|
+
- If the missing report is caused by team/config/lock errors from the Reviewer or internal Reviewer/Dev agent spawn, retry the Reviewer agent at most once only if it appears transient.
|
|
241
|
+
- If the report is still missing after that single check/retry, write `.prizmkit/refactor/{{REFACTOR_ID}}/failure-log.md` with the spawn/skill error and last observable state, then either perform a safe inline fallback review (spec/plan/diff/tests → write `review-report.md` with `## Verdict`) or stop with a clear recovery failure.
|
|
242
|
+
|
|
243
|
+
Read `review-report.md` and check the Verdict:
|
|
244
|
+
- `PASS` → proceed to next phase
|
|
245
|
+
- `NEEDS_FIXES` → log remaining findings and proceed (do not retry externally)
|
|
225
246
|
- **CP-RF-3**: Code review complete, tests pass, behavior preserved
|
|
226
247
|
- **Checkpoint update**: set step `prizmkit-code-review` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
227
248
|
|
|
@@ -8,6 +8,16 @@ If CRITIC:MISSING — skip this phase entirely and proceed. Log: "Critic agent n
|
|
|
8
8
|
|
|
9
9
|
**Choose ONE path based on `{{CRITIC_COUNT}}`:**
|
|
10
10
|
|
|
11
|
+
**Agent spawn failure policy**:
|
|
12
|
+
- If spawning Critic fails with team/config/lock errors, retry at most once.
|
|
13
|
+
- If the second attempt fails, do not keep spawning variants. Either create the required team once (when team tooling is available) or perform the plan challenge inline and write the required challenge report yourself.
|
|
14
|
+
- Record the fallback in the report; do not burn multiple minutes on repeated identical spawn failures.
|
|
15
|
+
|
|
16
|
+
**No silent report polling**:
|
|
17
|
+
- Do NOT run a long no-output loop waiting for `challenge-report*.md`.
|
|
18
|
+
- If you need to wait for a report file, use a short bounded check (≤120s) that prints elapsed time and reports present on every iteration.
|
|
19
|
+
- If reports are still missing after the bounded check, request one status update; if still missing, perform the missing challenge lens inline and continue.
|
|
20
|
+
|
|
11
21
|
**If {{CRITIC_COUNT}} = 1 → Single Critic** (skip to CP-2.5 after this):
|
|
12
22
|
|
|
13
23
|
**Spawn Agent**:
|
|
@@ -16,6 +16,16 @@ If CRITIC:MISSING — skip this phase entirely and proceed. Log: "Critic agent n
|
|
|
16
16
|
**Prompt**:
|
|
17
17
|
> {{AGENT_PROMPT_CRITIC_PLAN_CHALLENGE}}
|
|
18
18
|
|
|
19
|
+
**Agent spawn failure policy**:
|
|
20
|
+
- If spawning Critic fails with team/config/lock errors, retry at most once.
|
|
21
|
+
- If the second attempt fails, do not keep spawning variants. Either create the required team once (when team tooling is available) or perform the plan challenge inline and write `challenge-report.md` yourself.
|
|
22
|
+
- Record the fallback in the report; do not burn multiple minutes on repeated identical spawn failures.
|
|
23
|
+
|
|
24
|
+
**No silent report polling**:
|
|
25
|
+
- Do NOT run a long no-output loop waiting for `challenge-report.md`.
|
|
26
|
+
- If you need to wait for the report file, use a short bounded check (≤120s) that prints elapsed time and whether the report exists on every iteration.
|
|
27
|
+
- If the report is still missing after the bounded check, request one status update; if still missing, perform the challenge inline and continue.
|
|
28
|
+
|
|
19
29
|
Wait for Critic to return.
|
|
20
30
|
- Read challenge-report.md. For items marked CRITICAL/HIGH: decide whether to adjust plan.md or document why the plan stands.
|
|
21
31
|
- Max 1 plan revision round.
|
|
@@ -15,11 +15,23 @@
|
|
|
15
15
|
| subagent_type | prizm-dev-team-dev |
|
|
16
16
|
| run_in_background | false |
|
|
17
17
|
|
|
18
|
+
**Agent spawn failure policy**:
|
|
19
|
+
- If spawning Dev fails with team/config/lock errors, retry at most once.
|
|
20
|
+
- If the second attempt fails, do not enter Implementation Log polling or repeated recovery spawn loops.
|
|
21
|
+
- Use the documented inline/recovery fallback: write `failure-log.md` with the spawn error and last observable state, then either complete remaining tasks directly in the orchestrator or stop with a clear failure for recovery.
|
|
22
|
+
- Apply the same cap to Dev re-spawns for Implementation Log repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
23
|
+
|
|
18
24
|
**Prompt**:
|
|
19
25
|
> {{AGENT_PROMPT_DEV_IMPLEMENT}}
|
|
20
26
|
|
|
21
27
|
Wait for Dev to return. All tasks must be `[x]`, tests pass.
|
|
22
28
|
|
|
29
|
+
**No silent artifact polling**:
|
|
30
|
+
- Do NOT run a long no-output loop that only waits for `## Implementation Log` or any other file marker.
|
|
31
|
+
- If you must wait for Dev after spawning or sending a status request, use short bounded checks (≤120s) that print a heartbeat line each iteration with: elapsed time, remaining unchecked task count, whether `## Implementation Log` exists, and whether `git diff --stat` changed.
|
|
32
|
+
- If Dev has no transcript/file/diff progress for one bounded check, send one status request. If there is still no progress on the next bounded check, stop waiting, write `failure-log.md` with the last observable state, and follow Subagent Timeout Recovery.
|
|
33
|
+
- Prefer the Agent tool's completion notification or Dev's `COMPLETION_SIGNAL`; file presence alone is not a liveness signal.
|
|
34
|
+
|
|
23
35
|
**Gate Check — Implementation Log**:
|
|
24
36
|
After Dev agent returns, verify the Implementation Log was written:
|
|
25
37
|
```powershell
|
|
@@ -22,9 +22,21 @@ Select-String -Pattern '^\- \[ \]' .prizmkit/specs/{{FEATURE_SLUG}}/plan.md 2>$n
|
|
|
22
22
|
| subagent_type | prizm-dev-team-dev |
|
|
23
23
|
| run_in_background | false |
|
|
24
24
|
|
|
25
|
+
**Agent spawn failure policy**:
|
|
26
|
+
- If spawning Dev fails with team/config/lock errors, retry at most once.
|
|
27
|
+
- If the second attempt fails, do not enter Implementation Log polling or repeated recovery spawn loops.
|
|
28
|
+
- Use the documented inline/recovery fallback: write `failure-log.md` with the spawn error and last observable state, then either complete remaining tasks directly in the orchestrator or stop with a clear failure for recovery.
|
|
29
|
+
- Apply the same cap to Dev re-spawns for Implementation Log repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
|
|
30
|
+
|
|
25
31
|
**Prompt**:
|
|
26
32
|
> {{AGENT_PROMPT_DEV_IMPLEMENT}}
|
|
27
33
|
|
|
34
|
+
**No silent artifact polling**:
|
|
35
|
+
- Do NOT run a long no-output loop that only waits for `## Implementation Log` or any other file marker.
|
|
36
|
+
- If you must wait for Dev after spawning or sending a status request, use short bounded checks (≤120s) that print a heartbeat line each iteration with: elapsed time, remaining unchecked task count, whether `## Implementation Log` exists, and whether `git diff --stat` changed.
|
|
37
|
+
- If Dev has no transcript/file/diff progress for one bounded check, send one status request. If there is still no progress on the next bounded check, stop waiting, write `failure-log.md` with the last observable state, and follow Subagent Timeout Recovery.
|
|
38
|
+
- Prefer the Agent tool's completion notification or Dev's `COMPLETION_SIGNAL`; file presence alone is not a liveness signal.
|
|
39
|
+
|
|
28
40
|
**Gate Check — Implementation Log**:
|
|
29
41
|
After Dev agent returns, verify the Implementation Log was written:
|
|
30
42
|
```powershell
|
|
@@ -9,7 +9,11 @@ After `/prizmkit-code-review` returns, verify the review report:
|
|
|
9
9
|
```powershell
|
|
10
10
|
if (Select-String -Path .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md -Pattern "## Verdict" -Quiet) { "GATE:PASS" } else { "GATE:MISSING" }
|
|
11
11
|
```
|
|
12
|
-
If GATE:MISSING
|
|
12
|
+
If GATE:MISSING:
|
|
13
|
+
- Do not re-run `/prizmkit-code-review` in an unbounded report-repair loop.
|
|
14
|
+
- Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
|
|
15
|
+
- If the missing report is caused by team/config/lock errors from the internal Reviewer/Dev agent spawn, retry `/prizmkit-code-review` at most once only if it appears transient.
|
|
16
|
+
- If the report is still missing after that single check/retry, write `failure-log.md` with the spawn/skill error and last observable state, then either perform a safe inline fallback review (spec/plan/diff/tests → write `review-report.md` with `## Verdict`) or stop with a clear recovery failure.
|
|
13
17
|
|
|
14
18
|
Read `review-report.md` and check the Verdict:
|
|
15
19
|
- `PASS` → proceed to next phase
|
|
@@ -9,7 +9,11 @@ After `/prizmkit-code-review` returns, verify the review report:
|
|
|
9
9
|
```powershell
|
|
10
10
|
if (Select-String -Path .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md -Pattern "## Verdict" -Quiet) { "GATE:PASS" } else { "GATE:MISSING" }
|
|
11
11
|
```
|
|
12
|
-
If GATE:MISSING
|
|
12
|
+
If GATE:MISSING:
|
|
13
|
+
- Do not re-run `/prizmkit-code-review` in an unbounded report-repair loop.
|
|
14
|
+
- Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
|
|
15
|
+
- If the missing report is caused by team/config/lock errors from the internal Reviewer/Dev agent spawn, retry `/prizmkit-code-review` at most once only if it appears transient.
|
|
16
|
+
- If the report is still missing after that single check/retry, write `failure-log.md` with the spawn/skill error and last observable state, then either perform a safe inline fallback review (spec/plan/diff/tests → write `review-report.md` with `## Verdict`) or stop with a clear recovery failure.
|
|
13
17
|
|
|
14
18
|
Read `review-report.md` and check the Verdict:
|
|
15
19
|
- `PASS` → proceed to next phase
|