prizmkit 1.1.107 → 1.1.109

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 (86) hide show
  1. package/bin/create-prizmkit.js +4 -0
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/dev-pipeline/prizmkit_runtime/cli.py +20 -0
  4. package/bundled/dev-pipeline/prizmkit_runtime/commands.py +1 -0
  5. package/bundled/dev-pipeline/prizmkit_runtime/config.py +81 -14
  6. package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +21 -0
  7. package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +19 -8
  8. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -1
  9. package/bundled/dev-pipeline/prizmkit_runtime/launch_profiles.py +18 -0
  10. package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +208 -0
  11. package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +22 -2
  12. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +3 -1
  13. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +20 -1
  14. package/bundled/dev-pipeline/prizmkit_runtime/runtime_helper.py +508 -0
  15. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +151 -30
  16. package/bundled/dev-pipeline/scripts/continuation.py +0 -1
  17. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +8 -46
  18. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -34
  19. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +42 -33
  20. package/bundled/dev-pipeline/scripts/prizmkit-runtime-helper.py +27 -0
  21. package/bundled/dev-pipeline/scripts/utils.py +94 -0
  22. package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +4 -3
  23. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  24. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +39 -65
  25. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +58 -96
  26. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +54 -95
  27. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -18
  28. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +5 -17
  29. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -48
  30. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +7 -13
  31. package/bundled/dev-pipeline/templates/sections/feature-context.md +0 -4
  32. package/bundled/dev-pipeline/templates/sections/phase-browser-verification-auto.md +31 -61
  33. package/bundled/dev-pipeline/templates/sections/phase-browser-verification-opencli.md +21 -37
  34. package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +22 -69
  35. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +0 -14
  36. package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -14
  37. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -7
  38. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +1 -3
  39. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -7
  40. package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +7 -9
  41. package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +4 -8
  42. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -7
  43. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +0 -7
  44. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -7
  45. package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +3 -10
  46. package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +2 -9
  47. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +15 -16
  48. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +1 -8
  49. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +1 -8
  50. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +9 -15
  51. package/bundled/dev-pipeline/templates/sections/phase0-init.md +1 -8
  52. package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +2 -15
  53. package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +1 -1
  54. package/bundled/dev-pipeline/tests/conftest.py +0 -1
  55. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +392 -23
  56. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +37 -17
  57. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +118 -14
  58. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +79 -2
  59. package/bundled/dev-pipeline/tests/test_runtime_helper.py +211 -0
  60. package/bundled/dev-pipeline/tests/test_unified_cli.py +663 -13
  61. package/bundled/skills/_metadata.json +4 -4
  62. package/bundled/skills/app-planner/SKILL.md +1 -1
  63. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
  64. package/bundled/skills/bug-fix-workflow/SKILL.md +174 -128
  65. package/bundled/skills/bug-planner/SKILL.md +101 -15
  66. package/bundled/skills/bug-planner/references/severity-rules.md +20 -8
  67. package/bundled/skills/feature-planner/SKILL.md +87 -8
  68. package/bundled/skills/feature-planner/assets/planning-guide.md +34 -10
  69. package/bundled/skills/feature-workflow/SKILL.md +100 -81
  70. package/bundled/skills/{prizm-kit → prizmkit}/SKILL.md +1 -1
  71. package/bundled/skills/prizmkit-code-review/SKILL.md +1 -1
  72. package/bundled/skills/refactor-planner/SKILL.md +88 -7
  73. package/bundled/skills/refactor-planner/references/fast-path.md +2 -1
  74. package/bundled/skills/refactor-planner/references/planning-phases.md +29 -11
  75. package/bundled/skills/refactor-workflow/SKILL.md +116 -57
  76. package/bundled/templates/project-memory-template.md +1 -1
  77. package/package.json +1 -1
  78. package/src/ai-cli-launch.js +194 -0
  79. package/src/config.js +26 -14
  80. package/src/index.js +11 -44
  81. package/src/platforms.js +10 -3
  82. package/src/prompts.js +60 -2
  83. package/src/scaffold.js +26 -14
  84. package/bundled/dev-pipeline/scripts/prizmkit-test-gate.py +0 -446
  85. package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +0 -20
  86. package/bundled/dev-pipeline/tests/test_prizmkit_test_gate.py +0 -107
@@ -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,15 @@ 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 "{{RUNTIME_HELPER_CMD}} artifact touch .prizmkit/specs/{{FEATURE_SLUG}}/.prizmkit-test-started" in rendered
448
+ assert "mkdir -p .prizmkit/specs/{{FEATURE_SLUG}}" not in rendered
449
+ assert "when present" not in rendered
450
+ assert "Boundary Matrix" in rendered
451
+ assert "Boundary Completion Gate" in rendered
452
+ assert "Boundary-missing: 0" in rendered
453
+ assert "Happy-path-only: 0" in rendered
454
+ assert "Validator result: passed" in rendered
449
455
  assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/" in rendered
450
456
  assert ".prizmkit/bugfix/" in rendered # explicitly forbidden in the gate wording
451
457
  assert ".prizmkit/refactor/" in rendered # explicitly forbidden in the gate wording
@@ -456,6 +462,26 @@ class TestScopedFeatureTestGate:
456
462
  assert "before code review" not in rendered
457
463
  assert "Precondition: the `prizmkit-test` checkpoint is completed" not in rendered
458
464
 
465
+ def test_standard_sections_include_central_checkpoint_helper(self):
466
+ sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
467
+ sections = assemble_sections(
468
+ "standard",
469
+ str(sections_dir),
470
+ init_done=True,
471
+ is_resume=False,
472
+ critic_enabled=False,
473
+ browser_enabled=False,
474
+ )
475
+ rendered = "\n".join(content for _, content in sections)
476
+
477
+ assert "### Checkpoint Update Helper" in rendered
478
+ assert "{{CHECKPOINT_PYTHON_CMD}}" in rendered
479
+ assert "python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py" not in rendered
480
+ assert "--checkpoint-path {{CHECKPOINT_PATH}}" in rendered
481
+ assert "--step <step-id-or-skill-name>" in rendered
482
+ assert "Do not hand-edit `workflow-checkpoint.json` directly." in rendered
483
+ assert "pipeline progress mechanism" not in rendered
484
+
459
485
  def test_lite_sections_include_review_before_prizmkit_test_and_commit(self):
460
486
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
461
487
  sections = assemble_sections(
@@ -795,25 +821,32 @@ class TestScopedFeatureTestGate:
795
821
  # ---------------------------------------------------------------------------
796
822
 
797
823
  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()
824
+ def test_active_prompt_sources_do_not_reference_deleted_log_size_section(self):
825
+ deleted_section = "log-size-" + "awareness.md"
826
+ loader = "load_" + "log_size_section"
827
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
828
+ deleted_title = "Session " + "Checkpoint Awareness"
829
+ active_paths = [
830
+ Path("dev-pipeline/scripts/generate-bootstrap-prompt.py"),
831
+ Path("dev-pipeline/scripts/generate-bugfix-prompt.py"),
832
+ Path("dev-pipeline/scripts/generate-refactor-prompt.py"),
833
+ Path("dev-pipeline/templates/bootstrap-tier1.md"),
834
+ Path("dev-pipeline/templates/bootstrap-tier2.md"),
835
+ Path("dev-pipeline/templates/bootstrap-tier3.md"),
836
+ Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md"),
837
+ Path("dev-pipeline/templates/refactor-bootstrap-prompt.md"),
838
+ ]
839
+
840
+ assert (Path("dev-pipeline/templates/sections") / deleted_section).exists() is False
841
+ for path in active_paths:
842
+ content = path.read_text(encoding="utf-8")
843
+ for forbidden in (deleted_section, loader, placeholder, deleted_title):
844
+ assert forbidden not in content
814
845
 
815
846
  def test_assembled_prompts_do_not_spawn_log_monitor_or_require_compact(self):
816
847
  sections_dir = Path("dev-pipeline/templates/sections").resolve()
848
+ deleted_title = "Session " + "Checkpoint Awareness"
849
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
817
850
 
818
851
  for mode in ("lite", "standard", "full"):
819
852
  rendered = "\n".join(
@@ -822,8 +855,8 @@ class TestHeadlessRecoveryReframing:
822
855
  critic_enabled=False, browser_enabled=False,
823
856
  )
824
857
  )
825
- assert "Headless recovery must rely on durable checkpoints" in rendered
826
- assert "fresh continuation session" in rendered
858
+ assert deleted_title not in rendered
859
+ assert placeholder not in rendered
827
860
  assert REMOVED_MAX_LOG_ENV not in rendered
828
861
  assert REMOVED_MAX_LOG_HUMAN not in rendered
829
862
  assert ("209" + "7152") not in rendered
@@ -938,7 +971,7 @@ class TestContinuationHandoff:
938
971
  assert "Treat completed/skipped checkpoint steps as already done." in rendered
939
972
  assert "Do not read the previous full `session.log` unless explicitly necessary." in rendered
940
973
  assert "prefer synthesized artifacts and summaries over logs" in rendered
941
- assert "Do not attempt `/compact` as the recovery mechanism." in rendered
974
+ assert "Do not attempt `/compact` as the recovery mechanism." not in rendered
942
975
  assert ".prizmkit/specs/123-continuation/workflow-checkpoint.json" in rendered
943
976
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/session.log" in rendered
944
977
  assert ".prizmkit/state/features/F-123/sessions/F-123-new/logs/progress.json" in rendered
@@ -960,3 +993,339 @@ class TestContinuationHandoff:
960
993
  {"id": "S03", "skill": "later", "status": "pending"},
961
994
  ]}
962
995
  assert checkpoint_cursor(checkpoint)["first_cursor"] == "S02 (active)"
996
+
997
+
998
+ # ---------------------------------------------------------------------------
999
+ # F-033 headless prompt cleanup and platform-aware critic paths
1000
+ # ---------------------------------------------------------------------------
1001
+
1002
+ HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
1003
+ "### Interactive Claude Code Note",
1004
+ "### Project Conventions",
1005
+ "Read CLAUDE.md for project-level coding standards and architecture decisions.",
1006
+ "Scaffold / generated file awareness",
1007
+ "### Resume Behavior",
1008
+ "[NEEDS CLARIFICATION]",
1009
+ "pause for interactive input",
1010
+ "prizmkit-test-gate.py",
1011
+ "### Phase 0: Detect Test Commands",
1012
+ "TEST_CMDS",
1013
+ ]
1014
+
1015
+
1016
+ def _write_feature_list(path, feature):
1017
+ path.parent.mkdir(parents=True, exist_ok=True)
1018
+ path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
1019
+
1020
+
1021
+ def _render_feature_prompt(tmp_path, mode="standard", critic=False, template=None, platform="claude", browser_interaction=None):
1022
+ project = tmp_path / "project"
1023
+ project.mkdir(parents=True)
1024
+ (project / ".prizmkit" / "plans").mkdir(parents=True)
1025
+ (project / ".prizmkit" / "config.json").write_text(
1026
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
1027
+ )
1028
+ for platform_dir, suffix in ((".claude", ".md"), (".codex", ".toml")):
1029
+ agents_dir = project / platform_dir / "agents"
1030
+ agents_dir.mkdir(parents=True)
1031
+ for name in ("dev", "reviewer", "critic"):
1032
+ (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
1033
+ feature = {
1034
+ "id": "F-123",
1035
+ "title": "Prompt Cleanup",
1036
+ "description": "Clean prompt text",
1037
+ "estimated_complexity": {"lite": "low", "standard": "medium", "full": "high"}[mode],
1038
+ "acceptance_criteria": ["Prompts are clean"],
1039
+ "critic": critic,
1040
+ }
1041
+ if browser_interaction is not None:
1042
+ feature["browser_interaction"] = browser_interaction
1043
+ feature_list = project / ".prizmkit" / "plans" / "feature-list.json"
1044
+ _write_feature_list(feature_list, feature)
1045
+ output = project / "prompt.md"
1046
+ args = [
1047
+ "generate-bootstrap-prompt.py",
1048
+ "--feature-list", str(feature_list),
1049
+ "--feature-id", "F-123",
1050
+ "--session-id", "session-1",
1051
+ "--run-id", "run-1",
1052
+ "--retry-count", "0",
1053
+ "--resume-phase", "null",
1054
+ "--state-dir", str(project / ".prizmkit" / "state" / "features"),
1055
+ "--project-root", str(project),
1056
+ "--output", str(output),
1057
+ "--mode", mode,
1058
+ ]
1059
+ if critic:
1060
+ args.extend(["--critic", "true"])
1061
+ if template:
1062
+ args.extend(["--template", str(Path("dev-pipeline/templates") / template)])
1063
+ old_argv = os.sys.argv
1064
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
1065
+ try:
1066
+ os.sys.argv = args
1067
+ if old_platform is not None:
1068
+ os.environ.pop("PRIZMKIT_PLATFORM")
1069
+ try:
1070
+ generate_bootstrap_main()
1071
+ except SystemExit as exc:
1072
+ if exc.code not in (0, None):
1073
+ raise
1074
+ finally:
1075
+ os.sys.argv = old_argv
1076
+ if old_platform is not None:
1077
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
1078
+ return output.read_text(encoding="utf-8")
1079
+
1080
+
1081
+ def _ensure_agent_files(project, platforms=("claude", "codex")):
1082
+ for platform in platforms:
1083
+ platform_dir = ".codex" if platform == "codex" else ".claude"
1084
+ suffix = ".toml" if platform == "codex" else ".md"
1085
+ agents_dir = project / platform_dir / "agents"
1086
+ agents_dir.mkdir(parents=True, exist_ok=True)
1087
+ for name in ("dev", "reviewer", "critic"):
1088
+ (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
1089
+
1090
+
1091
+ def _render_bugfix_prompt(tmp_path, platform="claude", explicit_platform=None):
1092
+ from generate_bugfix_prompt import main as bugfix_main
1093
+
1094
+ project = tmp_path / "bugfix-project"
1095
+ project.mkdir(parents=True)
1096
+ (project / ".prizmkit").mkdir(exist_ok=True)
1097
+ (project / ".prizmkit" / "config.json").write_text(
1098
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
1099
+ )
1100
+ _ensure_agent_files(project)
1101
+ bug_list = project / ".prizmkit" / "plans" / "bug-fix-list.json"
1102
+ bug_list.parent.mkdir(parents=True)
1103
+ bug_list.write_text(json.dumps({
1104
+ "bugs": [{"id": "B-001", "title": "Broken", "description": "Fix it", "acceptance_criteria": ["Works"]}],
1105
+ "global_context": {"language": "Python"},
1106
+ }), encoding="utf-8")
1107
+ output = project / "bugfix-prompt.md"
1108
+ old_argv = os.sys.argv
1109
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
1110
+ try:
1111
+ if explicit_platform is None:
1112
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1113
+ else:
1114
+ os.environ["PRIZMKIT_PLATFORM"] = explicit_platform
1115
+ os.sys.argv = [
1116
+ "generate-bugfix-prompt.py",
1117
+ "--bug-list", str(bug_list),
1118
+ "--bug-id", "B-001",
1119
+ "--session-id", "session-1",
1120
+ "--run-id", "run-1",
1121
+ "--retry-count", "0",
1122
+ "--resume-phase", "null",
1123
+ "--state-dir", str(project / ".prizmkit" / "state" / "bugfix"),
1124
+ "--project-root", str(project),
1125
+ "--output", str(output),
1126
+ ]
1127
+ try:
1128
+ bugfix_main()
1129
+ except SystemExit as exc:
1130
+ if exc.code not in (0, None):
1131
+ raise
1132
+ finally:
1133
+ os.sys.argv = old_argv
1134
+ if old_platform is None:
1135
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1136
+ else:
1137
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
1138
+ return output.read_text(encoding="utf-8")
1139
+
1140
+
1141
+ def _render_refactor_prompt(tmp_path, platform="claude", explicit_platform=None):
1142
+ from generate_refactor_prompt import main as refactor_main
1143
+
1144
+ project = tmp_path / "refactor-project"
1145
+ project.mkdir(parents=True)
1146
+ (project / ".prizmkit").mkdir(exist_ok=True)
1147
+ (project / ".prizmkit" / "config.json").write_text(
1148
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
1149
+ )
1150
+ _ensure_agent_files(project)
1151
+ refactor_list = project / ".prizmkit" / "plans" / "refactor-list.json"
1152
+ refactor_list.parent.mkdir(parents=True)
1153
+ refactor_list.write_text(json.dumps({
1154
+ "refactors": [{
1155
+ "id": "R-001",
1156
+ "title": "Restructure",
1157
+ "description": "Preserve behavior",
1158
+ "acceptance_criteria": ["Behavior preserved"],
1159
+ "scope": {"files": ["src/a.py"], "modules": ["src"]},
1160
+ }],
1161
+ "global_context": {"language": "Python"},
1162
+ }), encoding="utf-8")
1163
+ output = project / "refactor-prompt.md"
1164
+ old_argv = os.sys.argv
1165
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
1166
+ try:
1167
+ if explicit_platform is None:
1168
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1169
+ else:
1170
+ os.environ["PRIZMKIT_PLATFORM"] = explicit_platform
1171
+ os.sys.argv = [
1172
+ "generate-refactor-prompt.py",
1173
+ "--refactor-list", str(refactor_list),
1174
+ "--refactor-id", "R-001",
1175
+ "--session-id", "session-1",
1176
+ "--run-id", "run-1",
1177
+ "--retry-count", "0",
1178
+ "--resume-phase", "null",
1179
+ "--state-dir", str(project / ".prizmkit" / "state" / "refactor"),
1180
+ "--output", str(output),
1181
+ ]
1182
+ old_cwd = os.getcwd()
1183
+ os.chdir(project)
1184
+ try:
1185
+ try:
1186
+ refactor_main()
1187
+ except SystemExit as exc:
1188
+ if exc.code not in (0, None):
1189
+ raise
1190
+ finally:
1191
+ os.chdir(old_cwd)
1192
+ finally:
1193
+ os.sys.argv = old_argv
1194
+ if old_platform is None:
1195
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
1196
+ else:
1197
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
1198
+ return output.read_text(encoding="utf-8")
1199
+
1200
+
1201
+ class TestHeadlessPromptCleanupF033:
1202
+ def test_rendered_feature_bugfix_refactor_prompts_are_clean(self, tmp_path):
1203
+ prompts = []
1204
+ for mode in ("lite", "standard", "full"):
1205
+ prompts.append(_render_feature_prompt(tmp_path / mode, mode=mode, critic=(mode != "lite")))
1206
+ prompts.append(_render_bugfix_prompt(tmp_path))
1207
+ prompts.append(_render_refactor_prompt(tmp_path))
1208
+
1209
+ for prompt in prompts:
1210
+ for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
1211
+ assert forbidden not in prompt
1212
+ if "Feature ID" in prompt:
1213
+ assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/123-prompt-cleanup/" in prompt
1214
+ assert ".prizmkit-test-started" in prompt
1215
+ assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1216
+ assert "prizmkit-runtime-helper.py" in prompt
1217
+ assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
1218
+
1219
+ def test_legacy_feature_templates_are_clean_when_rendered(self, tmp_path):
1220
+ for template, mode in (("bootstrap-tier1.md", "lite"), ("bootstrap-tier2.md", "standard"), ("bootstrap-tier3.md", "full")):
1221
+ prompt = _render_feature_prompt(tmp_path / template, mode=mode, critic=(mode != "lite"), template=template)
1222
+ for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
1223
+ assert forbidden not in prompt
1224
+ assert "prizmkit-test-gate.py" not in prompt
1225
+ assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
1226
+ assert "which playwright-cli" not in prompt
1227
+ assert "lsof -ti" not in prompt
1228
+ assert "sed -nE" not in prompt
1229
+ assert ".prizmkit-test-started" in prompt
1230
+ assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1231
+ assert "prizmkit-runtime-helper.py" in prompt
1232
+
1233
+ def test_legacy_feature_templates_with_browser_blocks_are_shell_free(self, tmp_path):
1234
+ forbidden = [
1235
+ "python3 {{INIT_SCRIPT_PATH}}",
1236
+ "which playwright-cli",
1237
+ "which opencli",
1238
+ "lsof -ti",
1239
+ "sed -nE",
1240
+ "SKILL_DIR",
1241
+ "DEV_SERVER_PID",
1242
+ "opencli doctor 2>/dev/null ||",
1243
+ "echo \"=== playwright-cli ===\" &&",
1244
+ ]
1245
+ cases = [
1246
+ ("bootstrap-tier1.md", "lite"),
1247
+ ("bootstrap-tier2.md", "standard"),
1248
+ ("bootstrap-tier3.md", "full"),
1249
+ ]
1250
+ for template, mode in cases:
1251
+ prompt = _render_feature_prompt(
1252
+ tmp_path / f"browser-{template}",
1253
+ mode=mode,
1254
+ critic=(mode != "lite"),
1255
+ template=template,
1256
+ browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
1257
+ )
1258
+ assert "prizmkit-runtime-helper.py" in prompt
1259
+ for snippet in forbidden:
1260
+ assert snippet not in prompt
1261
+
1262
+ def test_claude_config_wins_over_codex_directory_for_critic_path(self, tmp_path, monkeypatch):
1263
+ project = tmp_path / "mixed"
1264
+ (project / ".claude" / "agents").mkdir(parents=True)
1265
+ (project / ".codex" / "agents").mkdir(parents=True)
1266
+ (project / ".prizmkit").mkdir()
1267
+ (project / ".prizmkit" / "config.json").write_text(
1268
+ json.dumps({"platform": "claude", "ai_cli": "claude"}), encoding="utf-8"
1269
+ )
1270
+ monkeypatch.delenv("PRIZMKIT_PLATFORM", raising=False)
1271
+
1272
+ assert resolve_prompt_platform(str(project)) == "claude"
1273
+ prompt = _render_feature_prompt(tmp_path / "rendered-mixed", mode="standard", critic=True, platform="claude")
1274
+ assert ".claude/agents/prizm-dev-team-critic.md" in prompt
1275
+ assert ".codex/agents/prizm-dev-team-critic.toml" not in prompt
1276
+
1277
+ def test_bugfix_refactor_share_claude_platform_resolution(self, tmp_path, monkeypatch):
1278
+ from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
1279
+
1280
+ monkeypatch.delenv("PRIZMKIT_PLATFORM", raising=False)
1281
+ bug_project = tmp_path / "bugfix-replacements"
1282
+ bug_project.mkdir()
1283
+ (bug_project / ".prizmkit").mkdir()
1284
+ (bug_project / ".prizmkit" / "config.json").write_text(
1285
+ json.dumps({"platform": "claude", "ai_cli": "claude"}), encoding="utf-8"
1286
+ )
1287
+ _ensure_agent_files(bug_project)
1288
+ args = Namespace(
1289
+ bug_id="B-001", session_id="session-1", run_id="run-1",
1290
+ retry_count="0", resume_phase="null", state_dir=None,
1291
+ output="out.md", project_root=str(bug_project), template=None,
1292
+ mode=None, critic=None,
1293
+ )
1294
+ replacements = bugfix_build_replacements(
1295
+ args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
1296
+ )
1297
+ refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-mixed", platform="claude")
1298
+
1299
+ assert replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".claude/agents/prizm-dev-team-reviewer.md")
1300
+ assert ".claude/agents/prizm-dev-team-reviewer.md" in refactor_prompt
1301
+ assert ".codex/agents/prizm-dev-team-reviewer.toml" not in refactor_prompt
1302
+
1303
+ def test_bugfix_refactor_explicit_codex_override(self, tmp_path, monkeypatch):
1304
+ from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
1305
+
1306
+ bug_project = tmp_path / "bugfix-codex-replacements"
1307
+ bug_project.mkdir()
1308
+ _ensure_agent_files(bug_project)
1309
+ monkeypatch.setenv("PRIZMKIT_PLATFORM", "codex")
1310
+ args = Namespace(
1311
+ bug_id="B-001", session_id="session-1", run_id="run-1",
1312
+ retry_count="0", resume_phase="null", state_dir=None,
1313
+ output="out.md", project_root=str(bug_project), template=None,
1314
+ mode=None, critic=None,
1315
+ )
1316
+ replacements = bugfix_build_replacements(
1317
+ args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
1318
+ )
1319
+ refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-codex", platform="claude", explicit_platform="codex")
1320
+
1321
+ assert replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".codex/agents/prizm-dev-team-reviewer.toml")
1322
+ assert ".codex/agents/prizm-dev-team-reviewer.toml" in refactor_prompt
1323
+ assert ".claude/agents/prizm-dev-team-reviewer.md" not in refactor_prompt
1324
+
1325
+ def test_explicit_codex_env_still_renders_codex_critic_path(self, tmp_path, monkeypatch):
1326
+ project = tmp_path / "codex"
1327
+ (project / ".claude" / "agents").mkdir(parents=True)
1328
+ (project / ".codex" / "agents").mkdir(parents=True)
1329
+ monkeypatch.setenv("PRIZMKIT_PLATFORM", "codex")
1330
+
1331
+ 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,10 @@ 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 "{{CHECKPOINT_PYTHON_CMD}}" in replacements["{{CHECKPOINT_SYSTEM}}"]
205
+ assert "pipeline progress mechanism" not in replacements["{{CHECKPOINT_SYSTEM}}"]
206
+ assert "prizmkit-runtime-helper.py" in replacements["{{RUNTIME_HELPER_CMD}}"]
204
207
 
205
208
 
206
209
  # ---------------------------------------------------------------------------
@@ -226,21 +229,18 @@ class TestSharedHelpers:
226
229
  # ---------------------------------------------------------------------------
227
230
 
228
231
  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()
232
+ def test_bugfix_source_has_no_deleted_log_size_injection(self):
233
+ deleted_section = "log-size-" + "awareness.md"
234
+ loader = "load_" + "log_size_section"
235
+ placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
236
+ deleted_title = "Session " + "Checkpoint Awareness"
237
+ source = Path("dev-pipeline/scripts/generate-bugfix-prompt.py").read_text(encoding="utf-8")
238
+ template = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
239
+
240
+ assert (Path("dev-pipeline/templates/sections") / deleted_section).exists() is False
241
+ for content in (source, template):
242
+ for forbidden in (deleted_section, loader, placeholder, deleted_title):
243
+ assert forbidden not in content
244
244
 
245
245
  def test_bugfix_template_does_not_spawn_monitor_or_require_compact(self):
246
246
  content = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
@@ -278,8 +278,28 @@ class TestBugfixContinuationHandoff:
278
278
 
279
279
  assert prompt.startswith("BUGFIX PROMPT")
280
280
  assert "This is an automatic continuation after context overflow." in prompt
281
- assert "Do not attempt `/compact` as the recovery mechanism." in prompt
281
+ assert "Do not attempt `/compact` as the recovery mechanism." not in prompt
282
282
  assert ("Log " + "monitor") not in prompt
283
283
  assert ("log-" + "monitor") not in prompt
284
284
  assert (tmp_path / ".prizmkit/specs/B-007/continuation-summary.md").exists()
285
285
  assert (tmp_path / ".prizmkit/bugfix/B-007/continuation-summary.md").exists()
286
+
287
+
288
+ HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
289
+ "### Interactive Claude Code Note",
290
+ "### Project Conventions",
291
+ "Scaffold / generated file awareness",
292
+ "### Resume Behavior",
293
+ "[NEEDS CLARIFICATION]",
294
+ "pause for interactive input",
295
+ "prizmkit-test-gate.py",
296
+ "/compact",
297
+ "Session " + "Checkpoint Awareness",
298
+ "{{LOG_" + "SIZE_AWARENESS}}",
299
+ ]
300
+
301
+
302
+ def test_template_source_is_clean_for_headless_prompt_contract():
303
+ content = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
304
+ for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
305
+ assert forbidden not in content