prizmkit 1.1.72 → 1.1.76

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 (62) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/agents/prizm-dev-team-dev.md +11 -1
  3. package/bundled/dev-pipeline/.env.example +1 -0
  4. package/bundled/dev-pipeline/lib/common.sh +53 -0
  5. package/bundled/dev-pipeline/lib/heartbeat.sh +94 -42
  6. package/bundled/dev-pipeline/run-bugfix.sh +5 -0
  7. package/bundled/dev-pipeline/run-feature.sh +5 -0
  8. package/bundled/dev-pipeline/run-recovery.sh +6 -0
  9. package/bundled/dev-pipeline/run-refactor.sh +5 -0
  10. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +265 -5
  11. package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +21 -0
  12. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +1 -1
  13. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +5 -9
  14. package/bundled/dev-pipeline/templates/sections/feature-context.md +3 -18
  15. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +1 -1
  16. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +6 -12
  17. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +10 -3
  18. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -0
  19. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +4 -8
  20. package/bundled/dev-pipeline-windows/lib/common.ps1 +23 -0
  21. package/bundled/dev-pipeline-windows/lib/heartbeat.sh +439 -0
  22. package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +33 -2
  23. package/bundled/dev-pipeline-windows/run-recovery.ps1 +11 -0
  24. package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +265 -5
  25. package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +21 -0
  26. package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +1 -1
  27. package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +27 -0
  28. package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +543 -14
  29. package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +664 -14
  30. package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +741 -14
  31. package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +2 -2
  32. package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +1 -1
  33. package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +1 -1
  34. package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +1 -1
  35. package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +3 -3
  36. package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +1 -1
  37. package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +3 -18
  38. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +239 -40
  39. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +75 -26
  40. package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +142 -36
  41. package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +2 -2
  42. package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +1 -1
  43. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +1 -1
  44. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +7 -17
  45. package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +10 -3
  46. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +1 -1
  47. package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +1 -1
  48. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +3 -1
  49. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +7 -3
  50. package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +1 -3
  51. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +1 -1
  52. package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +1 -1
  53. package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +1 -1
  54. package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +2 -2
  55. package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +13 -17
  56. package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +2 -4
  57. package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +1 -1
  58. package/bundled/skills/_metadata.json +1 -1
  59. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +1 -0
  60. package/bundled/skills/feature-pipeline-launcher/SKILL.md +1 -0
  61. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +1 -0
  62. package/package.json +1 -1
@@ -137,7 +137,10 @@ class ProgressTracker:
137
137
  self.event_format = ""
138
138
  self.active_subagent_count = 0
139
139
  self.subagent_status_counts = Counter()
140
+ self._subagent_spawn_count = 0
140
141
  self.codex_child_thread_ids = set()
142
+ self.cb_session_id = ""
143
+ self.cb_cwd = ""
141
144
  self.claude_session_id = ""
142
145
  self.claude_cwd = ""
143
146
  self.claude_task_states = {}
@@ -147,6 +150,7 @@ class ProgressTracker:
147
150
  self.last_child_activity_at = ""
148
151
  self._codex_child_session_paths = {}
149
152
  self._claude_child_session_files = []
153
+ self._cb_child_session_files = []
150
154
  self._last_child_scan_at = 0.0
151
155
  self._last_claude_fallback_scan_at = 0.0
152
156
  self._last_claude_fallback_scan_key = ""
@@ -158,10 +162,13 @@ class ProgressTracker:
158
162
  def process_event(self, event):
159
163
  """Process a single stream-json event and update state.
160
164
 
161
- Supports two formats:
165
+ Supports three formats:
162
166
  1. Claude API raw stream: message_start, content_block_start, content_block_delta, etc.
163
167
  2. Claude Code verbose stream-json: assistant, user, tool_result, system, etc.
164
168
  (produced by claude/claude-internal --verbose --output-format stream-json)
169
+ 3. CodeBuddy stream-json: message/function_call/function_call_result events
170
+ (produced by cbc --print -y --output-format stream-json — message type
171
+ with sessionId/cwd metadata, function_call for tool invocations).
165
172
  """
166
173
  event_type = event.get("type", "")
167
174
 
@@ -233,6 +240,93 @@ class ProgressTracker:
233
240
 
234
241
  return
235
242
 
243
+ # ── CodeBuddy stream-json format ──────────────────────────────
244
+ # cbc --print -y --output-format stream-json emits:
245
+ # message (role=user/assistant), function_call, function_call_result,
246
+ # file-history-snapshot, error. The first user message carries
247
+ # sessionId and cwd metadata; later function_call items carry the
248
+ # same fields.
249
+ # NOTE: cbc emits tool invocations as EITHER tool_use blocks inside a
250
+ # message (role=assistant) event OR as standalone function_call events,
251
+ # but not both for the same invocation. The two handlers are
252
+ # complementary rather than redundant, so no deduplication is needed.
253
+ # Detect via event_type=="message" with a "sessionId" field.
254
+ # Must run before the Claude Code assistant / tool_result branches
255
+ # because those also accept those types but use different internals.
256
+ if event_type == "message" and isinstance(event.get("sessionId"), str):
257
+ self.event_format = self.event_format or "stream-json"
258
+ role = event.get("role", "")
259
+ sid = event.get("sessionId", "")
260
+ cwd = event.get("cwd", "")
261
+ if sid and not self.cb_session_id:
262
+ self.cb_session_id = sid.strip()
263
+ if cwd and not self.cb_cwd:
264
+ self.cb_cwd = cwd.strip()
265
+ if role == "assistant":
266
+ self.message_count += 1
267
+ self.is_active = True
268
+ content = event.get("content", [])
269
+ if isinstance(content, list):
270
+ for block in content:
271
+ block_type = block.get("type", "")
272
+ if block_type == "tool_use":
273
+ tool_name = block.get("name", "unknown")
274
+ self.current_tool = tool_name
275
+ self.tool_call_counts[tool_name] += 1
276
+ self.total_tool_calls += 1
277
+ tool_input = block.get("input", {})
278
+ if isinstance(tool_input, dict):
279
+ self._extract_tool_summary_from_dict(tool_input)
280
+ self._detect_phase(
281
+ json.dumps(tool_input, ensure_ascii=False)[:500]
282
+ )
283
+ # Track CodeBuddy Agent/Task tool invocations as
284
+ # potential sub-agent spawns (Gap 3 fix).
285
+ self._record_cb_agent_tool_call(tool_name, tool_input)
286
+ elif block_type == "text":
287
+ text = block.get("text", "")
288
+ if text.strip():
289
+ self.last_text_snippet = text.strip()[:120]
290
+ self._detect_phase(text)
291
+ self._detect_terminal_error(
292
+ text, require_error_context=True
293
+ )
294
+ return
295
+
296
+ # CodeBuddy function_call / function_call_result in stream-json
297
+ if event_type in ("function_call", "function_call_result"):
298
+ self.event_format = self.event_format or "stream-json"
299
+ sid = event.get("sessionId", "")
300
+ cwd = event.get("cwd", "")
301
+ if sid and not self.cb_session_id:
302
+ self.cb_session_id = sid.strip()
303
+ if cwd and not self.cb_cwd:
304
+ self.cb_cwd = cwd.strip()
305
+ if event_type == "function_call":
306
+ tool_name = event.get("name", "unknown")
307
+ self.current_tool = tool_name
308
+ self.tool_call_counts[tool_name] += 1
309
+ self.total_tool_calls += 1
310
+ self.is_active = True
311
+ # Extract summary from arguments
312
+ raw_args = event.get("arguments", "")
313
+ if isinstance(raw_args, str) and raw_args.strip():
314
+ self._extract_tool_summary(raw_args)
315
+ self._detect_phase(raw_args[:500])
316
+ # Track Agent/Task tool invocations as sub-agent spawns
317
+ self._record_cb_agent_tool_call(tool_name, raw_args)
318
+ elif event_type == "function_call_result":
319
+ self.current_tool = None
320
+ # Check result text for phase / error hints
321
+ result_text = event.get("output") or ""
322
+ if result_text and isinstance(result_text, str):
323
+ stripped = result_text.strip()
324
+ if stripped:
325
+ self.last_text_snippet = stripped[:120]
326
+ self._detect_phase(stripped)
327
+ self._detect_terminal_error(stripped, require_error_context=True)
328
+ return
329
+
236
330
  # ── Claude Code verbose format ──────────────────────────────
237
331
  if event_type == "assistant":
238
332
  self.event_format = self.event_format or "stream-json"
@@ -259,11 +353,52 @@ class ProgressTracker:
259
353
  self._detect_phase(text)
260
354
  self._detect_terminal_error(text, require_error_context=True)
261
355
 
262
- elif event_type == "tool_result" or event_type == "user":
356
+ elif event_type == "tool_result" or (
357
+ event_type == "user"
358
+ and not isinstance(event.get("sessionId"), str)
359
+ ):
263
360
  # tool_result contains output from tool execution
264
361
  self.event_format = self.event_format or "stream-json"
265
362
  self.is_active = True
266
363
 
364
+ # Check for error patterns in tool_result content (supports both formats):
365
+ # A) Top-level tool_result events: event["content"] is the result text
366
+ # B) Nested user events: event["message"]["content"][] has type=="tool_result" items
367
+ content_candidates = []
368
+
369
+ # Format A: top-level tool_result (Claude Code)
370
+ if event_type == "tool_result":
371
+ content_candidates.append(str(event.get("content", "")))
372
+
373
+ # Format B: nested inside user event (Claude Code, NOT CodeBuddy)
374
+ if event_type == "user":
375
+ message = event.get("message", {})
376
+ content_list = message.get("content", [])
377
+ if isinstance(content_list, list):
378
+ for item in content_list:
379
+ if isinstance(item, dict) and item.get("type") == "tool_result":
380
+ content_candidates.append(str(item.get("content", "")))
381
+
382
+ for result_text in content_candidates:
383
+ if "shorter than the provided offset" in result_text:
384
+ self.errors.append({
385
+ "type": "read_offset_overflow",
386
+ "tool": self.current_tool,
387
+ "at": datetime.now(timezone.utc).isoformat(),
388
+ })
389
+ break # one error per event is enough
390
+ elif "Wasted call" in result_text:
391
+ self.errors.append({
392
+ "type": "wasted_call",
393
+ "tool": self.current_tool,
394
+ "at": datetime.now(timezone.utc).isoformat(),
395
+ })
396
+ break
397
+
398
+ # Keep only last 20 errors to prevent unbounded growth in progress.json
399
+ if len(self.errors) > 20:
400
+ self.errors = self.errors[-20:]
401
+
267
402
  elif event_type == "system":
268
403
  # System events (hooks, init, task notifications, etc.) — track but don't count as messages.
269
404
  self.event_format = self.event_format or "stream-json"
@@ -427,15 +562,60 @@ class ProgressTracker:
427
562
  self._current_tool_input_parts = []
428
563
 
429
564
  elif event_type == "error":
565
+ self.event_format = self.event_format or "stream-json"
430
566
  error_msg = event.get("error", {}).get("message", "Unknown error")
567
+ errors = event.get("errors") or []
568
+ if isinstance(errors, list) and errors:
569
+ error_msg = "; ".join(str(e) for e in errors[:3])
431
570
  self.errors.append(error_msg)
432
571
  self._detect_terminal_error(str(error_msg))
433
572
 
573
+ # ── CodeBuddy file-history-snapshot (ignore) ─────────────────
574
+ elif event_type == "file-history-snapshot":
575
+ return
576
+
434
577
  # Check for subagent indicator
435
578
  if event.get("parent_tool_use_id"):
436
579
  # This is a sub-agent event; tool name is still tracked normally
437
580
  pass
438
581
 
582
+ def _record_cb_agent_tool_call(self, tool_name, raw_args):
583
+ """Record a CodeBuddy Agent/Task* tool invocation for sub-agent tracking.
584
+
585
+ CodeBuddy spawns sub-agents via:
586
+ - Agent(prompt=..., run_in_background=True/False) — synchronous or bg fork
587
+ - TaskCreate(subject=..., description=...) — scheduled task
588
+
589
+ (TaskUpdate/TaskOutput exist but are lifecycle-only — they track
590
+ existing tasks rather than creating new sub-agents, so we don't
591
+ count them as spawns.)
592
+
593
+ We don't get child session ids from these tool calls in the stream,
594
+ but recording the count lets the heartbeat code in heartbeat.sh apply
595
+ an extended stale-kill window just as it does for Codex spawn_agent.
596
+ """
597
+ if tool_name not in ("Agent", "TaskCreate"):
598
+ return
599
+ # Both dict (from message events) and str/JSON (from function_call events)
600
+ # are supported.
601
+ if isinstance(raw_args, dict):
602
+ args = raw_args
603
+ elif isinstance(raw_args, str):
604
+ try:
605
+ args = json.loads(raw_args)
606
+ except (json.JSONDecodeError, TypeError):
607
+ return
608
+ else:
609
+ return
610
+ # For Agent tool, subagent_type or run_in_background hints at delegation
611
+ if tool_name == "Agent":
612
+ subagent_type = args.get("subagent_type", "")
613
+ prompt = args.get("prompt", "")
614
+ if subagent_type or prompt:
615
+ self._subagent_spawn_count += 1
616
+ elif tool_name == "TaskCreate":
617
+ self._subagent_spawn_count += 1
618
+
439
619
  def _record_terminal_result(self, text="", is_error=False, api_error_status=None, api_error_code=""):
440
620
  """Record a Claude Code terminal result event."""
441
621
  terminal_text = str(text or "")
@@ -719,17 +899,85 @@ class ProgressTracker:
719
899
  return []
720
900
  return matches
721
901
 
902
+ def _cb_projects_dir(self):
903
+ """Return the CodeBuddy projects directory for transcript lookup.
904
+
905
+ CodeBuddy stores per-project session transcripts and sub-agent data
906
+ under ~/.codebuddy/projects/{projectDir}/{sessionId}/.
907
+ """
908
+ cb_config = os.environ.get("CODEBUDDY_CONFIG_DIR")
909
+ if cb_config:
910
+ return Path(cb_config).expanduser() / "projects"
911
+ cb_home = os.environ.get("CODEBUDDY_HOME")
912
+ if cb_home:
913
+ return Path(cb_home).expanduser() / "projects"
914
+ return Path.home() / ".codebuddy" / "projects"
915
+
916
+ def _cb_project_key(self):
917
+ """Encode cwd the same way CodeBuddy stores project subdirs.
918
+
919
+ CodeBuddy uses the same sanitisation as Claude Code (\\, /, : → -)
920
+ but strips the leading '-' so "/Users/test/MyProject" becomes
921
+ "Users-test-MyProject".
922
+ """
923
+ cwd = self.cb_cwd
924
+ if not cwd:
925
+ return ""
926
+ return cwd.replace("\\", "-").replace("/", "-").replace(":", "").lstrip("-")
927
+
928
+ def _find_cb_child_session_files(self):
929
+ """Find CodeBuddy subagent transcript data for this parent session.
930
+
931
+ CodeBuddy sub-agents store tool-results/{callId}.txt files; conversation
932
+ transcripts are not (as of 2026) written as agent-*.jsonl files in the
933
+ subagents/ directory. We track the tool-results .txt files as a proxy
934
+ for child activity so the heartbeat monitor can extend the stale-kill
935
+ window while sub-agents are running.
936
+ """
937
+ if not self.cb_session_id:
938
+ return []
939
+
940
+ projects_dir = self._cb_projects_dir()
941
+ if not projects_dir.exists():
942
+ return []
943
+
944
+ project_key = self._cb_project_key()
945
+ if not project_key:
946
+ return []
947
+
948
+ subagents_dir = (
949
+ projects_dir / project_key / self.cb_session_id / "subagents"
950
+ )
951
+ if not subagents_dir.exists():
952
+ return []
953
+
954
+ # Collect all files under each agent-{id} subdirectory. These are
955
+ # currently tool-results/{callId}.txt files, but the glob also picks
956
+ # up future agent-*.jsonl transcripts should CodeBuddy add them.
957
+ try:
958
+ return sorted(
959
+ p for p in subagents_dir.glob("**/*")
960
+ if p.is_file()
961
+ )
962
+ except OSError:
963
+ return []
964
+
722
965
  def refresh_child_session_activity(self, force=False):
723
966
  """Refresh child transcript file stats.
724
967
 
725
968
  The heartbeat monitor uses this activity signature to treat subagent
726
969
  transcript growth as real progress while the parent session is blocked
727
- waiting for a child agent/tool result. Supports Codex child threads and
728
- Claude Code in-process teammate transcripts.
970
+ waiting for a child agent/tool result. Supports Codex child threads,
971
+ Claude Code in-process teammate transcripts, and CodeBuddy sub-agent
972
+ file activity under subagents/.
729
973
  """
730
974
  previous_signature = self.child_activity_signature
731
975
 
732
- if not self.codex_child_thread_ids and not self.claude_session_id:
976
+ if (
977
+ not self.codex_child_thread_ids
978
+ and not self.claude_session_id
979
+ and not self.cb_session_id
980
+ ):
733
981
  self.child_session_files = []
734
982
  self.child_total_bytes = 0
735
983
  self.child_activity_signature = ""
@@ -750,6 +998,7 @@ class ProgressTracker:
750
998
  if found:
751
999
  self._codex_child_session_paths[thread_id] = found
752
1000
  self._claude_child_session_files = self._find_claude_child_session_files()
1001
+ self._cb_child_session_files = self._find_cb_child_session_files()
753
1002
  self._last_child_scan_at = now
754
1003
 
755
1004
  files = []
@@ -788,6 +1037,14 @@ class ProgressTracker:
788
1037
  for path in self._claude_child_session_files:
789
1038
  add_file("claude", path.stem, path)
790
1039
 
1040
+ for path in self._cb_child_session_files:
1041
+ # Identifier for CodeBuddy sub-agent files: use the parent
1042
+ # directory name (agent-{id}) so heartbeat can distinguish
1043
+ # activity across different sub-agent instances.
1044
+ parent_name = path.parent.name if hasattr(path, "parent") else ""
1045
+ identifier = parent_name if parent_name.startswith("agent-") else path.name
1046
+ add_file("codebuddy", identifier, path)
1047
+
791
1048
  self.child_session_files = files
792
1049
  self.child_total_bytes = total_bytes
793
1050
  self.child_activity_signature = "|".join(signature_parts)
@@ -823,11 +1080,14 @@ class ProgressTracker:
823
1080
  "total_tool_calls": self.total_tool_calls,
824
1081
  "active_subagent_count": self.active_subagent_count,
825
1082
  "subagent_states": subagent_states,
1083
+ "subagent_spawn_count": self._subagent_spawn_count,
826
1084
  "child_thread_ids": sorted(self.codex_child_thread_ids),
827
1085
  "child_session_files": self.child_session_files,
828
1086
  "child_total_bytes": self.child_total_bytes,
829
1087
  "child_activity_signature": self.child_activity_signature,
830
1088
  "last_child_activity_at": self.last_child_activity_at,
1089
+ "cb_session_id": self.cb_session_id,
1090
+ "cb_cwd": self.cb_cwd,
831
1091
  "last_text_snippet": self.last_text_snippet,
832
1092
  "last_result_is_error": self.last_result_is_error,
833
1093
  "api_error_status": self.api_error_status,
@@ -1,5 +1,23 @@
1
1
  "Read {{DEV_SUBAGENT_PATH}}. Implement feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}).
2
2
 
3
+ ## Task Summary Card
4
+
5
+ **Objective**: Implement {{FEATURE_TITLE}}.
6
+
7
+ **Primary files** (see context-snapshot.md Section 4 for full manifest):
8
+ - Review plan.md Tasks section for the complete task-to-file mapping.
9
+ - Each task's `— file:` suffix identifies the target file.
10
+
11
+ **Test command**: `{{TEST_CMD}}`
12
+
13
+ **Known baseline failures**: `{{BASELINE_FAILURES}}`
14
+
15
+ **DO NOT**:
16
+ - Re-read source files already listed in context-snapshot.md Section 4 File Manifest
17
+ - Create new files unless a plan.md task explicitly requires it
18
+ - Run git commands
19
+ - Use mock success data or fake rows in UI/tests
20
+
3
21
  ## Required Inputs
4
22
 
5
23
  1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` first.
@@ -35,6 +53,9 @@ Before returning, append `## Implementation Log` to `context-snapshot.md` with:
35
53
  - Carry forward the Dev-isolated subset: skip scaffold/generated files listed in `context-snapshot.md`; verify dependency versions before install/build commands that resolve dependencies; after build/compile commands, ensure outputs are ignored and never commit generated artifacts.
36
54
  - If tests fail, follow this Test Failure Recovery subset: classify failures as baseline, new regression, brittle test, or environment/tooling; fix new regressions and brittle tests while progress is being made; document baseline failures; write `failure-log.md` for blockers.
37
55
  - Do not run git commands; staging and commit are handled by the orchestrator.
56
+ - **Edit safety**: If an Edit fails with 'String to replace not found', grep for the target text before retrying. Never guess file offsets — verify them with a Read or grep first.
57
+ - **Read safety**: If 3 consecutive Reads to the same file return 'shorter than offset' or 'Wasted call', STOP and report BLOCKED.
58
+ - **Test early**: Run `{{TEST_CMD}}` after every 3 successful Edit operations. Capture output to /tmp/test-out.txt and grep for failures.
38
59
 
39
60
  Do not return success unless:
40
61
  1. implementation tasks are complete;
@@ -131,7 +131,7 @@ If MISSING — build it now:
131
131
  ```bash
132
132
  find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
133
133
  ```
134
- - **Section 3 — Prizm Context**: full content of root.prizm and relevant L1/L2 docs
134
+ - **Section 3 — Key TRAPS & RULES**: relevant TRAPS/RULES from prizm-docs (not full copies)
135
135
  - **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
136
136
  ### Files to Modify
137
137
  | File | Why Needed | Key Interfaces |
@@ -148,12 +148,9 @@ If `EXISTING_CODE` is non-empty: your spec/plan/tasks must reflect this existing
148
148
  Identify the top-level source directories from the results.
149
149
  3. Scan the detected source directories for files related to this feature; read each one
150
150
  4. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
151
- - **Section 1 — Feature Brief**: feature description + acceptance criteria (copy from above)
152
- - **Section 2 — Project Structure**: run the following to get a visual directory tree, then paste output:
153
- ```bash
154
- find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
155
- ```
156
- - **Section 3 — Prizm Context**: full content of root.prizm and relevant L1/L2 docs
151
+ - **Section 1 — Feature Brief**: feature description (2-3 lines) + acceptance criteria (copy from above)
152
+ - **Section 2 — Verification Gates**: copy the gate checklist from the Task Contract above
153
+ - **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs that apply to files in this scope. Do NOT copy full L0/L1/L2 content.
157
154
  - **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
158
155
  ### Files to Modify
159
156
  | File | Why Needed | Key Interfaces |
@@ -167,7 +164,6 @@ If `EXISTING_CODE` is non-empty: your spec/plan/tasks must reflect this existing
167
164
 
168
165
  ### Known TRAPS (from .prizmkit/prizm-docs/)
169
166
  - <trap entries extracted from L1/L2 docs>
170
- - **Section 5 — Existing Tests**: full content of related test files as code blocks
171
167
  4. Confirm: `ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`
172
168
 
173
169
  **After Step A**: Use context-snapshot.md Section 4 File Manifest to guide targeted file reads. Do NOT scan directories or read unrelated files.
@@ -200,7 +196,7 @@ Spawn failure cap: for team/config/lock errors, retry at most once for this Revi
200
196
 
201
197
  Prompt:
202
198
  > "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
203
- > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has project context, Section 4 has file manifest.
199
+ > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has TRAPS/RULES, Section 4 has file manifest.
204
200
  > 2. Cross-check `spec.md` and `plan.md` (including Tasks section) for consistency.
205
201
  > 3. Before flagging CRITICAL or HIGH issues, read the relevant source files listed in the File Manifest to verify.
206
202
  > Report: CRITICAL, HIGH, MEDIUM issues found (or 'No issues found')."
@@ -230,7 +226,7 @@ Spawn failure cap: for team/config/lock errors, retry at most once for this Crit
230
226
  Prompt:
231
227
  > "Read {{CRITIC_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
232
228
  > **MODE: Plan Challenge**
233
- > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has project context, Section 4 has file manifest.
229
+ > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has TRAPS/RULES, Section 4 has file manifest.
234
230
  > 2. Read `.prizmkit/prizm-docs/root.prizm` and relevant L1/L2 docs for affected modules.
235
231
  > 3. Read existing source files in the modules this plan touches.
236
232
  > 4. Challenge plan.md against the project's existing architecture, patterns, and style.
@@ -4,33 +4,18 @@
4
4
  Use this material according to the following priority:
5
5
 
6
6
  1. **Task Contract** — defines current scope and Verification Gates.
7
- 2. **User Raw Context** — authoritative constraints, but not automatic scope expansion.
8
- 3. **Completed Dependencies** — existing behavior and interfaces; do not re-implement them.
9
- 4. **Project Brief** — product background and architecture alignment.
10
- 5. **App Global Context** — stack, runtime, and testing conventions.
11
- 6. **Project Conventions** — repository-specific rules.
12
-
13
- ### User Raw Context
14
-
15
- {{USER_CONTEXT}}
7
+ 2. **Completed Dependencies** — existing interfaces; do not re-implement.
8
+ 3. **App Global Context** — stack, runtime, testing conventions.
16
9
 
17
10
  ### Completed Dependencies
18
11
 
19
- > Use this section to understand available interfaces and avoid duplicating completed work.
20
-
21
12
  {{COMPLETED_DEPENDENCIES}}
22
13
 
23
- ### Project Brief
24
-
25
- > Use this section for alignment only. Do not treat unrelated backlog items as current feature scope.
26
-
27
- {{PROJECT_BRIEF}}
28
-
29
14
  ### App Global Context
30
15
 
31
16
  {{GLOBAL_CONTEXT}}
32
17
 
33
18
  ### Project Conventions
34
19
 
35
- > Read {{PLATFORM_CONVENTIONS}} for project-level coding standards, architecture decisions, and development rules. Follow these conventions throughout implementation.
20
+ > Read {{PLATFORM_CONVENTIONS}} for project-level coding standards and architecture decisions.
36
21
  </feature-context>
@@ -1,4 +1,4 @@
1
- - **Section 3 — Prizm Context**: full content of root.prizm and relevant L1/L2 docs
1
+ - **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs. Do NOT copy full L0/L1/L2 content — the orchestrator already loaded them.
2
2
  - **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
3
3
  ### Files to Modify
4
4
  | File | Why Needed | Key Interfaces |
@@ -6,15 +6,9 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md 2>/dev/null && echo "EXI
6
6
 
7
7
  If MISSING — build it now:
8
8
  1. Read `.prizmkit/prizm-docs/root.prizm` and relevant L1/L2 prizm docs
9
- 2. Detect source code directories: read KEY_FILES and STRUCTURE sections from `root.prizm` to identify where source code lives (e.g. `src/`, `app/`, `lib/`, `cmd/`, `packages/`, or project root). If `root.prizm` is missing, scan the project tree:
10
- ```bash
11
- find . -maxdepth 2 -type f \( -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.go" -o -name "*.java" -o -name "*.rb" -o -name "*.rs" \) -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/vendor/*' | head -30
12
- ```
13
- Identify the top-level source directories from the results.
14
- 3. Scan the detected source directories for files related to this feature; read each one
15
- 4. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
16
- - **Section 1 — Task Contract**: Objective, scope rule, non-scope rule, and Verification Gates from the Task Contract above
17
- - **Section 2 — Project Structure**: run the following to get a visual directory tree, then paste output:
18
- ```bash
19
- find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
20
- ```
9
+ 2. Read source files listed in the feature's scope (use prizm-docs KEY_FILES for guidance)
10
+ 3. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
11
+ - **Section 1 Feature Brief**: 2-3 line feature description from Task Contract
12
+ - **Section 2 — Verification Gates**: gate checklist from Task Contract
13
+ - **Section 3 Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs that apply to files in this scope. Do NOT copy full L0/L1/L2 content.
14
+ - **Section 4 File Manifest**: file path, why needed, key interfaces. Format: Files to Modify | Files for Reference tables.
@@ -1,6 +1,13 @@
1
- - **Section 3 — Prizm Context**: content of root.prizm and relevant L1/L2 docs
2
- - **Section 4 — Existing Source Files**: **full verbatim content** of each related file in fenced code blocks (with `### path/to/file` heading and line count). Include ALL files needed for implementation and review downstream phases read this section instead of re-reading individual source files
3
- - **Section 5 — Existing Tests**: full content of related test files as code block
1
+ - **Section 3 — Key TRAPS & RULES**: extract only relevant TRAPS/RULES from prizm-docs that apply to files in this scope. Do NOT copy full L0/L1/L2 content.
2
+ - **Section 4 — File Manifest**: For each relevant file, list path, why needed, key interface signatures. Use table format:
3
+
4
+ ### Files to Modify
5
+ | File | Why Needed | Key Interfaces |
6
+ |------|-----------|----------------|
7
+
8
+ ### Files for Reference
9
+ | File | Why Needed | Key Interfaces |
10
+ |------|-----------|----------------|
4
11
 
5
12
 
6
13
  **Checkpoint update**: Run the update script to set step `context-snapshot` to `"completed"`:
@@ -7,6 +7,7 @@
7
7
  |-----------|-------|
8
8
  | subagent_type | prizm-dev-team-dev |
9
9
  | run_in_background | false |
10
+ | isolation | Leave empty — Dev works directly in the repo, no worktree |
10
11
 
11
12
  **Agent spawn failure policy**:
12
13
  - If spawning Dev fails with team/config/lock errors, retry at most once.
@@ -19,13 +19,10 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
19
19
  Identify the top-level source directories from the results.
20
20
  3. Scan the detected source directories for files related to this feature; read each one
21
21
  4. Write `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`:
22
- - **Section 1 — Task Contract**: Objective, scope rule, non-scope rule, and Verification Gates from the Task Contract above
23
- - **Section 2 — Project Structure**: run the following to get a visual directory tree, then paste output:
24
- ```bash
25
- find . -maxdepth 2 -type d -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/build/*' -not -path '*/__pycache__/*' -not -path '*/vendor/*' | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
26
- ```
27
- - **Section 3 — Prizm Context**: full content of root.prizm and relevant L1/L2 docs
28
- - **Section 4 — File Manifest**: For each file relevant to this feature, list: file path, why it's needed (modify/reference/test), key interface signatures (function names + params + return types). Do NOT include full file content — agents read files on-demand. Format:
22
+ - **Section 1 — Feature Brief**: feature description (copy from Task Contract above, 2-3 lines only)
23
+ - **Section 2 — Verification Gates**: copy the gate checklist from the Task Contract above
24
+ - **Section 3 — Key TRAPS & RULES**: extract TRAPS and RULES entries from loaded prizm-docs that apply to files in this feature's scope. Do NOT copy full L0/L1/L2 content — only the specific TRAPS/RULES lines relevant to the files listed in Section 4.
25
+ - **Section 4 File Manifest**: For each file relevant to this feature, list path, why needed (modify/reference/test), key interface signatures. Do NOT include full file content. Format:
29
26
  ### Files to Modify
30
27
  | File | Why Needed | Key Interfaces |
31
28
  |------|-----------|----------------|
@@ -38,7 +35,6 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
38
35
 
39
36
  ### Known TRAPS (from .prizmkit/prizm-docs/)
40
37
  - <trap entries extracted from L1/L2 docs>
41
- - **Section 5 — Existing Tests**: full content of related test files as code blocks
42
38
  5. Confirm: `ls .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md`
43
39
 
44
40
  **After Step A**: Use context-snapshot.md Section 4 File Manifest to guide targeted file reads. Do NOT scan directories or read unrelated files.
@@ -531,6 +531,26 @@ function Test-PrizmStreamJsonSupport {
531
531
  }
532
532
  }
533
533
 
534
+ function Test-PrizmEffort {
535
+ param([string]$CliCommand)
536
+ if ([string]::IsNullOrEmpty($env:PRIZMKIT_EFFORT)) { return }
537
+
538
+ $sessionPlatform = Get-PrizmSessionPlatform $CliCommand
539
+ $valid = @{
540
+ claude = @('low', 'medium', 'high', 'xhigh', 'max')
541
+ codex = @('low', 'medium', 'high', 'xhigh')
542
+ codebuddy = @('low', 'medium', 'high', 'xhigh')
543
+ }
544
+
545
+ $allowed = $valid[$sessionPlatform]
546
+ if ($env:PRIZMKIT_EFFORT -notin $allowed) {
547
+ Write-Host "ERROR: PRIZMKIT_EFFORT='$env:PRIZMKIT_EFFORT' is not supported by the detected CLI ($sessionPlatform)." -ForegroundColor Red
548
+ Write-Host " Supported values for $($sessionPlatform): $($allowed -join ', ')" -ForegroundColor Red
549
+ Write-Host " Set `$env:PRIZMKIT_EFFORT to one of the above, or unset it to use the CLI default." -ForegroundColor Red
550
+ exit 1
551
+ }
552
+ }
553
+
534
554
  function Start-PrizmProgressParser {
535
555
  param(
536
556
  [string[]]$PythonCommand,
@@ -656,6 +676,7 @@ function Invoke-PrizmAiSession {
656
676
  $cliArgs += @('-p', '--dangerously-skip-permissions')
657
677
  if ($env:VERBOSE -in @('1','true','yes','on') -or $useStreamJson) { $cliArgs += '--verbose' }
658
678
  if ($useStreamJson) { $cliArgs += @('--output-format', 'stream-json') }
679
+ if ($env:PRIZMKIT_EFFORT) { $cliArgs += @('--effort', $env:PRIZMKIT_EFFORT) }
659
680
  if ($Model) { $cliArgs += @('--model', $Model) }
660
681
  } elseif ($sessionPlatform -eq 'codex') {
661
682
  $cliArgs += @('--ask-for-approval', 'never', '--sandbox', 'danger-full-access')
@@ -666,11 +687,13 @@ function Invoke-PrizmAiSession {
666
687
  $cliArgs += @('exec', '--cd', $ProjectRoot, '--skip-git-repo-check')
667
688
  if ($useStreamJson) { $cliArgs += '--json' }
668
689
  if ($Model) { $cliArgs += @('--model', $Model) }
690
+ if ($env:PRIZMKIT_EFFORT) { $cliArgs += @('--config', "model_reasoning_effort=$env:PRIZMKIT_EFFORT") }
669
691
  $cliArgs += '-'
670
692
  } else {
671
693
  $cliArgs += @('--print', '-y')
672
694
  if ($env:VERBOSE -in @('1','true','yes','on')) { $cliArgs += '--verbose' }
673
695
  if ($useStreamJson) { $cliArgs += @('--output-format', 'stream-json') }
696
+ if ($env:PRIZMKIT_EFFORT) { $cliArgs += @('--effort', $env:PRIZMKIT_EFFORT) }
674
697
  if ($Model) { $cliArgs += @('--model', $Model) }
675
698
  }
676
699
  $generatedArgs = (($cliArgs | ForEach-Object { ConvertTo-PrizmProcessArgument $_ }) -join ' ')