prizmkit 1.1.114 → 1.1.115
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/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/.env.example +0 -1
- package/bundled/dev-pipeline/README.md +4 -6
- package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +40 -518
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +27 -99
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +283 -0
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +3 -13
- package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +25 -31
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -8
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +13 -7
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +13 -1
- package/bundled/dev-pipeline/scripts/continuation.py +8 -40
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +76 -261
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +16 -35
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +15 -34
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +6 -0
- package/bundled/dev-pipeline/scripts/init-pipeline.py +6 -0
- package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +6 -0
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +1 -1
- package/bundled/dev-pipeline/scripts/prompt_framework.py +114 -63
- package/bundled/dev-pipeline/scripts/update-bug-status.py +3 -7
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +54 -22
- package/bundled/dev-pipeline/scripts/update-feature-status.py +3 -7
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +3 -7
- package/bundled/dev-pipeline/scripts/utils.py +6 -37
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +9 -91
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +9 -66
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +10 -16
- package/bundled/dev-pipeline/templates/feature-list-schema.json +32 -17
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +0 -16
- package/bundled/dev-pipeline/templates/refactor-list-schema.json +10 -16
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +1 -1
- 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 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +1 -1
- package/bundled/dev-pipeline/tests/conftest.py +1 -0
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +164 -0
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +48 -76
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +25 -3
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +25 -5
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +128 -10
- package/bundled/dev-pipeline/tests/test_unified_cli.py +40 -9
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bug-planner/SKILL.md +5 -4
- package/bundled/skills/bug-planner/references/verification.md +29 -0
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +8 -0
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +2 -2
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +1 -6
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +6 -12
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +0 -1
- package/bundled/skills/feature-planner/SKILL.md +13 -12
- package/bundled/skills/feature-planner/assets/planning-guide.md +2 -2
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +8 -11
- package/bundled/skills/prizmkit-plan/SKILL.md +15 -1
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +101 -0
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +5 -5
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +2 -7
- package/bundled/skills/refactor-planner/SKILL.md +10 -8
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +9 -0
- package/bundled/team/prizm-dev-team.json +2 -9
- package/package.json +1 -1
- package/bundled/agents/prizm-dev-team-critic.md +0 -179
- package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +0 -8
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-critic.md +0 -22
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +0 -61
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +0 -38
- package/bundled/dev-pipeline/templates/sections/refactor-phase-critic.md +0 -22
- package/bundled/skills/bug-planner/references/critic-and-verification.md +0 -44
|
@@ -215,7 +215,6 @@ class TestExplicitProjectRoot:
|
|
|
215
215
|
output=str(main_checkout / ".prizmkit" / "state" / "features" / "F-001" / "sessions" / "session-1" / "bootstrap-prompt.md"),
|
|
216
216
|
template=None,
|
|
217
217
|
mode=None,
|
|
218
|
-
critic=None,
|
|
219
218
|
extract_baselines=False,
|
|
220
219
|
project_root=str(execution_root),
|
|
221
220
|
)
|
|
@@ -403,23 +402,6 @@ class TestProcessModeBlocks:
|
|
|
403
402
|
result = process_mode_blocks(tpl, "standard", init_done=False)
|
|
404
403
|
assert "need init" in result
|
|
405
404
|
|
|
406
|
-
def test_critic_enabled_keeps_critic_block(self):
|
|
407
|
-
tpl = "before\n{{IF_CRITIC_ENABLED}}\ncritic content\n{{END_IF_CRITIC_ENABLED}}\nafter"
|
|
408
|
-
result = process_mode_blocks(tpl, "standard", init_done=True, critic_enabled=True)
|
|
409
|
-
assert "critic content" in result
|
|
410
|
-
assert "IF_CRITIC_ENABLED" not in result
|
|
411
|
-
|
|
412
|
-
def test_critic_disabled_removes_critic_block(self):
|
|
413
|
-
tpl = "before\n{{IF_CRITIC_ENABLED}}\ncritic content\n{{END_IF_CRITIC_ENABLED}}\nafter"
|
|
414
|
-
result = process_mode_blocks(tpl, "standard", init_done=True, critic_enabled=False)
|
|
415
|
-
assert "critic content" not in result
|
|
416
|
-
assert "before" in result
|
|
417
|
-
assert "after" in result
|
|
418
|
-
|
|
419
|
-
def test_critic_default_is_disabled(self):
|
|
420
|
-
tpl = "{{IF_CRITIC_ENABLED}}critic{{END_IF_CRITIC_ENABLED}}"
|
|
421
|
-
result = process_mode_blocks(tpl, "standard", init_done=True)
|
|
422
|
-
assert "critic" not in result
|
|
423
405
|
|
|
424
406
|
|
|
425
407
|
|
|
@@ -435,7 +417,6 @@ class TestScopedFeatureTestGate:
|
|
|
435
417
|
str(sections_dir),
|
|
436
418
|
init_done=True,
|
|
437
419
|
is_resume=False,
|
|
438
|
-
critic_enabled=False,
|
|
439
420
|
browser_enabled=False,
|
|
440
421
|
)
|
|
441
422
|
names = [name for name, _ in sections]
|
|
@@ -470,7 +451,6 @@ class TestScopedFeatureTestGate:
|
|
|
470
451
|
str(sections_dir),
|
|
471
452
|
init_done=True,
|
|
472
453
|
is_resume=False,
|
|
473
|
-
critic_enabled=False,
|
|
474
454
|
browser_enabled=False,
|
|
475
455
|
)
|
|
476
456
|
rendered = "\n".join(content for _, content in sections)
|
|
@@ -490,7 +470,6 @@ class TestScopedFeatureTestGate:
|
|
|
490
470
|
str(sections_dir),
|
|
491
471
|
init_done=True,
|
|
492
472
|
is_resume=False,
|
|
493
|
-
critic_enabled=False,
|
|
494
473
|
browser_enabled=False,
|
|
495
474
|
)
|
|
496
475
|
names = [name for name, _ in sections]
|
|
@@ -506,7 +485,6 @@ class TestScopedFeatureTestGate:
|
|
|
506
485
|
str(sections_dir),
|
|
507
486
|
init_done=True,
|
|
508
487
|
is_resume=False,
|
|
509
|
-
critic_enabled=False,
|
|
510
488
|
browser_enabled=False,
|
|
511
489
|
)
|
|
512
490
|
checkpoint = generate_checkpoint_definition(
|
|
@@ -853,7 +831,7 @@ class TestHeadlessRecoveryReframing:
|
|
|
853
831
|
rendered = "\n".join(
|
|
854
832
|
section for _name, section in assemble_sections(
|
|
855
833
|
mode, str(sections_dir), init_done=True, is_resume=False,
|
|
856
|
-
|
|
834
|
+
browser_enabled=False,
|
|
857
835
|
)
|
|
858
836
|
)
|
|
859
837
|
assert deleted_title not in rendered
|
|
@@ -883,7 +861,7 @@ class TestDirectOrchestratorImplementationPrompts:
|
|
|
883
861
|
assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in replacements
|
|
884
862
|
assert FORBIDDEN_DEV_FIX_PLACEHOLDER not in replacements
|
|
885
863
|
assert FORBIDDEN_DEV_RESUME_PLACEHOLDER not in replacements
|
|
886
|
-
assert
|
|
864
|
+
assert ACTIVE_AGENT_PROMPTS == {}
|
|
887
865
|
assert "{{AGENT_PROMPT_REVIEWER_REVIEW}}" not in replacements
|
|
888
866
|
assert (templates_dir / "agent-prompts" / "reviewer-review.md").exists() is False
|
|
889
867
|
assert (templates_dir / "agent-prompts" / "dev-implement.md").exists() is False
|
|
@@ -897,7 +875,7 @@ class TestDirectOrchestratorImplementationPrompts:
|
|
|
897
875
|
rendered = "\n".join(
|
|
898
876
|
section for _name, section in assemble_sections(
|
|
899
877
|
mode, str(sections_dir), init_done=True, is_resume=False,
|
|
900
|
-
|
|
878
|
+
browser_enabled=False,
|
|
901
879
|
)
|
|
902
880
|
)
|
|
903
881
|
|
|
@@ -963,9 +941,9 @@ class TestContinuationHandoff:
|
|
|
963
941
|
continuation_summary_path="",
|
|
964
942
|
)
|
|
965
943
|
checkpoint = {"steps": [
|
|
966
|
-
{"id": "S01", "skill": "test-baseline", "status": "completed"},
|
|
967
|
-
{"id": "S02", "skill": "context-snapshot", "status": "skipped"},
|
|
968
|
-
{"id": "S03", "skill": "prizmkit-implement", "status": "pending"},
|
|
944
|
+
{"id": "S01", "skill": "test-baseline", "name": "Test Baseline", "status": "completed"},
|
|
945
|
+
{"id": "S02", "skill": "context-snapshot", "name": "Context Snapshot", "status": "skipped"},
|
|
946
|
+
{"id": "S03", "skill": "prizmkit-implement", "name": "Implement", "status": "pending"},
|
|
969
947
|
]}
|
|
970
948
|
|
|
971
949
|
rendered = append_continuation_handoff(
|
|
@@ -1000,15 +978,15 @@ class TestContinuationHandoff:
|
|
|
1000
978
|
|
|
1001
979
|
def test_checkpoint_cursor_prefers_in_progress_before_pending(self):
|
|
1002
980
|
checkpoint = {"steps": [
|
|
1003
|
-
{"id": "S01", "skill": "done", "status": "completed"},
|
|
1004
|
-
{"id": "S02", "skill": "active", "status": "in_progress"},
|
|
1005
|
-
{"id": "S03", "skill": "later", "status": "pending"},
|
|
981
|
+
{"id": "S01", "skill": "done", "name": "Done", "status": "completed"},
|
|
982
|
+
{"id": "S02", "skill": "active", "name": "Active", "status": "in_progress", "depends_on": "S01"},
|
|
983
|
+
{"id": "S03", "skill": "later", "name": "Later", "status": "pending", "depends_on": "S02"},
|
|
1006
984
|
]}
|
|
1007
985
|
assert checkpoint_cursor(checkpoint)["first_cursor"] == "S02 (active)"
|
|
1008
986
|
|
|
1009
987
|
|
|
1010
988
|
# ---------------------------------------------------------------------------
|
|
1011
|
-
# F-033 headless prompt cleanup and platform-aware
|
|
989
|
+
# F-033 headless prompt cleanup and platform-aware paths
|
|
1012
990
|
# ---------------------------------------------------------------------------
|
|
1013
991
|
|
|
1014
992
|
HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
|
|
@@ -1030,7 +1008,7 @@ def _write_feature_list(path, feature):
|
|
|
1030
1008
|
path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
|
|
1031
1009
|
|
|
1032
1010
|
|
|
1033
|
-
def _render_feature_prompt(tmp_path, mode="standard",
|
|
1011
|
+
def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None):
|
|
1034
1012
|
project = tmp_path / "project"
|
|
1035
1013
|
project.mkdir(parents=True)
|
|
1036
1014
|
(project / ".prizmkit" / "plans").mkdir(parents=True)
|
|
@@ -1040,7 +1018,7 @@ def _render_feature_prompt(tmp_path, mode="standard", critic=False, template=Non
|
|
|
1040
1018
|
for platform_dir, suffix in ((".claude", ".md"), (".codex", ".toml")):
|
|
1041
1019
|
agents_dir = project / platform_dir / "agents"
|
|
1042
1020
|
agents_dir.mkdir(parents=True)
|
|
1043
|
-
for name in ("dev", "reviewer"
|
|
1021
|
+
for name in ("dev", "reviewer"):
|
|
1044
1022
|
(agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
|
|
1045
1023
|
feature = {
|
|
1046
1024
|
"id": "F-123",
|
|
@@ -1048,7 +1026,6 @@ def _render_feature_prompt(tmp_path, mode="standard", critic=False, template=Non
|
|
|
1048
1026
|
"description": "Clean prompt text",
|
|
1049
1027
|
"estimated_complexity": {"lite": "low", "standard": "medium", "full": "high"}[mode],
|
|
1050
1028
|
"acceptance_criteria": ["Prompts are clean"],
|
|
1051
|
-
"critic": critic,
|
|
1052
1029
|
}
|
|
1053
1030
|
if browser_interaction is not None:
|
|
1054
1031
|
feature["browser_interaction"] = browser_interaction
|
|
@@ -1068,8 +1045,6 @@ def _render_feature_prompt(tmp_path, mode="standard", critic=False, template=Non
|
|
|
1068
1045
|
"--output", str(output),
|
|
1069
1046
|
"--mode", mode,
|
|
1070
1047
|
]
|
|
1071
|
-
if critic:
|
|
1072
|
-
args.extend(["--critic", "true"])
|
|
1073
1048
|
if template:
|
|
1074
1049
|
args.extend(["--template", str(Path("dev-pipeline/templates") / template)])
|
|
1075
1050
|
old_argv = os.sys.argv
|
|
@@ -1096,7 +1071,7 @@ def _ensure_agent_files(project, platforms=("claude", "codex")):
|
|
|
1096
1071
|
suffix = ".toml" if platform == "codex" else ".md"
|
|
1097
1072
|
agents_dir = project / platform_dir / "agents"
|
|
1098
1073
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
1099
|
-
for name in ("dev", "reviewer"
|
|
1074
|
+
for name in ("dev", "reviewer"):
|
|
1100
1075
|
(agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
|
|
1101
1076
|
|
|
1102
1077
|
|
|
@@ -1280,7 +1255,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1280
1255
|
def test_rendered_feature_bugfix_refactor_prompts_are_clean(self, tmp_path):
|
|
1281
1256
|
prompts = []
|
|
1282
1257
|
for mode in ("lite", "standard", "full"):
|
|
1283
|
-
prompts.append(_render_feature_prompt(tmp_path / mode, mode=mode
|
|
1258
|
+
prompts.append(_render_feature_prompt(tmp_path / mode, mode=mode))
|
|
1284
1259
|
prompts.append(_render_bugfix_prompt(tmp_path))
|
|
1285
1260
|
prompts.append(_render_refactor_prompt(tmp_path))
|
|
1286
1261
|
|
|
@@ -1309,7 +1284,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1309
1284
|
|
|
1310
1285
|
def test_legacy_feature_templates_are_clean_when_rendered(self, tmp_path):
|
|
1311
1286
|
for template, mode in (("bootstrap-tier1.md", "lite"), ("bootstrap-tier2.md", "standard"), ("bootstrap-tier3.md", "full")):
|
|
1312
|
-
prompt = _render_feature_prompt(tmp_path / template, mode=mode,
|
|
1287
|
+
prompt = _render_feature_prompt(tmp_path / template, mode=mode, template=template)
|
|
1313
1288
|
for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
|
|
1314
1289
|
assert forbidden not in prompt
|
|
1315
1290
|
assert "prizmkit-test-gate.py" not in prompt
|
|
@@ -1342,29 +1317,13 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1342
1317
|
prompt = _render_feature_prompt(
|
|
1343
1318
|
tmp_path / f"browser-{template}",
|
|
1344
1319
|
mode=mode,
|
|
1345
|
-
|
|
1346
|
-
template=template,
|
|
1320
|
+
template=template,
|
|
1347
1321
|
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1348
1322
|
)
|
|
1349
1323
|
assert "prizmkit-runtime-helper.py" in prompt
|
|
1350
1324
|
for snippet in forbidden:
|
|
1351
1325
|
assert snippet not in prompt
|
|
1352
1326
|
|
|
1353
|
-
def test_claude_config_wins_over_codex_directory_for_critic_path(self, tmp_path, monkeypatch):
|
|
1354
|
-
project = tmp_path / "mixed"
|
|
1355
|
-
(project / ".claude" / "agents").mkdir(parents=True)
|
|
1356
|
-
(project / ".codex" / "agents").mkdir(parents=True)
|
|
1357
|
-
(project / ".prizmkit").mkdir()
|
|
1358
|
-
(project / ".prizmkit" / "config.json").write_text(
|
|
1359
|
-
json.dumps({"platform": "claude", "ai_cli": "claude"}), encoding="utf-8"
|
|
1360
|
-
)
|
|
1361
|
-
monkeypatch.delenv("PRIZMKIT_PLATFORM", raising=False)
|
|
1362
|
-
|
|
1363
|
-
assert resolve_prompt_platform(str(project)) == "claude"
|
|
1364
|
-
prompt = _render_feature_prompt(tmp_path / "rendered-mixed", mode="standard", critic=True, platform="claude")
|
|
1365
|
-
assert ".claude/agents/prizm-dev-team-critic.md" in prompt
|
|
1366
|
-
assert ".codex/agents/prizm-dev-team-critic.toml" not in prompt
|
|
1367
|
-
|
|
1368
1327
|
def test_bugfix_refactor_share_claude_platform_resolution(self, tmp_path, monkeypatch):
|
|
1369
1328
|
from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
|
|
1370
1329
|
|
|
@@ -1380,7 +1339,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1380
1339
|
bug_id="B-001", session_id="session-1", run_id="run-1",
|
|
1381
1340
|
retry_count="0", resume_phase="null", state_dir=None,
|
|
1382
1341
|
output="out.md", project_root=str(bug_project), template=None,
|
|
1383
|
-
mode=None,
|
|
1342
|
+
mode=None,
|
|
1384
1343
|
)
|
|
1385
1344
|
replacements = bugfix_build_replacements(
|
|
1386
1345
|
args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
|
|
@@ -1401,7 +1360,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1401
1360
|
bug_id="B-001", session_id="session-1", run_id="run-1",
|
|
1402
1361
|
retry_count="0", resume_phase="null", state_dir=None,
|
|
1403
1362
|
output="out.md", project_root=str(bug_project), template=None,
|
|
1404
|
-
mode=None,
|
|
1363
|
+
mode=None,
|
|
1405
1364
|
)
|
|
1406
1365
|
replacements = bugfix_build_replacements(
|
|
1407
1366
|
args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
|
|
@@ -1411,15 +1370,6 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1411
1370
|
assert replacements["{{REVIEWER_SUBAGENT_PATH}}"].endswith(".codex/agents/prizm-dev-team-reviewer.toml")
|
|
1412
1371
|
assert ".claude/agents/prizm-dev-team-reviewer.md" not in refactor_prompt
|
|
1413
1372
|
|
|
1414
|
-
def test_explicit_codex_env_still_renders_codex_critic_path(self, tmp_path, monkeypatch):
|
|
1415
|
-
project = tmp_path / "codex"
|
|
1416
|
-
(project / ".claude" / "agents").mkdir(parents=True)
|
|
1417
|
-
(project / ".codex" / "agents").mkdir(parents=True)
|
|
1418
|
-
monkeypatch.setenv("PRIZMKIT_PLATFORM", "codex")
|
|
1419
|
-
|
|
1420
|
-
assert resolve_prompt_platform(str(project)) == "codex"
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
1373
|
|
|
1424
1374
|
class TestFeatureBootstrapShellExtraction:
|
|
1425
1375
|
RETIRED_EXTRACTED_SNIPPETS = [
|
|
@@ -1465,8 +1415,7 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1465
1415
|
prompt = _render_feature_prompt(
|
|
1466
1416
|
tmp_path / f"section-{mode}",
|
|
1467
1417
|
mode=mode,
|
|
1468
|
-
|
|
1469
|
-
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1418
|
+
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1470
1419
|
)
|
|
1471
1420
|
|
|
1472
1421
|
self.assert_shell_extracted_operations_removed(prompt)
|
|
@@ -1488,8 +1437,7 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1488
1437
|
prompt = _render_feature_prompt(
|
|
1489
1438
|
tmp_path / f"legacy-{template}",
|
|
1490
1439
|
mode=mode,
|
|
1491
|
-
|
|
1492
|
-
template=template,
|
|
1440
|
+
template=template,
|
|
1493
1441
|
browser_interaction={"tool": "playwright-cli", "verify_steps": ["Open the page"]},
|
|
1494
1442
|
)
|
|
1495
1443
|
|
|
@@ -1512,8 +1460,7 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1512
1460
|
tier2 = _render_feature_prompt(
|
|
1513
1461
|
tmp_path / "legacy-tier2-helper-map",
|
|
1514
1462
|
mode="standard",
|
|
1515
|
-
|
|
1516
|
-
template="bootstrap-tier2.md",
|
|
1463
|
+
template="bootstrap-tier2.md",
|
|
1517
1464
|
)
|
|
1518
1465
|
assert "artifact exists .prizmkit/specs/123-prompt-cleanup/failure-log.md" in tier2
|
|
1519
1466
|
assert "artifact tree . --max-depth 2" in tier2
|
|
@@ -1522,8 +1469,7 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1522
1469
|
tier3 = _render_feature_prompt(
|
|
1523
1470
|
tmp_path / "legacy-tier3-helper-map",
|
|
1524
1471
|
mode="full",
|
|
1525
|
-
|
|
1526
|
-
template="bootstrap-tier3.md",
|
|
1472
|
+
template="bootstrap-tier3.md",
|
|
1527
1473
|
)
|
|
1528
1474
|
assert "text search . --pattern \"123-prompt-cleanup\" --json" in tier3
|
|
1529
1475
|
assert "text count .prizmkit/specs/123-prompt-cleanup/plan.md --pattern \"- [ ]\"" in tier3
|
|
@@ -1619,3 +1565,29 @@ class TestDetectTestCommands:
|
|
|
1619
1565
|
# Should detect test:unit as a viable test script
|
|
1620
1566
|
assert "npm run test:unit" in result
|
|
1621
1567
|
|
|
1568
|
+
|
|
1569
|
+
|
|
1570
|
+
def test_feature_checkpoint_generation_skips_noop_and_expands_only_finalization_group():
|
|
1571
|
+
from generate_bootstrap_prompt import generate_checkpoint_definition
|
|
1572
|
+
|
|
1573
|
+
checkpoint = generate_checkpoint_definition(
|
|
1574
|
+
[
|
|
1575
|
+
("phase0-test-baseline", "noop"),
|
|
1576
|
+
("phase-context-snapshot", "context"),
|
|
1577
|
+
("phase-commit", "finalize"),
|
|
1578
|
+
],
|
|
1579
|
+
"standard",
|
|
1580
|
+
"feature-pipeline",
|
|
1581
|
+
"F-123",
|
|
1582
|
+
"123-prompt-cleanup",
|
|
1583
|
+
"session-1",
|
|
1584
|
+
)
|
|
1585
|
+
|
|
1586
|
+
assert [step["skill"] for step in checkpoint["steps"]] == [
|
|
1587
|
+
"context-snapshot",
|
|
1588
|
+
"prizmkit-retrospective",
|
|
1589
|
+
"prizmkit-committer",
|
|
1590
|
+
"completion-summary",
|
|
1591
|
+
]
|
|
1592
|
+
assert checkpoint["steps"][0]["depends_on"] is None
|
|
1593
|
+
assert checkpoint["steps"][1]["depends_on"] == "S01"
|
|
@@ -12,6 +12,7 @@ from generate_bugfix_prompt import (
|
|
|
12
12
|
format_global_context,
|
|
13
13
|
format_error_source_details,
|
|
14
14
|
format_environment,
|
|
15
|
+
generate_bugfix_checkpoint,
|
|
15
16
|
process_conditional_blocks,
|
|
16
17
|
render_template,
|
|
17
18
|
)
|
|
@@ -178,7 +179,6 @@ class TestExplicitProjectRoot:
|
|
|
178
179
|
output=str(main_checkout / ".prizmkit" / "state" / "bugfix" / "B-001" / "sessions" / "session-1" / "bootstrap-prompt.md"),
|
|
179
180
|
template=None,
|
|
180
181
|
mode=None,
|
|
181
|
-
critic=None,
|
|
182
182
|
project_root=str(execution_root),
|
|
183
183
|
)
|
|
184
184
|
bug = {"id": "B-001", "title": "Worktree Bug", "description": "d"}
|
|
@@ -275,7 +275,7 @@ class TestBugfixContinuationHandoff:
|
|
|
275
275
|
)
|
|
276
276
|
prompt = append_continuation_handoff(
|
|
277
277
|
"BUGFIX PROMPT", args, str(tmp_path), "bugfix", "B-007", "B-007",
|
|
278
|
-
{"steps": [{"id": "S01", "skill": "diagnose", "status": "pending"}]},
|
|
278
|
+
{"steps": [{"id": "S01", "skill": "diagnose", "name": "Diagnose", "status": "pending"}]},
|
|
279
279
|
)
|
|
280
280
|
|
|
281
281
|
assert prompt.startswith("BUGFIX PROMPT")
|
|
@@ -408,7 +408,7 @@ def _ensure_agent_files(project, platforms=None):
|
|
|
408
408
|
suffix = ".toml" if plat == "codex" else ".md"
|
|
409
409
|
agents_dir = project / platform_dir / "agents"
|
|
410
410
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
411
|
-
for name in ("dev", "reviewer"
|
|
411
|
+
for name in ("dev", "reviewer"):
|
|
412
412
|
(agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
|
|
413
413
|
|
|
414
414
|
|
|
@@ -439,3 +439,25 @@ class TestBugfixBootstrapShellExtraction:
|
|
|
439
439
|
content = Path("dev-pipeline/scripts/generate-bugfix-prompt.py").read_text(encoding="utf-8")
|
|
440
440
|
for snippet in RETIRED_EXTRACTED_SNIPPETS_BUGFIX:
|
|
441
441
|
assert snippet not in content, f"Shell snippet in generate-bugfix-prompt.py: {snippet}"
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
class TestBugfixCheckpointGeneration:
|
|
445
|
+
def test_setup_section_skips_and_finalization_group_expands(self):
|
|
446
|
+
checkpoint = generate_bugfix_checkpoint(
|
|
447
|
+
"B-001",
|
|
448
|
+
"session-1",
|
|
449
|
+
sections=[
|
|
450
|
+
("bugfix-phase-init", "setup"),
|
|
451
|
+
("bugfix-phase-implement", "implement"),
|
|
452
|
+
("bugfix-phase-commit-report", "finalize"),
|
|
453
|
+
],
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
assert [step["skill"] for step in checkpoint["steps"]] == [
|
|
457
|
+
"prizmkit-init",
|
|
458
|
+
"prizmkit-implement",
|
|
459
|
+
"prizmkit-retrospective",
|
|
460
|
+
"prizmkit-committer",
|
|
461
|
+
"bug-report",
|
|
462
|
+
]
|
|
463
|
+
assert checkpoint["steps"][2]["depends_on"] == "S02"
|
|
@@ -30,7 +30,7 @@ class TestRefactorContinuationHandoff:
|
|
|
30
30
|
)
|
|
31
31
|
prompt = append_continuation_handoff(
|
|
32
32
|
"REFACTOR PROMPT", args, str(tmp_path), "refactor", "R-007", "R-007",
|
|
33
|
-
{"steps": [{"id": "S01", "skill": "plan", "status": "pending"}]},
|
|
33
|
+
{"steps": [{"id": "S01", "skill": "plan", "name": "Plan", "status": "pending"}]},
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
assert prompt.startswith("REFACTOR PROMPT")
|
|
@@ -74,7 +74,6 @@ class TestExplicitProjectRoot:
|
|
|
74
74
|
output=str(project / "out.md"),
|
|
75
75
|
template=None,
|
|
76
76
|
mode=None,
|
|
77
|
-
critic=None,
|
|
78
77
|
)
|
|
79
78
|
refactor = {"id": "R-001", "title": "Restructure", "description": "d"}
|
|
80
79
|
|
|
@@ -109,7 +108,6 @@ class TestExplicitProjectRoot:
|
|
|
109
108
|
output=str(main_checkout / ".prizmkit" / "state" / "refactor" / "R-001" / "sessions" / "session-1" / "bootstrap-prompt.md"),
|
|
110
109
|
template=None,
|
|
111
110
|
mode=None,
|
|
112
|
-
critic=None,
|
|
113
111
|
project_root=str(execution_root),
|
|
114
112
|
)
|
|
115
113
|
refactor = {"id": "R-001", "title": "Worktree Refactor", "description": "d"}
|
|
@@ -161,7 +159,6 @@ class TestRefactorPromptMaxLogRemoval:
|
|
|
161
159
|
output="out.md",
|
|
162
160
|
template=None,
|
|
163
161
|
mode=None,
|
|
164
|
-
critic=None,
|
|
165
162
|
)
|
|
166
163
|
refactor = {"id": "R-001", "title": "Restructure", "description": "d"}
|
|
167
164
|
|
|
@@ -243,7 +240,7 @@ def _render_refactor_prompt_full(tmp_path, platform="claude"):
|
|
|
243
240
|
suffix = ".toml" if plat == "codex" else ".md"
|
|
244
241
|
agents_dir = project / platform_dir / "agents"
|
|
245
242
|
agents_dir.mkdir(parents=True, exist_ok=True)
|
|
246
|
-
for name in ("dev", "reviewer"
|
|
243
|
+
for name in ("dev", "reviewer"):
|
|
247
244
|
(agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
|
|
248
245
|
|
|
249
246
|
refactor_list = project / ".prizmkit" / "plans" / "refactor-list.json"
|
|
@@ -318,3 +315,26 @@ class TestRefactorBootstrapShellExtraction:
|
|
|
318
315
|
content = Path("dev-pipeline/scripts/generate-refactor-prompt.py").read_text(encoding="utf-8")
|
|
319
316
|
for snippet in RETIRED_EXTRACTED_SNIPPETS_REFACTOR:
|
|
320
317
|
assert snippet not in content, f"Shell snippet in generate-refactor-prompt.py: {snippet}"
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class TestRefactorCheckpointGeneration:
|
|
321
|
+
def test_setup_section_is_step_and_finalization_group_expands(self):
|
|
322
|
+
checkpoint = generate_refactor_checkpoint(
|
|
323
|
+
"R-001",
|
|
324
|
+
"session-1",
|
|
325
|
+
sections=[
|
|
326
|
+
("refactor-phase-init", "setup"),
|
|
327
|
+
("refactor-phase-implement", "implement"),
|
|
328
|
+
("refactor-phase-commit-report", "finalize"),
|
|
329
|
+
],
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
assert [step["skill"] for step in checkpoint["steps"]] == [
|
|
333
|
+
"prizmkit-init",
|
|
334
|
+
"prizmkit-implement",
|
|
335
|
+
"prizmkit-retrospective",
|
|
336
|
+
"prizmkit-committer",
|
|
337
|
+
"refactor-report",
|
|
338
|
+
"completion-summary",
|
|
339
|
+
]
|
|
340
|
+
assert checkpoint["steps"][2]["depends_on"] == "S02"
|
|
@@ -320,7 +320,7 @@ def test_runner_models_parse_legacy_args_and_environment(tmp_path):
|
|
|
320
320
|
invocation = parse_invocation(
|
|
321
321
|
family,
|
|
322
322
|
"run",
|
|
323
|
-
("custom-list.json", "F-2", "--features", "F-003:F-001", "--max-retries", "7", "--max-infra-retries=5", "--mode", "full"
|
|
323
|
+
("custom-list.json", "F-2", "--features", "F-003:F-001", "--max-retries", "7", "--max-infra-retries=5", "--mode", "full"),
|
|
324
324
|
)
|
|
325
325
|
env = RunnerEnvironment.from_env(
|
|
326
326
|
{
|
|
@@ -340,7 +340,6 @@ def test_runner_models_parse_legacy_args_and_environment(tmp_path):
|
|
|
340
340
|
assert invocation.max_retries == 7
|
|
341
341
|
assert invocation.max_infra_retries == 5
|
|
342
342
|
assert invocation.mode == "full"
|
|
343
|
-
assert invocation.critic is True
|
|
344
343
|
assert env.stop_on_failure is True
|
|
345
344
|
assert env.auto_push is True
|
|
346
345
|
assert env.dev_branch == "dev/custom"
|
|
@@ -765,7 +764,6 @@ def test_prompt_generation_result_preserves_per_item_model(monkeypatch, tmp_path
|
|
|
765
764
|
"model": "per-item-model",
|
|
766
765
|
"pipeline_mode": "standard",
|
|
767
766
|
"agent_count": 3,
|
|
768
|
-
"critic_enabled": False,
|
|
769
767
|
}
|
|
770
768
|
),
|
|
771
769
|
stderr="",
|
|
@@ -815,7 +813,6 @@ def test_prompt_generation_passes_explicit_execution_root_to_generator(monkeypat
|
|
|
815
813
|
"model": "",
|
|
816
814
|
"pipeline_mode": "lite",
|
|
817
815
|
"agent_count": 1,
|
|
818
|
-
"critic_enabled": False,
|
|
819
816
|
}
|
|
820
817
|
),
|
|
821
818
|
stderr="",
|
|
@@ -881,7 +878,6 @@ def _install_runner_session_fakes(monkeypatch, runners, *, statuses=("success",)
|
|
|
881
878
|
model="per-item-model",
|
|
882
879
|
pipeline_mode="lite",
|
|
883
880
|
agent_count=1,
|
|
884
|
-
critic_enabled=False,
|
|
885
881
|
checkpoint_path=execution_root / ".prizmkit" / "specs" / "001-low" / "workflow-checkpoint.json",
|
|
886
882
|
artifact_path=execution_root / ".prizmkit" / "specs" / "001-low",
|
|
887
883
|
raw={},
|
|
@@ -1144,7 +1140,6 @@ def test_explicit_worktree_materializes_ignored_support_assets_before_prompt_and
|
|
|
1144
1140
|
model="",
|
|
1145
1141
|
pipeline_mode="lite",
|
|
1146
1142
|
agent_count=1,
|
|
1147
|
-
critic_enabled=False,
|
|
1148
1143
|
checkpoint_path=execution_root / ".prizmkit" / "specs" / "001-support" / "workflow-checkpoint.json",
|
|
1149
1144
|
artifact_path=execution_root / ".prizmkit" / "specs" / "001-support",
|
|
1150
1145
|
raw={},
|
|
@@ -1294,7 +1289,7 @@ def test_no_worktree_recovers_completed_pending_branch_before_new_session(monkey
|
|
|
1294
1289
|
artifact_dir = paths.project_root / ".prizmkit" / "specs" / "001-low"
|
|
1295
1290
|
artifact_dir.mkdir(parents=True)
|
|
1296
1291
|
(artifact_dir / "workflow-checkpoint.json").write_text(
|
|
1297
|
-
json.dumps({"steps": [{"id": "S01", "skill": "done", "status": "completed"}]}),
|
|
1292
|
+
json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}),
|
|
1298
1293
|
encoding="utf-8",
|
|
1299
1294
|
)
|
|
1300
1295
|
updates = []
|
|
@@ -1530,7 +1525,7 @@ def test_foreground_runner_emits_structured_feature_progress(monkeypatch, tmp_pa
|
|
|
1530
1525
|
assert "Code retry: 1 / 3" in captured.err
|
|
1531
1526
|
assert "Preflight: no user-visible changes to preserve before feature task F-001" in captured.err
|
|
1532
1527
|
assert "Pipeline mode: lite (Tier 1 — Single Agent)" in captured.err
|
|
1533
|
-
assert "Agents: 1
|
|
1528
|
+
assert "Agents: 1" in captured.err
|
|
1534
1529
|
assert "Spawning AI CLI session: F-001-session-1" in captured.err
|
|
1535
1530
|
assert "Session result: success" in captured.err
|
|
1536
1531
|
|
|
@@ -1667,7 +1662,7 @@ def test_classification_accepts_late_infra_error_with_completed_checkpoint_and_c
|
|
|
1667
1662
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1668
1663
|
artifact_dir.mkdir(parents=True)
|
|
1669
1664
|
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
1670
|
-
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "status": "completed"}]}), encoding="utf-8")
|
|
1665
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
1671
1666
|
log = tmp_path / "session.log"
|
|
1672
1667
|
log.write_text("model quota exceeded; API error after commit", encoding="utf-8")
|
|
1673
1668
|
raw = AISessionResult(
|
|
@@ -1792,7 +1787,7 @@ def test_classification_accepts_terminal_success_with_completed_checkpoint_witho
|
|
|
1792
1787
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1793
1788
|
artifact_dir.mkdir(parents=True)
|
|
1794
1789
|
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
1795
|
-
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "status": "completed"}]}), encoding="utf-8")
|
|
1790
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
1796
1791
|
(artifact_dir / "completion-summary.json").write_text("{}", encoding="utf-8")
|
|
1797
1792
|
log = tmp_path / "session.log"
|
|
1798
1793
|
log.write_text("terminal success", encoding="utf-8")
|
|
@@ -2207,3 +2202,126 @@ def test_python_daemon_progress_uses_list_status_without_runtime_files(tmp_path)
|
|
|
2207
2202
|
else:
|
|
2208
2203
|
assert summary["auto_skipped"] == 1
|
|
2209
2204
|
|
|
2205
|
+
|
|
2206
|
+
|
|
2207
|
+
def _init_git_repo_for_invalid_checkpoint_test(tmp_path):
|
|
2208
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2209
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
2210
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
2211
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
2212
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
2213
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2214
|
+
return subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True).stdout.strip()
|
|
2215
|
+
|
|
2216
|
+
|
|
2217
|
+
def _terminal_success_result(tmp_path):
|
|
2218
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2219
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2220
|
+
|
|
2221
|
+
log = tmp_path / "session.log"
|
|
2222
|
+
log.write_text("terminal success", encoding="utf-8")
|
|
2223
|
+
return AISessionResult(
|
|
2224
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
2225
|
+
exit_code=0,
|
|
2226
|
+
timed_out=False,
|
|
2227
|
+
termination_reason=None,
|
|
2228
|
+
stale_marker=None,
|
|
2229
|
+
fatal_error_code="",
|
|
2230
|
+
progress_summary=ProgressSummary(result_subtype="success", terminal_result_text="Completed"),
|
|
2231
|
+
session_log=log,
|
|
2232
|
+
backup_log=tmp_path / "backup.log",
|
|
2233
|
+
log_recovery=None,
|
|
2234
|
+
started_epoch=0,
|
|
2235
|
+
ended_epoch=1,
|
|
2236
|
+
)
|
|
2237
|
+
|
|
2238
|
+
|
|
2239
|
+
def test_invalid_existing_checkpoint_blocks_terminal_success_with_completion_summary(tmp_path):
|
|
2240
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2241
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2242
|
+
|
|
2243
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
2244
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2245
|
+
artifact_dir.mkdir(parents=True)
|
|
2246
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2247
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "not-valid"}]}), encoding="utf-8")
|
|
2248
|
+
(artifact_dir / "completion-summary.json").write_text("{}", encoding="utf-8")
|
|
2249
|
+
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2250
|
+
|
|
2251
|
+
classification = classify_session(_terminal_success_result(tmp_path), project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2252
|
+
|
|
2253
|
+
assert classification.session_status == "crashed"
|
|
2254
|
+
assert classification.reason == "invalid_workflow_checkpoint:invalid_status"
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
def test_invalid_existing_checkpoint_blocks_head_change_success(tmp_path):
|
|
2258
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2259
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2260
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2261
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2262
|
+
|
|
2263
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
2264
|
+
(tmp_path / "done.txt").write_text("done", encoding="utf-8")
|
|
2265
|
+
subprocess.run(["git", "add", "done.txt"], cwd=tmp_path, check=True)
|
|
2266
|
+
subprocess.run(["git", "commit", "-m", "done"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2267
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2268
|
+
artifact_dir.mkdir(parents=True)
|
|
2269
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2270
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "bad", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
2271
|
+
raw = AISessionResult(
|
|
2272
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
2273
|
+
exit_code=1,
|
|
2274
|
+
timed_out=False,
|
|
2275
|
+
termination_reason=None,
|
|
2276
|
+
stale_marker=None,
|
|
2277
|
+
fatal_error_code="context_overflow",
|
|
2278
|
+
progress_summary=ProgressSummary(fatal_error_code="context_overflow"),
|
|
2279
|
+
session_log=tmp_path / "session.log",
|
|
2280
|
+
backup_log=tmp_path / "backup.log",
|
|
2281
|
+
log_recovery=None,
|
|
2282
|
+
started_epoch=0,
|
|
2283
|
+
ended_epoch=1,
|
|
2284
|
+
)
|
|
2285
|
+
raw.session_log.write_text("context overflow", encoding="utf-8")
|
|
2286
|
+
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2287
|
+
|
|
2288
|
+
classification = classify_session(raw, project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2289
|
+
|
|
2290
|
+
assert classification.session_status == "crashed"
|
|
2291
|
+
assert classification.reason == "invalid_workflow_checkpoint:malformed_step_id"
|
|
2292
|
+
|
|
2293
|
+
|
|
2294
|
+
def test_invalid_existing_checkpoint_blocks_workspace_auto_commit_success(tmp_path):
|
|
2295
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2296
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2297
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2298
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2299
|
+
|
|
2300
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
2301
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2302
|
+
artifact_dir.mkdir(parents=True)
|
|
2303
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2304
|
+
checkpoint.write_text(json.dumps({"steps": []}), encoding="utf-8")
|
|
2305
|
+
(tmp_path / "dirty.txt").write_text("dirty", encoding="utf-8")
|
|
2306
|
+
log = tmp_path / "session.log"
|
|
2307
|
+
log.write_text("clean exit but dirty workspace", encoding="utf-8")
|
|
2308
|
+
raw = AISessionResult(
|
|
2309
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
2310
|
+
exit_code=0,
|
|
2311
|
+
timed_out=False,
|
|
2312
|
+
termination_reason=None,
|
|
2313
|
+
stale_marker=None,
|
|
2314
|
+
fatal_error_code="",
|
|
2315
|
+
progress_summary=ProgressSummary(result_subtype="", terminal_result_text=""),
|
|
2316
|
+
session_log=log,
|
|
2317
|
+
backup_log=tmp_path / "backup.log",
|
|
2318
|
+
log_recovery=None,
|
|
2319
|
+
started_epoch=0,
|
|
2320
|
+
ended_epoch=1,
|
|
2321
|
+
)
|
|
2322
|
+
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2323
|
+
|
|
2324
|
+
classification = classify_session(raw, project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2325
|
+
|
|
2326
|
+
assert classification.session_status == "crashed"
|
|
2327
|
+
assert classification.reason == "invalid_workflow_checkpoint:empty_steps"
|