prizmkit 1.1.107 → 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 (78) 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_recovery.py +3 -1
  9. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +20 -1
  10. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +151 -30
  11. package/bundled/dev-pipeline/scripts/continuation.py +0 -1
  12. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +6 -46
  13. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +7 -34
  14. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +40 -33
  15. package/bundled/dev-pipeline/scripts/utils.py +123 -0
  16. package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +4 -3
  17. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  18. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +8 -7
  19. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +25 -36
  20. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +16 -31
  21. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +4 -16
  22. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +3 -15
  23. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -48
  24. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +7 -13
  25. package/bundled/dev-pipeline/templates/sections/feature-context.md +0 -4
  26. package/bundled/dev-pipeline/templates/sections/phase-browser-verification-auto.md +0 -7
  27. package/bundled/dev-pipeline/templates/sections/phase-browser-verification-opencli.md +0 -7
  28. package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +0 -7
  29. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +0 -14
  30. package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -14
  31. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -7
  32. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +0 -2
  33. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -7
  34. package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +6 -8
  35. package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +3 -7
  36. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -7
  37. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +0 -7
  38. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -7
  39. package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +2 -9
  40. package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +1 -8
  41. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +15 -16
  42. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -7
  43. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +0 -7
  44. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +2 -9
  45. package/bundled/dev-pipeline/templates/sections/phase0-init.md +0 -7
  46. package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +2 -15
  47. package/bundled/dev-pipeline/tests/conftest.py +0 -1
  48. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +350 -23
  49. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +36 -17
  50. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +117 -14
  51. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +79 -2
  52. package/bundled/dev-pipeline/tests/test_unified_cli.py +658 -13
  53. package/bundled/skills/_metadata.json +4 -4
  54. package/bundled/skills/app-planner/SKILL.md +1 -1
  55. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
  56. package/bundled/skills/bug-fix-workflow/SKILL.md +174 -128
  57. package/bundled/skills/bug-planner/SKILL.md +101 -15
  58. package/bundled/skills/bug-planner/references/severity-rules.md +20 -8
  59. package/bundled/skills/feature-planner/SKILL.md +87 -8
  60. package/bundled/skills/feature-planner/assets/planning-guide.md +34 -10
  61. package/bundled/skills/feature-workflow/SKILL.md +100 -81
  62. package/bundled/skills/{prizm-kit → prizmkit}/SKILL.md +1 -1
  63. package/bundled/skills/prizmkit-code-review/SKILL.md +1 -1
  64. package/bundled/skills/refactor-planner/SKILL.md +88 -7
  65. package/bundled/skills/refactor-planner/references/fast-path.md +2 -1
  66. package/bundled/skills/refactor-planner/references/planning-phases.md +29 -11
  67. package/bundled/skills/refactor-workflow/SKILL.md +116 -57
  68. package/bundled/templates/project-memory-template.md +1 -1
  69. package/package.json +1 -1
  70. package/src/ai-cli-launch.js +194 -0
  71. package/src/config.js +26 -14
  72. package/src/index.js +11 -44
  73. package/src/platforms.js +10 -3
  74. package/src/prompts.js +60 -2
  75. package/src/scaffold.js +26 -14
  76. package/bundled/dev-pipeline/scripts/prizmkit-test-gate.py +0 -446
  77. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +0 -20
  78. package/bundled/dev-pipeline/tests/test_prizmkit_test_gate.py +0 -107
@@ -1,12 +1,12 @@
1
1
  ### Scoped Feature Test Gate — PrizmKit Test
2
2
 
3
- **Goal**: After code review completes, generate and verify tests for this feature's changed scope without embedding the full gate implementation in this prompt.
3
+ **Goal**: After code review completes, generate and verify tests for this feature's changed scope through the `/prizmkit-test` skill.
4
4
 
5
- Create a start marker immediately before invoking the skill so the gate can reject stale reports from older sessions:
5
+ Create a current-run marker immediately before invoking the skill:
6
6
 
7
7
  ```bash
8
- python3 "$PIPELINE_DIR/scripts/prizmkit-test-gate.py" start \
9
- --feature-slug "{{FEATURE_SLUG}}"
8
+ mkdir -p .prizmkit/specs/{{FEATURE_SLUG}}
9
+ touch .prizmkit/specs/{{FEATURE_SLUG}}/.prizmkit-test-started
10
10
  ```
11
11
 
12
12
  Run `/prizmkit-test` with the feature artifact directory:
@@ -23,17 +23,16 @@ Rules:
23
23
  - If failures are baseline or unrelated to this feature, document them in the report and continue only when no in-scope failure remains unexplained.
24
24
 
25
25
  **Gate Check — Test Report**:
26
- After `/prizmkit-test` returns, run the gate script to locate the newest current-run report, validate scope/freshness/boundary coverage, write `test-report-path.txt`, and mark the `prizmkit-test` checkpoint completed only on `GATE:PASS`:
27
-
28
- ```bash
29
- python3 "$PIPELINE_DIR/scripts/prizmkit-test-gate.py" check \
30
- --feature-slug "{{FEATURE_SLUG}}" \
31
- --artifact-dir ".prizmkit/specs/{{FEATURE_SLUG}}/" \
32
- --checkpoint-path "{{CHECKPOINT_PATH}}"
33
- ```
26
+ After `/prizmkit-test` returns, read `.prizmkit/specs/{{FEATURE_SLUG}}/test-report-path.txt` if it exists; otherwise locate the newest `.prizmkit/test/*/test-report.md` created by the current test run. Accept the report only when it states:
27
+ - `Scope` mode is `this-change`
28
+ - `Artifact Dir` matches `.prizmkit/specs/{{FEATURE_SLUG}}/`
29
+ - `Verdict` is `PASS`
30
+ - `Boundary Matrix` is present
31
+ - `Boundary Completion Gate` is present and reports `Boundary-missing: 0`, `Happy-path-only: 0`, and `Completion gate passed: yes`
32
+ - `Boundary Validation` is present and reports `Validator result: passed`
34
33
 
35
34
  Gate outcomes:
36
- - `GATE:PASS` → append the report path and a 3-5 bullet summary to `context-snapshot.md` under `## PrizmKit Test Gate`, then proceed to the next checkpoint step. The gate script already wrote `test-report-path.txt` and marked `prizmkit-test` completed.
37
- - `GATE:FAIL` with `NEEDS_FIXES` → read the report's `In-Scope Failures`, fix the feature implementation or generated tests within this feature scope, rerun `/prizmkit-test`, and repeat the gate check. Do not proceed to retrospective/commit while the verdict is not `PASS`.
38
- - `GATE:FAIL` with `BLOCKED`, wrong mode, wrong artifact dir, unreadable report, stale scope, boundary failure, validator failure, or checkpoint update failure → write `.prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md` with the scoped test failure and stop for recovery.
39
- - `GATE:MISSING` → perform one bounded status check: inspect `/prizmkit-test` output and `.prizmkit/test/` for a report directory. If no current-run report exists, write `failure-log.md` and stop for recovery.
35
+ - `PASS` → ensure `.prizmkit/specs/{{FEATURE_SLUG}}/test-report-path.txt` points to the accepted report, append the report path and a 3-5 bullet summary to `context-snapshot.md` under `## PrizmKit Test Gate`, then proceed.
36
+ - `NEEDS_FIXES` → read the report's `In-Scope Failures`, fix the feature implementation or generated tests within this feature scope, rerun `/prizmkit-test`, and repeat the gate check. Do not proceed to retrospective/commit while the verdict is not `PASS`.
37
+ - `BLOCKED`, wrong mode, wrong artifact dir, unreadable report, stale scope, or boundary failure → write `.prizmkit/specs/{{FEATURE_SLUG}}/failure-log.md` with the scoped test failure and stop for recovery.
38
+ - Missing report → perform one bounded status check: inspect `/prizmkit-test` output and `.prizmkit/test/` for a report directory. If no current-run report exists, write `failure-log.md` and stop for recovery.
@@ -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
- ```
@@ -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,10 @@ 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
30
 
31
31
  REMOVED_MAX_LOG_ENV = "MAX_" + "LOG_SIZE"
32
32
  REMOVED_MAX_LOG_HUMAN = REMOVED_MAX_LOG_ENV + "_HUMAN"
@@ -443,9 +443,14 @@ class TestScopedFeatureTestGate:
443
443
  assert names.index("phase-implement") < names.index("phase-review") < names.index("phase-prizmkit-test")
444
444
 
445
445
  rendered = "\n".join(content for _, content in sections)
446
- assert "prizmkit-test-gate.py" in rendered
447
- assert "prizmkit-test-gate.py\" start" in rendered
448
- 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
449
454
  assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/" in rendered
450
455
  assert ".prizmkit/bugfix/" in rendered # explicitly forbidden in the gate wording
451
456
  assert ".prizmkit/refactor/" in rendered # explicitly forbidden in the gate wording
@@ -456,6 +461,25 @@ class TestScopedFeatureTestGate:
456
461
  assert "before code review" not in rendered
457
462
  assert "Precondition: the `prizmkit-test` checkpoint is completed" not in rendered
458
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
+
459
483
  def test_lite_sections_include_review_before_prizmkit_test_and_commit(self):
460
484
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
461
485
  sections = assemble_sections(
@@ -795,25 +819,32 @@ class TestScopedFeatureTestGate:
795
819
  # ---------------------------------------------------------------------------
796
820
 
797
821
  class TestHeadlessRecoveryReframing:
798
- def test_log_size_awareness_is_passive_checkpoint_guidance(self):
799
- content = load_log_size_section(str(Path("dev-pipeline/scripts").resolve()))
800
-
801
- assert "Session Checkpoint Awareness" in content
802
- assert "passive checkpoint guidance" in content
803
- assert "durable checkpoints and artifacts" in content
804
- assert "fresh continuation session" in content
805
- assert "optional interactive convenience only" in content
806
- assert REMOVED_MAX_LOG_ENV not in content
807
- assert ("maximum-" + "log-size") not in content
808
- assert ("log size " + "limit") not in content.lower()
809
- assert "COMPACT_NEEDED" not in content
810
- assert "monitor-log" not in content
811
- assert "background monitoring subagent" not in content
812
- assert "Execute `/compact`" not in content
813
- 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
814
843
 
815
844
  def test_assembled_prompts_do_not_spawn_log_monitor_or_require_compact(self):
816
845
  sections_dir = Path("dev-pipeline/templates/sections").resolve()
846
+ deleted_title = "Session " + "Checkpoint Awareness"
847
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
817
848
 
818
849
  for mode in ("lite", "standard", "full"):
819
850
  rendered = "\n".join(
@@ -822,8 +853,8 @@ class TestHeadlessRecoveryReframing:
822
853
  critic_enabled=False, browser_enabled=False,
823
854
  )
824
855
  )
825
- assert "Headless recovery must rely on durable checkpoints" in rendered
826
- assert "fresh continuation session" in rendered
856
+ assert deleted_title not in rendered
857
+ assert placeholder not in rendered
827
858
  assert REMOVED_MAX_LOG_ENV not in rendered
828
859
  assert REMOVED_MAX_LOG_HUMAN not in rendered
829
860
  assert ("209" + "7152") not in rendered
@@ -938,7 +969,7 @@ class TestContinuationHandoff:
938
969
  assert "Treat completed/skipped checkpoint steps as already done." in rendered
939
970
  assert "Do not read the previous full `session.log` unless explicitly necessary." in rendered
940
971
  assert "prefer synthesized artifacts and summaries over logs" in rendered
941
- assert "Do not attempt `/compact` as the recovery mechanism." in rendered
972
+ assert "Do not attempt `/compact` as the recovery mechanism." not in rendered
942
973
  assert ".prizmkit/specs/123-continuation/workflow-checkpoint.json" in rendered
943
974
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/session.log" in rendered
944
975
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/progress.json" in rendered
@@ -960,3 +991,299 @@ class TestContinuationHandoff:
960
991
  {"id": "S03", "skill": "later", "status": "pending"},
961
992
  ]}
962
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"
@@ -12,7 +12,6 @@ from generate_bugfix_prompt import (
12
12
  format_environment,
13
13
  process_conditional_blocks,
14
14
  render_template,
15
- load_log_size_section,
16
15
  )
17
16
  from continuation import append_continuation_handoff
18
17
 
@@ -201,6 +200,9 @@ class TestExplicitProjectRoot:
201
200
  )
202
201
  assert "{{" + REMOVED_MAX_LOG_ENV + "}}" not in replacements
203
202
  assert "{{" + REMOVED_MAX_LOG_HUMAN + "}}" not in replacements
203
+ assert "### Checkpoint Update Helper" in replacements["{{CHECKPOINT_SYSTEM}}"]
204
+ assert "python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py" in replacements["{{CHECKPOINT_SYSTEM}}"]
205
+ assert "pipeline progress mechanism" not in replacements["{{CHECKPOINT_SYSTEM}}"]
204
206
 
205
207
 
206
208
  # ---------------------------------------------------------------------------
@@ -226,21 +228,18 @@ class TestSharedHelpers:
226
228
  # ---------------------------------------------------------------------------
227
229
 
228
230
  class TestHeadlessRecoveryReframing:
229
- def test_bugfix_log_size_section_is_passive_checkpoint_guidance(self):
230
- content = load_log_size_section(str(Path("dev-pipeline/scripts").resolve()))
231
-
232
- assert "passive checkpoint guidance" in content
233
- assert "Headless recovery must rely on durable checkpoints" in content
234
- assert "fresh continuation session" in content
235
- assert "optional interactive convenience only" in content
236
- assert REMOVED_MAX_LOG_ENV not in content
237
- assert ("maximum-" + "log-size") not in content
238
- assert ("log size " + "limit") not in content.lower()
239
- assert "COMPACT_NEEDED" not in content
240
- assert "monitor-log" not in content
241
- assert "background monitoring subagent" not in content
242
- assert "Execute `/compact`" not in content
243
- assert "run `/compact`" not in content.lower()
231
+ def test_bugfix_source_has_no_deleted_log_size_injection(self):
232
+ deleted_section = "log-size-" + "awareness.md"
233
+ loader = "load_" + "log_size_section"
234
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
235
+ deleted_title = "Session " + "Checkpoint Awareness"
236
+ source = Path("dev-pipeline/scripts/generate-bugfix-prompt.py").read_text(encoding="utf-8")
237
+ template = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
238
+
239
+ assert (Path("dev-pipeline/templates/sections") / deleted_section).exists() is False
240
+ for content in (source, template):
241
+ for forbidden in (deleted_section, loader, placeholder, deleted_title):
242
+ assert forbidden not in content
244
243
 
245
244
  def test_bugfix_template_does_not_spawn_monitor_or_require_compact(self):
246
245
  content = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
@@ -278,8 +277,28 @@ class TestBugfixContinuationHandoff:
278
277
 
279
278
  assert prompt.startswith("BUGFIX PROMPT")
280
279
  assert "This is an automatic continuation after context overflow." in prompt
281
- assert "Do not attempt `/compact` as the recovery mechanism." in prompt
280
+ assert "Do not attempt `/compact` as the recovery mechanism." not in prompt
282
281
  assert ("Log " + "monitor") not in prompt
283
282
  assert ("log-" + "monitor") not in prompt
284
283
  assert (tmp_path / ".prizmkit/specs/B-007/continuation-summary.md").exists()
285
284
  assert (tmp_path / ".prizmkit/bugfix/B-007/continuation-summary.md").exists()
285
+
286
+
287
+ HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
288
+ "### Interactive Claude Code Note",
289
+ "### Project Conventions",
290
+ "Scaffold / generated file awareness",
291
+ "### Resume Behavior",
292
+ "[NEEDS CLARIFICATION]",
293
+ "pause for interactive input",
294
+ "prizmkit-test-gate.py",
295
+ "/compact",
296
+ "Session " + "Checkpoint Awareness",
297
+ "{{LOG_" + "SIZE_AWARENESS}}",
298
+ ]
299
+
300
+
301
+ def test_template_source_is_clean_for_headless_prompt_contract():
302
+ content = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
303
+ for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
304
+ assert forbidden not in content