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.
Files changed (91) hide show
  1. package/bin/create-prizmkit.js +1 -5
  2. package/bundled/VERSION.json +3 -3
  3. package/bundled/dev-pipeline/README.md +41 -38
  4. package/bundled/dev-pipeline/assets/skill-subagent-integration.md +76 -0
  5. package/bundled/dev-pipeline/prizmkit_runtime/config.py +2 -8
  6. package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -1
  7. package/bundled/dev-pipeline/prizmkit_runtime/platform_detection.py +3 -3
  8. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +23 -25
  9. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +8 -2
  10. package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +10 -1
  11. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +88 -32
  12. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +28 -6
  13. package/bundled/dev-pipeline/scripts/continuation.py +51 -6
  14. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +60 -102
  15. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +1 -19
  16. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +1 -19
  17. package/bundled/dev-pipeline/scripts/{init-dev-team.py → init-change-artifact.py} +6 -16
  18. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +364 -76
  19. package/bundled/dev-pipeline/scripts/update-bug-status.py +57 -14
  20. package/bundled/dev-pipeline/scripts/update-feature-status.py +54 -5
  21. package/bundled/dev-pipeline/scripts/update-refactor-status.py +57 -14
  22. package/bundled/dev-pipeline/scripts/utils.py +1 -1
  23. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
  24. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +9 -11
  25. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +2 -1
  26. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +2 -2
  27. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
  28. package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
  29. package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
  30. package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -2
  31. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -1
  32. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +6 -7
  33. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +6 -7
  34. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +4 -2
  35. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +3 -3
  36. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +5 -3
  37. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +20 -0
  38. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
  39. package/bundled/dev-pipeline/tests/test_auto_skip.py +5 -5
  40. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +214 -144
  41. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +25 -12
  42. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +11 -14
  43. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +235 -32
  44. package/bundled/dev-pipeline/tests/test_unified_cli.py +219 -16
  45. package/bundled/skills/_metadata.json +4 -4
  46. package/bundled/skills/app-planner/SKILL.md +2 -2
  47. package/bundled/skills/app-planner/references/architecture-decisions.md +1 -3
  48. package/bundled/skills/prizmkit-code-review/SKILL.md +50 -47
  49. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +13 -8
  50. package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +47 -33
  51. package/bundled/skills/prizmkit-code-review/references/reviewer-workspace-protocol.md +90 -0
  52. package/bundled/skills/prizmkit-implement/SKILL.md +21 -1
  53. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +67 -0
  54. package/bundled/skills/prizmkit-test/SKILL.md +159 -155
  55. package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +420 -0
  56. package/bundled/skills/prizmkit-test/assets/behavior-risk-matrix.schema.json +116 -0
  57. package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +112 -0
  58. package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +97 -0
  59. package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +76 -192
  60. package/bundled/skills/prizmkit-test/references/contract-mock-protocol.md +65 -0
  61. package/bundled/skills/prizmkit-test/references/evidence-protocol.md +194 -0
  62. package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +78 -0
  63. package/bundled/skills/prizmkit-test/references/examples.md +65 -108
  64. package/bundled/skills/prizmkit-test/references/service-boundary-test-catalog.md +10 -7
  65. package/bundled/skills/prizmkit-test/references/test-generation-steps.md +90 -82
  66. package/bundled/skills/prizmkit-test/references/test-report-template.md +77 -98
  67. package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +97 -0
  68. package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +723 -0
  69. package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +1426 -0
  70. package/package.json +1 -1
  71. package/src/clean.js +12 -8
  72. package/src/config.js +24 -42
  73. package/src/index.js +1 -10
  74. package/src/manifest.js +3 -9
  75. package/src/metadata.js +0 -26
  76. package/src/prompts.js +0 -13
  77. package/src/scaffold.js +76 -201
  78. package/src/upgrade.js +16 -33
  79. package/bundled/adapters/claude/agent-adapter.js +0 -96
  80. package/bundled/adapters/claude/team-adapter.js +0 -183
  81. package/bundled/adapters/codebuddy/agent-adapter.js +0 -42
  82. package/bundled/adapters/codebuddy/team-adapter.js +0 -46
  83. package/bundled/adapters/codex/agent-adapter.js +0 -38
  84. package/bundled/adapters/codex/team-adapter.js +0 -37
  85. package/bundled/agents/prizm-dev-team-dev.md +0 -123
  86. package/bundled/agents/prizm-dev-team-reviewer.md +0 -113
  87. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +0 -65
  88. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +0 -436
  89. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +0 -510
  90. package/bundled/skills/prizmkit-test/scripts/validate_boundary_report.py +0 -347
  91. 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 replacements["{{DEV_SUBAGENT_PATH}}"].startswith(str(execution_root.resolve()))
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-tier1.md"),
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 test_low(self):
336
- assert determine_pipeline_mode("low") == "lite"
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 test_medium(self):
339
- assert determine_pipeline_mode("medium") == "lite"
340
-
341
- def test_high(self):
342
- assert determine_pipeline_mode("high") == "standard"
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 test_legacy_tier_templates_use_direct_orchestrator_implementation(self):
898
- for template_name in ("bootstrap-tier2.md", "bootstrap-tier3.md"):
899
- rendered = (Path("dev-pipeline/templates") / template_name).read_text(encoding="utf-8")
900
-
901
- assert "/prizmkit-implement" in rendered
902
- assert "Implement Orchestrator Direct" in rendered
903
- assert "Spawn Dev subagent" not in rendered
904
- assert "Spawn Dev agent" not in rendered
905
- assert "Spawn Reviewer agent" not in rendered
906
- assert "Reviewer Agent Def" not in rendered
907
- assert "subagent_type=\"prizm-dev-team-reviewer\"" not in rendered
908
- assert "Implement — Dev" not in rendered
909
- assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
910
- assert "{{AGENT_PROMPT_REVIEWER_REVIEW}}" not in rendered
911
- assert "reviewer-review.md" not in rendered
912
- assert "dev-implement.md" not in rendered
913
- assert "Bug Fix Documentation Policy" not in rendered
914
- assert "For bug-fix sessions" not in rendered
915
- assert "fix(<scope>)" not in rendered
916
- assert "port check 3000" not in rendered
917
- assert "localhost:3000" not in rendered
918
- assert FORBIDDEN_REVIEW_DEV_FIX not in rendered
919
- assert FORBIDDEN_REVIEWER_DEV_SPAWN not in rendered
920
- assert FORBIDDEN_EXPLORE_SUBAGENT not in rendered
921
- assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
922
- assert FORBIDDEN_THREE_STRIKE not in rendered
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="context_overflow",
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 context overflow." in rendered
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 "Start with `git status` and `git diff`." in rendered
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
- args.extend(["--template", str(Path("dev-pipeline/templates") / template)])
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 _ensure_agent_files(project, platforms=("claude", "codex")):
1069
- for platform in platforms:
1070
- platform_dir = ".codex" if platform == "codex" else ".claude"
1071
- suffix = ".toml" if platform == "codex" else ".md"
1072
- agents_dir = project / platform_dir / "agents"
1073
- agents_dir.mkdir(parents=True, exist_ok=True)
1074
- for name in ("dev", "reviewer"):
1075
- (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
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
- _ensure_agent_files(project)
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
- _ensure_agent_files(project)
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 test_legacy_feature_templates_are_clean_when_rendered(self, tmp_path):
1286
- for template, mode in (("bootstrap-tier1.md", "lite"), ("bootstrap-tier2.md", "standard"), ("bootstrap-tier3.md", "full")):
1287
- prompt = _render_feature_prompt(tmp_path / template, mode=mode, template=template)
1288
- for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
1289
- assert forbidden not in prompt
1290
- assert "prizmkit-test-gate.py" not in prompt
1291
- assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
1292
- assert "which playwright-cli" not in prompt
1293
- assert "lsof -ti" not in prompt
1294
- assert "sed -nE" not in prompt
1295
- assert ".prizmkit-test-started" in prompt
1296
- assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1297
- assert "prizmkit-runtime-helper.py" in prompt
1298
-
1299
- def test_legacy_feature_templates_with_browser_blocks_are_shell_free(self, tmp_path):
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
- cases = [
1312
- ("bootstrap-tier1.md", "lite"),
1313
- ("bootstrap-tier2.md", "standard"),
1314
- ("bootstrap-tier3.md", "full"),
1315
- ]
1316
- for template, mode in cases:
1317
- prompt = _render_feature_prompt(
1318
- tmp_path / f"browser-{template}",
1319
- mode=mode,
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
- _ensure_agent_files(bug_project)
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 replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".claude/agents/prizm-dev-team-reviewer.md")
1350
- assert ".codex/agents/prizm-dev-team-reviewer.toml" not in refactor_prompt
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
- _ensure_agent_files(bug_project)
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 replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".codex/agents/prizm-dev-team-reviewer.toml")
1371
- assert ".claude/agents/prizm-dev-team-reviewer.md" not in refactor_prompt
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 test_rendered_legacy_tier_feature_prompts_use_runtime_helper_not_extracted_bash(self, tmp_path):
1431
- cases = [
1432
- ("bootstrap-tier1.md", "lite"),
1433
- ("bootstrap-tier2.md", "standard"),
1434
- ("bootstrap-tier3.md", "full"),
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
- template="bootstrap-tier3.md",
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 replacements["{{DEV_SUBAGENT_PATH}}"].startswith(str(execution_root.resolve()))
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="context_overflow",
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 context overflow." in prompt
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
- _ensure_agent_files(project)
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 _ensure_agent_files(project, platforms=None):
403
- """Create minimal agent files so platform resolution succeeds."""
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
- platform_dir = ".codex" if plat == "codex" else ".claude"
408
- suffix = ".toml" if plat == "codex" else ".md"
409
- agents_dir = project / platform_dir / "agents"
410
- agents_dir.mkdir(parents=True, exist_ok=True)
411
- for name in ("dev", "reviewer"):
412
- (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
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: