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
@@ -4,7 +4,7 @@ Review the completed implementation before the scoped feature test gate runs. Us
4
4
 
5
5
  Run `/prizmkit-code-review` with `artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/`.
6
6
 
7
- The skill runs an internal review-fix loop (Reviewer Agent → filter → Dev Agent fix, max 3 rounds) and writes `review-report.md` to the artifact directory.
7
+ 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.
8
8
 
9
9
  **Gate Check — Review Report**:
10
10
  After `/prizmkit-code-review` returns, verify the review report:
@@ -13,8 +13,8 @@ grep -q "## Verdict" .prizmkit/specs/{{FEATURE_SLUG}}/review-report.md && echo "
13
13
  ```
14
14
  If GATE:MISSING:
15
15
  - Do not re-run `/prizmkit-code-review` in an unbounded report-repair loop.
16
- - Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer/Dev spawn messages.
17
- - 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
+ - Perform one bounded status check; retry at most once: inspect the skill output, `review-report.md` path, and any Reviewer agent spawn messages.
17
+ - If the missing report is caused by team/config/lock errors from the Reviewer agent spawn or review skill error, retry `/prizmkit-code-review` at most once only if it appears transient.
18
18
  - 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.
19
19
 
20
20
  Read `review-report.md` and check the Verdict:
@@ -24,10 +24,3 @@ Read `review-report.md` and check the Verdict:
24
24
  **CP-3**: Review complete, report written.
25
25
 
26
26
 
27
- **Checkpoint update**: Run the update script to set step `prizmkit-code-review` to `"completed"`:
28
- ```bash
29
- python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
30
- --checkpoint-path {{CHECKPOINT_PATH}} \
31
- --step prizmkit-code-review \
32
- --status completed
33
- ```
@@ -45,7 +45,7 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/ 2>/dev/null
45
45
  ls .prizmkit/specs/{{FEATURE_SLUG}}/spec.md .prizmkit/specs/{{FEATURE_SLUG}}/plan.md 2>/dev/null
46
46
  ```
47
47
 
48
- - spec.md missing: Run `/prizmkit-plan` → generate spec.md. Resolve any `[NEEDS CLARIFICATION]` markers using the task description do NOT pause for interactive input.
48
+ - spec.md missing: Run `/prizmkit-plan` → generate spec.md. Resolve uncertain requirements from the task description, existing code, and conservative existing-project patterns.
49
49
  - plan.md missing: Run `/prizmkit-plan` → generate plan.md (change approach, components, interface design, data model, testing strategy, risk assessment, and Tasks section with `[ ]` checkboxes)
50
50
 
51
51
  > All files go under `.prizmkit/specs/{{FEATURE_SLUG}}/`. Confirm each with `ls` after writing.
@@ -54,16 +54,9 @@ ls .prizmkit/specs/{{FEATURE_SLUG}}/spec.md .prizmkit/specs/{{FEATURE_SLUG}}/pla
54
54
  Before proceeding past CP-1, verify:
55
55
  1. Plan.md Data Model section references existing schema/model files (scan for `*.prisma`, `*.sql`, `migrations/`, `models/`, `*.entity.*` files; read them if not already in context-snapshot)
56
56
  2. All new tables/fields follow existing naming conventions, ID strategy, timestamp patterns, and constraint style
57
- 3. No `[NEEDS CLARIFICATION]` remains in Data Model section — resolve by reading existing code and making a conservative choice that matches existing patterns. Document the resolution in plan.md.
57
+ 3. No unresolved data model uncertainty remains in the Data Model section — resolve by reading existing code and making a conservative choice that matches existing patterns. Document the resolution in plan.md.
58
58
  4. If a DB design decision genuinely cannot be resolved from existing code alone, document the assumption made and flag it in the Implementation Log for user review.
59
59
 
60
60
  **CP-1**: Both spec.md and plan.md exist.
61
61
 
62
62
 
63
- **Checkpoint update**: Run the update script to set step `context-snapshot-and-plan` to `"completed"`:
64
- ```bash
65
- python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
66
- --checkpoint-path {{CHECKPOINT_PATH}} \
67
- --step context-snapshot-and-plan \
68
- --status completed
69
- ```
@@ -4,10 +4,3 @@
4
4
  - **CP-0**: Verify `.prizmkit/prizm-docs/root.prizm`, `.prizmkit/config.json` exist
5
5
 
6
6
 
7
- **Checkpoint update**: Run the update script to set step `prizmkit-init` to `"completed"`:
8
- ```bash
9
- python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
10
- --checkpoint-path {{CHECKPOINT_PATH}} \
11
- --step prizmkit-init \
12
- --status completed
13
- ```
@@ -1,16 +1,3 @@
1
- ### Phase 0: Detect Test Commands
1
+ ### Phase 0: Project Already Initialized
2
2
 
3
- **Step 1 Detect test commands**: 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`.
4
-
5
- **Step 2 — Do not run tests yet**: This phase only records the commands that will be used by the post-review PrizmKit Test gate. Do not run baseline, periodic, or full-suite tests here; test execution happens after code review.
6
-
7
- > **Test Output Rule**: When the post-review gate runs, capture test output to a temp file (`tee /tmp/test-out.txt`). Then grep the file instead of re-running the suite.
8
-
9
-
10
- **Checkpoint update**: Run the update script to set step `test-baseline` to `"completed"`:
11
- ```bash
12
- python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
13
- --checkpoint-path {{CHECKPOINT_PATH}} \
14
- --step test-baseline \
15
- --status completed
16
- ```
3
+ The project is already initialized. Proceed to context loading and planning. Test execution belongs to the post-review `/prizmkit-test` gate.
@@ -25,5 +25,4 @@ def _load_hyphenated_module(module_name, filename):
25
25
  _load_hyphenated_module("generate_bootstrap_prompt", "generate-bootstrap-prompt.py")
26
26
  _load_hyphenated_module("generate_bugfix_prompt", "generate-bugfix-prompt.py")
27
27
  _load_hyphenated_module("generate_refactor_prompt", "generate-refactor-prompt.py")
28
- _load_hyphenated_module("prizmkit_test_gate", "prizmkit-test-gate.py")
29
28
  _load_hyphenated_module("update_feature_status", "update-feature-status.py")
@@ -23,10 +23,24 @@ from generate_bootstrap_prompt import (
23
23
  generate_checkpoint_definition,
24
24
  merge_checkpoint_state,
25
25
  load_active_agent_prompts,
26
- load_log_size_section,
27
26
  main as generate_bootstrap_main,
28
27
  )
29
28
  from continuation import append_continuation_handoff, checkpoint_cursor
29
+ from utils import resolve_prompt_platform
30
+
31
+ REMOVED_MAX_LOG_ENV = "MAX_" + "LOG_SIZE"
32
+ REMOVED_MAX_LOG_HUMAN = REMOVED_MAX_LOG_ENV + "_HUMAN"
33
+
34
+ FORBIDDEN_DEV_FIX_PROMPT = "dev-" + "fix.md"
35
+ FORBIDDEN_DEV_RESUME_PROMPT = "dev-" + "resume.md"
36
+ FORBIDDEN_DEV_FIX_PLACEHOLDER = "{{AGENT_PROMPT_DEV_" + "FIX}}"
37
+ FORBIDDEN_DEV_RESUME_PLACEHOLDER = "{{AGENT_PROMPT_DEV_" + "RESUME}}"
38
+ FORBIDDEN_REVIEW_DEV_FIX = "Reviewer Agent → filter → " + "Dev Agent fix"
39
+ FORBIDDEN_REVIEWER_DEV_SPAWN = "Reviewer/" + "Dev agent spawn"
40
+ FORBIDDEN_EXPLORE_SUBAGENT = "subagent_type: " + "Explore"
41
+ FORBIDDEN_TEST_CMD_PLACEHOLDER = "(" + "$TEST_CMD" + ")"
42
+ FORBIDDEN_THREE_STRIKE = "3-" + "strike"
43
+ FORBIDDEN_TWENTY_STEP = "20-" + "step window"
30
44
 
31
45
 
32
46
  def scoped_report_text(verdict="PASS", boundary_missing=0,
@@ -222,6 +236,11 @@ class TestExplicitProjectRoot:
222
236
  assert not replacements["{{SESSION_STATUS_PATH}}"].startswith(str(execution_root.resolve()))
223
237
  assert replacements["{{PROJECT_BRIEF}}"] == "worktree brief"
224
238
  assert replacements["{{DEV_SUBAGENT_PATH}}"].startswith(str(execution_root.resolve()))
239
+ assert replacements["{{SESSION_LOG_PATH}}"].endswith(
240
+ ".prizmkit/state/features/F-001/sessions/session-1/logs/session.log"
241
+ )
242
+ assert "{{" + REMOVED_MAX_LOG_ENV + "}}" not in replacements
243
+ assert "{{" + REMOVED_MAX_LOG_HUMAN + "}}" not in replacements
225
244
 
226
245
 
227
246
  def test_main_writes_checkpoint_under_cli_project_root_and_status_under_state_dir(self, tmp_path, monkeypatch):
@@ -424,9 +443,14 @@ class TestScopedFeatureTestGate:
424
443
  assert names.index("phase-implement") < names.index("phase-review") < names.index("phase-prizmkit-test")
425
444
 
426
445
  rendered = "\n".join(content for _, content in sections)
427
- assert "prizmkit-test-gate.py" in rendered
428
- assert "prizmkit-test-gate.py\" start" in rendered
429
- assert "prizmkit-test-gate.py\" check" in rendered
446
+ assert "prizmkit-test-gate.py" not in rendered
447
+ assert ".prizmkit-test-started" in rendered
448
+ assert "when present" not in rendered
449
+ assert "Boundary Matrix" in rendered
450
+ assert "Boundary Completion Gate" in rendered
451
+ assert "Boundary-missing: 0" in rendered
452
+ assert "Happy-path-only: 0" in rendered
453
+ assert "Validator result: passed" in rendered
430
454
  assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/" in rendered
431
455
  assert ".prizmkit/bugfix/" in rendered # explicitly forbidden in the gate wording
432
456
  assert ".prizmkit/refactor/" in rendered # explicitly forbidden in the gate wording
@@ -437,6 +461,25 @@ class TestScopedFeatureTestGate:
437
461
  assert "before code review" not in rendered
438
462
  assert "Precondition: the `prizmkit-test` checkpoint is completed" not in rendered
439
463
 
464
+ def test_standard_sections_include_central_checkpoint_helper(self):
465
+ sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
466
+ sections = assemble_sections(
467
+ "standard",
468
+ str(sections_dir),
469
+ init_done=True,
470
+ is_resume=False,
471
+ critic_enabled=False,
472
+ browser_enabled=False,
473
+ )
474
+ rendered = "\n".join(content for _, content in sections)
475
+
476
+ assert "### Checkpoint Update Helper" in rendered
477
+ assert "python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py" in rendered
478
+ assert "--checkpoint-path {{CHECKPOINT_PATH}}" in rendered
479
+ assert "--step <step-id-or-skill-name>" in rendered
480
+ assert "Do not hand-edit `workflow-checkpoint.json` directly." in rendered
481
+ assert "pipeline progress mechanism" not in rendered
482
+
440
483
  def test_lite_sections_include_review_before_prizmkit_test_and_commit(self):
441
484
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
442
485
  sections = assemble_sections(
@@ -776,22 +819,32 @@ class TestScopedFeatureTestGate:
776
819
  # ---------------------------------------------------------------------------
777
820
 
778
821
  class TestHeadlessRecoveryReframing:
779
- def test_log_size_awareness_is_passive_checkpoint_guidance(self):
780
- content = load_log_size_section(str(Path("dev-pipeline/scripts").resolve()))
781
-
782
- assert "Session Log Budget Awareness" in content
783
- assert "passive checkpoint guidance" in content
784
- assert "durable checkpoints and artifacts" in content
785
- assert "fresh continuation session" in content
786
- assert "optional interactive convenience only" in content
787
- assert "COMPACT_NEEDED" not in content
788
- assert "monitor-log" not in content
789
- assert "background monitoring subagent" not in content
790
- assert "Execute `/compact`" not in content
791
- assert "run `/compact`" not in content.lower()
822
+ def test_active_prompt_sources_do_not_reference_deleted_log_size_section(self):
823
+ deleted_section = "log-size-" + "awareness.md"
824
+ loader = "load_" + "log_size_section"
825
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
826
+ deleted_title = "Session " + "Checkpoint Awareness"
827
+ active_paths = [
828
+ Path("dev-pipeline/scripts/generate-bootstrap-prompt.py"),
829
+ Path("dev-pipeline/scripts/generate-bugfix-prompt.py"),
830
+ Path("dev-pipeline/scripts/generate-refactor-prompt.py"),
831
+ Path("dev-pipeline/templates/bootstrap-tier1.md"),
832
+ Path("dev-pipeline/templates/bootstrap-tier2.md"),
833
+ Path("dev-pipeline/templates/bootstrap-tier3.md"),
834
+ Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md"),
835
+ Path("dev-pipeline/templates/refactor-bootstrap-prompt.md"),
836
+ ]
837
+
838
+ assert (Path("dev-pipeline/templates/sections") / deleted_section).exists() is False
839
+ for path in active_paths:
840
+ content = path.read_text(encoding="utf-8")
841
+ for forbidden in (deleted_section, loader, placeholder, deleted_title):
842
+ assert forbidden not in content
792
843
 
793
844
  def test_assembled_prompts_do_not_spawn_log_monitor_or_require_compact(self):
794
845
  sections_dir = Path("dev-pipeline/templates/sections").resolve()
846
+ deleted_title = "Session " + "Checkpoint Awareness"
847
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
795
848
 
796
849
  for mode in ("lite", "standard", "full"):
797
850
  rendered = "\n".join(
@@ -800,8 +853,12 @@ class TestHeadlessRecoveryReframing:
800
853
  critic_enabled=False, browser_enabled=False,
801
854
  )
802
855
  )
803
- assert "Headless recovery must rely on durable checkpoints" in rendered
804
- assert "fresh continuation session" in rendered
856
+ assert deleted_title not in rendered
857
+ assert placeholder not in rendered
858
+ assert REMOVED_MAX_LOG_ENV not in rendered
859
+ assert REMOVED_MAX_LOG_HUMAN not in rendered
860
+ assert ("209" + "7152") not in rendered
861
+ assert ("2" + "MB") not in rendered
805
862
  assert "monitor-log" not in rendered
806
863
  assert "COMPACT_NEEDED" not in rendered
807
864
  assert "log-size monitor" not in rendered
@@ -818,9 +875,16 @@ class TestDirectOrchestratorImplementationPrompts:
818
875
  replacements = load_active_agent_prompts(str(templates_dir))
819
876
 
820
877
  assert "dev-implement.md" not in ACTIVE_AGENT_PROMPTS
878
+ assert FORBIDDEN_DEV_FIX_PROMPT not in ACTIVE_AGENT_PROMPTS
879
+ assert FORBIDDEN_DEV_RESUME_PROMPT not in ACTIVE_AGENT_PROMPTS
821
880
  assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in replacements
881
+ assert FORBIDDEN_DEV_FIX_PLACEHOLDER not in replacements
882
+ assert FORBIDDEN_DEV_RESUME_PLACEHOLDER not in replacements
822
883
  assert "{{AGENT_PROMPT_CRITIC_PLAN_CHALLENGE}}" in replacements
884
+ assert "{{AGENT_PROMPT_REVIEWER_REVIEW}}" in replacements
823
885
  assert (templates_dir / "agent-prompts" / "dev-implement.md").exists() is False
886
+ assert (templates_dir / "agent-prompts" / FORBIDDEN_DEV_FIX_PROMPT).exists() is False
887
+ assert (templates_dir / "agent-prompts" / FORBIDDEN_DEV_RESUME_PROMPT).exists() is False
824
888
 
825
889
  def test_sections_all_modes_use_direct_orchestrator_implementation(self):
826
890
  sections_dir = Path("dev-pipeline/templates/sections").resolve()
@@ -840,7 +904,13 @@ class TestDirectOrchestratorImplementationPrompts:
840
904
  assert "Implement — Dev" not in rendered
841
905
  assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
842
906
  assert "dev-implement.md" not in rendered
843
- assert "Reviewer Agent → filter → Dev Agent fix" in rendered
907
+ assert FORBIDDEN_REVIEW_DEV_FIX not in rendered
908
+ assert FORBIDDEN_REVIEWER_DEV_SPAWN not in rendered
909
+ assert FORBIDDEN_EXPLORE_SUBAGENT not in rendered
910
+ assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
911
+ assert FORBIDDEN_THREE_STRIKE not in rendered
912
+ assert FORBIDDEN_TWENTY_STEP not in rendered
913
+ assert "Reviewer Agent → filter → orchestrator fix" in rendered
844
914
 
845
915
  def test_legacy_tier_templates_use_direct_orchestrator_implementation(self):
846
916
  for template_name in ("bootstrap-tier2.md", "bootstrap-tier3.md"):
@@ -853,6 +923,12 @@ class TestDirectOrchestratorImplementationPrompts:
853
923
  assert "Implement — Dev" not in rendered
854
924
  assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
855
925
  assert "dev-implement.md" not in rendered
926
+ assert FORBIDDEN_REVIEW_DEV_FIX not in rendered
927
+ assert FORBIDDEN_REVIEWER_DEV_SPAWN not in rendered
928
+ assert FORBIDDEN_EXPLORE_SUBAGENT not in rendered
929
+ assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
930
+ assert FORBIDDEN_THREE_STRIKE not in rendered
931
+ assert FORBIDDEN_TWENTY_STEP not in rendered
856
932
 
857
933
 
858
934
  # ---------------------------------------------------------------------------
@@ -893,11 +969,13 @@ class TestContinuationHandoff:
893
969
  assert "Treat completed/skipped checkpoint steps as already done." in rendered
894
970
  assert "Do not read the previous full `session.log` unless explicitly necessary." in rendered
895
971
  assert "prefer synthesized artifacts and summaries over logs" in rendered
896
- assert "Do not attempt `/compact` as the recovery mechanism." in rendered
972
+ assert "Do not attempt `/compact` as the recovery mechanism." not in rendered
897
973
  assert ".prizmkit/specs/123-continuation/workflow-checkpoint.json" in rendered
898
974
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/session.log" in rendered
899
975
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/progress.json" in rendered
900
976
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/ai.pid" in rendered
977
+ assert ("Log " + "monitor") not in rendered
978
+ assert ("log-" + "monitor") not in rendered
901
979
 
902
980
  summary = tmp_path / ".prizmkit/specs/123-continuation/continuation-summary.md"
903
981
  assert summary.exists()
@@ -913,3 +991,299 @@ class TestContinuationHandoff:
913
991
  {"id": "S03", "skill": "later", "status": "pending"},
914
992
  ]}
915
993
  assert checkpoint_cursor(checkpoint)["first_cursor"] == "S02 (active)"
994
+
995
+
996
+ # ---------------------------------------------------------------------------
997
+ # F-033 headless prompt cleanup and platform-aware critic paths
998
+ # ---------------------------------------------------------------------------
999
+
1000
+ HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
1001
+ "### Interactive Claude Code Note",
1002
+ "### Project Conventions",
1003
+ "Read CLAUDE.md for project-level coding standards and architecture decisions.",
1004
+ "Scaffold / generated file awareness",
1005
+ "### Resume Behavior",
1006
+ "[NEEDS CLARIFICATION]",
1007
+ "pause for interactive input",
1008
+ "prizmkit-test-gate.py",
1009
+ "### Phase 0: Detect Test Commands",
1010
+ "TEST_CMDS",
1011
+ ]
1012
+
1013
+
1014
+ def _write_feature_list(path, feature):
1015
+ path.parent.mkdir(parents=True, exist_ok=True)
1016
+ path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
1017
+
1018
+
1019
+ def _render_feature_prompt(tmp_path, mode="standard", critic=False, template=None, platform="claude"):
1020
+ project = tmp_path / "project"
1021
+ project.mkdir(parents=True)
1022
+ (project / ".prizmkit" / "plans").mkdir(parents=True)
1023
+ (project / ".prizmkit" / "config.json").write_text(
1024
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
1025
+ )
1026
+ for platform_dir, suffix in ((".claude", ".md"), (".codex", ".toml")):
1027
+ agents_dir = project / platform_dir / "agents"
1028
+ agents_dir.mkdir(parents=True)
1029
+ for name in ("dev", "reviewer", "critic"):
1030
+ (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
1031
+ feature = {
1032
+ "id": "F-123",
1033
+ "title": "Prompt Cleanup",
1034
+ "description": "Clean prompt text",
1035
+ "estimated_complexity": {"lite": "low", "standard": "medium", "full": "high"}[mode],
1036
+ "acceptance_criteria": ["Prompts are clean"],
1037
+ "critic": critic,
1038
+ }
1039
+ feature_list = project / ".prizmkit" / "plans" / "feature-list.json"
1040
+ _write_feature_list(feature_list, feature)
1041
+ output = project / "prompt.md"
1042
+ args = [
1043
+ "generate-bootstrap-prompt.py",
1044
+ "--feature-list", str(feature_list),
1045
+ "--feature-id", "F-123",
1046
+ "--session-id", "session-1",
1047
+ "--run-id", "run-1",
1048
+ "--retry-count", "0",
1049
+ "--resume-phase", "null",
1050
+ "--state-dir", str(project / ".prizmkit" / "state" / "features"),
1051
+ "--project-root", str(project),
1052
+ "--output", str(output),
1053
+ "--mode", mode,
1054
+ ]
1055
+ if critic:
1056
+ args.extend(["--critic", "true"])
1057
+ if template:
1058
+ args.extend(["--template", str(Path("dev-pipeline/templates") / template)])
1059
+ old_argv = os.sys.argv
1060
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
1061
+ try:
1062
+ os.sys.argv = args
1063
+ if old_platform is not None:
1064
+ os.environ.pop("PRIZMKIT_PLATFORM")
1065
+ try:
1066
+ generate_bootstrap_main()
1067
+ except SystemExit as exc:
1068
+ if exc.code not in (0, None):
1069
+ raise
1070
+ finally:
1071
+ os.sys.argv = old_argv
1072
+ if old_platform is not None:
1073
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
1074
+ return output.read_text(encoding="utf-8")
1075
+
1076
+
1077
+ def _ensure_agent_files(project, platforms=("claude", "codex")):
1078
+ for platform in platforms:
1079
+ platform_dir = ".codex" if platform == "codex" else ".claude"
1080
+ suffix = ".toml" if platform == "codex" else ".md"
1081
+ agents_dir = project / platform_dir / "agents"
1082
+ agents_dir.mkdir(parents=True, exist_ok=True)
1083
+ for name in ("dev", "reviewer", "critic"):
1084
+ (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
1085
+
1086
+
1087
+ def _render_bugfix_prompt(tmp_path, platform="claude", explicit_platform=None):
1088
+ from generate_bugfix_prompt import main as bugfix_main
1089
+
1090
+ project = tmp_path / "bugfix-project"
1091
+ project.mkdir(parents=True)
1092
+ (project / ".prizmkit").mkdir(exist_ok=True)
1093
+ (project / ".prizmkit" / "config.json").write_text(
1094
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
1095
+ )
1096
+ _ensure_agent_files(project)
1097
+ bug_list = project / ".prizmkit" / "plans" / "bug-fix-list.json"
1098
+ bug_list.parent.mkdir(parents=True)
1099
+ bug_list.write_text(json.dumps({
1100
+ "bugs": [{"id": "B-001", "title": "Broken", "description": "Fix it", "acceptance_criteria": ["Works"]}],
1101
+ "global_context": {"language": "Python"},
1102
+ }), encoding="utf-8")
1103
+ output = project / "bugfix-prompt.md"
1104
+ old_argv = os.sys.argv
1105
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
1106
+ try:
1107
+ if explicit_platform is None:
1108
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1109
+ else:
1110
+ os.environ["PRIZMKIT_PLATFORM"] = explicit_platform
1111
+ os.sys.argv = [
1112
+ "generate-bugfix-prompt.py",
1113
+ "--bug-list", str(bug_list),
1114
+ "--bug-id", "B-001",
1115
+ "--session-id", "session-1",
1116
+ "--run-id", "run-1",
1117
+ "--retry-count", "0",
1118
+ "--resume-phase", "null",
1119
+ "--state-dir", str(project / ".prizmkit" / "state" / "bugfix"),
1120
+ "--project-root", str(project),
1121
+ "--output", str(output),
1122
+ ]
1123
+ try:
1124
+ bugfix_main()
1125
+ except SystemExit as exc:
1126
+ if exc.code not in (0, None):
1127
+ raise
1128
+ finally:
1129
+ os.sys.argv = old_argv
1130
+ if old_platform is None:
1131
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1132
+ else:
1133
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
1134
+ return output.read_text(encoding="utf-8")
1135
+
1136
+
1137
+ def _render_refactor_prompt(tmp_path, platform="claude", explicit_platform=None):
1138
+ from generate_refactor_prompt import main as refactor_main
1139
+
1140
+ project = tmp_path / "refactor-project"
1141
+ project.mkdir(parents=True)
1142
+ (project / ".prizmkit").mkdir(exist_ok=True)
1143
+ (project / ".prizmkit" / "config.json").write_text(
1144
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
1145
+ )
1146
+ _ensure_agent_files(project)
1147
+ refactor_list = project / ".prizmkit" / "plans" / "refactor-list.json"
1148
+ refactor_list.parent.mkdir(parents=True)
1149
+ refactor_list.write_text(json.dumps({
1150
+ "refactors": [{
1151
+ "id": "R-001",
1152
+ "title": "Restructure",
1153
+ "description": "Preserve behavior",
1154
+ "acceptance_criteria": ["Behavior preserved"],
1155
+ "scope": {"files": ["src/a.py"], "modules": ["src"]},
1156
+ }],
1157
+ "global_context": {"language": "Python"},
1158
+ }), encoding="utf-8")
1159
+ output = project / "refactor-prompt.md"
1160
+ old_argv = os.sys.argv
1161
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
1162
+ try:
1163
+ if explicit_platform is None:
1164
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1165
+ else:
1166
+ os.environ["PRIZMKIT_PLATFORM"] = explicit_platform
1167
+ os.sys.argv = [
1168
+ "generate-refactor-prompt.py",
1169
+ "--refactor-list", str(refactor_list),
1170
+ "--refactor-id", "R-001",
1171
+ "--session-id", "session-1",
1172
+ "--run-id", "run-1",
1173
+ "--retry-count", "0",
1174
+ "--resume-phase", "null",
1175
+ "--state-dir", str(project / ".prizmkit" / "state" / "refactor"),
1176
+ "--output", str(output),
1177
+ ]
1178
+ old_cwd = os.getcwd()
1179
+ os.chdir(project)
1180
+ try:
1181
+ try:
1182
+ refactor_main()
1183
+ except SystemExit as exc:
1184
+ if exc.code not in (0, None):
1185
+ raise
1186
+ finally:
1187
+ os.chdir(old_cwd)
1188
+ finally:
1189
+ os.sys.argv = old_argv
1190
+ if old_platform is None:
1191
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1192
+ else:
1193
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
1194
+ return output.read_text(encoding="utf-8")
1195
+
1196
+
1197
+ class TestHeadlessPromptCleanupF033:
1198
+ def test_rendered_feature_bugfix_refactor_prompts_are_clean(self, tmp_path):
1199
+ prompts = []
1200
+ for mode in ("lite", "standard", "full"):
1201
+ prompts.append(_render_feature_prompt(tmp_path / mode, mode=mode, critic=(mode != "lite")))
1202
+ prompts.append(_render_bugfix_prompt(tmp_path))
1203
+ prompts.append(_render_refactor_prompt(tmp_path))
1204
+
1205
+ for prompt in prompts:
1206
+ for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
1207
+ assert forbidden not in prompt
1208
+ if "Feature ID" in prompt:
1209
+ assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/123-prompt-cleanup/" in prompt
1210
+ assert ".prizmkit-test-started" in prompt
1211
+
1212
+ def test_legacy_feature_templates_are_clean_when_rendered(self, tmp_path):
1213
+ for template, mode in (("bootstrap-tier1.md", "lite"), ("bootstrap-tier2.md", "standard"), ("bootstrap-tier3.md", "full")):
1214
+ prompt = _render_feature_prompt(tmp_path / template, mode=mode, critic=(mode != "lite"), template=template)
1215
+ for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
1216
+ assert forbidden not in prompt
1217
+ assert "prizmkit-test-gate.py" not in prompt
1218
+ assert ".prizmkit-test-started" in prompt
1219
+
1220
+ def test_claude_config_wins_over_codex_directory_for_critic_path(self, tmp_path, monkeypatch):
1221
+ project = tmp_path / "mixed"
1222
+ (project / ".claude" / "agents").mkdir(parents=True)
1223
+ (project / ".codex" / "agents").mkdir(parents=True)
1224
+ (project / ".prizmkit").mkdir()
1225
+ (project / ".prizmkit" / "config.json").write_text(
1226
+ json.dumps({"platform": "claude", "ai_cli": "claude"}), encoding="utf-8"
1227
+ )
1228
+ monkeypatch.delenv("PRIZMKIT_PLATFORM", raising=False)
1229
+
1230
+ assert resolve_prompt_platform(str(project)) == "claude"
1231
+ prompt = _render_feature_prompt(tmp_path / "rendered-mixed", mode="standard", critic=True, platform="claude")
1232
+ assert ".claude/agents/prizm-dev-team-critic.md" in prompt
1233
+ assert ".codex/agents/prizm-dev-team-critic.toml" not in prompt
1234
+
1235
+ def test_bugfix_refactor_share_claude_platform_resolution(self, tmp_path, monkeypatch):
1236
+ from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
1237
+
1238
+ monkeypatch.delenv("PRIZMKIT_PLATFORM", raising=False)
1239
+ bug_project = tmp_path / "bugfix-replacements"
1240
+ bug_project.mkdir()
1241
+ (bug_project / ".prizmkit").mkdir()
1242
+ (bug_project / ".prizmkit" / "config.json").write_text(
1243
+ json.dumps({"platform": "claude", "ai_cli": "claude"}), encoding="utf-8"
1244
+ )
1245
+ _ensure_agent_files(bug_project)
1246
+ args = Namespace(
1247
+ bug_id="B-001", session_id="session-1", run_id="run-1",
1248
+ retry_count="0", resume_phase="null", state_dir=None,
1249
+ output="out.md", project_root=str(bug_project), template=None,
1250
+ mode=None, critic=None,
1251
+ )
1252
+ replacements = bugfix_build_replacements(
1253
+ args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
1254
+ )
1255
+ refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-mixed", platform="claude")
1256
+
1257
+ assert replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".claude/agents/prizm-dev-team-reviewer.md")
1258
+ assert ".claude/agents/prizm-dev-team-reviewer.md" in refactor_prompt
1259
+ assert ".codex/agents/prizm-dev-team-reviewer.toml" not in refactor_prompt
1260
+
1261
+ def test_bugfix_refactor_explicit_codex_override(self, tmp_path, monkeypatch):
1262
+ from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
1263
+
1264
+ bug_project = tmp_path / "bugfix-codex-replacements"
1265
+ bug_project.mkdir()
1266
+ _ensure_agent_files(bug_project)
1267
+ monkeypatch.setenv("PRIZMKIT_PLATFORM", "codex")
1268
+ args = Namespace(
1269
+ bug_id="B-001", session_id="session-1", run_id="run-1",
1270
+ retry_count="0", resume_phase="null", state_dir=None,
1271
+ output="out.md", project_root=str(bug_project), template=None,
1272
+ mode=None, critic=None,
1273
+ )
1274
+ replacements = bugfix_build_replacements(
1275
+ args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
1276
+ )
1277
+ refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-codex", platform="claude", explicit_platform="codex")
1278
+
1279
+ assert replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".codex/agents/prizm-dev-team-reviewer.toml")
1280
+ assert ".codex/agents/prizm-dev-team-reviewer.toml" in refactor_prompt
1281
+ assert ".claude/agents/prizm-dev-team-reviewer.md" not in refactor_prompt
1282
+
1283
+ def test_explicit_codex_env_still_renders_codex_critic_path(self, tmp_path, monkeypatch):
1284
+ project = tmp_path / "codex"
1285
+ (project / ".claude" / "agents").mkdir(parents=True)
1286
+ (project / ".codex" / "agents").mkdir(parents=True)
1287
+ monkeypatch.setenv("PRIZMKIT_PLATFORM", "codex")
1288
+
1289
+ assert resolve_prompt_platform(str(project)) == "codex"