prizmkit 1.1.139 → 1.1.141
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/prizmkit_runtime/checkpoint_state.py +41 -121
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -6
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +1 -2
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +150 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +21 -804
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +4 -6
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +1 -1
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +4 -6
- package/bundled/dev-pipeline/scripts/prompt_framework.py +56 -55
- package/bundled/dev-pipeline/scripts/update-bug-status.py +1 -2
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +14 -96
- package/bundled/dev-pipeline/scripts/update-feature-status.py +1 -2
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +1 -2
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +3 -3
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +7 -18
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -6
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +5 -5
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +13 -10
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -9
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +14 -26
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -3
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +13 -10
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +287 -384
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +119 -684
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +17 -40
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +20 -23
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +29 -32
- package/bundled/skills/_metadata.json +6 -6
- package/bundled/skills/prizmkit/SKILL.md +8 -13
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-code-review/SKILL.md +3 -3
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-committer/SKILL.md +3 -3
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -2
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-test/SKILL.md +217 -187
- package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +119 -0
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +269 -0
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +193 -0
- package/bundled/skills/prizmkit-test/references/test-report-template.md +164 -79
- package/bundled/skills/prizmkit-workflow/SKILL.md +15 -23
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +61 -80
- package/package.json +1 -1
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +0 -476
- package/bundled/skills/prizmkit-test/assets/behavior-risk-matrix.schema.json +0 -116
- package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +0 -111
- package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +0 -66
- package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +0 -104
- package/bundled/skills/prizmkit-test/references/contract-mock-protocol.md +0 -65
- package/bundled/skills/prizmkit-test/references/evidence-protocol.md +0 -208
- package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +0 -80
- package/bundled/skills/prizmkit-test/references/examples.md +0 -100
- package/bundled/skills/prizmkit-test/references/service-boundary-test-catalog.md +0 -228
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +0 -118
- package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +0 -110
- package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +0 -181
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +0 -1851
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +0 -1793
|
@@ -4,7 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
|
|
7
|
-
from prizmkit_runtime.checkpoint_state import
|
|
7
|
+
from prizmkit_runtime.checkpoint_state import (
|
|
8
|
+
load_checkpoint_state,
|
|
9
|
+
read_checkpoint_cursor,
|
|
10
|
+
validate_checkpoint_data,
|
|
11
|
+
)
|
|
8
12
|
from update_checkpoint import update_checkpoint
|
|
9
13
|
|
|
10
14
|
|
|
@@ -24,10 +28,74 @@ def _step(step_id, skill="skill", name="Step", status="pending", depends_on=None
|
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
def _write(path, data):
|
|
31
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
27
32
|
path.write_text(json.dumps(data), encoding="utf-8")
|
|
28
33
|
return path
|
|
29
34
|
|
|
30
35
|
|
|
36
|
+
def _write_test_artifacts(root, slug="feature", *, result="TEST_PASS"):
|
|
37
|
+
artifact_dir = root / ".prizmkit" / "specs" / slug
|
|
38
|
+
artifact_dir.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
(artifact_dir / "test-report.md").write_text(
|
|
40
|
+
f"# Test Report\n\n## Final Result\n\n- Result: {result}\n",
|
|
41
|
+
encoding="utf-8",
|
|
42
|
+
)
|
|
43
|
+
projection = {
|
|
44
|
+
"schema_version": 1,
|
|
45
|
+
"result": result,
|
|
46
|
+
"report": "test-report.md",
|
|
47
|
+
"main_review_rounds": 1,
|
|
48
|
+
"independent_review": {
|
|
49
|
+
"status": "downgraded",
|
|
50
|
+
"responses": 0,
|
|
51
|
+
"downgrade_reason": "read-only continuation capability unavailable",
|
|
52
|
+
"final_state_rechecked": False,
|
|
53
|
+
},
|
|
54
|
+
"repair_rounds": 0,
|
|
55
|
+
"production_changed": False,
|
|
56
|
+
"review_required": False,
|
|
57
|
+
"review_scope": None,
|
|
58
|
+
"unresolved_items": [],
|
|
59
|
+
}
|
|
60
|
+
(artifact_dir / "test-result.json").write_text(
|
|
61
|
+
json.dumps(projection), encoding="utf-8"
|
|
62
|
+
)
|
|
63
|
+
return artifact_dir
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _family_checkpoint(root=None, *, semantic=None, terminal=True):
|
|
67
|
+
skills = [
|
|
68
|
+
"prizmkit-implement",
|
|
69
|
+
"prizmkit-code-review",
|
|
70
|
+
"prizmkit-test",
|
|
71
|
+
"prizmkit-retrospective",
|
|
72
|
+
"prizmkit-committer",
|
|
73
|
+
"completion-summary",
|
|
74
|
+
]
|
|
75
|
+
steps = []
|
|
76
|
+
previous = None
|
|
77
|
+
for index, skill in enumerate(skills, start=1):
|
|
78
|
+
step = _step(f"S{index:02d}", skill, skill, "completed", previous)
|
|
79
|
+
if skill == "prizmkit-code-review":
|
|
80
|
+
step["stage_result"] = "REVIEW_PASS"
|
|
81
|
+
if skill == "prizmkit-test":
|
|
82
|
+
step["stage_result"] = "TEST_PASS"
|
|
83
|
+
step["required_artifacts"] = [
|
|
84
|
+
".prizmkit/specs/feature/test-report.md",
|
|
85
|
+
".prizmkit/specs/feature/test-result.json",
|
|
86
|
+
]
|
|
87
|
+
steps.append(step)
|
|
88
|
+
previous = step["id"]
|
|
89
|
+
if root is not None:
|
|
90
|
+
_write_test_artifacts(root)
|
|
91
|
+
return {
|
|
92
|
+
"version": 1,
|
|
93
|
+
"workflow_type": "feature-pipeline",
|
|
94
|
+
"steps": steps,
|
|
95
|
+
"feature_state": semantic or ({"terminal_status": "WORKFLOW_COMPLETED"} if terminal else {}),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
31
99
|
def test_valid_checkpoint_current_step_uses_first_non_terminal(tmp_path):
|
|
32
100
|
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
33
101
|
_step("S01", "plan", "Plan", "completed"),
|
|
@@ -44,36 +112,17 @@ def test_valid_checkpoint_current_step_uses_first_non_terminal(tmp_path):
|
|
|
44
112
|
assert read_checkpoint_cursor(path) == 2
|
|
45
113
|
|
|
46
114
|
|
|
47
|
-
def test_all_completed_checkpoint_has_complete_cursor(tmp_path):
|
|
48
|
-
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
49
|
-
_step("S01", "plan", "Plan", "completed"),
|
|
50
|
-
_step("S02", "commit", "Commit", "skipped", "S01"),
|
|
51
|
-
]))
|
|
52
|
-
|
|
53
|
-
state = load_checkpoint_state(path)
|
|
54
|
-
|
|
55
|
-
assert state.valid is True
|
|
56
|
-
assert state.all_completed is True
|
|
57
|
-
assert state.current_step is None
|
|
58
|
-
assert state.current_step_name == ""
|
|
59
|
-
assert state.cursor == 2
|
|
60
|
-
assert state.cursor_signature == "complete"
|
|
61
|
-
|
|
62
|
-
|
|
63
115
|
def test_missing_and_malformed_checkpoints_are_invalid(tmp_path):
|
|
64
116
|
missing = load_checkpoint_state(tmp_path / "missing.json")
|
|
65
117
|
assert missing.valid is False
|
|
66
118
|
assert missing.exists is False
|
|
67
119
|
assert missing.error_code == "missing"
|
|
68
|
-
assert missing.current_step_name == ""
|
|
69
120
|
|
|
70
121
|
malformed_path = tmp_path / "bad.json"
|
|
71
122
|
malformed_path.write_text("{not-json", encoding="utf-8")
|
|
72
123
|
malformed = load_checkpoint_state(malformed_path)
|
|
73
124
|
assert malformed.valid is False
|
|
74
|
-
assert malformed.exists is True
|
|
75
125
|
assert malformed.error_code == "malformed_json"
|
|
76
|
-
assert malformed.current_step_name == ""
|
|
77
126
|
|
|
78
127
|
|
|
79
128
|
def test_structural_validation_error_codes(tmp_path):
|
|
@@ -83,7 +132,6 @@ def test_structural_validation_error_codes(tmp_path):
|
|
|
83
132
|
("duplicate_step_id", _checkpoint([_step("S01"), _step("S01")])),
|
|
84
133
|
("invalid_status", _checkpoint([_step("S01", status="done")])),
|
|
85
134
|
("invalid_depends_on", _checkpoint([_step("S01", depends_on="S99")])),
|
|
86
|
-
("invalid_depends_on", _checkpoint([_step("S01", depends_on="S02"), _step("S02")])),
|
|
87
135
|
("malformed_step_id", _checkpoint([_step("one")])),
|
|
88
136
|
("invalid_step", {"steps": ["not-object"]}),
|
|
89
137
|
]
|
|
@@ -92,183 +140,67 @@ def test_structural_validation_error_codes(tmp_path):
|
|
|
92
140
|
state = load_checkpoint_state(path)
|
|
93
141
|
assert state.valid is False
|
|
94
142
|
assert state.error_code == expected_code
|
|
95
|
-
assert state.current_step_name == ""
|
|
96
143
|
assert read_checkpoint_cursor(path) is None
|
|
97
144
|
|
|
98
145
|
|
|
99
|
-
def
|
|
146
|
+
def test_duplicate_skills_require_exact_step_id(tmp_path):
|
|
100
147
|
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
101
|
-
_step("S01", "
|
|
102
|
-
_step("S02", "
|
|
148
|
+
_step("S01", "prizmkit-committer", "First", "completed"),
|
|
149
|
+
_step("S02", "prizmkit-committer", "Second", "pending", "S01"),
|
|
103
150
|
]))
|
|
104
151
|
|
|
105
|
-
|
|
152
|
+
rejected = update_checkpoint(str(path), "prizmkit-committer", "completed")
|
|
153
|
+
accepted = update_checkpoint(str(path), "S02", "completed")
|
|
106
154
|
|
|
107
|
-
assert
|
|
108
|
-
assert
|
|
109
|
-
assert
|
|
155
|
+
assert rejected["ok"] is False
|
|
156
|
+
assert "matches multiple checkpoint steps" in rejected["error"]
|
|
157
|
+
assert accepted["ok"] is True
|
|
110
158
|
|
|
111
159
|
|
|
112
|
-
def
|
|
113
|
-
path = _write(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
160
|
+
def test_semantic_completion_requires_family_sequence_and_domain_results(tmp_path):
|
|
161
|
+
path = _write(
|
|
162
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
163
|
+
_family_checkpoint(tmp_path),
|
|
164
|
+
)
|
|
117
165
|
|
|
118
|
-
state = load_checkpoint_state(path)
|
|
166
|
+
state = load_checkpoint_state(path, project_root=tmp_path)
|
|
119
167
|
|
|
120
168
|
assert state.valid is True
|
|
121
|
-
assert state.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
126
|
-
_step("S01", "prizmkit-committer", "Commit First", "pending"),
|
|
127
|
-
_step("S02", "prizmkit-committer", "Commit Second", "pending", "S01"),
|
|
128
|
-
]))
|
|
129
|
-
before = path.read_text(encoding="utf-8")
|
|
130
|
-
|
|
131
|
-
result = update_checkpoint(str(path), "prizmkit-committer", "completed")
|
|
132
|
-
|
|
133
|
-
assert result["ok"] is False
|
|
134
|
-
assert "matches multiple checkpoint steps" in result["error"]
|
|
135
|
-
assert "Use an explicit step ID" in result["error"]
|
|
136
|
-
assert path.read_text(encoding="utf-8") == before
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def test_update_checkpoint_exact_id_updates_one_duplicate_skill_step(tmp_path):
|
|
140
|
-
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
141
|
-
_step("S01", "prizmkit-committer", "Commit First", "completed"),
|
|
142
|
-
_step("S02", "prizmkit-committer", "Commit Second", "pending", "S01"),
|
|
143
|
-
]))
|
|
144
|
-
|
|
145
|
-
result = update_checkpoint(str(path), "S02", "completed")
|
|
146
|
-
data = json.loads(path.read_text(encoding="utf-8"))
|
|
147
|
-
|
|
148
|
-
assert result["ok"] is True
|
|
149
|
-
assert result["step_id"] == "S02"
|
|
150
|
-
assert data["steps"][0]["status"] == "completed"
|
|
151
|
-
assert data["steps"][1]["status"] == "completed"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def test_update_checkpoint_invalid_checkpoint_fails_without_mutation(tmp_path):
|
|
155
|
-
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
156
|
-
_step("S01", "plan", "Plan", "done"),
|
|
157
|
-
]))
|
|
158
|
-
before = path.read_text(encoding="utf-8")
|
|
159
|
-
|
|
160
|
-
result = update_checkpoint(str(path), "S01", "completed")
|
|
161
|
-
|
|
162
|
-
assert result["ok"] is False
|
|
163
|
-
assert "invalid_status" in result["error"]
|
|
164
|
-
assert path.read_text(encoding="utf-8") == before
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def _family_checkpoint(workflow_type, skills, *, semantic=None):
|
|
169
|
-
steps = []
|
|
170
|
-
previous = None
|
|
171
|
-
for index, skill in enumerate(skills, start=1):
|
|
172
|
-
step = _step(f"S{index:02d}", skill, skill, "completed", previous)
|
|
173
|
-
if skill == "prizmkit-code-review":
|
|
174
|
-
step["stage_result"] = "REVIEW_PASS"
|
|
175
|
-
if skill == "prizmkit-test":
|
|
176
|
-
step["stage_result"] = "TEST_PASS"
|
|
177
|
-
steps.append(step)
|
|
178
|
-
previous = step["id"]
|
|
179
|
-
payload = {"version": 1, "workflow_type": workflow_type, "steps": steps}
|
|
180
|
-
state_key = {
|
|
181
|
-
"feature-pipeline": "feature_state",
|
|
182
|
-
"bugfix-pipeline": "bugfix_state",
|
|
183
|
-
"refactor-pipeline": "refactor_state",
|
|
184
|
-
}[workflow_type]
|
|
185
|
-
payload[state_key] = semantic or {}
|
|
186
|
-
return payload
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
def _write_test_evidence(root, verdict="TEST_PASS", validation_result="passed", evidence_id="evidence-1"):
|
|
190
|
-
evidence = root / ".prizmkit" / "test" / "evidence" / evidence_id
|
|
191
|
-
evidence.mkdir(parents=True)
|
|
192
|
-
manifest = evidence / "manifest.json"
|
|
193
|
-
verdict_path = evidence / "verdict.json"
|
|
194
|
-
validation = evidence / "validation.json"
|
|
195
|
-
manifest.write_text(json.dumps({"evidence_id": evidence_id, "final_verdict": verdict}), encoding="utf-8")
|
|
196
|
-
verdict_path.write_text(json.dumps({"verdict": verdict}), encoding="utf-8")
|
|
197
|
-
validation.write_text(json.dumps({"verdict": verdict, "result": validation_result}), encoding="utf-8")
|
|
198
|
-
return {
|
|
199
|
-
"manifest": str(manifest.relative_to(root)),
|
|
200
|
-
"verdict": str(verdict_path.relative_to(root)),
|
|
201
|
-
"validation": str(validation.relative_to(root)),
|
|
202
|
-
}
|
|
169
|
+
assert state.all_completed is True
|
|
170
|
+
assert state.semantic_complete is True
|
|
171
|
+
assert state.semantic.test_result_valid is True
|
|
172
|
+
assert state.semantic.terminal_status == "WORKFLOW_COMPLETED"
|
|
203
173
|
|
|
204
174
|
|
|
205
|
-
def
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
210
|
-
monkeypatch.setattr(
|
|
211
|
-
"prizmkit_runtime.checkpoint_state._evidence_validator_passes",
|
|
212
|
-
lambda *_args: True,
|
|
213
|
-
)
|
|
214
|
-
paths = _write_test_evidence(tmp_path)
|
|
215
|
-
skills = [
|
|
216
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
217
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
218
|
-
]
|
|
219
|
-
payload = _family_checkpoint(
|
|
220
|
-
"feature-pipeline",
|
|
221
|
-
skills,
|
|
222
|
-
semantic={
|
|
223
|
-
"repair_round": 0,
|
|
224
|
-
"authoritative_evidence_paths": paths,
|
|
225
|
-
"terminal_status": "WORKFLOW_COMPLETED",
|
|
226
|
-
},
|
|
227
|
-
)
|
|
228
|
-
payload["steps"][2]["authoritative_evidence_paths"] = paths
|
|
229
|
-
checkpoint = tmp_path / ".prizmkit" / "specs" / "feature" / "workflow-checkpoint.json"
|
|
230
|
-
checkpoint.parent.mkdir(parents=True)
|
|
231
|
-
_write(checkpoint, payload)
|
|
175
|
+
def test_completion_does_not_require_retired_evidence_paths(tmp_path):
|
|
176
|
+
payload = _family_checkpoint(tmp_path)
|
|
177
|
+
assert "authoritative_evidence_paths" not in payload["steps"][2]
|
|
178
|
+
assert "authoritative_evidence_paths" not in payload["feature_state"]
|
|
232
179
|
|
|
233
|
-
state =
|
|
180
|
+
state = validate_checkpoint_data(payload, project_root=tmp_path)
|
|
234
181
|
|
|
235
|
-
assert state.valid is True
|
|
236
|
-
assert state.all_completed is True
|
|
237
182
|
assert state.semantic_complete is True
|
|
238
|
-
assert state.semantic.
|
|
239
|
-
assert state.semantic.terminal_status == "WORKFLOW_COMPLETED"
|
|
183
|
+
assert state.semantic.test_result_valid is True
|
|
240
184
|
|
|
241
185
|
|
|
242
|
-
def
|
|
243
|
-
|
|
244
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
245
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
246
|
-
]
|
|
247
|
-
payload = _family_checkpoint("feature-pipeline", skills)
|
|
186
|
+
def test_completed_steps_without_review_or_test_results_do_not_complete(tmp_path):
|
|
187
|
+
payload = _family_checkpoint(tmp_path)
|
|
248
188
|
payload["steps"][1].pop("stage_result")
|
|
249
189
|
payload["steps"][2].pop("stage_result")
|
|
250
|
-
payload["feature_state"]["terminal_status"] = "WORKFLOW_COMPLETED"
|
|
251
190
|
|
|
252
191
|
state = validate_checkpoint_data(payload, project_root=tmp_path)
|
|
253
192
|
|
|
254
|
-
assert state.valid is True
|
|
255
|
-
assert state.all_completed is True
|
|
256
193
|
assert state.semantic_complete is False
|
|
257
194
|
assert state.semantic.error_code == "incomplete_semantic_evidence"
|
|
258
195
|
|
|
259
196
|
|
|
260
197
|
def test_family_sequence_mismatch_cannot_complete(tmp_path):
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
]
|
|
266
|
-
payload =
|
|
267
|
-
"feature-pipeline",
|
|
268
|
-
skills,
|
|
269
|
-
semantic={"authoritative_evidence_paths": paths, "terminal_status": "WORKFLOW_COMPLETED"},
|
|
270
|
-
)
|
|
271
|
-
payload["steps"][1]["authoritative_evidence_paths"] = paths
|
|
198
|
+
payload = _family_checkpoint(tmp_path)
|
|
199
|
+
payload["steps"][1], payload["steps"][2] = payload["steps"][2], payload["steps"][1]
|
|
200
|
+
payload["steps"][1]["id"] = "S02"
|
|
201
|
+
payload["steps"][2]["id"] = "S03"
|
|
202
|
+
payload["steps"][1]["depends_on"] = "S01"
|
|
203
|
+
payload["steps"][2]["depends_on"] = "S02"
|
|
272
204
|
|
|
273
205
|
state = validate_checkpoint_data(payload, project_root=tmp_path)
|
|
274
206
|
|
|
@@ -277,263 +209,99 @@ def test_family_sequence_mismatch_cannot_complete(tmp_path):
|
|
|
277
209
|
assert state.semantic.error_code == "invalid_stage_sequence"
|
|
278
210
|
|
|
279
211
|
|
|
280
|
-
def
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
]
|
|
286
|
-
payload = _family_checkpoint(
|
|
287
|
-
"feature-pipeline",
|
|
288
|
-
skills,
|
|
289
|
-
semantic={"authoritative_evidence_paths": paths, "terminal_status": "WORKFLOW_COMPLETED"},
|
|
290
|
-
)
|
|
291
|
-
payload["steps"][2]["authoritative_evidence_paths"] = paths
|
|
292
|
-
|
|
293
|
-
state = validate_checkpoint_data(payload, project_root=tmp_path)
|
|
294
|
-
|
|
295
|
-
assert state.semantic_complete is False
|
|
296
|
-
assert state.semantic.evidence_valid is False
|
|
297
|
-
assert state.semantic.error_code == "incomplete_semantic_evidence"
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def test_blocked_terminal_is_semantic_and_requires_reason(tmp_path):
|
|
301
|
-
payload = _family_checkpoint(
|
|
302
|
-
"feature-pipeline",
|
|
303
|
-
[
|
|
304
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
305
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
306
|
-
],
|
|
307
|
-
semantic={
|
|
308
|
-
"repair_scope": "production",
|
|
309
|
-
"repair_round": 3,
|
|
310
|
-
"blocked_reason": "repair_round_limit",
|
|
311
|
-
"terminal_status": "WORKFLOW_BLOCKED",
|
|
312
|
-
},
|
|
313
|
-
)
|
|
314
|
-
payload["steps"][2]["stage_result"] = "TEST_FAIL"
|
|
315
|
-
|
|
316
|
-
state = validate_checkpoint_data(payload, project_root=tmp_path)
|
|
317
|
-
|
|
318
|
-
assert state.semantic_blocked is True
|
|
319
|
-
assert state.semantic_complete is False
|
|
320
|
-
assert state.semantic.blocked_reason == "repair_round_limit"
|
|
321
|
-
assert state.cursor_signature == "blocked:repair_round_limit"
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
def test_step_progress_must_be_a_terminal_prefix(tmp_path):
|
|
325
|
-
payload = _checkpoint([
|
|
326
|
-
_step("S01", "plan", "Plan", "pending"),
|
|
327
|
-
_step("S02", "implement", "Implement", "completed", "S01"),
|
|
328
|
-
])
|
|
329
|
-
|
|
330
|
-
state = validate_checkpoint_data(payload)
|
|
331
|
-
|
|
332
|
-
assert state.valid is False
|
|
333
|
-
assert state.error_code == "invalid_step_progress"
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def test_update_checkpoint_rejects_out_of_order_transition(tmp_path):
|
|
338
|
-
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
339
|
-
_step("S01", "plan", "Plan", "pending"),
|
|
340
|
-
_step("S02", "implement", "Implement", "pending", "S01"),
|
|
341
|
-
]))
|
|
342
|
-
|
|
343
|
-
result = update_checkpoint(str(path), "S02", "in_progress")
|
|
344
|
-
|
|
345
|
-
assert result["ok"] is False
|
|
346
|
-
assert "before all predecessors" in result["error"]
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
def test_update_checkpoint_blocks_unknown_repair_scope_without_mutating_retry_budget(tmp_path):
|
|
350
|
-
payload = _family_checkpoint(
|
|
351
|
-
"feature-pipeline",
|
|
352
|
-
[
|
|
353
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
354
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
355
|
-
],
|
|
356
|
-
)
|
|
357
|
-
payload["steps"][1]["status"] = "in_progress"
|
|
358
|
-
for step in payload["steps"][2:]:
|
|
212
|
+
def test_test_needs_fixes_is_valid_domain_result_and_blocks_l4(tmp_path):
|
|
213
|
+
payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
|
|
214
|
+
payload["steps"][2]["status"] = "in_progress"
|
|
215
|
+
payload["steps"][2].pop("stage_result")
|
|
216
|
+
for step in payload["steps"][3:]:
|
|
359
217
|
step["status"] = "pending"
|
|
360
218
|
step.pop("stage_result", None)
|
|
361
|
-
|
|
219
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
362
220
|
|
|
363
221
|
result = update_checkpoint(
|
|
364
|
-
str(
|
|
365
|
-
"prizmkit-
|
|
222
|
+
str(checkpoint),
|
|
223
|
+
"prizmkit-test",
|
|
366
224
|
"failed",
|
|
367
|
-
semantic_update={"stage_result": "
|
|
225
|
+
semantic_update={"stage_result": "TEST_NEEDS_FIXES"},
|
|
368
226
|
)
|
|
369
|
-
|
|
227
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
370
228
|
|
|
371
229
|
assert result["ok"] is True
|
|
372
|
-
assert
|
|
373
|
-
assert
|
|
374
|
-
assert "
|
|
230
|
+
assert persisted["steps"][2]["stage_result"] == "TEST_NEEDS_FIXES"
|
|
231
|
+
assert persisted["feature_state"]["stage_result"] == "TEST_NEEDS_FIXES"
|
|
232
|
+
assert persisted["feature_state"]["blocked_reason"] == "test_needs_fixes"
|
|
233
|
+
assert persisted["feature_state"]["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
234
|
+
assert "authoritative_evidence_paths" not in persisted["steps"][2]
|
|
375
235
|
|
|
376
236
|
|
|
377
|
-
def
|
|
378
|
-
payload = _family_checkpoint(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
383
|
-
],
|
|
384
|
-
semantic={"repair_scope": "production", "repair_round": 3},
|
|
385
|
-
)
|
|
386
|
-
payload["steps"][1]["status"] = "in_progress"
|
|
387
|
-
for step in payload["steps"][2:]:
|
|
237
|
+
def test_test_blocked_preserves_l1_result_without_evidence_discovery(tmp_path):
|
|
238
|
+
payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
|
|
239
|
+
payload["steps"][2]["status"] = "in_progress"
|
|
240
|
+
payload["steps"][2].pop("stage_result")
|
|
241
|
+
for step in payload["steps"][3:]:
|
|
388
242
|
step["status"] = "pending"
|
|
389
243
|
step.pop("stage_result", None)
|
|
390
|
-
|
|
244
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
391
245
|
|
|
392
246
|
result = update_checkpoint(
|
|
393
|
-
str(
|
|
394
|
-
"prizmkit-
|
|
247
|
+
str(checkpoint),
|
|
248
|
+
"prizmkit-test",
|
|
395
249
|
"failed",
|
|
396
|
-
semantic_update={"stage_result": "
|
|
250
|
+
semantic_update={"stage_result": "TEST_BLOCKED", "blocked_reason": "safe environment unavailable"},
|
|
397
251
|
)
|
|
398
|
-
|
|
252
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
399
253
|
|
|
400
254
|
assert result["ok"] is True
|
|
401
|
-
assert
|
|
402
|
-
assert
|
|
403
|
-
assert
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
def test_declared_final_artifact_must_exist_for_semantic_completion(tmp_path, monkeypatch):
|
|
408
|
-
monkeypatch.setattr(
|
|
409
|
-
"prizmkit_runtime.checkpoint_state._resolve_evidence_validator",
|
|
410
|
-
lambda _root: tmp_path / "validator.py",
|
|
411
|
-
)
|
|
412
|
-
monkeypatch.setattr(
|
|
413
|
-
"prizmkit_runtime.checkpoint_state._evidence_validator_passes",
|
|
414
|
-
lambda *_args: True,
|
|
415
|
-
)
|
|
416
|
-
paths = _write_test_evidence(tmp_path)
|
|
417
|
-
skills = [
|
|
418
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
419
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
420
|
-
]
|
|
421
|
-
payload = _family_checkpoint(
|
|
422
|
-
"feature-pipeline",
|
|
423
|
-
skills,
|
|
424
|
-
semantic={"authoritative_evidence_paths": paths, "terminal_status": "WORKFLOW_COMPLETED"},
|
|
425
|
-
)
|
|
426
|
-
payload["steps"][2]["authoritative_evidence_paths"] = paths
|
|
427
|
-
payload["steps"][-1]["required_artifacts"] = [".prizmkit/specs/feature/completion-summary.json"]
|
|
428
|
-
checkpoint = tmp_path / ".prizmkit" / "specs" / "feature" / "workflow-checkpoint.json"
|
|
429
|
-
checkpoint.parent.mkdir(parents=True)
|
|
430
|
-
_write(checkpoint, payload)
|
|
431
|
-
|
|
432
|
-
missing = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
433
|
-
(checkpoint.parent / "completion-summary.json").write_text("{}", encoding="utf-8")
|
|
434
|
-
present = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
435
|
-
|
|
436
|
-
assert missing.semantic_complete is False
|
|
437
|
-
assert missing.semantic.artifacts_valid is False
|
|
438
|
-
assert present.semantic_complete is True
|
|
439
|
-
assert present.semantic.artifacts_valid is True
|
|
255
|
+
assert persisted["steps"][2]["stage_result"] == "TEST_BLOCKED"
|
|
256
|
+
assert persisted["feature_state"]["stage_result"] == "TEST_BLOCKED"
|
|
257
|
+
assert persisted["feature_state"]["blocked_reason"] == "test_blocked"
|
|
258
|
+
assert "authoritative_evidence_paths" not in persisted["feature_state"]
|
|
440
259
|
|
|
441
260
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
artifact = tmp_path / ".prizmkit" / "specs" / "feature"
|
|
445
|
-
artifact.mkdir(parents=True)
|
|
446
|
-
evidence = tmp_path / ".prizmkit" / "test" / "evidence" / "evidence-1"
|
|
447
|
-
evidence.mkdir(parents=True)
|
|
448
|
-
for name in ("manifest.json", "verdict.json", "validation.json"):
|
|
449
|
-
(evidence / name).write_text("{}", encoding="utf-8")
|
|
450
|
-
report = evidence / "test-report.md"
|
|
451
|
-
report.write_text("blocked", encoding="utf-8")
|
|
452
|
-
(artifact / "test-report-path.txt").write_text(str(report), encoding="utf-8")
|
|
453
|
-
|
|
454
|
-
payload = _family_checkpoint(
|
|
455
|
-
"feature-pipeline",
|
|
456
|
-
[
|
|
457
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
458
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
459
|
-
],
|
|
460
|
-
semantic={"artifact_dir": ".prizmkit/specs/feature"},
|
|
461
|
-
)
|
|
462
|
-
payload["feature_state"]["artifact_dir"] = ".prizmkit/specs/feature"
|
|
463
|
-
payload["steps"][1]["stage_result"] = "REVIEW_PASS"
|
|
261
|
+
def test_update_checkpoint_completes_test_pass_without_validator(tmp_path):
|
|
262
|
+
payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
|
|
464
263
|
payload["steps"][2]["status"] = "in_progress"
|
|
465
264
|
payload["steps"][2].pop("stage_result")
|
|
466
265
|
for step in payload["steps"][3:]:
|
|
467
266
|
step["status"] = "pending"
|
|
468
267
|
step.pop("stage_result", None)
|
|
469
|
-
checkpoint = _write(tmp_path / "
|
|
268
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
470
269
|
|
|
471
270
|
result = update_checkpoint(
|
|
472
271
|
str(checkpoint),
|
|
473
272
|
"prizmkit-test",
|
|
474
|
-
"
|
|
475
|
-
semantic_update={
|
|
476
|
-
"stage_result": "TEST_BLOCKED",
|
|
477
|
-
"blocked_reason": "test_blocked",
|
|
478
|
-
"terminal_status": "WORKFLOW_BLOCKED",
|
|
479
|
-
},
|
|
273
|
+
"completed",
|
|
274
|
+
semantic_update={"stage_result": "TEST_PASS"},
|
|
480
275
|
)
|
|
481
276
|
|
|
482
277
|
assert result["ok"] is True
|
|
483
|
-
|
|
484
|
-
assert
|
|
485
|
-
assert
|
|
486
|
-
assert paths["validation"] == ".prizmkit/test/evidence/evidence-1/validation.json"
|
|
278
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
279
|
+
assert persisted["steps"][2]["status"] == "completed"
|
|
280
|
+
assert persisted["steps"][2]["stage_result"] == "TEST_PASS"
|
|
487
281
|
|
|
488
282
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
paths = _write_test_evidence(tmp_path)
|
|
492
|
-
payload = _family_checkpoint(
|
|
493
|
-
"feature-pipeline",
|
|
494
|
-
[
|
|
495
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
496
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
497
|
-
],
|
|
498
|
-
)
|
|
283
|
+
def test_update_checkpoint_rejects_test_completion_for_non_pass(tmp_path):
|
|
284
|
+
payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
|
|
499
285
|
payload["steps"][2]["status"] = "in_progress"
|
|
286
|
+
payload["steps"][2].pop("stage_result")
|
|
500
287
|
for step in payload["steps"][3:]:
|
|
501
288
|
step["status"] = "pending"
|
|
502
289
|
step.pop("stage_result", None)
|
|
503
|
-
checkpoint = tmp_path / "workflow-checkpoint.json"
|
|
504
|
-
_write(checkpoint, payload)
|
|
505
|
-
monkeypatch.setattr(
|
|
506
|
-
"prizmkit_runtime.checkpoint_state._resolve_evidence_validator",
|
|
507
|
-
lambda _root: tmp_path / "validator.py",
|
|
508
|
-
)
|
|
509
|
-
monkeypatch.setattr(
|
|
510
|
-
"prizmkit_runtime.checkpoint_state._evidence_validator_passes",
|
|
511
|
-
lambda *_args: False,
|
|
512
|
-
)
|
|
290
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
513
291
|
|
|
514
292
|
result = update_checkpoint(
|
|
515
293
|
str(checkpoint),
|
|
516
294
|
"prizmkit-test",
|
|
517
295
|
"completed",
|
|
518
|
-
semantic_update={
|
|
519
|
-
"stage_result": "TEST_PASS",
|
|
520
|
-
"authoritative_evidence_paths": paths,
|
|
521
|
-
},
|
|
296
|
+
semantic_update={"stage_result": "TEST_NEEDS_FIXES"},
|
|
522
297
|
)
|
|
523
298
|
|
|
524
299
|
assert result["ok"] is False
|
|
525
|
-
assert "
|
|
526
|
-
|
|
300
|
+
assert "requires TEST_PASS" in result["error"]
|
|
527
301
|
|
|
528
302
|
|
|
529
303
|
def test_update_checkpoint_rejects_test_before_review_pass(tmp_path):
|
|
530
|
-
payload = _family_checkpoint(
|
|
531
|
-
"feature-pipeline",
|
|
532
|
-
[
|
|
533
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
534
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
535
|
-
],
|
|
536
|
-
)
|
|
304
|
+
payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
|
|
537
305
|
payload["steps"][1]["status"] = "skipped"
|
|
538
306
|
payload["steps"][1].pop("stage_result", None)
|
|
539
307
|
payload["steps"][2]["status"] = "pending"
|
|
@@ -549,13 +317,7 @@ def test_update_checkpoint_rejects_test_before_review_pass(tmp_path):
|
|
|
549
317
|
|
|
550
318
|
|
|
551
319
|
def test_update_checkpoint_rejects_finalization_before_test_pass(tmp_path):
|
|
552
|
-
payload = _family_checkpoint(
|
|
553
|
-
"feature-pipeline",
|
|
554
|
-
[
|
|
555
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
556
|
-
"prizmkit-retrospective", "prizmkit-committer", "completion-summary",
|
|
557
|
-
],
|
|
558
|
-
)
|
|
320
|
+
payload = _family_checkpoint(tmp_path, semantic={}, terminal=False)
|
|
559
321
|
payload["steps"][2]["status"] = "skipped"
|
|
560
322
|
payload["steps"][2].pop("stage_result", None)
|
|
561
323
|
payload["steps"][3]["status"] = "pending"
|
|
@@ -568,3 +330,144 @@ def test_update_checkpoint_rejects_finalization_before_test_pass(tmp_path):
|
|
|
568
330
|
|
|
569
331
|
assert result["ok"] is False
|
|
570
332
|
assert "TEST_PASS" in result["error"]
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_declared_final_artifact_must_exist_for_completion(tmp_path):
|
|
336
|
+
payload = _family_checkpoint(tmp_path)
|
|
337
|
+
payload["steps"][-1]["required_artifacts"] = [
|
|
338
|
+
".prizmkit/specs/feature/completion-summary.json"
|
|
339
|
+
]
|
|
340
|
+
checkpoint = _write(
|
|
341
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
342
|
+
payload,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
missing = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
346
|
+
(checkpoint.parent / "completion-summary.json").write_text("{}", encoding="utf-8")
|
|
347
|
+
present = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
348
|
+
|
|
349
|
+
assert missing.semantic_complete is False
|
|
350
|
+
assert missing.semantic.artifacts_valid is False
|
|
351
|
+
assert present.semantic_complete is True
|
|
352
|
+
assert present.semantic.artifacts_valid is True
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def test_completion_rejects_invalid_test_result_projection(tmp_path):
|
|
356
|
+
payload = _family_checkpoint(tmp_path)
|
|
357
|
+
checkpoint = _write(
|
|
358
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
359
|
+
payload,
|
|
360
|
+
)
|
|
361
|
+
result_path = checkpoint.parent / "test-result.json"
|
|
362
|
+
result = json.loads(result_path.read_text(encoding="utf-8"))
|
|
363
|
+
result["main_review_rounds"] = 0
|
|
364
|
+
result_path.write_text(json.dumps(result), encoding="utf-8")
|
|
365
|
+
|
|
366
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
367
|
+
|
|
368
|
+
assert state.semantic_complete is False
|
|
369
|
+
assert state.semantic.artifacts_valid is False
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def test_completion_rejects_inconsistent_independent_review_metadata(tmp_path):
|
|
373
|
+
payload = _family_checkpoint(tmp_path)
|
|
374
|
+
checkpoint = _write(
|
|
375
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
376
|
+
payload,
|
|
377
|
+
)
|
|
378
|
+
result_path = checkpoint.parent / "test-result.json"
|
|
379
|
+
result = json.loads(result_path.read_text(encoding="utf-8"))
|
|
380
|
+
result["independent_review"] = {
|
|
381
|
+
"status": "completed",
|
|
382
|
+
"responses": 0,
|
|
383
|
+
"downgrade_reason": None,
|
|
384
|
+
"final_state_rechecked": True,
|
|
385
|
+
}
|
|
386
|
+
result_path.write_text(json.dumps(result), encoding="utf-8")
|
|
387
|
+
|
|
388
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
389
|
+
|
|
390
|
+
assert state.semantic_complete is False
|
|
391
|
+
assert state.semantic.artifacts_valid is False
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def test_completion_rejects_mismatched_test_report_result(tmp_path):
|
|
395
|
+
payload = _family_checkpoint(tmp_path)
|
|
396
|
+
checkpoint = _write(
|
|
397
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
398
|
+
payload,
|
|
399
|
+
)
|
|
400
|
+
(checkpoint.parent / "test-report.md").write_text(
|
|
401
|
+
"# Test Report\n\n## Final Result\n\n- Result: TEST_BLOCKED\n",
|
|
402
|
+
encoding="utf-8",
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
406
|
+
|
|
407
|
+
assert state.semantic_complete is False
|
|
408
|
+
assert state.semantic.artifacts_valid is False
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def test_completion_rejects_report_with_result_only_outside_final_section(tmp_path):
|
|
412
|
+
payload = _family_checkpoint(tmp_path)
|
|
413
|
+
checkpoint = _write(
|
|
414
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
415
|
+
payload,
|
|
416
|
+
)
|
|
417
|
+
(checkpoint.parent / "test-report.md").write_text(
|
|
418
|
+
"# Test Report\n\n## Final Result\n\n- Result: TEST_BLOCKED\n\n"
|
|
419
|
+
"## Remaining Risks and Unresolved Items\n\n- Expected eventual result: TEST_PASS\n",
|
|
420
|
+
encoding="utf-8",
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
424
|
+
|
|
425
|
+
assert state.semantic_complete is False
|
|
426
|
+
assert state.semantic.artifacts_valid is False
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def test_completion_rejects_test_artifacts_outside_project(tmp_path):
|
|
430
|
+
outside = tmp_path.parent / f"{tmp_path.name}-outside"
|
|
431
|
+
outside.mkdir()
|
|
432
|
+
_write_test_artifacts(outside)
|
|
433
|
+
payload = _family_checkpoint(tmp_path)
|
|
434
|
+
test_step = payload["steps"][2]
|
|
435
|
+
test_step["required_artifacts"] = [
|
|
436
|
+
f"../{outside.name}/.prizmkit/specs/feature/test-report.md",
|
|
437
|
+
f"../{outside.name}/.prizmkit/specs/feature/test-result.json",
|
|
438
|
+
]
|
|
439
|
+
checkpoint = _write(
|
|
440
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
441
|
+
payload,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
445
|
+
|
|
446
|
+
assert state.semantic_complete is False
|
|
447
|
+
assert state.semantic.artifacts_valid is False
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def test_review_outer_repair_cap_remains_runtime_owned(tmp_path):
|
|
451
|
+
payload = _family_checkpoint(
|
|
452
|
+
tmp_path, semantic={"repair_scope": "production", "repair_round": 3},
|
|
453
|
+
terminal=False,
|
|
454
|
+
)
|
|
455
|
+
payload["steps"][1]["status"] = "in_progress"
|
|
456
|
+
payload["steps"][1].pop("stage_result")
|
|
457
|
+
for step in payload["steps"][2:]:
|
|
458
|
+
step["status"] = "pending"
|
|
459
|
+
step.pop("stage_result", None)
|
|
460
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
461
|
+
|
|
462
|
+
result = update_checkpoint(
|
|
463
|
+
str(checkpoint),
|
|
464
|
+
"prizmkit-code-review",
|
|
465
|
+
"failed",
|
|
466
|
+
semantic_update={"stage_result": "REVIEW_NEEDS_FIXES", "repair_scope": "production"},
|
|
467
|
+
)
|
|
468
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
469
|
+
|
|
470
|
+
assert result["ok"] is True
|
|
471
|
+
assert persisted["feature_state"]["repair_round"] == 3
|
|
472
|
+
assert persisted["feature_state"]["blocked_reason"] == "repair_round_limit"
|
|
473
|
+
assert persisted["feature_state"]["terminal_status"] == "WORKFLOW_BLOCKED"
|