prizmkit 1.1.106 → 1.1.108

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 (85) hide show
  1. package/bin/create-prizmkit.js +4 -0
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/dev-pipeline/prizmkit_runtime/config.py +81 -14
  4. package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +19 -8
  5. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +0 -1
  6. package/bundled/dev-pipeline/prizmkit_runtime/launch_profiles.py +18 -0
  7. package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +22 -2
  8. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +56 -5
  9. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -8
  10. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +11 -1
  11. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +298 -63
  12. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +243 -37
  13. package/bundled/dev-pipeline/scripts/continuation.py +0 -5
  14. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +10 -82
  15. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +7 -64
  16. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +40 -63
  17. package/bundled/dev-pipeline/scripts/utils.py +123 -0
  18. package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +4 -3
  19. package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +5 -6
  20. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  21. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +36 -35
  22. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +53 -80
  23. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +43 -78
  24. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +5 -17
  25. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +6 -18
  26. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -48
  27. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +7 -14
  28. package/bundled/dev-pipeline/templates/sections/feature-context.md +0 -4
  29. package/bundled/dev-pipeline/templates/sections/phase-browser-verification-auto.md +0 -7
  30. package/bundled/dev-pipeline/templates/sections/phase-browser-verification-opencli.md +0 -7
  31. package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +0 -7
  32. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +0 -14
  33. package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -14
  34. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -7
  35. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +0 -2
  36. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -7
  37. package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +6 -8
  38. package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +3 -7
  39. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +4 -16
  40. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +4 -17
  41. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -7
  42. package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +2 -9
  43. package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +1 -8
  44. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +15 -16
  45. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +3 -10
  46. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +3 -10
  47. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +2 -9
  48. package/bundled/dev-pipeline/templates/sections/phase0-init.md +0 -7
  49. package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +2 -15
  50. package/bundled/dev-pipeline/tests/conftest.py +0 -1
  51. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +395 -21
  52. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +50 -14
  53. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +154 -2
  54. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +431 -3
  55. package/bundled/dev-pipeline/tests/test_unified_cli.py +857 -5
  56. package/bundled/skills/_metadata.json +4 -4
  57. package/bundled/skills/app-planner/SKILL.md +1 -1
  58. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
  59. package/bundled/skills/bug-fix-workflow/SKILL.md +174 -128
  60. package/bundled/skills/bug-planner/SKILL.md +101 -15
  61. package/bundled/skills/bug-planner/references/severity-rules.md +20 -8
  62. package/bundled/skills/feature-planner/SKILL.md +87 -8
  63. package/bundled/skills/feature-planner/assets/planning-guide.md +34 -10
  64. package/bundled/skills/feature-workflow/SKILL.md +100 -81
  65. package/bundled/skills/{prizm-kit → prizmkit}/SKILL.md +1 -1
  66. package/bundled/skills/prizmkit-code-review/SKILL.md +12 -13
  67. package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +10 -10
  68. package/bundled/skills/refactor-planner/SKILL.md +88 -7
  69. package/bundled/skills/refactor-planner/references/fast-path.md +2 -1
  70. package/bundled/skills/refactor-planner/references/planning-phases.md +29 -11
  71. package/bundled/skills/refactor-workflow/SKILL.md +116 -57
  72. package/bundled/templates/project-memory-template.md +1 -1
  73. package/package.json +1 -1
  74. package/src/ai-cli-launch.js +194 -0
  75. package/src/config.js +26 -14
  76. package/src/index.js +11 -44
  77. package/src/platforms.js +10 -3
  78. package/src/prompts.js +60 -2
  79. package/src/scaffold.js +26 -14
  80. package/bundled/dev-pipeline/scripts/prizmkit-test-gate.py +0 -446
  81. package/bundled/dev-pipeline/templates/agent-prompts/dev-fix.md +0 -7
  82. package/bundled/dev-pipeline/templates/agent-prompts/dev-resume.md +0 -5
  83. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +0 -20
  84. package/bundled/dev-pipeline/tests/test_prizmkit_test_gate.py +0 -107
  85. package/bundled/skills/prizmkit-code-review/references/dev-agent-prompt.md +0 -30
@@ -19,12 +19,11 @@ import os
19
19
  import re
20
20
  import sys
21
21
 
22
- from utils import enrich_global_context, load_json_file, read_platform_conventions, setup_logging
22
+ from utils import enrich_global_context, load_json_file, read_checkpoint_system_section, resolve_prompt_platform, setup_logging
23
23
  from continuation import add_continuation_args, append_continuation_handoff, is_continuation
24
24
 
25
25
 
26
26
  DEFAULT_MAX_RETRIES = 3
27
- DEFAULT_MAX_LOG_SIZE = 2097152
28
27
 
29
28
  LOGGER = setup_logging("generate-bugfix-prompt")
30
29
 
@@ -66,27 +65,6 @@ def read_text_file(path):
66
65
 
67
66
 
68
67
 
69
- def _format_bytes(n):
70
- """Format a byte count to a human-readable string."""
71
- if n >= 1048576:
72
- return "{}MB".format(n // 1048576)
73
- elif n >= 1024:
74
- return "{}KB".format(n // 1024)
75
- return "{}B".format(n)
76
-
77
-
78
- def _parse_non_negative_int_env(name, default):
79
- """Parse a non-negative integer environment variable."""
80
- value = os.environ.get(name, str(default))
81
- try:
82
- parsed = int(value)
83
- except ValueError:
84
- raise ValueError("{} must be a non-negative integer".format(name))
85
- if parsed < 0:
86
- raise ValueError("{} must be a non-negative integer".format(name))
87
- return parsed
88
-
89
-
90
68
  def _parse_positive_int_env(name, default):
91
69
  """Parse a positive integer environment variable."""
92
70
  value = os.environ.get(name, str(default))
@@ -99,23 +77,6 @@ def _parse_positive_int_env(name, default):
99
77
  return parsed
100
78
 
101
79
 
102
- def load_log_size_section(script_dir):
103
- """Load the optional log-size awareness prompt section."""
104
- max_log_size = _parse_non_negative_int_env("MAX_LOG_SIZE", DEFAULT_MAX_LOG_SIZE)
105
- if max_log_size == 0:
106
- return ""
107
-
108
- section_path = os.path.join(
109
- script_dir, "..", "templates", "sections", "log-size-awareness.md",
110
- )
111
- if not os.path.isfile(section_path):
112
- return ""
113
-
114
- content, err = read_text_file(section_path)
115
- if err:
116
- return ""
117
- return content
118
-
119
80
  def find_bug(bugs, bug_id):
120
81
  """Find and return the bug dict matching the given ID."""
121
82
  for bug in bugs:
@@ -287,17 +248,9 @@ def build_replacements(args, bug, global_context, script_dir):
287
248
  project_root = _args_project_root(args, script_dir)
288
249
 
289
250
  # Platform-aware agent/team path resolution
290
- platform = os.environ.get("PRIZMKIT_PLATFORM", "")
251
+ platform = resolve_prompt_platform(project_root)
291
252
  home_dir = os.path.expanduser("~")
292
253
 
293
- if not platform:
294
- if os.path.isdir(os.path.join(project_root, ".codex", "agents")):
295
- platform = "codex"
296
- elif os.path.isdir(os.path.join(project_root, ".claude", "agents")):
297
- platform = "claude"
298
- else:
299
- platform = "codebuddy"
300
-
301
254
  if platform == "claude":
302
255
  agents_dir = os.path.join(project_root, ".claude", "agents")
303
256
  team_config_path = os.path.join(project_root, ".claude", "team-info.json")
@@ -373,12 +326,7 @@ def build_replacements(args, bug, global_context, script_dir):
373
326
  browser_enabled = True
374
327
  browser_verify_steps = " # (reproduce bug and verify fix)"
375
328
 
376
- max_log_size = _parse_non_negative_int_env("MAX_LOG_SIZE", DEFAULT_MAX_LOG_SIZE)
377
-
378
329
  replacements = {
379
- "{{LOG_SIZE_AWARENESS}}": load_log_size_section(script_dir),
380
- "{{MAX_LOG_SIZE}}": str(max_log_size),
381
- "{{MAX_LOG_SIZE_HUMAN}}": _format_bytes(max_log_size),
382
330
  "{{SESSION_LOG_PATH}}": os.path.join(
383
331
  ".prizmkit", "state", "bugfix", args.bug_id,
384
332
  "sessions", args.session_id, "logs", "session.log",
@@ -398,12 +346,15 @@ def build_replacements(args, bug, global_context, script_dir):
398
346
  ),
399
347
  "{{ENVIRONMENT}}": format_environment(bug.get("environment")),
400
348
  "{{GLOBAL_CONTEXT}}": format_global_context(global_context, project_root),
401
- "{{PLATFORM_CONVENTIONS}}": read_platform_conventions(project_root),
402
349
  "{{TEAM_CONFIG_PATH}}": team_config_path,
403
350
  "{{DEV_SUBAGENT_PATH}}": dev_subagent,
404
351
  "{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
405
352
  "{{SESSION_STATUS_PATH}}": session_status_path,
406
353
  "{{PROJECT_ROOT}}": project_root,
354
+ "{{CHECKPOINT_PATH}}": os.path.join(
355
+ ".prizmkit", "bugfix", args.bug_id, "workflow-checkpoint.json",
356
+ ),
357
+ "{{CHECKPOINT_SYSTEM}}": read_checkpoint_system_section(script_dir),
407
358
  "{{FIX_SCOPE}}": fix_scope,
408
359
  "{{TIMESTAMP}}": "", # Placeholder, agent fills in the timestamp
409
360
  "{{BROWSER_ENABLED}}": "true" if browser_enabled else "false",
@@ -659,15 +610,6 @@ def main():
659
610
 
660
611
  # Build replacements
661
612
  replacements = build_replacements(args, bug, global_context, script_dir)
662
- if is_continuation(args):
663
- replacements["{{LOG_SIZE_AWARENESS}}"] = ""
664
-
665
- # Add checkpoint path to replacements
666
- checkpoint_rel = os.path.join(
667
- ".prizmkit", "bugfix", args.bug_id, "workflow-checkpoint.json",
668
- )
669
- replacements["{{CHECKPOINT_PATH}}"] = checkpoint_rel
670
-
671
613
  # Render the template
672
614
  rendered = render_template(template_content, replacements, bug)
673
615
 
@@ -678,6 +620,7 @@ def main():
678
620
 
679
621
  # Generate checkpoint file
680
622
  project_root = _args_project_root(args, script_dir)
623
+ checkpoint_rel = replacements["{{CHECKPOINT_PATH}}"]
681
624
  checkpoint_path = os.path.join(project_root, checkpoint_rel)
682
625
  checkpoint_dir = os.path.dirname(checkpoint_path)
683
626
  os.makedirs(checkpoint_dir, exist_ok=True)
@@ -19,12 +19,11 @@ import os
19
19
  import re
20
20
  import sys
21
21
 
22
- from utils import enrich_global_context, load_json_file, read_platform_conventions, setup_logging
22
+ from utils import enrich_global_context, load_json_file, read_checkpoint_system_section, resolve_prompt_platform, setup_logging
23
23
  from continuation import add_continuation_args, append_continuation_handoff, is_continuation
24
24
 
25
25
 
26
26
  DEFAULT_MAX_RETRIES = 3
27
- DEFAULT_MAX_LOG_SIZE = 2097152
28
27
 
29
28
  LOGGER = setup_logging("generate-refactor-prompt")
30
29
 
@@ -135,6 +134,7 @@ def parse_args():
135
134
  parser.add_argument("--resume-phase", required=True, help='Phase to resume from, or "null" for fresh start')
136
135
  parser.add_argument("--state-dir", default=None, help="State directory (default: .prizmkit/state/refactor)")
137
136
  parser.add_argument("--output", required=True, help="Output path for the rendered prompt")
137
+ parser.add_argument("--project-root", default=None, help="AI-visible project root used for rendered prompt paths")
138
138
  parser.add_argument("--template", default=None, help="Custom template path. Defaults to {script_dir}/../templates/refactor-bootstrap-prompt.md")
139
139
  parser.add_argument("--mode", default=None, help="Pipeline execution mode override: lite, standard, full")
140
140
  parser.add_argument("--critic", default=None, help="Enable critic agent: true/false")
@@ -156,27 +156,6 @@ def read_text_file(path):
156
156
 
157
157
 
158
158
 
159
- def _format_bytes(n):
160
- """Format a byte count to a human-readable string."""
161
- if n >= 1048576:
162
- return "{}MB".format(n // 1048576)
163
- elif n >= 1024:
164
- return "{}KB".format(n // 1024)
165
- return "{}B".format(n)
166
-
167
-
168
- def _parse_non_negative_int_env(name, default):
169
- """Parse a non-negative integer environment variable."""
170
- value = os.environ.get(name, str(default))
171
- try:
172
- parsed = int(value)
173
- except ValueError:
174
- raise ValueError("{} must be a non-negative integer".format(name))
175
- if parsed < 0:
176
- raise ValueError("{} must be a non-negative integer".format(name))
177
- return parsed
178
-
179
-
180
159
  def _parse_positive_int_env(name, default):
181
160
  """Parse a positive integer environment variable."""
182
161
  value = os.environ.get(name, str(default))
@@ -189,23 +168,6 @@ def _parse_positive_int_env(name, default):
189
168
  return parsed
190
169
 
191
170
 
192
- def load_log_size_section(script_dir):
193
- """Load the optional log-size awareness prompt section."""
194
- max_log_size = _parse_non_negative_int_env("MAX_LOG_SIZE", DEFAULT_MAX_LOG_SIZE)
195
- if max_log_size == 0:
196
- return ""
197
-
198
- section_path = os.path.join(
199
- script_dir, "..", "templates", "sections", "log-size-awareness.md",
200
- )
201
- if not os.path.isfile(section_path):
202
- return ""
203
-
204
- content, err = read_text_file(section_path)
205
- if err:
206
- return ""
207
- return content
208
-
209
171
  def find_refactor(refactors, refactor_id):
210
172
  """Find and return the refactor dict matching the given ID."""
211
173
  for refactor in refactors:
@@ -422,22 +384,39 @@ def resolve_project_root(script_dir):
422
384
  return os.path.abspath(pipeline_parent)
423
385
 
424
386
 
387
+ def _project_root_from_prizmkit_path(path):
388
+ """Infer project root from a path nested under .prizmkit/."""
389
+ if not path:
390
+ return ""
391
+ current = os.path.abspath(path)
392
+ if not os.path.isdir(current):
393
+ current = os.path.dirname(current)
394
+ while current and current != os.path.dirname(current):
395
+ if os.path.basename(current) == ".prizmkit":
396
+ return os.path.abspath(os.path.dirname(current))
397
+ current = os.path.dirname(current)
398
+ return ""
399
+
400
+
401
+ def _args_project_root(args, script_dir):
402
+ """Return explicit AI-visible project root, plan-path root, or script-layout default."""
403
+ explicit = getattr(args, "project_root", None)
404
+ if explicit:
405
+ return os.path.abspath(explicit)
406
+ plan_root = _project_root_from_prizmkit_path(getattr(args, "refactor_list", ""))
407
+ if plan_root:
408
+ return plan_root
409
+ return resolve_project_root(script_dir)
410
+
411
+
425
412
  def build_replacements(args, refactor, refactors, global_context, script_dir):
426
413
  """Build the full dict of placeholder -> replacement value."""
427
- project_root = resolve_project_root(script_dir)
414
+ project_root = _args_project_root(args, script_dir)
428
415
 
429
416
  # Platform-aware agent/team path resolution
430
- platform = os.environ.get("PRIZMKIT_PLATFORM", "")
417
+ platform = resolve_prompt_platform(project_root)
431
418
  home_dir = os.path.expanduser("~")
432
419
 
433
- if not platform:
434
- if os.path.isdir(os.path.join(project_root, ".codex", "agents")):
435
- platform = "codex"
436
- elif os.path.isdir(os.path.join(project_root, ".claude", "agents")):
437
- platform = "claude"
438
- else:
439
- platform = "codebuddy"
440
-
441
420
  if platform == "claude":
442
421
  agents_dir = os.path.join(project_root, ".claude", "agents")
443
422
  team_config_path = os.path.join(project_root, ".claude", "team-info.json")
@@ -454,10 +433,16 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
454
433
  dev_subagent = os.path.join(agents_dir, f"prizm-dev-team-dev{agent_ext}")
455
434
  reviewer_subagent = os.path.join(agents_dir, f"prizm-dev-team-reviewer{agent_ext}")
456
435
 
457
- # Session status path
436
+ # Session status is controlled by the main runner state dir, even when
437
+ # rendered project paths are AI-visible worktree paths.
438
+ state_dir = (
439
+ os.path.abspath(args.state_dir)
440
+ if args.state_dir
441
+ else os.path.join(project_root, ".prizmkit", "state", "refactor")
442
+ )
458
443
  session_status_path = os.path.join(
459
- project_root, ".prizmkit", "state", "refactor", args.refactor_id,
460
- "sessions", args.session_id, "session-status.json"
444
+ state_dir, args.refactor_id, "sessions", args.session_id,
445
+ "session-status.json",
461
446
  )
462
447
 
463
448
  # Scope
@@ -518,12 +503,7 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
518
503
  browser_enabled = True
519
504
  browser_verify_steps = " # (validate UI renders correctly and feature still works)"
520
505
 
521
- max_log_size = _parse_non_negative_int_env("MAX_LOG_SIZE", DEFAULT_MAX_LOG_SIZE)
522
-
523
506
  replacements = {
524
- "{{LOG_SIZE_AWARENESS}}": load_log_size_section(script_dir),
525
- "{{MAX_LOG_SIZE}}": str(max_log_size),
526
- "{{MAX_LOG_SIZE_HUMAN}}": _format_bytes(max_log_size),
527
507
  "{{SESSION_LOG_PATH}}": os.path.join(
528
508
  ".prizmkit", "state", "refactor", args.refactor_id,
529
509
  "sessions", args.session_id, "logs", "session.log",
@@ -557,8 +537,8 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
557
537
  "{{CHECKPOINT_PATH}}": os.path.join(
558
538
  ".prizmkit", "refactor", args.refactor_id, "workflow-checkpoint.json",
559
539
  ),
540
+ "{{CHECKPOINT_SYSTEM}}": read_checkpoint_system_section(script_dir),
560
541
  "{{TIMESTAMP}}": "", # Placeholder — agent fills in timestamp
561
- "{{PLATFORM_CONVENTIONS}}": read_platform_conventions(project_root),
562
542
  "{{BROWSER_ENABLED}}": "true" if browser_enabled else "false",
563
543
  "{{BROWSER_TOOL}}": browser_tool,
564
544
  "{{BROWSER_VERIFY_STEPS}}": browser_verify_steps,
@@ -726,9 +706,6 @@ def main():
726
706
 
727
707
  # Build replacements
728
708
  replacements = build_replacements(args, refactor, refactors, global_context, script_dir)
729
- if is_continuation(args):
730
- replacements["{{LOG_SIZE_AWARENESS}}"] = ""
731
-
732
709
  # Render the template
733
710
  rendered = render_template(template_content, replacements, args.resume_phase, refactor)
734
711
 
@@ -738,7 +715,7 @@ def main():
738
715
  emit_failure(err)
739
716
 
740
717
  # Generate checkpoint file
741
- project_root = resolve_project_root(script_dir)
718
+ project_root = _args_project_root(args, script_dir)
742
719
  checkpoint_rel = os.path.join(
743
720
  ".prizmkit", "refactor", args.refactor_id, "workflow-checkpoint.json",
744
721
  )
@@ -79,6 +79,46 @@ def error_out(message, code=1):
79
79
  sys.exit(code)
80
80
 
81
81
 
82
+ def read_checkpoint_system_section(script_dir):
83
+ """Read the shared checkpoint prompt section for feature/bugfix/refactor prompts."""
84
+ path = os.path.join(
85
+ os.path.abspath(script_dir), "..", "templates", "sections", "checkpoint-system.md"
86
+ )
87
+ try:
88
+ with open(path, "r", encoding="utf-8") as f:
89
+ return f.read()
90
+ except IOError:
91
+ return """## Workflow Checkpoint System
92
+
93
+ A checkpoint file tracks your progress through this workflow:
94
+
95
+ **Path**: `{{CHECKPOINT_PATH}}`
96
+
97
+ ### Checkpoint Update Helper
98
+
99
+ Use this helper to update checkpoint status. Do not hand-edit `workflow-checkpoint.json` directly.
100
+
101
+ ```bash
102
+ python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py \\
103
+ --checkpoint-path {{CHECKPOINT_PATH}} \\
104
+ --step <step-id-or-skill-name> \\
105
+ --status <in_progress|completed|failed|skipped> \\
106
+ --note "<optional note>"
107
+ ```
108
+
109
+ - Use the step `skill` value such as `prizmkit-implement`, or the step id such as `S04`.
110
+ - Omit `--note` when there is no useful failure or handoff note.
111
+
112
+ ### Rules
113
+
114
+ 1. **Before each skill**: Read `workflow-checkpoint.json`, verify the previous step has `status: "completed"` or `status: "skipped"`. If it is still `"pending"` or `"in_progress"`, complete it first before moving on.
115
+ 2. **Starting a skill**: Use the checkpoint update helper to mark the current step as `status: "in_progress"`.
116
+ 3. **After each skill completes**: Use the checkpoint update helper to mark the current step as `status: "completed"`.
117
+ 4. **On failure**: Use the checkpoint update helper to mark the step as `status: "failed"`, write `failure-log.md` with the reason, and continue only when the workflow rules allow it.
118
+ 5. **On session exit**: The checkpoint file reflects your actual progress. Do NOT manually set future steps to `"completed"`.
119
+ """
120
+
121
+
82
122
  def pad_right(text, width):
83
123
  """Pad text with spaces to fill width, accounting for ANSI escape codes."""
84
124
  i = 0
@@ -520,6 +560,89 @@ def enrich_global_context(global_context, project_root):
520
560
  return global_context
521
561
 
522
562
 
563
+
564
+ def read_json_object(path):
565
+ """Read a JSON object, returning an empty dict when unavailable."""
566
+ try:
567
+ with open(path, "r", encoding="utf-8") as f:
568
+ data = json.load(f)
569
+ except (IOError, OSError, json.JSONDecodeError):
570
+ return {}
571
+ return data if isinstance(data, dict) else {}
572
+
573
+
574
+ def platform_from_command(command):
575
+ """Map an AI CLI command/config value to a PrizmKit platform name."""
576
+ cmd = str(command or "").strip().lower()
577
+ if not cmd:
578
+ return ""
579
+ base = os.path.basename(cmd.split()[0])
580
+ if base in ("claude", "claude-code") or "claude" in base:
581
+ return "claude"
582
+ if base in ("codex", "openai-codex") or "codex" in base:
583
+ return "codex"
584
+ if base in ("cbc", "codebuddy") or "codebuddy" in base:
585
+ return "codebuddy"
586
+ return ""
587
+
588
+
589
+ def valid_prompt_platform(value):
590
+ """Normalize a platform value for prompt path rendering."""
591
+ value = str(value or "").strip().lower()
592
+ if value in ("claude", "codex", "codebuddy"):
593
+ return value
594
+ return ""
595
+
596
+
597
+ def resolve_prompt_platform(project_root):
598
+ """Resolve the platform whose agent paths should appear in prompts.
599
+
600
+ Explicit ``PRIZMKIT_PLATFORM`` wins. Otherwise prefer active/configured AI
601
+ CLI because mixed installs contain several platform directories. Filesystem
602
+ markers are only a final fallback.
603
+ """
604
+ explicit = valid_prompt_platform(os.environ.get("PRIZMKIT_PLATFORM", ""))
605
+ if explicit:
606
+ return explicit
607
+
608
+ for env_name in ("AI_CLI", "CLAUDECODE", "CLAUDE_CODE"):
609
+ detected = platform_from_command(os.environ.get(env_name, ""))
610
+ if detected:
611
+ return detected
612
+
613
+ config = read_json_object(os.path.join(project_root, ".prizmkit", "config.json"))
614
+ for key in ("ai_cli", "aiCli"):
615
+ detected = platform_from_command(config.get(key, ""))
616
+ if detected:
617
+ return detected
618
+ detected = valid_prompt_platform(config.get("platform", ""))
619
+ if detected:
620
+ return detected
621
+
622
+ manifest = read_json_object(os.path.join(project_root, ".prizmkit", "manifest.json"))
623
+ options = manifest.get("options", {}) if isinstance(manifest.get("options"), dict) else {}
624
+ for value in (manifest.get("aiCli", ""), options.get("aiCli", "")):
625
+ detected = platform_from_command(value)
626
+ if detected:
627
+ return detected
628
+ detected = valid_prompt_platform(manifest.get("platform", ""))
629
+ if detected:
630
+ return detected
631
+
632
+ marker_order = (
633
+ ("claude", os.path.join(project_root, ".claude", "agents")),
634
+ ("codex", os.path.join(project_root, ".codex", "agents")),
635
+ ("codebuddy", os.path.join(project_root, ".codebuddy", "agents")),
636
+ )
637
+ for candidate, marker in marker_order:
638
+ if os.path.isdir(marker):
639
+ return candidate
640
+
641
+ raise RuntimeError(
642
+ "PrizmKit agents not found. None of .claude/agents/, .codex/agents/, or .codebuddy/agents/ exists. "
643
+ "Run `npx prizmkit install` first, set .prizmkit/config.json ai_cli/platform, or set PRIZMKIT_PLATFORM=claude|codex|codebuddy explicitly."
644
+ )
645
+
523
646
  def read_platform_conventions(project_root):
524
647
  """Resolve the platform-specific project conventions file.
525
648
 
@@ -1,7 +1,8 @@
1
1
  "Read {{CRITIC_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
2
2
  **MODE: Plan Challenge**
3
3
  1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has project context, Section 4 has file manifest.
4
- 2. Read `.prizmkit/prizm-docs/root.prizm` and relevant L1/L2 docs for affected modules.
5
- 3. Read existing source files in the modules this plan touches.
6
- 4. Challenge plan.md against the project's existing architecture, patterns, and style.
4
+ 2. Use context-snapshot.md as the shared source of truth. Do not create or enter AI tool-created `.claude/worktrees/...` subagent worktrees; use default/no worktree isolation and do not request worktree isolation.
5
+ 3. PrizmKit pipeline-managed linked git worktrees under `.prizmkit/state/worktrees/...` are a separate runner feature controlled by `USE_WORKTREE`; do not confuse them with AI tool-created `.claude/worktrees/...`.
6
+ 4. Read additional `.prizmkit/prizm-docs/` or source excerpts only when needed to verify a specific challenge.
7
+ 5. Challenge plan.md against the project's existing architecture, patterns, and style.
7
8
  Write `.prizmkit/specs/{{FEATURE_SLUG}}/challenge-report.md` with findings (or 'No significant challenges')."
@@ -1,7 +1,6 @@
1
1
  "Read {{REVIEWER_SUBAGENT_PATH}}. For feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}):
2
- 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/spec.md` (if it exists) for goals and Verification Gates; if spec.md does not exist, read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` Section 1 Task Contract instead
3
- 2. Read `.prizmkit/specs/{{FEATURE_SLUG}}/plan.md` for architecture decisions and completed tasks
4
- 3. Run /prizmkit-code-review with artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/. The skill will run its internal review-fix loop (Reviewer filter Dev fix, max 3 rounds) and write review-report.md.
5
- 4. Do not run test suites here. The orchestrator runs `/prizmkit-test` after review completes and then fixes code/tests until the scoped gate passes.
6
- 5. review-report.md will be written to .prizmkit/specs/{{FEATURE_SLUG}}/ by prizmkit-code-review.
7
- Report: verdict (PASS/NEEDS_FIXES), number of rounds, findings fixed/rejected."
2
+ 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/spec.md` (if it exists) for goals and Verification Gates; if spec.md does not exist, read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` Section 1 Task Contract instead.
3
+ 2. Read `.prizmkit/specs/{{FEATURE_SLUG}}/plan.md` for architecture decisions and completed tasks.
4
+ 3. Review the current workspace changes and return structured findings only. Do not run `/prizmkit-code-review`, do not apply fixes, do not run test suites, and do not spawn other agents.
5
+ 4. The main orchestrator owns the review-fix loop: Reviewer Agent filter orchestrator fix.
6
+ Report: PASS/NEEDS_FIXES, findings with severity/location/problem/suggestion/verification, and a concise summary."
@@ -44,7 +44,7 @@ Infer what needs to be done from the feature context above and follow the standa
44
44
  - Do NOT push to remote — the user will push manually.
45
45
  - Write all artifacts to `.prizmkit/specs/{{FEATURE_SLUG}}/`.
46
46
  - If a step fails after 3 attempts, write a status report and stop.
47
- - **Anti re-read**: once you read a file into context, do NOT read it again — use `grep -n`/`sed -n` for specific lines. 3 full Reads of same file report BLOCKED.
47
+ - **Never re-read unmodified files**: once you read a file into context, do NOT read it again unless it was modified by an Edit/Write after your last read — use `grep -n`/`sed -n` for specific lines. Before issuing a Read, ask: "Have I read this file before, and has it changed since?" If no — do NOT read it. Subagents (Critic, Reviewer) must rely on context-snapshot.md rather than re-reading files the orchestrator already read.
48
48
  - **Read offset safety**: if Read returns "shorter than offset"/"empty" — run `wc -l` then `sed -n`, do NOT retry same offset.
49
49
  - **Hard Round Cap**: after 6 total test-fix rounds, STOP trial-and-error. Switch to read-once-then-rewrite. Do NOT exceed 7 rounds.
50
50
  - **Layered test**: debug with single-file tests, only run full suite as gate when single-file is green.
@@ -46,22 +46,18 @@ You are running in **headless non-interactive mode** with a FINITE context windo
46
46
 
47
47
  1. **context-snapshot.md is your single source of truth** — After Phase 1 builds it, ALWAYS read context-snapshot.md instead of re-reading individual source files
48
48
  2. **Never re-read your own writes** — After you create/modify a file, do NOT read it back to verify. Trust your write was correct.
49
- 3. **Stay focused** — Do NOT explore code unrelated to this feature. No curiosity-driven reads.
49
+ 3. **Never re-read unmodified files** — Once you have read a file into context, do NOT read it again unless it was modified by an Edit/Write operation after your last read. Use `grep -n '<pattern>' <file>` or `sed -n '<start>,<end>p' <file>` for targeted line lookups instead of full-file Read. Before issuing a Read, ask yourself: "Have I read this file before, and has it changed since?" If the answer is "no, it hasn't changed" — do NOT read it. This applies to all files including those summarized in context-snapshot.md Section 4. Subagents (Critic, Reviewer) must receive context-snapshot.md and rely on it rather than independently re-reading the same files the orchestrator already read.
50
+ 4. **Stay focused** — Do NOT explore code unrelated to this feature. No curiosity-driven reads.
50
51
  4. **One task at a time** — In Phase 3 (implement), complete and test one task before starting the next.
51
52
  5. **Minimize tool output** — Never load full command output into context. First capture to a temp file (`cmd 2>&1 | tee /tmp/out.txt | tail -20`), then scan the head/tail to identify relevant fields, and use targeted filtering (`grep`, `sed`, `awk`) to extract only the information needed for the current task. Only read the filtered result — never the raw full output.
52
53
  6. **No intermediate commits** — Do NOT run `git add`/`git commit` during Phase 1-3. All changes are committed once at the end in Phase 4 via `/prizmkit-committer`.
53
- 7. **Capture test output once** — When running test suites, always use `($TEST_CMD) 2>&1 | tee /tmp/test-out.txt | tail -20`. Then grep `/tmp/test-out.txt` for details. Never re-run the suite just to apply a different filter.
54
- 8. **Scaffold / generated file awareness** — Files created by scaffolding tools (`npm init`, `npx create-*`, etc.) are boilerplate. Tag them, record in context-snapshot.md, and NEVER re-read them. If you need to modify one, edit directly without reading first.
54
+ 7. **Capture test output once** — When running test suites, always use `<test-command> 2>&1 | tee /tmp/test-out.txt | tail -20`. Then grep `/tmp/test-out.txt` for details. Never re-run the suite just to apply a different filter.
55
55
  9. **Package version verification (BLOCKING)** — Before writing ANY dependency version, verify it exists via registry query (`npm view <pkg> dist-tags.latest`, `pip index versions <pkg>`, etc.). NEVER guess version numbers.
56
56
  10. **Build artifact hygiene** — After build/compile, ensure output is in `.gitignore`. Never commit binaries/build output.
57
- 11. **Anti re-read hard rule (BLOCKING)** — Once you have read a file into context, do NOT read the full file again. Use `grep -n '<pattern>' <file>` or `sed -n '<start>,<end>p' <file>` for specific lines. **20-step window**: full Read forbidden within 20 tool calls of last read. **3-strike**: 3 full Reads of same file → report BLOCKED. Applies to orchestrator AND subagents.
58
- 12. **Read offset safety (BLOCKING)** — If Read returns "shorter than offset"/"File is empty"/"Wasted call" — do NOT retry same offset. Run `wc -l <file>` to get real line count, then `sed -n` for exact range. 2 consecutive offset errors → STOP Reading that file. Never guess offsets — `grep -n '<anchor>'` first.
57
+ 11. **Read offset safety (BLOCKING)** — If Read returns "shorter than offset"/"File is empty"/"Wasted call" — do NOT retry same offset. Run `wc -l <file>` to get real line count, then `sed -n` for exact range. 2 consecutive offset errors STOP Reading that file. Never guess offsets `grep -n '<anchor>'` first.
59
58
 
60
59
  ---
61
60
 
62
- {{LOG_SIZE_AWARENESS}}
63
- ---
64
-
65
61
  ## PrizmKit Directory Convention
66
62
 
67
63
  ```
@@ -115,7 +111,7 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
115
111
 
116
112
  If plan.md missing, run `/prizmkit-plan` with `artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/` to generate `plan.md`:
117
113
  - The plan.md should include: key components, data flow, files to create/modify, and a Tasks section with `[ ]` checkboxes (each task = one implementable unit). Keep under 80 lines.
118
- - Resolve any `[NEEDS CLARIFICATION]` markers using the feature description do NOT pause for interactive input.
114
+ - Resolve uncertain requirements from the feature description, existing code, and conservative existing-project patterns.
119
115
 
120
116
  **Database Design Gate** (if feature involves data persistence — new tables, schema changes, new entities):
121
117
  Before proceeding past CP-1:
@@ -125,7 +121,7 @@ Before proceeding past CP-1:
125
121
 
126
122
  **CP-1**: plan.md exists with Tasks section.
127
123
 
128
- ### Phase 3: Implement + Test
124
+ ### Phase 3: Implement
129
125
 
130
126
  **Build artifacts**: After any build/compile command (`go build`, `npm run build`, `tsc`, etc.), ensure the output binary or build directory is in `.gitignore`:
131
127
  ```bash
@@ -134,51 +130,56 @@ grep -q '^/binary-name$' .gitignore || echo '/binary-name' >> .gitignore
134
130
  ```
135
131
  Never commit compiled binaries, build output, or generated artifacts.
136
132
 
137
- **3a.** Detect test commands and record baseline:
138
-
139
- You know this project's tech stack. Identify ALL test commands that apply (e.g., `go test ./...`, `npm test`, `cargo test`, `pytest`, `make test`, etc.). Record them as `TEST_CMDS` (one or more commands). Then record baseline:
140
- ```bash
141
- # Run each test command, capture output
142
- ($TEST_CMD) 2>&1 | tee /tmp/test-baseline.txt | tail -20
143
- ```
144
-
145
- **3b.** Run `/prizmkit-implement` — this handles the full implementation cycle:
133
+ **3a.** Run `/prizmkit-implement` this handles the implementation cycle:
146
134
  - Reads plan.md Tasks section from `.prizmkit/specs/{{FEATURE_SLUG}}/`
147
135
  - Reads context from `context-snapshot.md` (Prizm docs, TRAPS, file manifest)
148
136
  - Implements task-by-task, marking each `[x]` immediately
149
137
  - Creates/updates L2 `.prizm` docs when creating new modules or significantly modifying existing ones — AI selectively decides which modules warrant L2 based on complexity and importance
150
- - Defers scoped/full test execution until after review
138
+ - Does not run the scoped/full test gate; testing happens after code review in the PrizmKit Test phase
151
139
  - Writes '## Implementation Log' to `context-snapshot.md`
152
140
 
153
- **3b-checkpoint.** Context management — if plan.md has more than 5 tasks, update durable checkpoints/artifacts after every 3 tasks, minimize output, and continue. If context overflow occurs in a headless run, the runner starts a fresh continuation session from those checkpoints and artifacts. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
154
-
155
- **3c.** After implement completes, verify:
141
+ **3b.** After implement completes, verify:
156
142
  1. All tasks in plan.md are `[x]`
157
143
  2. **Verification Gate Self-Check**: for each acceptance criterion, write one line of evidence (test name / file:line / fixture path). Any gate without evidence is BLOCKED, not success.
158
- 3. **Layered test strategy**: do not run mandatory periodic/full-suite tests during implementation. Use only tiny targeted checks when needed for local debugging; the scoped feature test gate runs after review.
159
- 4. Verify each acceptance criterion from Section 1 of context-snapshot.md is met check mentally, do NOT re-read files you already wrote
160
- 5. If any criterion is not met, fix it now using the convergence-based recovery loop below
144
+ 3. Use only tiny targeted checks when needed for local debugging; the scoped feature test gate runs after review.
145
+ 4. If any criterion is not met, fix it within this feature scope before proceeding.
146
+
147
+ **CP-2**: All acceptance criteria met or blocked gates documented in `failure-log.md`. Test execution is deferred until after code review.
161
148
 
162
- **CP-2**: All acceptance criteria met, all tests pass.
149
+ ### Review Code Review
163
150
 
164
- ### Test Failure Recovery (Convergence-Based)
151
+ Review the completed implementation before the scoped feature test gate runs. Use `artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/` so the code-review skill can read spec, plan, context snapshot, implementation log, and current diff artifacts. Do not require or read a prior `/prizmkit-test` report here; test generation and test-fix looping happen in the next phase.
165
152
 
166
- When tests fail, use convergence recovery — keep fixing while progress is being made:
153
+ Run `/prizmkit-code-review` with `artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/`.
167
154
 
168
- 1. **Run tests, record results**: count failures, exclude baseline failures
169
- 2. **Check termination**: All pass → done | Plateau (same failures 3 rounds) → stop | Failures decreased → continue
170
- 3. **Fix and iterate**: analyze, fix, re-run `($TEST_CMD)`, go back to step 1
155
+ The skill runs an internal review-fix loop (Reviewer Agent → filter → orchestrator fix, max 3 rounds) and writes `review-report.md` to the artifact directory.
171
156
 
172
- **Key rule**: If failures decrease (even by 1), plateau counter resets. Do NOT block commit for unresolved failures document and defer to next session.
157
+ **Gate CheckReview Report**:
158
+ After `/prizmkit-code-review` returns, verify the review report:
159
+ ```bash
160
+ grep -q "## Verdict" .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md && echo "GATE:PASS" || echo "GATE:MISSING"
161
+ ```
162
+ If GATE:MISSING:
163
+ - Do not re-run `/prizmkit-code-review` in an unbounded report-repair loop.
164
+ - Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer agent spawn messages.
165
+ - If the missing report is caused by Reviewer agent spawn or review skill error, retry `/prizmkit-code-review` at most once only if it appears transient.
166
+ - 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.
173
167
 
174
- **Hard Round Cap (BLOCKING)**: After **6 total test-fix rounds**, STOP trial-and-error. Write failure-log listing all hypotheses tried. Switch to: read full relevant source section ONCE (via `grep -n`+`sed -n`, never full Read), understand control flow, rewrite failing block in one shot. Do NOT exceed 7 total rounds.
168
+ Read `review-report.md` and check the Verdict:
169
+ - `PASS` → proceed to next phase
170
+ - `NEEDS_FIXES` → the skill exhausted its max rounds; log the remaining findings and proceed (do not retry externally)
175
171
 
172
+ **CP-3**: Review complete, report written.
176
173
 
177
174
  ### Scoped Feature Test Gate — PrizmKit Test
178
175
 
179
176
  Run `/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/` after review and before commit.
180
177
 
181
- Before invoking the skill, create `.prizmkit/specs/{{FEATURE_SLUG}}/.prizmkit-test-started` so stale reports are ignored.
178
+ Before invoking the skill, create a current-run marker so stale reports are ignored:
179
+ ```bash
180
+ mkdir -p .prizmkit/specs/{{FEATURE_SLUG}}
181
+ touch .prizmkit/specs/{{FEATURE_SLUG}}/.prizmkit-test-started
182
+ ```
182
183
 
183
184
  Gate requirements:
184
185
  - Generate or update tests only for this feature's changed files, changed public interfaces, and acceptance criteria.