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.
- package/bin/create-prizmkit.js +4 -0
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +20 -0
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +1 -0
- package/bundled/dev-pipeline/prizmkit_runtime/config.py +81 -14
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +21 -0
- package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +19 -8
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/launch_profiles.py +18 -0
- package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +208 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +22 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +3 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +20 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_helper.py +508 -0
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +151 -30
- package/bundled/dev-pipeline/scripts/continuation.py +0 -1
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +8 -46
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -34
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +42 -33
- package/bundled/dev-pipeline/scripts/prizmkit-runtime-helper.py +27 -0
- package/bundled/dev-pipeline/scripts/utils.py +94 -0
- package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +4 -3
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +39 -65
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +58 -96
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +54 -95
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -18
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +5 -17
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -48
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +7 -13
- package/bundled/dev-pipeline/templates/sections/feature-context.md +0 -4
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification-auto.md +31 -61
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification-opencli.md +21 -37
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +22 -69
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +0 -14
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -14
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -7
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +1 -3
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -7
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +7 -9
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +4 -8
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +0 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -7
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +3 -10
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +2 -9
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +15 -16
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +1 -8
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +1 -8
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +9 -15
- package/bundled/dev-pipeline/templates/sections/phase0-init.md +1 -8
- package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +2 -15
- package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +1 -1
- package/bundled/dev-pipeline/tests/conftest.py +0 -1
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +392 -23
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +37 -17
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +118 -14
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +79 -2
- package/bundled/dev-pipeline/tests/test_runtime_helper.py +211 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +663 -13
- package/bundled/skills/_metadata.json +4 -4
- package/bundled/skills/app-planner/SKILL.md +1 -1
- package/bundled/skills/app-planner/references/architecture-decisions.md +1 -1
- package/bundled/skills/bug-fix-workflow/SKILL.md +174 -128
- package/bundled/skills/bug-planner/SKILL.md +101 -15
- package/bundled/skills/bug-planner/references/severity-rules.md +20 -8
- package/bundled/skills/feature-planner/SKILL.md +87 -8
- package/bundled/skills/feature-planner/assets/planning-guide.md +34 -10
- package/bundled/skills/feature-workflow/SKILL.md +100 -81
- package/bundled/skills/{prizm-kit → prizmkit}/SKILL.md +1 -1
- package/bundled/skills/prizmkit-code-review/SKILL.md +1 -1
- package/bundled/skills/refactor-planner/SKILL.md +88 -7
- package/bundled/skills/refactor-planner/references/fast-path.md +2 -1
- package/bundled/skills/refactor-planner/references/planning-phases.md +29 -11
- package/bundled/skills/refactor-workflow/SKILL.md +116 -57
- package/bundled/templates/project-memory-template.md +1 -1
- package/package.json +1 -1
- package/src/ai-cli-launch.js +194 -0
- package/src/config.js +26 -14
- package/src/index.js +11 -44
- package/src/platforms.js +10 -3
- package/src/prompts.js +60 -2
- package/src/scaffold.js +26 -14
- package/bundled/dev-pipeline/scripts/prizmkit-test-gate.py +0 -446
- package/bundled/dev-pipeline/templates/sections/log-size-awareness.md +0 -20
- 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-
|
|
448
|
-
assert "
|
|
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
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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
|
|
826
|
-
assert
|
|
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
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
assert ("
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|