prizmkit 1.1.130 → 1.1.131
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 +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
- package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +15 -1
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +396 -4
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +54 -1
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +54 -1
- package/bundled/dev-pipeline/scripts/prompt_framework.py +152 -1
- package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
- package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +20 -14
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +21 -4
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +18 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +5 -15
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +16 -0
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +26 -23
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +12 -8
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +310 -22
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +132 -0
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +411 -18
- package/bundled/dev-pipeline/tests/test_unified_cli.py +3 -3
- package/bundled/skills/_metadata.json +62 -25
- package/bundled/skills/app-planner/SKILL.md +4 -3
- package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
- package/bundled/skills/bug-planner/SKILL.md +12 -27
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
- package/bundled/skills/feature-planner/SKILL.md +27 -22
- package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
- package/bundled/skills/feature-workflow/SKILL.md +94 -121
- package/bundled/skills/prizmkit/SKILL.md +130 -94
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-code-review/SKILL.md +114 -84
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +1 -1
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-committer/SKILL.md +131 -47
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-init/SKILL.md +5 -5
- package/bundled/skills/prizmkit-plan/SKILL.md +132 -105
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-test/SKILL.md +28 -6
- package/bundled/skills/prizmkit-test/references/examples.md +1 -1
- package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
- package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/recovery-workflow/SKILL.md +62 -241
- package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
- package/bundled/skills/recovery-workflow/references/detection.md +28 -52
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
- package/bundled/skills/refactor-planner/SKILL.md +12 -57
- package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
- package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
- package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
- package/bundled/skills/refactor-workflow/SKILL.md +98 -139
- package/bundled/templates/project-memory-template.md +4 -4
- package/package.json +1 -1
- package/src/index.js +4 -0
- package/src/prompts.js +10 -3
- package/src/scaffold.js +1 -1
- package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
- package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
|
@@ -10,6 +10,8 @@ from generate_refactor_prompt import (
|
|
|
10
10
|
build_replacements,
|
|
11
11
|
generate_refactor_checkpoint,
|
|
12
12
|
merge_refactor_checkpoint_state,
|
|
13
|
+
refactor_checkpoint_metadata,
|
|
14
|
+
route_refactor_repair,
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
REMOVED_MAX_LOG_ENV = "MAX_" + "LOG_SIZE"
|
|
@@ -54,12 +56,24 @@ class TestRefactorCheckpointMerge:
|
|
|
54
56
|
def test_preserves_skipped_without_artifacts(self, tmp_path):
|
|
55
57
|
fresh = generate_refactor_checkpoint("R-008", "R-008-new")
|
|
56
58
|
existing = generate_refactor_checkpoint("R-008", "R-008-old")
|
|
59
|
+
existing["refactor_state"] = refactor_checkpoint_metadata(
|
|
60
|
+
".prizmkit/refactor/R-008",
|
|
61
|
+
stage="test",
|
|
62
|
+
status="TEST_FAIL",
|
|
63
|
+
stage_result="TEST_FAIL",
|
|
64
|
+
repair_scope="public-interface",
|
|
65
|
+
repair_round=2,
|
|
66
|
+
next_stage="prizmkit-implement",
|
|
67
|
+
resume_from="prizmkit-implement",
|
|
68
|
+
)
|
|
57
69
|
existing["steps"][0]["status"] = "skipped"
|
|
58
70
|
|
|
59
71
|
merged = merge_refactor_checkpoint_state(existing, fresh, str(tmp_path))
|
|
60
72
|
|
|
61
73
|
assert merged["steps"][0]["status"] == "skipped"
|
|
62
74
|
assert merged["steps"][1]["status"] == "pending"
|
|
75
|
+
assert merged["refactor_state"]["repair_scope"] == "public-interface"
|
|
76
|
+
assert merged["refactor_state"]["repair_round"] == 2
|
|
63
77
|
|
|
64
78
|
|
|
65
79
|
class TestExplicitProjectRoot:
|
|
@@ -197,6 +211,11 @@ def test_template_source_is_clean_for_headless_prompt_contract():
|
|
|
197
211
|
content = Path("dev-pipeline/templates/refactor-bootstrap-prompt.md").read_text(encoding="utf-8")
|
|
198
212
|
for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
|
|
199
213
|
assert forbidden not in content
|
|
214
|
+
assert "prizmkit-workflow" not in content
|
|
215
|
+
assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/refactor/{{REFACTOR_ID}}/" in content
|
|
216
|
+
assert "local_commit_authorized=true" in content
|
|
217
|
+
assert "push_authorized=false" in content
|
|
218
|
+
assert "Behavior-preservation checks remain supporting evidence" in content
|
|
200
219
|
|
|
201
220
|
|
|
202
221
|
# ---------------------------------------------------------------------------
|
|
@@ -315,6 +334,78 @@ class TestRefactorBootstrapShellExtraction:
|
|
|
315
334
|
|
|
316
335
|
|
|
317
336
|
class TestRefactorCheckpointGeneration:
|
|
337
|
+
def test_default_checkpoint_has_all_atomic_gates_in_order(self):
|
|
338
|
+
checkpoint = generate_refactor_checkpoint("R-001", "session-1")
|
|
339
|
+
|
|
340
|
+
assert [step["skill"] for step in checkpoint["steps"]] == [
|
|
341
|
+
"prizmkit-init",
|
|
342
|
+
"prizmkit-plan",
|
|
343
|
+
"prizmkit-implement",
|
|
344
|
+
"prizmkit-code-review",
|
|
345
|
+
"prizmkit-test",
|
|
346
|
+
"prizmkit-retrospective",
|
|
347
|
+
"prizmkit-committer",
|
|
348
|
+
"refactor-report",
|
|
349
|
+
"completion-summary",
|
|
350
|
+
]
|
|
351
|
+
assert checkpoint["steps"][4]["depends_on"] == "S04"
|
|
352
|
+
assert checkpoint["steps"][5]["depends_on"] == "S05"
|
|
353
|
+
|
|
354
|
+
def test_refactor_repair_routing_preserves_behavior_review_and_test(self):
|
|
355
|
+
test_infra = route_refactor_repair(
|
|
356
|
+
"TEST_FAIL", "test-infrastructure", repair_round=1,
|
|
357
|
+
)
|
|
358
|
+
production = route_refactor_repair("TEST_FAIL", "public-interface", repair_round=1)
|
|
359
|
+
blocked = route_refactor_repair("TEST_BLOCKED", "production", repair_round=1)
|
|
360
|
+
|
|
361
|
+
assert test_infra["route"] == ["prizmkit-implement", "prizmkit-test"]
|
|
362
|
+
assert production["route"] == [
|
|
363
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
364
|
+
]
|
|
365
|
+
unknown = route_refactor_repair("TEST_FAIL", "contract", repair_round=1)
|
|
366
|
+
exhausted = route_refactor_repair("TEST_FAIL", "production", repair_round=3)
|
|
367
|
+
assert unknown["blocked"] is True
|
|
368
|
+
assert unknown["route"] == []
|
|
369
|
+
assert exhausted["blocked"] is True
|
|
370
|
+
assert exhausted["repair_round"] == 3
|
|
371
|
+
assert blocked["blocked"] is True
|
|
372
|
+
assert blocked["route"] == []
|
|
373
|
+
assert blocked["next_stage"] is None
|
|
374
|
+
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
375
|
+
assert blocked["blocked_reason"] == "test_blocked"
|
|
376
|
+
|
|
377
|
+
def test_refactor_checkpoint_metadata_preserves_artifact_and_evidence_handoff(self):
|
|
378
|
+
metadata = refactor_checkpoint_metadata(
|
|
379
|
+
".prizmkit/refactor/R-001",
|
|
380
|
+
evidence={"manifest_path": "manifest.json"},
|
|
381
|
+
stage="test",
|
|
382
|
+
status="TEST_PASS",
|
|
383
|
+
stage_result="TEST_PASS",
|
|
384
|
+
next_stage="prizmkit-retrospective",
|
|
385
|
+
resume_from="prizmkit-retrospective",
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
assert metadata["artifact_dir"] == ".prizmkit/refactor/R-001"
|
|
389
|
+
assert metadata["authoritative_evidence_paths"]["manifest"] == "manifest.json"
|
|
390
|
+
assert metadata["next_stage"] == "prizmkit-retrospective"
|
|
391
|
+
assert metadata["resume_from"] == "prizmkit-retrospective"
|
|
392
|
+
|
|
393
|
+
def test_rendered_refactor_prompt_is_atomic_headless_and_local_commit_only(self, tmp_path):
|
|
394
|
+
prompt = _render_refactor_prompt_full(tmp_path)
|
|
395
|
+
|
|
396
|
+
assert prompt.index("/prizmkit-code-review") < prompt.index(
|
|
397
|
+
"/prizmkit-test scope=this-change artifact_dir=.prizmkit/refactor/R-001/"
|
|
398
|
+
) < prompt.index("### Headless Commit Authorization")
|
|
399
|
+
assert "prizmkit-workflow" not in prompt
|
|
400
|
+
assert "Behavior-preservation checks collected during planning and implementation remain supporting evidence" in prompt
|
|
401
|
+
assert '"local_commit_authorized": true' in prompt
|
|
402
|
+
assert '"push_authorized": false' in prompt
|
|
403
|
+
assert "Do not ask questions, wait for user input" in prompt
|
|
404
|
+
assert "automatic push" not in prompt.lower()
|
|
405
|
+
assert "push_authorized\": true" not in prompt
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
class TestRefactorCheckpointGenerationLegacy:
|
|
318
409
|
def test_setup_section_is_step_and_finalization_group_expands(self):
|
|
319
410
|
checkpoint = generate_refactor_checkpoint(
|
|
320
411
|
"R-001",
|
|
@@ -90,6 +90,93 @@ def _item_id_from_next_result(kind, result):
|
|
|
90
90
|
return result.data[key]
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
def _write_semantic_completion_checkpoint(project_root, artifact_dir, workflow_type="feature-pipeline"):
|
|
94
|
+
validator = (
|
|
95
|
+
project_root / ".claude" / "command-assets" / "prizmkit-test"
|
|
96
|
+
/ "scripts" / "validate_test_evidence.py"
|
|
97
|
+
)
|
|
98
|
+
validator.parent.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
validator.write_text("import sys\nsys.exit(0)\n", encoding="utf-8")
|
|
100
|
+
contracts = {
|
|
101
|
+
"feature-pipeline": (
|
|
102
|
+
"feature_state",
|
|
103
|
+
[
|
|
104
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
105
|
+
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
106
|
+
],
|
|
107
|
+
),
|
|
108
|
+
"bugfix-pipeline": (
|
|
109
|
+
"bugfix_state",
|
|
110
|
+
[
|
|
111
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
112
|
+
"prizmkit-retrospective", "prizmkit-committer", "bug-report",
|
|
113
|
+
],
|
|
114
|
+
),
|
|
115
|
+
"refactor-pipeline": (
|
|
116
|
+
"refactor_state",
|
|
117
|
+
[
|
|
118
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
119
|
+
"prizmkit-retrospective", "prizmkit-committer", "refactor-report",
|
|
120
|
+
"completion-summary",
|
|
121
|
+
],
|
|
122
|
+
),
|
|
123
|
+
}
|
|
124
|
+
state_key, skills = contracts[workflow_type]
|
|
125
|
+
evidence_id = "semantic-evidence"
|
|
126
|
+
evidence_dir = project_root / ".prizmkit" / "test" / "evidence" / evidence_id
|
|
127
|
+
evidence_dir.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
evidence = {
|
|
129
|
+
"manifest": evidence_dir / "manifest.json",
|
|
130
|
+
"verdict": evidence_dir / "verdict.json",
|
|
131
|
+
"validation": evidence_dir / "validation.json",
|
|
132
|
+
}
|
|
133
|
+
evidence["manifest"].write_text(
|
|
134
|
+
json.dumps({"evidence_id": evidence_id, "final_verdict": "TEST_PASS"}),
|
|
135
|
+
encoding="utf-8",
|
|
136
|
+
)
|
|
137
|
+
evidence["verdict"].write_text(json.dumps({"verdict": "TEST_PASS"}), encoding="utf-8")
|
|
138
|
+
evidence["validation"].write_text(
|
|
139
|
+
json.dumps({"verdict": "TEST_PASS", "result": "passed"}), encoding="utf-8"
|
|
140
|
+
)
|
|
141
|
+
relative_evidence = {
|
|
142
|
+
key: str(path.relative_to(project_root)) for key, path in evidence.items()
|
|
143
|
+
}
|
|
144
|
+
steps = []
|
|
145
|
+
previous = None
|
|
146
|
+
for index, skill in enumerate(skills, start=1):
|
|
147
|
+
step = {
|
|
148
|
+
"id": f"S{index:02d}",
|
|
149
|
+
"skill": skill,
|
|
150
|
+
"name": skill,
|
|
151
|
+
"status": "completed",
|
|
152
|
+
"required_artifacts": [],
|
|
153
|
+
"depends_on": previous,
|
|
154
|
+
}
|
|
155
|
+
if skill == "prizmkit-code-review":
|
|
156
|
+
step["stage_result"] = "REVIEW_PASS"
|
|
157
|
+
if skill == "prizmkit-test":
|
|
158
|
+
step["stage_result"] = "TEST_PASS"
|
|
159
|
+
step["authoritative_evidence_paths"] = relative_evidence
|
|
160
|
+
steps.append(step)
|
|
161
|
+
previous = step["id"]
|
|
162
|
+
artifact_dir.mkdir(parents=True, exist_ok=True)
|
|
163
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
164
|
+
checkpoint.write_text(
|
|
165
|
+
json.dumps({
|
|
166
|
+
"version": 1,
|
|
167
|
+
"workflow_type": workflow_type,
|
|
168
|
+
"steps": steps,
|
|
169
|
+
state_key: {
|
|
170
|
+
"repair_round": 0,
|
|
171
|
+
"authoritative_evidence_paths": relative_evidence,
|
|
172
|
+
"terminal_status": "WORKFLOW_COMPLETED",
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
encoding="utf-8",
|
|
176
|
+
)
|
|
177
|
+
return checkpoint
|
|
178
|
+
|
|
179
|
+
|
|
93
180
|
def test_retired_shell_runtime_files_are_absent_from_source():
|
|
94
181
|
retired = [
|
|
95
182
|
"run-feature.sh",
|
|
@@ -346,6 +433,7 @@ def test_runner_models_parse_legacy_args_and_environment(tmp_path):
|
|
|
346
433
|
assert env.stale_kill_threshold_seconds == 9
|
|
347
434
|
assert env.max_retries == 9
|
|
348
435
|
assert env.strict_behavior_check is False
|
|
436
|
+
assert not hasattr(env, "enable_deploy")
|
|
349
437
|
assert not hasattr(env, "max_" + "log_size")
|
|
350
438
|
|
|
351
439
|
|
|
@@ -1443,8 +1531,7 @@ def test_has_branch_completion_evidence_requires_task_branch_ahead_of_base(tmp_p
|
|
|
1443
1531
|
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
1444
1532
|
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
1445
1533
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1446
|
-
artifact_dir
|
|
1447
|
-
(artifact_dir / "completion-summary.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
1534
|
+
_write_semantic_completion_checkpoint(tmp_path, artifact_dir)
|
|
1448
1535
|
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
1449
1536
|
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
1450
1537
|
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
@@ -1505,11 +1592,7 @@ def test_no_worktree_recovers_completed_pending_branch_before_new_session(monkey
|
|
|
1505
1592
|
session_dir = paths.feature_state_dir / "F-001" / "sessions" / "F-001-session-old"
|
|
1506
1593
|
session_dir.mkdir(parents=True)
|
|
1507
1594
|
artifact_dir = paths.project_root / ".prizmkit" / "specs" / "001-low"
|
|
1508
|
-
artifact_dir
|
|
1509
|
-
(artifact_dir / "workflow-checkpoint.json").write_text(
|
|
1510
|
-
json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}),
|
|
1511
|
-
encoding="utf-8",
|
|
1512
|
-
)
|
|
1595
|
+
_write_semantic_completion_checkpoint(paths.project_root, artifact_dir)
|
|
1513
1596
|
updates = []
|
|
1514
1597
|
events = []
|
|
1515
1598
|
|
|
@@ -2555,9 +2638,7 @@ def test_classification_accepts_late_infra_error_with_completed_checkpoint_and_c
|
|
|
2555
2638
|
subprocess.run(["git", "add", "done.txt"], cwd=tmp_path, check=True)
|
|
2556
2639
|
subprocess.run(["git", "commit", "-m", "done"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2557
2640
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
2558
|
-
artifact_dir
|
|
2559
|
-
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2560
|
-
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
2641
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact_dir)
|
|
2561
2642
|
log = tmp_path / "session.log"
|
|
2562
2643
|
log.write_text("model quota exceeded; API error after commit", encoding="utf-8")
|
|
2563
2644
|
raw = AISessionResult(
|
|
@@ -2579,10 +2660,10 @@ def test_classification_accepts_late_infra_error_with_completed_checkpoint_and_c
|
|
|
2579
2660
|
classification = classify_session(raw, project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2580
2661
|
|
|
2581
2662
|
assert classification.session_status == "success"
|
|
2582
|
-
assert classification.reason == "
|
|
2663
|
+
assert classification.reason == "semantic_checkpoint_with_late_runtime_error"
|
|
2583
2664
|
|
|
2584
2665
|
|
|
2585
|
-
def
|
|
2666
|
+
def test_classification_rejects_late_infra_with_completion_summary_only(tmp_path):
|
|
2586
2667
|
from prizmkit_runtime.runner_classification import classify_session
|
|
2587
2668
|
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2588
2669
|
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
@@ -2627,8 +2708,8 @@ def test_classification_accepts_late_infra_with_completion_artifact_without_new_
|
|
|
2627
2708
|
|
|
2628
2709
|
classification = classify_session(raw, project_root=tmp_path, base_head=side_head, prompt=prompt)
|
|
2629
2710
|
|
|
2630
|
-
assert classification.session_status == "
|
|
2631
|
-
assert classification.reason == "
|
|
2711
|
+
assert classification.session_status == "infra_error"
|
|
2712
|
+
assert classification.reason == "ai_runtime_or_provider_error"
|
|
2632
2713
|
|
|
2633
2714
|
|
|
2634
2715
|
def test_classification_keeps_pre_completion_provider_error_as_infra_error(tmp_path):
|
|
@@ -2681,9 +2762,7 @@ def test_classification_accepts_terminal_success_with_completed_checkpoint_witho
|
|
|
2681
2762
|
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2682
2763
|
base_head = subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True).stdout.strip()
|
|
2683
2764
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
2684
|
-
artifact_dir
|
|
2685
|
-
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2686
|
-
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
2765
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact_dir)
|
|
2687
2766
|
(artifact_dir / "completion-summary.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
2688
2767
|
log = tmp_path / "session.log"
|
|
2689
2768
|
log.write_text("terminal success", encoding="utf-8")
|
|
@@ -2709,7 +2788,7 @@ def test_classification_accepts_terminal_success_with_completed_checkpoint_witho
|
|
|
2709
2788
|
|
|
2710
2789
|
classification = classify_session(raw, project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2711
2790
|
assert classification.session_status == "success"
|
|
2712
|
-
assert classification.reason == "
|
|
2791
|
+
assert classification.reason == "terminal_success_with_semantic_checkpoint"
|
|
2713
2792
|
|
|
2714
2793
|
|
|
2715
2794
|
def test_classification_preserves_success_before_context_overflow(tmp_path):
|
|
@@ -3475,3 +3554,317 @@ def test_timeout_and_stale_termination_override_provider_error_markers(tmp_path,
|
|
|
3475
3554
|
|
|
3476
3555
|
assert classification.session_status == expected_status
|
|
3477
3556
|
assert classification.reason == expected_reason
|
|
3557
|
+
|
|
3558
|
+
|
|
3559
|
+
|
|
3560
|
+
@pytest.mark.parametrize("workflow_type", ["feature-pipeline", "bugfix-pipeline", "refactor-pipeline"])
|
|
3561
|
+
def test_semantic_completion_classification_has_cross_family_parity(tmp_path, workflow_type):
|
|
3562
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
3563
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3564
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
3565
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
3566
|
+
|
|
3567
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3568
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
3569
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
3570
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
3571
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
3572
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3573
|
+
artifact = tmp_path / ".prizmkit" / "artifacts" / workflow_type
|
|
3574
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact, workflow_type)
|
|
3575
|
+
log = tmp_path / f"{workflow_type}.log"
|
|
3576
|
+
log.write_text("success", encoding="utf-8")
|
|
3577
|
+
raw = AISessionResult(
|
|
3578
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
3579
|
+
exit_code=0,
|
|
3580
|
+
timed_out=False,
|
|
3581
|
+
termination_reason=None,
|
|
3582
|
+
stale_marker=None,
|
|
3583
|
+
fatal_error_code="",
|
|
3584
|
+
progress_summary=ProgressSummary(result_subtype="success"),
|
|
3585
|
+
session_log=log,
|
|
3586
|
+
backup_log=tmp_path / "backup.log",
|
|
3587
|
+
log_recovery=None,
|
|
3588
|
+
started_epoch=0,
|
|
3589
|
+
ended_epoch=1,
|
|
3590
|
+
)
|
|
3591
|
+
|
|
3592
|
+
classification = classify_session(
|
|
3593
|
+
raw,
|
|
3594
|
+
project_root=tmp_path,
|
|
3595
|
+
base_head="",
|
|
3596
|
+
prompt=PromptGenerationResult(
|
|
3597
|
+
output_path=tmp_path / "prompt.md",
|
|
3598
|
+
checkpoint_path=checkpoint,
|
|
3599
|
+
artifact_path=artifact,
|
|
3600
|
+
),
|
|
3601
|
+
)
|
|
3602
|
+
|
|
3603
|
+
assert classification.session_status == "success"
|
|
3604
|
+
assert classification.checkpoint_state["terminal_status"] == "WORKFLOW_COMPLETED"
|
|
3605
|
+
|
|
3606
|
+
|
|
3607
|
+
def test_semantic_blocked_classification_is_terminal_and_retry_neutral(tmp_path):
|
|
3608
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
3609
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3610
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
3611
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
3612
|
+
|
|
3613
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3614
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
3615
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
3616
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
3617
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
3618
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3619
|
+
artifact = tmp_path / ".prizmkit" / "specs" / "blocked"
|
|
3620
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact)
|
|
3621
|
+
data = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
3622
|
+
data["feature_state"].update(
|
|
3623
|
+
terminal_status="WORKFLOW_BLOCKED",
|
|
3624
|
+
blocked_reason="repair_round_limit",
|
|
3625
|
+
repair_round=3,
|
|
3626
|
+
)
|
|
3627
|
+
checkpoint.write_text(json.dumps(data), encoding="utf-8")
|
|
3628
|
+
log = tmp_path / "blocked.log"
|
|
3629
|
+
log.write_text("ambiguous failure", encoding="utf-8")
|
|
3630
|
+
raw = AISessionResult(
|
|
3631
|
+
command=AISessionCommand(("fake",), None, "codebuddy", "stdin"),
|
|
3632
|
+
exit_code=1,
|
|
3633
|
+
timed_out=False,
|
|
3634
|
+
termination_reason=None,
|
|
3635
|
+
stale_marker=None,
|
|
3636
|
+
fatal_error_code="",
|
|
3637
|
+
progress_summary=ProgressSummary(terminal_result_text="ambiguous failure"),
|
|
3638
|
+
session_log=log,
|
|
3639
|
+
backup_log=tmp_path / "backup.log",
|
|
3640
|
+
log_recovery=None,
|
|
3641
|
+
started_epoch=0,
|
|
3642
|
+
ended_epoch=1,
|
|
3643
|
+
)
|
|
3644
|
+
|
|
3645
|
+
classification = classify_session(
|
|
3646
|
+
raw,
|
|
3647
|
+
project_root=tmp_path,
|
|
3648
|
+
base_head="",
|
|
3649
|
+
prompt=PromptGenerationResult(
|
|
3650
|
+
output_path=tmp_path / "prompt.md",
|
|
3651
|
+
checkpoint_path=checkpoint,
|
|
3652
|
+
artifact_path=artifact,
|
|
3653
|
+
),
|
|
3654
|
+
)
|
|
3655
|
+
|
|
3656
|
+
assert classification.session_status == "workflow_blocked"
|
|
3657
|
+
assert classification.reason == "repair_round_limit"
|
|
3658
|
+
assert classification.checkpoint_state["repair_round"] == 3
|
|
3659
|
+
|
|
3660
|
+
|
|
3661
|
+
def test_recovery_outcome_preserves_dirty_partial_work_and_uses_semantic_checkpoint(tmp_path):
|
|
3662
|
+
from prizmkit_runtime.runner_recovery import _recovery_outcome
|
|
3663
|
+
|
|
3664
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3665
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
3666
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
3667
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
3668
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
3669
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3670
|
+
(tmp_path / "partial.txt").write_text("preserve me", encoding="utf-8")
|
|
3671
|
+
artifact = tmp_path / ".prizmkit" / "specs" / "done"
|
|
3672
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact)
|
|
3673
|
+
|
|
3674
|
+
outcome, semantic = _recovery_outcome(tmp_path, 1, checkpoint_path=checkpoint)
|
|
3675
|
+
|
|
3676
|
+
assert outcome == "success"
|
|
3677
|
+
assert semantic["terminal_status"] == "WORKFLOW_COMPLETED"
|
|
3678
|
+
assert (tmp_path / "partial.txt").read_text(encoding="utf-8") == "preserve me"
|
|
3679
|
+
assert subprocess.run(
|
|
3680
|
+
["git", "status", "--porcelain", "--", "partial.txt"],
|
|
3681
|
+
cwd=tmp_path,
|
|
3682
|
+
check=True,
|
|
3683
|
+
stdout=subprocess.PIPE,
|
|
3684
|
+
text=True,
|
|
3685
|
+
).stdout.startswith("??")
|
|
3686
|
+
|
|
3687
|
+
|
|
3688
|
+
def test_runtime_semantic_paths_do_not_invoke_composite_workflow():
|
|
3689
|
+
sources = [
|
|
3690
|
+
PIPELINE_ROOT / "prizmkit_runtime" / "checkpoint_state.py",
|
|
3691
|
+
PIPELINE_ROOT / "prizmkit_runtime" / "runner_classification.py",
|
|
3692
|
+
PIPELINE_ROOT / "prizmkit_runtime" / "runner_recovery.py",
|
|
3693
|
+
PIPELINE_ROOT / "prizmkit_runtime" / "runners.py",
|
|
3694
|
+
]
|
|
3695
|
+
|
|
3696
|
+
for source in sources:
|
|
3697
|
+
assert "prizmkit-workflow" not in source.read_text(encoding="utf-8")
|
|
3698
|
+
|
|
3699
|
+
|
|
3700
|
+
|
|
3701
|
+
@pytest.mark.parametrize(
|
|
3702
|
+
("kind", "item_id", "blocked_status"),
|
|
3703
|
+
[("feature", "F-001", "failed"), ("bugfix", "B-001", "needs_info"), ("refactor", "R-001", "failed")],
|
|
3704
|
+
)
|
|
3705
|
+
def test_family_status_persists_identical_semantic_blocked_snapshot_without_retry_spend(
|
|
3706
|
+
tmp_path, kind, item_id, blocked_status
|
|
3707
|
+
):
|
|
3708
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
3709
|
+
from prizmkit_runtime.runner_status import start_item, update_item
|
|
3710
|
+
|
|
3711
|
+
paths = _make_paths(tmp_path)
|
|
3712
|
+
item = {
|
|
3713
|
+
"id": item_id,
|
|
3714
|
+
"title": "Semantic blocked",
|
|
3715
|
+
"status": "pending",
|
|
3716
|
+
"priority": "high",
|
|
3717
|
+
"dependencies": [],
|
|
3718
|
+
}
|
|
3719
|
+
if kind == "bugfix":
|
|
3720
|
+
item.update(severity="high", description="blocked", reproduction_steps=["run"])
|
|
3721
|
+
elif kind == "refactor":
|
|
3722
|
+
item.update(description="blocked", targets=["src"], goals=["preserve"])
|
|
3723
|
+
_write_family_plan(paths, kind, [item])
|
|
3724
|
+
family = family_for(kind, paths)
|
|
3725
|
+
invocation = parse_invocation(family, "run", ())
|
|
3726
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": family.plan_path})
|
|
3727
|
+
assert start_item(family, invocation, item_id, paths.project_root).ok is True
|
|
3728
|
+
semantic = {
|
|
3729
|
+
"current_stage": None,
|
|
3730
|
+
"next_stage": None,
|
|
3731
|
+
"stage_result": "TEST_BLOCKED",
|
|
3732
|
+
"repair_scope": "production",
|
|
3733
|
+
"repair_round": 2,
|
|
3734
|
+
"authoritative_evidence_paths": {
|
|
3735
|
+
"manifest": ".prizmkit/test/evidence/id/manifest.json",
|
|
3736
|
+
"verdict": ".prizmkit/test/evidence/id/verdict.json",
|
|
3737
|
+
"validation": ".prizmkit/test/evidence/id/validation.json",
|
|
3738
|
+
},
|
|
3739
|
+
"blocked_reason": "test_blocked",
|
|
3740
|
+
"terminal_status": "WORKFLOW_BLOCKED",
|
|
3741
|
+
}
|
|
3742
|
+
|
|
3743
|
+
result = update_item(
|
|
3744
|
+
family,
|
|
3745
|
+
invocation,
|
|
3746
|
+
item_id,
|
|
3747
|
+
"workflow_blocked",
|
|
3748
|
+
"session-1",
|
|
3749
|
+
paths.project_root,
|
|
3750
|
+
checkpoint_state=semantic,
|
|
3751
|
+
)
|
|
3752
|
+
status = json.loads((family.state_dir / item_id / "status.json").read_text(encoding="utf-8"))
|
|
3753
|
+
|
|
3754
|
+
assert result.ok is True
|
|
3755
|
+
assert result.data["new_status"] == blocked_status
|
|
3756
|
+
assert result.data["checkpoint_state"] == semantic
|
|
3757
|
+
assert result.data["restart_policy"] == "terminal_blocked"
|
|
3758
|
+
assert status["retry_count"] == 0
|
|
3759
|
+
assert status["infra_error_count"] == 0
|
|
3760
|
+
for key, value in semantic.items():
|
|
3761
|
+
assert status[key] == value
|
|
3762
|
+
|
|
3763
|
+
|
|
3764
|
+
|
|
3765
|
+
def test_recovery_detector_returns_no_candidate_for_empty_project(tmp_path):
|
|
3766
|
+
import importlib.util
|
|
3767
|
+
|
|
3768
|
+
detector_path = (
|
|
3769
|
+
REPO_ROOT / "core" / "skills" / "orchestration-skill" / "workflows"
|
|
3770
|
+
/ "recovery-workflow" / "scripts" / "detect-recovery-state.py"
|
|
3771
|
+
)
|
|
3772
|
+
spec = importlib.util.spec_from_file_location("recovery_detector_empty", detector_path)
|
|
3773
|
+
module = importlib.util.module_from_spec(spec)
|
|
3774
|
+
spec.loader.exec_module(module)
|
|
3775
|
+
|
|
3776
|
+
report = module.build_report(tmp_path)
|
|
3777
|
+
|
|
3778
|
+
assert report["detected"] is False
|
|
3779
|
+
assert report["candidates"] == []
|
|
3780
|
+
|
|
3781
|
+
|
|
3782
|
+
def test_recovery_detector_returns_active_checkout_command(tmp_path, monkeypatch):
|
|
3783
|
+
import importlib.util
|
|
3784
|
+
|
|
3785
|
+
detector_path = (
|
|
3786
|
+
REPO_ROOT / "core" / "skills" / "orchestration-skill" / "workflows"
|
|
3787
|
+
/ "recovery-workflow" / "scripts" / "detect-recovery-state.py"
|
|
3788
|
+
)
|
|
3789
|
+
spec = importlib.util.spec_from_file_location("recovery_detector_active", detector_path)
|
|
3790
|
+
module = importlib.util.module_from_spec(spec)
|
|
3791
|
+
spec.loader.exec_module(module)
|
|
3792
|
+
|
|
3793
|
+
checkout_path = tmp_path / ".prizmkit" / "state" / "features" / "F-001" / "checkout.json"
|
|
3794
|
+
checkout_path.parent.mkdir(parents=True)
|
|
3795
|
+
checkout_path.write_text(
|
|
3796
|
+
json.dumps({
|
|
3797
|
+
"task_type": "feature",
|
|
3798
|
+
"task_id": "F-001",
|
|
3799
|
+
"state": "active",
|
|
3800
|
+
"active_dev_branch": "dev/F-001-resume",
|
|
3801
|
+
"base_branch": "main",
|
|
3802
|
+
"checkout_mode": "branch",
|
|
3803
|
+
}),
|
|
3804
|
+
encoding="utf-8",
|
|
3805
|
+
)
|
|
3806
|
+
monkeypatch.setattr(module, "branch_exists", lambda *_args: True)
|
|
3807
|
+
|
|
3808
|
+
report = module.build_report(tmp_path)
|
|
3809
|
+
|
|
3810
|
+
assert report["detected"] is True
|
|
3811
|
+
assert report["selection_required"] is False
|
|
3812
|
+
assert report["candidates"][0]["continue_command"] == (
|
|
3813
|
+
"python3 ./.prizmkit/dev-pipeline/cli.py feature run F-001"
|
|
3814
|
+
)
|
|
3815
|
+
|
|
3816
|
+
|
|
3817
|
+
def test_recovery_detector_ignores_completed_checkout(tmp_path, monkeypatch):
|
|
3818
|
+
import importlib.util
|
|
3819
|
+
|
|
3820
|
+
detector_path = (
|
|
3821
|
+
REPO_ROOT / "core" / "skills" / "orchestration-skill" / "workflows"
|
|
3822
|
+
/ "recovery-workflow" / "scripts" / "detect-recovery-state.py"
|
|
3823
|
+
)
|
|
3824
|
+
spec = importlib.util.spec_from_file_location("recovery_detector_completed", detector_path)
|
|
3825
|
+
module = importlib.util.module_from_spec(spec)
|
|
3826
|
+
spec.loader.exec_module(module)
|
|
3827
|
+
|
|
3828
|
+
checkout_path = tmp_path / ".prizmkit" / "state" / "bugfix" / "B-001" / "checkout.json"
|
|
3829
|
+
checkout_path.parent.mkdir(parents=True)
|
|
3830
|
+
checkout_path.write_text(
|
|
3831
|
+
json.dumps({
|
|
3832
|
+
"task_type": "bugfix",
|
|
3833
|
+
"task_id": "B-001",
|
|
3834
|
+
"state": "completed",
|
|
3835
|
+
"active_dev_branch": "bugfix/B-001-done",
|
|
3836
|
+
"base_branch": "main",
|
|
3837
|
+
}),
|
|
3838
|
+
encoding="utf-8",
|
|
3839
|
+
)
|
|
3840
|
+
monkeypatch.setattr(module, "branch_exists", lambda *_args: True)
|
|
3841
|
+
|
|
3842
|
+
report = module.build_report(tmp_path)
|
|
3843
|
+
|
|
3844
|
+
assert report["detected"] is False
|
|
3845
|
+
|
|
3846
|
+
|
|
3847
|
+
def test_l4_runtime_and_recovery_sources_do_not_depend_on_direct_l1_composite():
|
|
3848
|
+
runtime_root = REPO_ROOT / "dev-pipeline"
|
|
3849
|
+
source_files = list(runtime_root.rglob("*.py"))
|
|
3850
|
+
for source in source_files:
|
|
3851
|
+
if "__pycache__" in source.parts or "tests" in source.parts or not source.is_file():
|
|
3852
|
+
continue
|
|
3853
|
+
assert "prizmkit-workflow" not in source.read_text(encoding="utf-8")
|
|
3854
|
+
|
|
3855
|
+
|
|
3856
|
+
def test_recovery_detector_has_no_direct_lifecycle_fallback(tmp_path):
|
|
3857
|
+
import importlib.util
|
|
3858
|
+
|
|
3859
|
+
detector_path = (
|
|
3860
|
+
REPO_ROOT / "core" / "skills" / "orchestration-skill" / "workflows"
|
|
3861
|
+
/ "recovery-workflow" / "scripts" / "detect-recovery-state.py"
|
|
3862
|
+
)
|
|
3863
|
+
spec = importlib.util.spec_from_file_location("recovery_detector_contract", detector_path)
|
|
3864
|
+
module = importlib.util.module_from_spec(spec)
|
|
3865
|
+
spec.loader.exec_module(module)
|
|
3866
|
+
|
|
3867
|
+
report = module.build_report(tmp_path)
|
|
3868
|
+
|
|
3869
|
+
assert "continue_command" not in report
|
|
3870
|
+
assert report["detected"] is False
|
|
@@ -839,8 +839,8 @@ def test_headless_review_guidance_uses_main_agent_review_loop():
|
|
|
839
839
|
assert "isolation:" not in text, path
|
|
840
840
|
|
|
841
841
|
skill_text = skill_path.read_text(encoding="utf-8")
|
|
842
|
-
assert "staged and unstaged
|
|
843
|
-
assert "untracked files" in skill_text
|
|
842
|
+
assert "the staged diff, and the unstaged diff" in skill_text
|
|
843
|
+
assert "untracked, deleted, and renamed files" in skill_text
|
|
844
844
|
assert "accepted = 0" in skill_text
|
|
845
845
|
assert "ten completed review rounds" in skill_text
|
|
846
846
|
assert "## Final Result" in skill_text
|
|
@@ -1576,7 +1576,7 @@ def test_recovery_sessions_receive_live_heartbeat_configuration(tmp_path, monkey
|
|
|
1576
1576
|
"run",
|
|
1577
1577
|
lambda *_args, **_kwargs: type("Completed", (), {"returncode": 0, "stdout": "0", "stderr": ""})(),
|
|
1578
1578
|
)
|
|
1579
|
-
monkeypatch.setattr(runner_recovery, "_recovery_outcome", lambda *_args: "no_changes")
|
|
1579
|
+
monkeypatch.setattr(runner_recovery, "_recovery_outcome", lambda *_args, **_kwargs: ("no_changes", {}))
|
|
1580
1580
|
|
|
1581
1581
|
class FakeLauncher:
|
|
1582
1582
|
def __init__(self, config):
|