prizmkit 1.1.119 → 1.1.121
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 +1 -5
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/README.md +41 -38
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +76 -0
- package/bundled/dev-pipeline/prizmkit_runtime/config.py +2 -8
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +23 -25
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +8 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +10 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +88 -32
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +28 -6
- package/bundled/dev-pipeline/scripts/continuation.py +51 -6
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +60 -102
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +1 -19
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +1 -19
- package/bundled/dev-pipeline/scripts/{init-dev-team.py → init-change-artifact.py} +6 -16
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +364 -76
- package/bundled/dev-pipeline/scripts/update-bug-status.py +57 -14
- package/bundled/dev-pipeline/scripts/update-feature-status.py +54 -5
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +57 -14
- package/bundled/dev-pipeline/scripts/utils.py +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +9 -11
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +2 -1
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -2
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +6 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +6 -7
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +4 -2
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +5 -3
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +20 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
- package/bundled/dev-pipeline/tests/test_auto_skip.py +5 -5
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +214 -144
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +25 -12
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +11 -14
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +235 -32
- package/bundled/dev-pipeline/tests/test_unified_cli.py +219 -16
- package/bundled/skills/_metadata.json +4 -4
- package/bundled/skills/app-planner/SKILL.md +2 -2
- package/bundled/skills/app-planner/references/architecture-decisions.md +1 -3
- package/bundled/skills/prizmkit-code-review/SKILL.md +50 -47
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +13 -8
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +47 -33
- package/bundled/skills/prizmkit-code-review/references/reviewer-workspace-protocol.md +90 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +21 -1
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +67 -0
- package/bundled/skills/prizmkit-test/SKILL.md +159 -155
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +420 -0
- package/bundled/skills/prizmkit-test/assets/behavior-risk-matrix.schema.json +116 -0
- package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +112 -0
- package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +97 -0
- package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +76 -192
- package/bundled/skills/prizmkit-test/references/contract-mock-protocol.md +65 -0
- package/bundled/skills/prizmkit-test/references/evidence-protocol.md +194 -0
- package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +78 -0
- package/bundled/skills/prizmkit-test/references/examples.md +65 -108
- package/bundled/skills/prizmkit-test/references/service-boundary-test-catalog.md +10 -7
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +90 -82
- package/bundled/skills/prizmkit-test/references/test-report-template.md +77 -98
- package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +97 -0
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +723 -0
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +1426 -0
- package/package.json +1 -1
- package/src/clean.js +12 -8
- package/src/config.js +24 -42
- package/src/index.js +1 -10
- package/src/manifest.js +3 -9
- package/src/metadata.js +0 -26
- package/src/prompts.js +0 -13
- package/src/scaffold.js +76 -201
- package/src/upgrade.js +16 -33
- package/bundled/adapters/claude/agent-adapter.js +0 -96
- package/bundled/adapters/claude/team-adapter.js +0 -183
- package/bundled/adapters/codebuddy/agent-adapter.js +0 -42
- package/bundled/adapters/codebuddy/team-adapter.js +0 -46
- package/bundled/adapters/codex/agent-adapter.js +0 -38
- package/bundled/adapters/codex/team-adapter.js +0 -37
- package/bundled/agents/prizm-dev-team-dev.md +0 -123
- package/bundled/agents/prizm-dev-team-reviewer.md +0 -113
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +0 -65
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +0 -436
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +0 -510
- package/bundled/skills/prizmkit-test/scripts/validate_boundary_report.py +0 -347
- package/bundled/team/prizm-dev-team.json +0 -27
|
@@ -17,6 +17,7 @@ from generate_bootstrap_prompt import (
|
|
|
17
17
|
format_global_context,
|
|
18
18
|
get_completed_dependencies,
|
|
19
19
|
determine_pipeline_mode,
|
|
20
|
+
determine_legacy_template_mode,
|
|
20
21
|
process_conditional_blocks,
|
|
21
22
|
process_mode_blocks,
|
|
22
23
|
assemble_sections,
|
|
@@ -235,7 +236,7 @@ class TestExplicitProjectRoot:
|
|
|
235
236
|
)
|
|
236
237
|
assert not replacements["{{SESSION_STATUS_PATH}}"].startswith(str(execution_root.resolve()))
|
|
237
238
|
assert replacements["{{PROJECT_BRIEF}}"] == "worktree brief"
|
|
238
|
-
assert
|
|
239
|
+
assert "{{" + "DEV_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
239
240
|
assert replacements["{{SESSION_LOG_PATH}}"].endswith(
|
|
240
241
|
".prizmkit/state/features/F-001/sessions/session-1/logs/session.log"
|
|
241
242
|
)
|
|
@@ -254,9 +255,6 @@ class TestExplicitProjectRoot:
|
|
|
254
255
|
feature_list.parent.mkdir(parents=True)
|
|
255
256
|
state_dir.mkdir(parents=True)
|
|
256
257
|
execution_root.mkdir(parents=True)
|
|
257
|
-
(execution_root / ".claude" / "agents").mkdir(parents=True)
|
|
258
|
-
(execution_root / ".claude" / "agents" / "prizm-dev-team-dev.md").write_text("dev", encoding="utf-8")
|
|
259
|
-
(execution_root / ".claude" / "agents" / "prizm-dev-team-reviewer.md").write_text("reviewer", encoding="utf-8")
|
|
260
258
|
feature_list.write_text(
|
|
261
259
|
json.dumps({"features": [{"id": "F-001", "title": "Worktree Prompt", "estimated_complexity": "medium", "status": "pending"}]}),
|
|
262
260
|
encoding="utf-8",
|
|
@@ -285,7 +283,7 @@ class TestExplicitProjectRoot:
|
|
|
285
283
|
"--output",
|
|
286
284
|
str(output_path),
|
|
287
285
|
"--template",
|
|
288
|
-
str(pipeline_root / "templates" / "bootstrap-
|
|
286
|
+
str(pipeline_root / "templates" / "bootstrap-tier3.md"),
|
|
289
287
|
],
|
|
290
288
|
)
|
|
291
289
|
|
|
@@ -332,20 +330,16 @@ class TestGetCompletedDependencies:
|
|
|
332
330
|
# ---------------------------------------------------------------------------
|
|
333
331
|
|
|
334
332
|
class TestDeterminePipelineMode:
|
|
335
|
-
def
|
|
336
|
-
|
|
333
|
+
def test_all_complexities_use_unified_full_guidance(self):
|
|
334
|
+
for complexity in ("low", "medium", "high", "critical", "banana", None):
|
|
335
|
+
assert determine_pipeline_mode(complexity) == "full"
|
|
337
336
|
|
|
338
|
-
def
|
|
339
|
-
assert
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
assert
|
|
343
|
-
|
|
344
|
-
def test_critical(self):
|
|
345
|
-
assert determine_pipeline_mode("critical") == "full"
|
|
346
|
-
|
|
347
|
-
def test_unknown(self):
|
|
348
|
-
assert determine_pipeline_mode("banana") == "lite"
|
|
337
|
+
def test_legacy_template_mode_mapping_remains_available_for_custom_templates(self):
|
|
338
|
+
assert determine_legacy_template_mode("low") == "lite"
|
|
339
|
+
assert determine_legacy_template_mode("medium") == "lite"
|
|
340
|
+
assert determine_legacy_template_mode("high") == "standard"
|
|
341
|
+
assert determine_legacy_template_mode("critical") == "full"
|
|
342
|
+
assert determine_legacy_template_mode("banana") == "lite"
|
|
349
343
|
|
|
350
344
|
|
|
351
345
|
# ---------------------------------------------------------------------------
|
|
@@ -807,10 +801,6 @@ class TestHeadlessRecoveryReframing:
|
|
|
807
801
|
deleted_title = "Session " + "Checkpoint Awareness"
|
|
808
802
|
active_paths = [
|
|
809
803
|
Path("dev-pipeline/scripts/generate-bootstrap-prompt.py"),
|
|
810
|
-
Path("dev-pipeline/scripts/generate-bugfix-prompt.py"),
|
|
811
|
-
Path("dev-pipeline/scripts/generate-refactor-prompt.py"),
|
|
812
|
-
Path("dev-pipeline/templates/bootstrap-tier1.md"),
|
|
813
|
-
Path("dev-pipeline/templates/bootstrap-tier2.md"),
|
|
814
804
|
Path("dev-pipeline/templates/bootstrap-tier3.md"),
|
|
815
805
|
Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md"),
|
|
816
806
|
Path("dev-pipeline/templates/refactor-bootstrap-prompt.md"),
|
|
@@ -894,33 +884,32 @@ class TestDirectOrchestratorImplementationPrompts:
|
|
|
894
884
|
assert FORBIDDEN_TWENTY_STEP not in rendered
|
|
895
885
|
assert "Reviewer Agent → filter → orchestrator fix" in rendered
|
|
896
886
|
|
|
897
|
-
def
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
assert FORBIDDEN_TWENTY_STEP not in rendered
|
|
887
|
+
def test_retained_tier3_fallback_template_uses_direct_orchestrator_implementation(self):
|
|
888
|
+
rendered = Path("dev-pipeline/templates/bootstrap-tier3.md").read_text(encoding="utf-8")
|
|
889
|
+
|
|
890
|
+
assert "/prizmkit-implement" in rendered
|
|
891
|
+
assert "Implement — Orchestrator Direct" in rendered
|
|
892
|
+
assert "Spawn Dev subagent" not in rendered
|
|
893
|
+
assert "Spawn Dev agent" not in rendered
|
|
894
|
+
assert "Spawn Reviewer agent" not in rendered
|
|
895
|
+
assert "Reviewer Agent Def" not in rendered
|
|
896
|
+
assert "subagent_type=\"" + "-".join(["prizm", "dev", "team", "reviewer"]) + "\"" not in rendered
|
|
897
|
+
assert "Implement — Dev" not in rendered
|
|
898
|
+
assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
|
|
899
|
+
assert "{{AGENT_PROMPT_REVIEWER_REVIEW}}" not in rendered
|
|
900
|
+
assert "reviewer-review.md" not in rendered
|
|
901
|
+
assert "dev-implement.md" not in rendered
|
|
902
|
+
assert "Bug Fix Documentation Policy" not in rendered
|
|
903
|
+
assert "For bug-fix sessions" not in rendered
|
|
904
|
+
assert "fix(<scope>)" not in rendered
|
|
905
|
+
assert "port check 3000" not in rendered
|
|
906
|
+
assert "localhost:3000" not in rendered
|
|
907
|
+
assert FORBIDDEN_REVIEW_DEV_FIX not in rendered
|
|
908
|
+
assert FORBIDDEN_REVIEWER_DEV_SPAWN not in rendered
|
|
909
|
+
assert FORBIDDEN_EXPLORE_SUBAGENT not in rendered
|
|
910
|
+
assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
|
|
911
|
+
assert FORBIDDEN_THREE_STRIKE not in rendered
|
|
912
|
+
assert FORBIDDEN_TWENTY_STEP not in rendered
|
|
924
913
|
|
|
925
914
|
|
|
926
915
|
# ---------------------------------------------------------------------------
|
|
@@ -932,7 +921,14 @@ class TestContinuationHandoff:
|
|
|
932
921
|
args = Namespace(
|
|
933
922
|
output=str(tmp_path / ".prizmkit/state/features/F-123/sessions/F-123-new/bootstrap-prompt.md"),
|
|
934
923
|
state_dir=str(tmp_path / ".prizmkit/state/features"),
|
|
935
|
-
continuation_mode="
|
|
924
|
+
continuation_mode="ai_error",
|
|
925
|
+
failure_classification="ai_runtime_or_provider_error",
|
|
926
|
+
code_retry_count="1",
|
|
927
|
+
infra_error_count="2",
|
|
928
|
+
max_code_retries="3",
|
|
929
|
+
max_infra_retries="3",
|
|
930
|
+
no_progress_count="0",
|
|
931
|
+
progress_fingerprint=json.dumps({"git_diff_fingerprint": "diff", "head_commit": "abc", "checkpoint_cursor": "S03:pending"}),
|
|
936
932
|
previous_session_id="F-123-old",
|
|
937
933
|
continuation_count="2",
|
|
938
934
|
context_overflow_count="2",
|
|
@@ -953,11 +949,11 @@ class TestContinuationHandoff:
|
|
|
953
949
|
|
|
954
950
|
assert rendered.startswith("NORMAL BOOTSTRAP")
|
|
955
951
|
assert "## Continuation Addendum" in rendered
|
|
956
|
-
assert "This is an automatic continuation after
|
|
952
|
+
assert "This is an automatic continuation after the previous launched AI session ended with `ai_runtime_or_provider_error`." in rendered
|
|
957
953
|
assert "This is a fresh headless AI CLI session for the same task." in rendered
|
|
958
954
|
assert "Continue on the current branch/worktree." in rendered
|
|
959
955
|
assert "Do not reset, clean, switch branches, or discard work." in rendered
|
|
960
|
-
assert "
|
|
956
|
+
assert "Inspect `git status` and `git diff` before editing." in rendered
|
|
961
957
|
assert "Treat completed/skipped checkpoint steps as already done." in rendered
|
|
962
958
|
assert "Do not read the previous full `session.log` unless explicitly necessary." in rendered
|
|
963
959
|
assert "prefer synthesized artifacts and summaries over logs" in rendered
|
|
@@ -1008,23 +1004,18 @@ def _write_feature_list(path, feature):
|
|
|
1008
1004
|
path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
|
|
1009
1005
|
|
|
1010
1006
|
|
|
1011
|
-
def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None):
|
|
1007
|
+
def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None, complexity=None):
|
|
1012
1008
|
project = tmp_path / "project"
|
|
1013
1009
|
project.mkdir(parents=True)
|
|
1014
1010
|
(project / ".prizmkit" / "plans").mkdir(parents=True)
|
|
1015
1011
|
(project / ".prizmkit" / "config.json").write_text(
|
|
1016
1012
|
json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
|
|
1017
1013
|
)
|
|
1018
|
-
for platform_dir, suffix in ((".claude", ".md"), (".codex", ".toml")):
|
|
1019
|
-
agents_dir = project / platform_dir / "agents"
|
|
1020
|
-
agents_dir.mkdir(parents=True)
|
|
1021
|
-
for name in ("dev", "reviewer"):
|
|
1022
|
-
(agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
|
|
1023
1014
|
feature = {
|
|
1024
1015
|
"id": "F-123",
|
|
1025
1016
|
"title": "Prompt Cleanup",
|
|
1026
1017
|
"description": "Clean prompt text",
|
|
1027
|
-
"estimated_complexity": {"lite": "low", "standard": "medium", "full": "high"}[mode],
|
|
1018
|
+
"estimated_complexity": complexity or {"lite": "low", "standard": "medium", "full": "high"}[mode],
|
|
1028
1019
|
"acceptance_criteria": ["Prompts are clean"],
|
|
1029
1020
|
}
|
|
1030
1021
|
if browser_interaction is not None:
|
|
@@ -1046,7 +1037,10 @@ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="c
|
|
|
1046
1037
|
"--mode", mode,
|
|
1047
1038
|
]
|
|
1048
1039
|
if template:
|
|
1049
|
-
|
|
1040
|
+
template_path = Path(template)
|
|
1041
|
+
if not template_path.is_absolute():
|
|
1042
|
+
template_path = Path("dev-pipeline/templates") / template_path
|
|
1043
|
+
args.extend(["--template", str(template_path)])
|
|
1050
1044
|
old_argv = os.sys.argv
|
|
1051
1045
|
old_platform = os.environ.get("PRIZMKIT_PLATFORM")
|
|
1052
1046
|
try:
|
|
@@ -1065,14 +1059,23 @@ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="c
|
|
|
1065
1059
|
return output.read_text(encoding="utf-8")
|
|
1066
1060
|
|
|
1067
1061
|
|
|
1068
|
-
def
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1062
|
+
def _ensure_skill_files(project, platforms=None):
|
|
1063
|
+
"""Create minimal skill files so platform resolution succeeds."""
|
|
1064
|
+
if platforms is None:
|
|
1065
|
+
platforms = ("claude", "codex")
|
|
1066
|
+
for plat in platforms:
|
|
1067
|
+
if plat == "codex":
|
|
1068
|
+
skill_dir = project / ".agents" / "skills" / "prizmkit"
|
|
1069
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
1070
|
+
(skill_dir / "SKILL.md").write_text("skill\n", encoding="utf-8")
|
|
1071
|
+
elif plat == "codebuddy":
|
|
1072
|
+
skill_dir = project / ".codebuddy" / "skills" / "prizmkit"
|
|
1073
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
1074
|
+
(skill_dir / "SKILL.md").write_text("skill\n", encoding="utf-8")
|
|
1075
|
+
else:
|
|
1076
|
+
command_dir = project / ".claude" / "commands"
|
|
1077
|
+
command_dir.mkdir(parents=True, exist_ok=True)
|
|
1078
|
+
(command_dir / "prizmkit.md").write_text("skill\n", encoding="utf-8")
|
|
1076
1079
|
|
|
1077
1080
|
|
|
1078
1081
|
def _render_bugfix_prompt(
|
|
@@ -1087,7 +1090,7 @@ def _render_bugfix_prompt(
|
|
|
1087
1090
|
(project / ".prizmkit" / "config.json").write_text(
|
|
1088
1091
|
json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
|
|
1089
1092
|
)
|
|
1090
|
-
|
|
1093
|
+
_ensure_skill_files(project)
|
|
1091
1094
|
bug_list = project / ".prizmkit" / "plans" / "bug-fix-list.json"
|
|
1092
1095
|
bug_list.parent.mkdir(parents=True)
|
|
1093
1096
|
bug = {"id": "B-001", "title": "Broken", "description": "Fix it", "acceptance_criteria": ["Works"]}
|
|
@@ -1140,7 +1143,7 @@ def _render_refactor_prompt(tmp_path, platform="claude", explicit_platform=None,
|
|
|
1140
1143
|
(project / ".prizmkit" / "config.json").write_text(
|
|
1141
1144
|
json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
|
|
1142
1145
|
)
|
|
1143
|
-
|
|
1146
|
+
_ensure_skill_files(project)
|
|
1144
1147
|
refactor_list = project / ".prizmkit" / "plans" / "refactor-list.json"
|
|
1145
1148
|
refactor_list.parent.mkdir(parents=True)
|
|
1146
1149
|
refactor = {
|
|
@@ -1190,6 +1193,100 @@ def _render_refactor_prompt(tmp_path, platform="claude", explicit_platform=None,
|
|
|
1190
1193
|
return output.read_text(encoding="utf-8")
|
|
1191
1194
|
|
|
1192
1195
|
|
|
1196
|
+
class TestFeaturePromptUnifiedFullGuidance:
|
|
1197
|
+
def test_default_sections_prompt_uses_full_guidance_for_all_complexities(self, tmp_path):
|
|
1198
|
+
prompts = {
|
|
1199
|
+
complexity: _render_feature_prompt(
|
|
1200
|
+
tmp_path / f"complexity-{complexity}",
|
|
1201
|
+
mode="lite",
|
|
1202
|
+
complexity=complexity,
|
|
1203
|
+
)
|
|
1204
|
+
for complexity in ("low", "medium", "high", "critical")
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
for complexity, prompt in prompts.items():
|
|
1208
|
+
assert f"**Complexity**: {complexity}" in prompt
|
|
1209
|
+
assert "## Task Contract" in prompt
|
|
1210
|
+
assert "## Workflow Checkpoint System" in prompt
|
|
1211
|
+
assert "### Specify + Plan (Full Workflow)" in prompt
|
|
1212
|
+
assert "### Plan Self-Check — Orchestrator Direct" in prompt
|
|
1213
|
+
assert "### Scoped Feature Test Gate — PrizmKit Test" in prompt
|
|
1214
|
+
assert "## Test Failure Recovery Protocol" in prompt
|
|
1215
|
+
assert "Boundary Matrix" in prompt
|
|
1216
|
+
assert "### Browser Verification — MANDATORY" in prompt
|
|
1217
|
+
assert "### TERMINAL STOP — Commit Completed" in prompt
|
|
1218
|
+
assert "# Dev-Pipeline Session Bootstrap — Tier 1" not in prompt
|
|
1219
|
+
assert "# Dev-Pipeline Session Bootstrap — Tier 2" not in prompt
|
|
1220
|
+
assert "### Phase 0: SKIP (already initialized)" not in prompt
|
|
1221
|
+
|
|
1222
|
+
# The complexity value remains planning metadata, but guidance markers are
|
|
1223
|
+
# the same full/tier3-equivalent composition for every complexity.
|
|
1224
|
+
def test_cli_mode_override_does_not_downgrade_default_sections_guidance(self, tmp_path):
|
|
1225
|
+
prompt = _render_feature_prompt(tmp_path / "mode-lite", mode="lite", complexity="low")
|
|
1226
|
+
|
|
1227
|
+
assert "{{PIPELINE_MODE}}" not in prompt
|
|
1228
|
+
assert "### Specify + Plan (Full Workflow)" in prompt
|
|
1229
|
+
assert "### Plan Self-Check — Orchestrator Direct" in prompt
|
|
1230
|
+
assert "### Phase 0: SKIP (already initialized)" not in prompt
|
|
1231
|
+
|
|
1232
|
+
def test_explicit_custom_template_override_still_renders(self, tmp_path):
|
|
1233
|
+
custom = tmp_path / "custom-template.md"
|
|
1234
|
+
custom.write_text(
|
|
1235
|
+
"CUSTOM {{FEATURE_ID}} {{FEATURE_TITLE}} {{COMPLEXITY}} {{IF_FRESH_START}}fresh{{END_IF_FRESH_START}}",
|
|
1236
|
+
encoding="utf-8",
|
|
1237
|
+
)
|
|
1238
|
+
|
|
1239
|
+
prompt = _render_feature_prompt(
|
|
1240
|
+
tmp_path / "custom-template-project",
|
|
1241
|
+
mode="lite",
|
|
1242
|
+
complexity="low",
|
|
1243
|
+
template=custom,
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
assert prompt == "CUSTOM F-123 Prompt Cleanup low fresh"
|
|
1247
|
+
|
|
1248
|
+
def test_fallback_without_sections_prefers_tier3_then_bootstrap_prompt(self, tmp_path, monkeypatch):
|
|
1249
|
+
import generate_bootstrap_prompt as gbp
|
|
1250
|
+
|
|
1251
|
+
original_isdir = os.path.isdir
|
|
1252
|
+
original_isfile = os.path.isfile
|
|
1253
|
+
requested = []
|
|
1254
|
+
|
|
1255
|
+
def fake_isdir(path):
|
|
1256
|
+
if str(path).endswith(os.path.join("templates", "sections")):
|
|
1257
|
+
return False
|
|
1258
|
+
return original_isdir(path)
|
|
1259
|
+
|
|
1260
|
+
def fake_isfile(path):
|
|
1261
|
+
requested.append(Path(path).name)
|
|
1262
|
+
return original_isfile(path)
|
|
1263
|
+
|
|
1264
|
+
monkeypatch.setattr(gbp.os.path, "isdir", fake_isdir)
|
|
1265
|
+
monkeypatch.setattr(gbp.os.path, "isfile", fake_isfile)
|
|
1266
|
+
prompt = _render_feature_prompt(tmp_path / "fallback-tier3", mode="lite", complexity="low")
|
|
1267
|
+
|
|
1268
|
+
assert requested.index("bootstrap-tier3.md") < requested.index("bootstrap-prompt.md") if "bootstrap-prompt.md" in requested else True
|
|
1269
|
+
assert "# Dev-Pipeline Session Bootstrap — Tier 3" in prompt
|
|
1270
|
+
assert "bootstrap-tier1.md" not in requested
|
|
1271
|
+
assert "bootstrap-tier2.md" not in requested
|
|
1272
|
+
|
|
1273
|
+
requested.clear()
|
|
1274
|
+
|
|
1275
|
+
def fake_isfile_without_tier3(path):
|
|
1276
|
+
requested.append(Path(path).name)
|
|
1277
|
+
if Path(path).name == "bootstrap-tier3.md":
|
|
1278
|
+
return False
|
|
1279
|
+
return original_isfile(path)
|
|
1280
|
+
|
|
1281
|
+
monkeypatch.setattr(gbp.os.path, "isfile", fake_isfile_without_tier3)
|
|
1282
|
+
prompt = _render_feature_prompt(tmp_path / "fallback-bootstrap-prompt", mode="lite", complexity="low")
|
|
1283
|
+
|
|
1284
|
+
assert "bootstrap-tier3.md" in requested
|
|
1285
|
+
assert "bootstrap-tier1.md" not in requested
|
|
1286
|
+
assert "bootstrap-tier2.md" not in requested
|
|
1287
|
+
assert "# Dev-Pipeline Fallback Bootstrap Prompt" in prompt
|
|
1288
|
+
|
|
1289
|
+
|
|
1193
1290
|
class TestBrowserBlockingPromptPolicy:
|
|
1194
1291
|
def test_feature_bugfix_refactor_blocking_browser_policy_stops_before_commit(self, tmp_path):
|
|
1195
1292
|
browser_interaction = {
|
|
@@ -1282,21 +1379,24 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1282
1379
|
assert "which " not in prompt # no which-command shell glue
|
|
1283
1380
|
assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
|
|
1284
1381
|
|
|
1285
|
-
def
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1382
|
+
def test_retained_tier3_template_is_clean_when_rendered(self, tmp_path):
|
|
1383
|
+
prompt = _render_feature_prompt(tmp_path / "bootstrap-tier3.md", mode="full", template="bootstrap-tier3.md")
|
|
1384
|
+
for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
|
|
1385
|
+
assert forbidden not in prompt
|
|
1386
|
+
assert "prizmkit-test-gate.py" not in prompt
|
|
1387
|
+
assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
|
|
1388
|
+
assert "which playwright-cli" not in prompt
|
|
1389
|
+
assert "lsof -ti" not in prompt
|
|
1390
|
+
assert "sed -nE" not in prompt
|
|
1391
|
+
assert ".prizmkit-test-started" in prompt
|
|
1392
|
+
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1393
|
+
assert "prizmkit-runtime-helper.py" in prompt
|
|
1394
|
+
|
|
1395
|
+
def test_retired_tier1_tier2_templates_are_removed(self):
|
|
1396
|
+
assert not Path("dev-pipeline/templates/bootstrap-tier1.md").exists()
|
|
1397
|
+
assert not Path("dev-pipeline/templates/bootstrap-tier2.md").exists()
|
|
1398
|
+
|
|
1399
|
+
def test_retained_tier3_template_with_browser_blocks_is_shell_free(self, tmp_path):
|
|
1300
1400
|
forbidden = [
|
|
1301
1401
|
"python3 {{INIT_SCRIPT_PATH}}",
|
|
1302
1402
|
"which playwright-cli",
|
|
@@ -1308,21 +1408,15 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1308
1408
|
"opencli doctor 2>/dev/null ||",
|
|
1309
1409
|
"echo \"=== playwright-cli ===\" &&",
|
|
1310
1410
|
]
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
template=template,
|
|
1321
|
-
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1322
|
-
)
|
|
1323
|
-
assert "prizmkit-runtime-helper.py" in prompt
|
|
1324
|
-
for snippet in forbidden:
|
|
1325
|
-
assert snippet not in prompt
|
|
1411
|
+
prompt = _render_feature_prompt(
|
|
1412
|
+
tmp_path / "browser-bootstrap-tier3.md",
|
|
1413
|
+
mode="full",
|
|
1414
|
+
template="bootstrap-tier3.md",
|
|
1415
|
+
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1416
|
+
)
|
|
1417
|
+
assert "prizmkit-runtime-helper.py" in prompt
|
|
1418
|
+
for snippet in forbidden:
|
|
1419
|
+
assert snippet not in prompt
|
|
1326
1420
|
|
|
1327
1421
|
def test_bugfix_refactor_share_claude_platform_resolution(self, tmp_path, monkeypatch):
|
|
1328
1422
|
from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
|
|
@@ -1334,7 +1428,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1334
1428
|
(bug_project / ".prizmkit" / "config.json").write_text(
|
|
1335
1429
|
json.dumps({"platform": "claude", "ai_cli": "claude"}), encoding="utf-8"
|
|
1336
1430
|
)
|
|
1337
|
-
|
|
1431
|
+
_ensure_skill_files(bug_project)
|
|
1338
1432
|
args = Namespace(
|
|
1339
1433
|
bug_id="B-001", session_id="session-1", run_id="run-1",
|
|
1340
1434
|
retry_count="0", resume_phase="null", state_dir=None,
|
|
@@ -1346,15 +1440,15 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1346
1440
|
)
|
|
1347
1441
|
refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-mixed", platform="claude")
|
|
1348
1442
|
|
|
1349
|
-
assert
|
|
1350
|
-
assert "
|
|
1443
|
+
assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
1444
|
+
assert "reviewer-agent-prompt.md" in refactor_prompt
|
|
1351
1445
|
|
|
1352
1446
|
def test_bugfix_refactor_explicit_codex_override(self, tmp_path, monkeypatch):
|
|
1353
1447
|
from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
|
|
1354
1448
|
|
|
1355
1449
|
bug_project = tmp_path / "bugfix-codex-replacements"
|
|
1356
1450
|
bug_project.mkdir()
|
|
1357
|
-
|
|
1451
|
+
_ensure_skill_files(bug_project)
|
|
1358
1452
|
monkeypatch.setenv("PRIZMKIT_PLATFORM", "codex")
|
|
1359
1453
|
args = Namespace(
|
|
1360
1454
|
bug_id="B-001", session_id="session-1", run_id="run-1",
|
|
@@ -1367,8 +1461,8 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1367
1461
|
)
|
|
1368
1462
|
refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-codex", platform="claude", explicit_platform="codex")
|
|
1369
1463
|
|
|
1370
|
-
assert
|
|
1371
|
-
assert "
|
|
1464
|
+
assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
1465
|
+
assert "reviewer-agent-prompt.md" in refactor_prompt
|
|
1372
1466
|
|
|
1373
1467
|
|
|
1374
1468
|
class TestFeatureBootstrapShellExtraction:
|
|
@@ -1427,49 +1521,25 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1427
1521
|
assert "port check 3000" not in prompt
|
|
1428
1522
|
assert "localhost:3000" not in prompt
|
|
1429
1523
|
|
|
1430
|
-
def
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
for template, mode in cases:
|
|
1437
|
-
prompt = _render_feature_prompt(
|
|
1438
|
-
tmp_path / f"legacy-{template}",
|
|
1439
|
-
mode=mode,
|
|
1440
|
-
template=template,
|
|
1441
|
-
browser_interaction={"tool": "playwright-cli", "verify_steps": ["Open the page"]},
|
|
1442
|
-
)
|
|
1443
|
-
|
|
1444
|
-
self.assert_shell_extracted_operations_removed(prompt)
|
|
1445
|
-
assert "process start --pid-file .prizmkit/state/browser-dev-server.pid" in prompt
|
|
1446
|
-
assert "process cleanup-pid <pid-from-.prizmkit/state/browser-dev-server.pid>" in prompt
|
|
1447
|
-
assert "port check 3000" not in prompt
|
|
1448
|
-
assert "localhost:3000" not in prompt
|
|
1449
|
-
|
|
1450
|
-
def test_legacy_tier_specific_extracted_operations_map_to_helper_commands(self, tmp_path):
|
|
1451
|
-
tier1 = _render_feature_prompt(
|
|
1452
|
-
tmp_path / "legacy-tier1-helper-map",
|
|
1453
|
-
mode="lite",
|
|
1454
|
-
template="bootstrap-tier1.md",
|
|
1455
|
-
)
|
|
1456
|
-
assert "artifact tree . --max-depth 2" in tier1
|
|
1457
|
-
assert "text contains .gitignore --pattern \"/binary-name\"" in tier1
|
|
1458
|
-
assert "NEEDS_IGNORE_ENTRY" in tier1
|
|
1459
|
-
|
|
1460
|
-
tier2 = _render_feature_prompt(
|
|
1461
|
-
tmp_path / "legacy-tier2-helper-map",
|
|
1462
|
-
mode="standard",
|
|
1463
|
-
template="bootstrap-tier2.md",
|
|
1524
|
+
def test_rendered_retained_tier3_feature_prompt_uses_runtime_helper_not_extracted_bash(self, tmp_path):
|
|
1525
|
+
prompt = _render_feature_prompt(
|
|
1526
|
+
tmp_path / "legacy-bootstrap-tier3.md",
|
|
1527
|
+
mode="full",
|
|
1528
|
+
template="bootstrap-tier3.md",
|
|
1529
|
+
browser_interaction={"tool": "playwright-cli", "verify_steps": ["Open the page"]},
|
|
1464
1530
|
)
|
|
1465
|
-
assert "artifact exists .prizmkit/specs/123-prompt-cleanup/failure-log.md" in tier2
|
|
1466
|
-
assert "artifact tree . --max-depth 2" in tier2
|
|
1467
|
-
assert "text count .prizmkit/specs/123-prompt-cleanup/plan.md --pattern \"- [ ]\"" in tier2
|
|
1468
1531
|
|
|
1532
|
+
self.assert_shell_extracted_operations_removed(prompt)
|
|
1533
|
+
assert "process start --pid-file .prizmkit/state/browser-dev-server.pid" in prompt
|
|
1534
|
+
assert "process cleanup-pid <pid-from-.prizmkit/state/browser-dev-server.pid>" in prompt
|
|
1535
|
+
assert "port check 3000" not in prompt
|
|
1536
|
+
assert "localhost:3000" not in prompt
|
|
1537
|
+
|
|
1538
|
+
def test_retained_tier3_specific_extracted_operations_map_to_helper_commands(self, tmp_path):
|
|
1469
1539
|
tier3 = _render_feature_prompt(
|
|
1470
1540
|
tmp_path / "legacy-tier3-helper-map",
|
|
1471
1541
|
mode="full",
|
|
1472
|
-
|
|
1542
|
+
template="bootstrap-tier3.md",
|
|
1473
1543
|
)
|
|
1474
1544
|
assert "text search . --pattern \"123-prompt-cleanup\" --json" in tier3
|
|
1475
1545
|
assert "text count .prizmkit/specs/123-prompt-cleanup/plan.md --pattern \"- [ ]\"" in tier3
|
|
@@ -196,7 +196,7 @@ class TestExplicitProjectRoot:
|
|
|
196
196
|
"sessions" / "session-1" / "session-status.json"
|
|
197
197
|
)
|
|
198
198
|
assert not replacements["{{SESSION_STATUS_PATH}}"].startswith(str(execution_root.resolve()))
|
|
199
|
-
assert
|
|
199
|
+
assert "{{" + "DEV_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
200
200
|
assert replacements["{{SESSION_LOG_PATH}}"].endswith(
|
|
201
201
|
".prizmkit/state/bugfix/B-001/sessions/session-1/logs/session.log"
|
|
202
202
|
)
|
|
@@ -265,7 +265,14 @@ class TestBugfixContinuationHandoff:
|
|
|
265
265
|
args = Namespace(
|
|
266
266
|
output=str(tmp_path / ".prizmkit/state/bugfix/B-007/sessions/B-007-new/bootstrap-prompt.md"),
|
|
267
267
|
state_dir=str(tmp_path / ".prizmkit/state/bugfix"),
|
|
268
|
-
continuation_mode="
|
|
268
|
+
continuation_mode="ai_error",
|
|
269
|
+
failure_classification="ai_runtime_or_provider_error",
|
|
270
|
+
code_retry_count="1",
|
|
271
|
+
infra_error_count="2",
|
|
272
|
+
max_code_retries="3",
|
|
273
|
+
max_infra_retries="3",
|
|
274
|
+
no_progress_count="0",
|
|
275
|
+
progress_fingerprint=json.dumps({"git_diff_fingerprint": "diff", "head_commit": "abc", "checkpoint_cursor": "S03:pending"}),
|
|
269
276
|
previous_session_id="B-007-old",
|
|
270
277
|
continuation_count="1",
|
|
271
278
|
context_overflow_count="1",
|
|
@@ -279,7 +286,7 @@ class TestBugfixContinuationHandoff:
|
|
|
279
286
|
)
|
|
280
287
|
|
|
281
288
|
assert prompt.startswith("BUGFIX PROMPT")
|
|
282
|
-
assert "This is an automatic continuation after
|
|
289
|
+
assert "This is an automatic continuation after the previous launched AI session ended with `ai_runtime_or_provider_error`." in prompt
|
|
283
290
|
assert "Do not attempt `/compact` as the recovery mechanism." not in prompt
|
|
284
291
|
assert ("Log " + "monitor") not in prompt
|
|
285
292
|
assert ("log-" + "monitor") not in prompt
|
|
@@ -360,7 +367,7 @@ def _render_bugfix_prompt(tmp_path, platform="claude"):
|
|
|
360
367
|
(project / ".prizmkit" / "config.json").write_text(
|
|
361
368
|
json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
|
|
362
369
|
)
|
|
363
|
-
|
|
370
|
+
_ensure_skill_files(project)
|
|
364
371
|
bug_list = project / ".prizmkit" / "plans" / "bug-fix-list.json"
|
|
365
372
|
bug_list.parent.mkdir(parents=True)
|
|
366
373
|
bug_list.write_text(json.dumps({
|
|
@@ -399,17 +406,23 @@ def _render_bugfix_prompt(tmp_path, platform="claude"):
|
|
|
399
406
|
return output.read_text(encoding="utf-8")
|
|
400
407
|
|
|
401
408
|
|
|
402
|
-
def
|
|
403
|
-
"""Create minimal
|
|
409
|
+
def _ensure_skill_files(project, platforms=None):
|
|
410
|
+
"""Create minimal skill files so platform resolution succeeds."""
|
|
404
411
|
if platforms is None:
|
|
405
412
|
platforms = ("claude", "codex")
|
|
406
413
|
for plat in platforms:
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
414
|
+
if plat == "codex":
|
|
415
|
+
skill_dir = project / ".agents" / "skills" / "prizmkit"
|
|
416
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
417
|
+
(skill_dir / "SKILL.md").write_text("skill\n", encoding="utf-8")
|
|
418
|
+
elif plat == "codebuddy":
|
|
419
|
+
skill_dir = project / ".codebuddy" / "skills" / "prizmkit"
|
|
420
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
421
|
+
(skill_dir / "SKILL.md").write_text("skill\n", encoding="utf-8")
|
|
422
|
+
else:
|
|
423
|
+
command_dir = project / ".claude" / "commands"
|
|
424
|
+
command_dir.mkdir(parents=True, exist_ok=True)
|
|
425
|
+
(command_dir / "prizmkit.md").write_text("skill\n", encoding="utf-8")
|
|
413
426
|
|
|
414
427
|
|
|
415
428
|
class TestBugfixBootstrapShellExtraction:
|