prizmkit 1.1.141 → 1.1.143
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/README.md +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +124 -4
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +13 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +4 -1
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +20 -10
- package/bundled/dev-pipeline/scripts/check-session-status.py +11 -1
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +2 -2
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +2 -2
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +2 -2
- package/bundled/dev-pipeline/scripts/prompt_framework.py +12 -1
- package/bundled/dev-pipeline/scripts/update-bug-status.py +20 -0
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +38 -9
- package/bundled/dev-pipeline/scripts/update-feature-status.py +20 -0
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +20 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +1 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +5 -1
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +1 -1
- package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +231 -0
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +26 -1
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +4 -2
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +5 -2
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +147 -4
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/prizmkit/SKILL.md +6 -6
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +23 -11
- package/bundled/skills/prizmkit-code-review/SKILL.md +4 -4
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +23 -11
- package/bundled/skills/prizmkit-committer/SKILL.md +5 -4
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +23 -11
- package/bundled/skills/prizmkit-implement/SKILL.md +3 -2
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +23 -11
- package/bundled/skills/prizmkit-plan/SKILL.md +2 -1
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +23 -11
- package/bundled/skills/prizmkit-retrospective/SKILL.md +6 -4
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +23 -11
- package/bundled/skills/prizmkit-test/SKILL.md +1 -1
- package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
- package/bundled/skills/prizmkit-workflow/SKILL.md +13 -13
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +23 -11
- package/bundled/templates/hooks/commit-intent-status.py +50 -0
- package/bundled/templates/hooks/commit-intent.json +2 -2
- package/bundled/templates/hooks/post-command-prizm-drift.py +44 -0
- package/package.json +1 -1
- package/src/scaffold.js +2 -0
|
@@ -237,7 +237,7 @@ For each Verification Gate from the Task Contract, write one evidence line to Im
|
|
|
237
237
|
|
|
238
238
|
Review the completed implementation before the full Feature test gate. Run `/prizmkit-code-review artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/` and follow the skill's current review contract for review execution, repairs, verification, evidence, and handoff. Do not invoke another review skill or add a second review path outside that contract.
|
|
239
239
|
|
|
240
|
-
- `REVIEW_PASS` → record `stage=code-review`, `status=
|
|
240
|
+
- `REVIEW_PASS` → record `stage=code-review`, `status=completed`, `stage_result=REVIEW_PASS`, `next_stage=prizmkit-test`, and `resume_from=prizmkit-test`; then run the test gate.
|
|
241
241
|
- `REVIEW_NEEDS_FIXES` → increment the shared outer `repair_round` and route `prizmkit-implement` → `prizmkit-code-review` → `prizmkit-test`.
|
|
242
242
|
- Stop as `WORKFLOW_BLOCKED` when the outer repair round is already three. The Main-Agent review loop has its separate limit of up to ten completed rounds.
|
|
243
243
|
|
|
@@ -16,4 +16,4 @@ Missing required evidence, an unsafe repair, a failed verification, or failure t
|
|
|
16
16
|
- `REVIEW_NEEDS_FIXES` → route through bounded implementation/review/test repair; do not commit.
|
|
17
17
|
- `REVIEW_INCOMPLETE` or `REVIEW_INVALID` → inspect progress once, write `failure-log.md`, and stop. Do not accept old or incidental verdict text and do not synthesize a passing result outside the skill.
|
|
18
18
|
|
|
19
|
-
**Checkpoint update**: Set step `prizmkit-code-review` to `completed` with `stage_result=REVIEW_PASS` only for an accepted valid Final Result.
|
|
19
|
+
**Checkpoint update**: Set step `prizmkit-code-review` to `completed` with `stage_result=REVIEW_PASS` only for an accepted valid Final Result. Set it to `failed` with `stage_result=REVIEW_NEEDS_FIXES` when the review returns known unresolved findings.
|
|
@@ -19,8 +19,12 @@ Use this helper to update checkpoint status. Do not hand-edit `workflow-checkpoi
|
|
|
19
19
|
|
|
20
20
|
- Use the step `skill` value such as `prizmkit-implement`, or the step id such as `S04`.
|
|
21
21
|
- Omit `--stage-result` for stages without a domain result.
|
|
22
|
+
- Complete `prizmkit-plan` with `--status completed --stage-result PLAN_READY`.
|
|
23
|
+
- Complete `prizmkit-implement` with `--status completed --stage-result IMPLEMENTED`.
|
|
22
24
|
- Complete `prizmkit-code-review` only with `--status completed --stage-result REVIEW_PASS`.
|
|
23
25
|
- Complete `prizmkit-test` only after validating the sibling report/result pair, with `--status completed --stage-result TEST_PASS`.
|
|
26
|
+
- Complete `prizmkit-retrospective` with `--status completed --stage-result RETRO_COMPLETE`.
|
|
27
|
+
- Record commit authorization wait with `--status in_progress --stage-result COMMIT_PENDING`; after the local commit, use `--status completed --stage-result COMMITTED`.
|
|
24
28
|
- For `TEST_NEEDS_FIXES` or `TEST_BLOCKED`, use `--status failed --stage-result <result>` and preserve its report/result and blocker; never mark the test step completed.
|
|
25
29
|
- Omit `--note` when there is no useful failure or handoff note.
|
|
26
30
|
|
|
@@ -28,7 +32,7 @@ Use this helper to update checkpoint status. Do not hand-edit `workflow-checkpoi
|
|
|
28
32
|
|
|
29
33
|
1. Before each skill, verify its predecessor is `completed` or `skipped`.
|
|
30
34
|
2. Starting a skill marks its step `in_progress`; a valid success marks it `completed`; a non-pass records its truthful `stage_result` and leaves later stages incomplete.
|
|
31
|
-
3. Pipeline metadata owns only `current_stage`, `next_stage`, `stage_result`, `repair_scope`, `repair_round`, `blocked_reason`, and `terminal_status
|
|
35
|
+
3. Pipeline metadata owns only `current_stage`, `next_stage`, `stage_result`, `repair_scope`, `repair_round`, `blocked_reason`, and `terminal_status`; lifecycle `status` belongs to each checkpoint step.
|
|
32
36
|
4. Skills own their reports and terminal results. The pipeline must not duplicate a skill's internal state machine or require evidence manifests, hashes, or attestations.
|
|
33
37
|
5. Preserve all reports and session artifacts on recovery. Never replace skill artifacts with the pipeline checkpoint or merge their schemas.
|
|
34
38
|
6. `TEST_NEEDS_FIXES` is a known testing-domain non-pass. `TEST_BLOCKED` records terminal `WORKFLOW_BLOCKED` with a deterministic reason. Neither is an AI CLI crash.
|
|
@@ -4,8 +4,8 @@ Review the completed implementation before the full Feature test gate runs. A re
|
|
|
4
4
|
|
|
5
5
|
Use `artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/` so `/prizmkit-code-review` can read the spec, plan, context snapshot, implementation log, and current diff. Follow the skill's current review contract for review execution, repairs, verification, evidence, and handoff. Do not invoke another review skill or add a second review path outside that contract.
|
|
6
6
|
|
|
7
|
-
- `REVIEW_PASS` → update checkpoint metadata with `stage=code-review`, `status=
|
|
8
|
-
- `REVIEW_NEEDS_FIXES` → preserve findings, increment the shared outer `repair_round`, route `prizmkit-implement` → `prizmkit-code-review` → `prizmkit-test`, and do not accept any test result from before the repaired review.
|
|
7
|
+
- `REVIEW_PASS` → update checkpoint metadata with `stage=code-review`, `status=completed`, `stage_result=REVIEW_PASS`, `next_stage=prizmkit-test`, and `resume_from=prizmkit-test`; then run the full test gate.
|
|
8
|
+
- `REVIEW_NEEDS_FIXES` → update the review step with `status=failed` and `stage_result=REVIEW_NEEDS_FIXES`, preserve findings, increment the shared outer `repair_round`, route `prizmkit-implement` → `prizmkit-code-review` → `prizmkit-test`, and do not accept any test result from before the repaired review.
|
|
9
9
|
- If the outer repair round is already three, record `WORKFLOW_BLOCKED`; do not start another repair route.
|
|
10
10
|
|
|
11
11
|
Missing required evidence, unsafe repair, failed verification, or failure to converge under the review contract produces `REVIEW_NEEDS_FIXES`.
|
|
@@ -16,4 +16,4 @@ Missing required evidence, an unsafe repair, a failed verification, or failure t
|
|
|
16
16
|
|
|
17
17
|
Verify directly that the review checks behavior preservation, public API compatibility, imports, tests, and rollback assumptions.
|
|
18
18
|
|
|
19
|
-
**Checkpoint update**: Set step `prizmkit-code-review` to `completed` with `stage_result=REVIEW_PASS` only for an accepted valid Final Result. Do not complete the checkpoint for incomplete or invalid reports.
|
|
19
|
+
**Checkpoint update**: Set step `prizmkit-code-review` to `completed` with `stage_result=REVIEW_PASS` only for an accepted valid Final Result. Set it to `failed` with `stage_result=REVIEW_NEEDS_FIXES` when the review returns known unresolved findings. Do not complete the checkpoint for incomplete or invalid reports.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"status": {
|
|
17
17
|
"type": "string",
|
|
18
|
-
"enum": ["success", "partial", "failed", "commit_missing", "docs_missing", "merge_conflict", "context_overflow"]
|
|
18
|
+
"enum": ["success", "partial", "failed", "crashed", "timed_out", "infra_error", "commit_missing", "docs_missing", "merge_conflict", "context_overflow", "stalled_context_continuation", "workflow_blocked", "workflow_skipped"]
|
|
19
19
|
},
|
|
20
20
|
"completed_phases": {
|
|
21
21
|
"type": "array",
|
|
@@ -5,6 +5,9 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
|
|
7
7
|
from prizmkit_runtime.checkpoint_state import (
|
|
8
|
+
VALID_CHECKPOINT_STATUSES,
|
|
9
|
+
VALID_STAGE_RESULTS,
|
|
10
|
+
expected_status_for_stage_result,
|
|
8
11
|
load_checkpoint_state,
|
|
9
12
|
read_checkpoint_cursor,
|
|
10
13
|
validate_checkpoint_data,
|
|
@@ -143,6 +146,169 @@ def test_structural_validation_error_codes(tmp_path):
|
|
|
143
146
|
assert read_checkpoint_cursor(path) is None
|
|
144
147
|
|
|
145
148
|
|
|
149
|
+
def test_domain_results_map_to_lifecycle_statuses():
|
|
150
|
+
expected = {
|
|
151
|
+
"PLAN_READY": "completed",
|
|
152
|
+
"PLAN_BLOCKED": "failed",
|
|
153
|
+
"IMPLEMENTED": "completed",
|
|
154
|
+
"IMPLEMENT_REPAIR": "failed",
|
|
155
|
+
"IMPLEMENT_BLOCKED": "failed",
|
|
156
|
+
"REVIEW_PASS": "completed",
|
|
157
|
+
"REVIEW_NEEDS_FIXES": "failed",
|
|
158
|
+
"TEST_PASS": "completed",
|
|
159
|
+
"TEST_NEEDS_FIXES": "failed",
|
|
160
|
+
"TEST_BLOCKED": "failed",
|
|
161
|
+
"RETRO_COMPLETE": "completed",
|
|
162
|
+
"RETRO_BLOCKED": "failed",
|
|
163
|
+
"COMMIT_PENDING": "in_progress",
|
|
164
|
+
"COMMITTED": "completed",
|
|
165
|
+
"COMMIT_BLOCKED": "failed",
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
assert VALID_CHECKPOINT_STATUSES == {
|
|
169
|
+
"pending", "in_progress", "completed", "failed", "skipped",
|
|
170
|
+
}
|
|
171
|
+
for result, status in expected.items():
|
|
172
|
+
assert expected_status_for_stage_result(result) == status
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_structural_validation_rejects_domain_result_as_status(tmp_path):
|
|
176
|
+
for domain_status in [
|
|
177
|
+
"PLAN_READY", "IMPLEMENTED", "REVIEW_PASS", "REVIEW_NEEDS_FIXES",
|
|
178
|
+
"TEST_PASS", "TEST_NEEDS_FIXES", "TEST_BLOCKED", "RETRO_COMPLETE",
|
|
179
|
+
"COMMIT_PENDING", "COMMITTED",
|
|
180
|
+
]:
|
|
181
|
+
state = validate_checkpoint_data(
|
|
182
|
+
_checkpoint([_step("S01", status=domain_status)]),
|
|
183
|
+
path=tmp_path / f"{domain_status}.json",
|
|
184
|
+
)
|
|
185
|
+
assert state.valid is False
|
|
186
|
+
assert state.error_code == "invalid_status"
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_structural_validation_rejects_status_result_mismatch(tmp_path):
|
|
190
|
+
step = _step("S01", "prizmkit-code-review", "Review", "failed")
|
|
191
|
+
step["stage_result"] = "REVIEW_PASS"
|
|
192
|
+
|
|
193
|
+
state = validate_checkpoint_data(
|
|
194
|
+
_checkpoint([step]),
|
|
195
|
+
path=tmp_path / "mismatch.json",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
assert state.valid is False
|
|
199
|
+
assert state.error_code == "stage_result_status_mismatch"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_structural_validation_rejects_stage_result_owner_mismatch(tmp_path):
|
|
203
|
+
for skill in ("prizmkit-test", "custom-stage"):
|
|
204
|
+
step = _step("S01", skill, "Step", "completed")
|
|
205
|
+
step["stage_result"] = "REVIEW_PASS"
|
|
206
|
+
|
|
207
|
+
state = validate_checkpoint_data(
|
|
208
|
+
_checkpoint([step]),
|
|
209
|
+
path=tmp_path / f"{skill}-owner-mismatch.json",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
assert state.valid is False
|
|
213
|
+
assert state.error_code == "stage_result_owner_mismatch"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_semantic_metadata_rejects_legacy_or_mismatched_status(tmp_path):
|
|
217
|
+
invalid = [
|
|
218
|
+
({"status": "TEST_PASS", "stage_result": "TEST_PASS"}, "invalid_semantic_status"),
|
|
219
|
+
({"status": "failed", "stage_result": "TEST_PASS"}, "semantic_stage_result_status_mismatch"),
|
|
220
|
+
({"status": "completed", "stage_result": "UNKNOWN_RESULT"}, "invalid_semantic_stage_result"),
|
|
221
|
+
({"stage": "test", "status": "completed", "stage_result": "REVIEW_PASS"}, "semantic_stage_result_owner_mismatch"),
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
for index, (semantic, error_code) in enumerate(invalid):
|
|
225
|
+
state = validate_checkpoint_data(
|
|
226
|
+
_checkpoint([_step("S01")]) | {"feature_state": semantic},
|
|
227
|
+
path=tmp_path / f"semantic-status-{index}.json",
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
assert state.valid is True
|
|
231
|
+
assert state.semantic.error_code == error_code
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def test_update_checkpoint_reenters_failed_stage_without_stale_result(tmp_path):
|
|
235
|
+
payload = _family_checkpoint(
|
|
236
|
+
tmp_path,
|
|
237
|
+
semantic={
|
|
238
|
+
"stage": "code-review",
|
|
239
|
+
"status": "failed",
|
|
240
|
+
"stage_result": "REVIEW_NEEDS_FIXES",
|
|
241
|
+
"repair_scope": "production",
|
|
242
|
+
"repair_round": 1,
|
|
243
|
+
},
|
|
244
|
+
terminal=False,
|
|
245
|
+
)
|
|
246
|
+
payload["steps"][1]["status"] = "failed"
|
|
247
|
+
payload["steps"][1]["stage_result"] = "REVIEW_NEEDS_FIXES"
|
|
248
|
+
for step in payload["steps"][2:]:
|
|
249
|
+
step["status"] = "pending"
|
|
250
|
+
step.pop("stage_result", None)
|
|
251
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
252
|
+
|
|
253
|
+
result = update_checkpoint(
|
|
254
|
+
str(checkpoint),
|
|
255
|
+
"prizmkit-code-review",
|
|
256
|
+
"in_progress",
|
|
257
|
+
)
|
|
258
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
259
|
+
|
|
260
|
+
assert result["ok"] is True
|
|
261
|
+
assert result["semantic"]["current_stage"] == "prizmkit-code-review"
|
|
262
|
+
assert result["semantic"]["next_stage"] == "prizmkit-code-review"
|
|
263
|
+
assert persisted["steps"][1]["status"] == "in_progress"
|
|
264
|
+
assert "stage_result" not in persisted["steps"][1]
|
|
265
|
+
assert persisted["feature_state"]["stage"] == "code-review"
|
|
266
|
+
assert persisted["feature_state"]["status"] == "in_progress"
|
|
267
|
+
assert "stage_result" not in persisted["feature_state"]
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_checkpoint_updater_supports_every_owned_stage_result(tmp_path):
|
|
271
|
+
cases = [
|
|
272
|
+
("prizmkit-plan", "PLAN_READY", "completed"),
|
|
273
|
+
("prizmkit-implement", "IMPLEMENTED", "completed"),
|
|
274
|
+
("prizmkit-code-review", "REVIEW_PASS", "completed"),
|
|
275
|
+
("prizmkit-test", "TEST_BLOCKED", "failed"),
|
|
276
|
+
("prizmkit-retrospective", "RETRO_COMPLETE", "completed"),
|
|
277
|
+
("prizmkit-committer", "COMMIT_PENDING", "in_progress"),
|
|
278
|
+
("prizmkit-committer", "COMMITTED", "completed"),
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
assert {stage_result for _skill, stage_result, _status in cases} <= VALID_STAGE_RESULTS
|
|
282
|
+
for index, (skill, stage_result, status) in enumerate(cases):
|
|
283
|
+
step = _step("S01", skill, skill, "pending")
|
|
284
|
+
checkpoint = _write(
|
|
285
|
+
tmp_path / f"owned-result-{index}.json",
|
|
286
|
+
_checkpoint([step]),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
result = update_checkpoint(
|
|
290
|
+
str(checkpoint),
|
|
291
|
+
skill,
|
|
292
|
+
status,
|
|
293
|
+
semantic_update={"stage_result": stage_result},
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
assert result["ok"] is True
|
|
297
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
298
|
+
assert persisted["steps"][0]["status"] == status
|
|
299
|
+
assert persisted["steps"][0]["stage_result"] == stage_result
|
|
300
|
+
assert persisted["feature_state"]["stage"] == {
|
|
301
|
+
"prizmkit-plan": "plan",
|
|
302
|
+
"prizmkit-implement": "implement",
|
|
303
|
+
"prizmkit-code-review": "code-review",
|
|
304
|
+
"prizmkit-test": "test",
|
|
305
|
+
"prizmkit-retrospective": "retrospective",
|
|
306
|
+
"prizmkit-committer": "committer",
|
|
307
|
+
}[skill]
|
|
308
|
+
assert persisted["feature_state"]["status"] == status
|
|
309
|
+
assert persisted["feature_state"]["stage_result"] == stage_result
|
|
310
|
+
|
|
311
|
+
|
|
146
312
|
def test_duplicate_skills_require_exact_step_id(tmp_path):
|
|
147
313
|
path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
|
|
148
314
|
_step("S01", "prizmkit-committer", "First", "completed"),
|
|
@@ -305,6 +471,7 @@ def test_update_checkpoint_rejects_test_before_review_pass(tmp_path):
|
|
|
305
471
|
payload["steps"][1]["status"] = "skipped"
|
|
306
472
|
payload["steps"][1].pop("stage_result", None)
|
|
307
473
|
payload["steps"][2]["status"] = "pending"
|
|
474
|
+
payload["steps"][2].pop("stage_result", None)
|
|
308
475
|
for step in payload["steps"][3:]:
|
|
309
476
|
step["status"] = "pending"
|
|
310
477
|
step.pop("stage_result", None)
|
|
@@ -391,6 +558,42 @@ def test_completion_rejects_inconsistent_independent_review_metadata(tmp_path):
|
|
|
391
558
|
assert state.semantic.artifacts_valid is False
|
|
392
559
|
|
|
393
560
|
|
|
561
|
+
def test_completion_accepts_legacy_terminal_result_report(tmp_path):
|
|
562
|
+
payload = _family_checkpoint(tmp_path)
|
|
563
|
+
checkpoint = _write(
|
|
564
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
565
|
+
payload,
|
|
566
|
+
)
|
|
567
|
+
(checkpoint.parent / "test-report.md").write_text(
|
|
568
|
+
"# Test Report\n\n## Terminal Result\n\n- Result: TEST_PASS\n\n"
|
|
569
|
+
"## Final Decision\n\nTEST_PASS\n",
|
|
570
|
+
encoding="utf-8",
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
574
|
+
|
|
575
|
+
assert state.semantic_complete is True
|
|
576
|
+
assert state.semantic.artifacts_valid is True
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def test_completion_rejects_conflicting_supported_report_results(tmp_path):
|
|
580
|
+
payload = _family_checkpoint(tmp_path)
|
|
581
|
+
checkpoint = _write(
|
|
582
|
+
tmp_path / ".prizmkit/specs/feature/workflow-checkpoint.json",
|
|
583
|
+
payload,
|
|
584
|
+
)
|
|
585
|
+
(checkpoint.parent / "test-report.md").write_text(
|
|
586
|
+
"# Test Report\n\n## Terminal Result\n\n- Result: TEST_PASS\n\n"
|
|
587
|
+
"## Final Decision\n\nTEST_BLOCKED\n",
|
|
588
|
+
encoding="utf-8",
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
state = load_checkpoint_state(checkpoint, project_root=tmp_path)
|
|
592
|
+
|
|
593
|
+
assert state.semantic_complete is False
|
|
594
|
+
assert state.semantic.artifacts_valid is False
|
|
595
|
+
|
|
596
|
+
|
|
394
597
|
def test_completion_rejects_mismatched_test_report_result(tmp_path):
|
|
395
598
|
payload = _family_checkpoint(tmp_path)
|
|
396
599
|
checkpoint = _write(
|
|
@@ -447,6 +650,34 @@ def test_completion_rejects_test_artifacts_outside_project(tmp_path):
|
|
|
447
650
|
assert state.semantic.artifacts_valid is False
|
|
448
651
|
|
|
449
652
|
|
|
653
|
+
def test_review_needs_fixes_preserves_implementation_repair_route(tmp_path):
|
|
654
|
+
payload = _family_checkpoint(
|
|
655
|
+
tmp_path,
|
|
656
|
+
semantic={"repair_scope": "production", "repair_round": 0},
|
|
657
|
+
terminal=False,
|
|
658
|
+
)
|
|
659
|
+
payload["steps"][1]["status"] = "in_progress"
|
|
660
|
+
payload["steps"][1].pop("stage_result")
|
|
661
|
+
for step in payload["steps"][2:]:
|
|
662
|
+
step["status"] = "pending"
|
|
663
|
+
step.pop("stage_result", None)
|
|
664
|
+
checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
|
|
665
|
+
|
|
666
|
+
result = update_checkpoint(
|
|
667
|
+
str(checkpoint),
|
|
668
|
+
"prizmkit-code-review",
|
|
669
|
+
"failed",
|
|
670
|
+
semantic_update={"stage_result": "REVIEW_NEEDS_FIXES", "repair_scope": "production"},
|
|
671
|
+
)
|
|
672
|
+
persisted = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
673
|
+
|
|
674
|
+
assert result["ok"] is True
|
|
675
|
+
assert result["semantic"]["next_stage"] == "prizmkit-implement"
|
|
676
|
+
assert persisted["feature_state"]["repair_round"] == 1
|
|
677
|
+
assert persisted["feature_state"]["current_stage"] == "prizmkit-code-review"
|
|
678
|
+
assert persisted["feature_state"]["next_stage"] == "prizmkit-implement"
|
|
679
|
+
|
|
680
|
+
|
|
450
681
|
def test_review_outer_repair_cap_remains_runtime_owned(tmp_path):
|
|
451
682
|
payload = _family_checkpoint(
|
|
452
683
|
tmp_path, semantic={"repair_scope": "production", "repair_round": 3},
|
|
@@ -31,6 +31,7 @@ from generate_bootstrap_prompt import (
|
|
|
31
31
|
main as generate_bootstrap_main,
|
|
32
32
|
)
|
|
33
33
|
from continuation import append_continuation_handoff, checkpoint_cursor
|
|
34
|
+
from prompt_framework import pipeline_checkpoint_metadata
|
|
34
35
|
from utils import resolve_prompt_platform
|
|
35
36
|
|
|
36
37
|
REMOVED_MAX_LOG_ENV = "MAX_" + "LOG_SIZE"
|
|
@@ -472,8 +473,13 @@ class TestScopedFeatureTestGate:
|
|
|
472
473
|
assert "{{CHECKPOINT_PYTHON_CMD}}" in rendered
|
|
473
474
|
assert "--checkpoint-path {{CHECKPOINT_PATH}}" in rendered
|
|
474
475
|
assert "--stage-result <optional-domain-result>" in rendered
|
|
476
|
+
assert "--status completed --stage-result PLAN_READY" in rendered
|
|
477
|
+
assert "--status completed --stage-result IMPLEMENTED" in rendered
|
|
475
478
|
assert "--status completed --stage-result REVIEW_PASS" in rendered
|
|
476
479
|
assert "--status completed --stage-result TEST_PASS" in rendered
|
|
480
|
+
assert "--status completed --stage-result RETRO_COMPLETE" in rendered
|
|
481
|
+
assert "--status in_progress --stage-result COMMIT_PENDING" in rendered
|
|
482
|
+
assert "--status completed --stage-result COMMITTED" in rendered
|
|
477
483
|
assert "Do not hand-edit `workflow-checkpoint.json` directly." in rendered
|
|
478
484
|
assert "authoritative_evidence_paths" not in rendered
|
|
479
485
|
|
|
@@ -562,14 +568,33 @@ class TestScopedFeatureTestGate:
|
|
|
562
568
|
assert blocked["blocked_reason"] == "test_blocked"
|
|
563
569
|
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
564
570
|
|
|
571
|
+
def test_pipeline_checkpoint_metadata_rejects_legacy_or_mismatched_status(self):
|
|
572
|
+
invalid = [
|
|
573
|
+
{"status": "TEST_PASS", "stage_result": "TEST_PASS"},
|
|
574
|
+
{"status": "failed", "stage_result": "TEST_PASS"},
|
|
575
|
+
{"status": "completed", "stage_result": "UNKNOWN_RESULT"},
|
|
576
|
+
]
|
|
577
|
+
|
|
578
|
+
for values in invalid:
|
|
579
|
+
try:
|
|
580
|
+
pipeline_checkpoint_metadata(
|
|
581
|
+
".prizmkit/specs/contract-test",
|
|
582
|
+
stage="test",
|
|
583
|
+
**values,
|
|
584
|
+
)
|
|
585
|
+
except ValueError:
|
|
586
|
+
continue
|
|
587
|
+
raise AssertionError("invalid lifecycle metadata was accepted: {}".format(values))
|
|
588
|
+
|
|
565
589
|
def test_feature_checkpoint_metadata_has_no_evidence_paths(self):
|
|
566
590
|
metadata = feature_checkpoint_metadata(
|
|
567
591
|
".prizmkit/specs/123-scope-test",
|
|
568
|
-
stage="test", status="
|
|
592
|
+
stage="test", status="failed",
|
|
569
593
|
stage_result="TEST_NEEDS_FIXES", repair_round=0,
|
|
570
594
|
next_stage=None, resume_from=None,
|
|
571
595
|
)
|
|
572
596
|
|
|
597
|
+
assert metadata["status"] == "failed"
|
|
573
598
|
assert metadata["stage_result"] == "TEST_NEEDS_FIXES"
|
|
574
599
|
assert "authoritative_evidence_paths" not in metadata
|
|
575
600
|
assert metadata["artifact_dir"] == ".prizmkit/specs/123-scope-test"
|
|
@@ -472,7 +472,7 @@ class TestBugfixCheckpointGeneration:
|
|
|
472
472
|
existing["bugfix_state"] = bugfix_checkpoint_metadata(
|
|
473
473
|
".prizmkit/bugfix/B-001",
|
|
474
474
|
stage="test",
|
|
475
|
-
status="
|
|
475
|
+
status="failed",
|
|
476
476
|
stage_result="TEST_BLOCKED",
|
|
477
477
|
repair_round=2,
|
|
478
478
|
next_stage="prizmkit-test",
|
|
@@ -481,6 +481,7 @@ class TestBugfixCheckpointGeneration:
|
|
|
481
481
|
|
|
482
482
|
merged = merge_bugfix_checkpoint_state(existing, fresh, str(tmp_path))
|
|
483
483
|
|
|
484
|
+
assert merged["bugfix_state"]["status"] == "failed"
|
|
484
485
|
assert merged["bugfix_state"]["stage_result"] == "TEST_BLOCKED"
|
|
485
486
|
assert merged["bugfix_state"]["resume_from"] == "prizmkit-test"
|
|
486
487
|
|
|
@@ -527,7 +528,7 @@ class TestBugfixCheckpointGeneration:
|
|
|
527
528
|
metadata = bugfix_checkpoint_metadata(
|
|
528
529
|
".prizmkit/bugfix/B-001",
|
|
529
530
|
stage="test",
|
|
530
|
-
status="
|
|
531
|
+
status="failed",
|
|
531
532
|
stage_result="TEST_NEEDS_FIXES",
|
|
532
533
|
repair_round=0,
|
|
533
534
|
next_stage=None,
|
|
@@ -535,6 +536,7 @@ class TestBugfixCheckpointGeneration:
|
|
|
535
536
|
)
|
|
536
537
|
|
|
537
538
|
assert metadata["artifact_dir"] == ".prizmkit/bugfix/B-001"
|
|
539
|
+
assert metadata["status"] == "failed"
|
|
538
540
|
assert metadata["stage_result"] == "TEST_NEEDS_FIXES"
|
|
539
541
|
assert "authoritative_evidence_paths" not in metadata
|
|
540
542
|
|
|
@@ -59,7 +59,7 @@ class TestRefactorCheckpointMerge:
|
|
|
59
59
|
existing["refactor_state"] = refactor_checkpoint_metadata(
|
|
60
60
|
".prizmkit/refactor/R-008",
|
|
61
61
|
stage="test",
|
|
62
|
-
status="
|
|
62
|
+
status="failed",
|
|
63
63
|
stage_result="TEST_NEEDS_FIXES",
|
|
64
64
|
repair_scope="public-interface",
|
|
65
65
|
repair_round=2,
|
|
@@ -72,6 +72,7 @@ class TestRefactorCheckpointMerge:
|
|
|
72
72
|
|
|
73
73
|
assert merged["steps"][0]["status"] == "skipped"
|
|
74
74
|
assert merged["steps"][1]["status"] == "pending"
|
|
75
|
+
assert merged["refactor_state"]["status"] == "failed"
|
|
75
76
|
assert merged["refactor_state"]["repair_scope"] == "public-interface"
|
|
76
77
|
assert merged["refactor_state"]["repair_round"] == 2
|
|
77
78
|
|
|
@@ -376,13 +377,15 @@ class TestRefactorCheckpointGeneration:
|
|
|
376
377
|
metadata = refactor_checkpoint_metadata(
|
|
377
378
|
".prizmkit/refactor/R-001",
|
|
378
379
|
stage="test",
|
|
379
|
-
status="
|
|
380
|
+
status="completed",
|
|
380
381
|
stage_result="TEST_PASS",
|
|
381
382
|
next_stage="prizmkit-retrospective",
|
|
382
383
|
resume_from="prizmkit-retrospective",
|
|
383
384
|
)
|
|
384
385
|
|
|
385
386
|
assert metadata["artifact_dir"] == ".prizmkit/refactor/R-001"
|
|
387
|
+
assert metadata["status"] == "completed"
|
|
388
|
+
assert metadata["stage_result"] == "TEST_PASS"
|
|
386
389
|
assert "authoritative_evidence_paths" not in metadata
|
|
387
390
|
assert metadata["next_stage"] == "prizmkit-retrospective"
|
|
388
391
|
assert metadata["resume_from"] == "prizmkit-retrospective"
|
|
@@ -3426,7 +3426,150 @@ def _terminal_success_result(tmp_path):
|
|
|
3426
3426
|
)
|
|
3427
3427
|
|
|
3428
3428
|
|
|
3429
|
-
def
|
|
3429
|
+
def test_session_status_parser_preserves_current_runtime_outcomes():
|
|
3430
|
+
import importlib.util
|
|
3431
|
+
|
|
3432
|
+
script = PIPELINE_ROOT / "scripts" / "check-session-status.py"
|
|
3433
|
+
spec = importlib.util.spec_from_file_location("check_session_status_contract", script)
|
|
3434
|
+
module = importlib.util.module_from_spec(spec)
|
|
3435
|
+
spec.loader.exec_module(module)
|
|
3436
|
+
|
|
3437
|
+
for status in (
|
|
3438
|
+
"infra_error",
|
|
3439
|
+
"stalled_context_continuation",
|
|
3440
|
+
"workflow_blocked",
|
|
3441
|
+
"workflow_skipped",
|
|
3442
|
+
):
|
|
3443
|
+
assert module.determine_status({"status": status}) == status
|
|
3444
|
+
|
|
3445
|
+
|
|
3446
|
+
def test_terminal_success_without_semantic_completion_is_skipped(tmp_path):
|
|
3447
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
3448
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3449
|
+
|
|
3450
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
3451
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-incomplete"
|
|
3452
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact_dir)
|
|
3453
|
+
payload = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
3454
|
+
payload["steps"][2]["required_artifacts"] = [
|
|
3455
|
+
".prizmkit/specs/001-incomplete/test-report.md",
|
|
3456
|
+
".prizmkit/specs/001-incomplete/missing-test-result.json",
|
|
3457
|
+
]
|
|
3458
|
+
checkpoint.write_text(json.dumps(payload), encoding="utf-8")
|
|
3459
|
+
prompt = PromptGenerationResult(
|
|
3460
|
+
output_path=tmp_path / "prompt.md",
|
|
3461
|
+
checkpoint_path=checkpoint,
|
|
3462
|
+
artifact_path=artifact_dir,
|
|
3463
|
+
)
|
|
3464
|
+
|
|
3465
|
+
classification = classify_session(
|
|
3466
|
+
_terminal_success_result(tmp_path),
|
|
3467
|
+
project_root=tmp_path,
|
|
3468
|
+
base_head=base_head,
|
|
3469
|
+
prompt=prompt,
|
|
3470
|
+
)
|
|
3471
|
+
|
|
3472
|
+
assert classification.session_status == "workflow_skipped"
|
|
3473
|
+
assert classification.reason == "no_semantic_completion:incomplete_semantic_evidence"
|
|
3474
|
+
assert classification.fatal_error_code == ""
|
|
3475
|
+
|
|
3476
|
+
|
|
3477
|
+
def test_terminal_success_text_does_not_hide_provider_failure(tmp_path):
|
|
3478
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
3479
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3480
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
3481
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
3482
|
+
|
|
3483
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
3484
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-provider-error"
|
|
3485
|
+
checkpoint = _write_semantic_completion_checkpoint(tmp_path, artifact_dir)
|
|
3486
|
+
payload = json.loads(checkpoint.read_text(encoding="utf-8"))
|
|
3487
|
+
payload["steps"][2]["required_artifacts"] = [
|
|
3488
|
+
".prizmkit/specs/001-provider-error/test-report.md",
|
|
3489
|
+
".prizmkit/specs/001-provider-error/missing-test-result.json",
|
|
3490
|
+
]
|
|
3491
|
+
checkpoint.write_text(json.dumps(payload), encoding="utf-8")
|
|
3492
|
+
log = tmp_path / "provider-error.log"
|
|
3493
|
+
log.write_text("gateway failure after output", encoding="utf-8")
|
|
3494
|
+
raw = AISessionResult(
|
|
3495
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
3496
|
+
exit_code=0,
|
|
3497
|
+
timed_out=False,
|
|
3498
|
+
termination_reason=None,
|
|
3499
|
+
stale_marker=None,
|
|
3500
|
+
fatal_error_code="provider_error",
|
|
3501
|
+
progress_summary=ProgressSummary(result_subtype="success"),
|
|
3502
|
+
session_log=log,
|
|
3503
|
+
backup_log=tmp_path / "backup.log",
|
|
3504
|
+
log_recovery=None,
|
|
3505
|
+
started_epoch=0,
|
|
3506
|
+
ended_epoch=1,
|
|
3507
|
+
)
|
|
3508
|
+
|
|
3509
|
+
classification = classify_session(
|
|
3510
|
+
raw,
|
|
3511
|
+
project_root=tmp_path,
|
|
3512
|
+
base_head=base_head,
|
|
3513
|
+
prompt=PromptGenerationResult(
|
|
3514
|
+
output_path=tmp_path / "prompt.md",
|
|
3515
|
+
checkpoint_path=checkpoint,
|
|
3516
|
+
artifact_path=artifact_dir,
|
|
3517
|
+
),
|
|
3518
|
+
)
|
|
3519
|
+
|
|
3520
|
+
assert classification.session_status == "infra_error"
|
|
3521
|
+
assert classification.reason == "ai_runtime_or_provider_error"
|
|
3522
|
+
|
|
3523
|
+
|
|
3524
|
+
@pytest.mark.parametrize(
|
|
3525
|
+
("kind", "item_id"),
|
|
3526
|
+
[("feature", "F-001"), ("bugfix", "B-001"), ("refactor", "R-001")],
|
|
3527
|
+
)
|
|
3528
|
+
def test_family_status_skips_semantic_completion_failure_without_retry_spend(
|
|
3529
|
+
tmp_path, kind, item_id
|
|
3530
|
+
):
|
|
3531
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
3532
|
+
from prizmkit_runtime.runner_status import start_item, update_item
|
|
3533
|
+
|
|
3534
|
+
paths = _make_paths(tmp_path / kind)
|
|
3535
|
+
item = {
|
|
3536
|
+
"id": item_id,
|
|
3537
|
+
"title": "Skip invalid completion evidence",
|
|
3538
|
+
"status": "pending",
|
|
3539
|
+
"priority": "high",
|
|
3540
|
+
"dependencies": [],
|
|
3541
|
+
}
|
|
3542
|
+
if kind == "bugfix":
|
|
3543
|
+
item.update(severity="high", description="invalid evidence", reproduction_steps=["run"])
|
|
3544
|
+
elif kind == "refactor":
|
|
3545
|
+
item.update(description="invalid evidence", targets=["src"], goals=["preserve"])
|
|
3546
|
+
_write_family_plan(paths, kind, [item])
|
|
3547
|
+
family = family_for(kind, paths)
|
|
3548
|
+
invocation = parse_invocation(family, "run", ())
|
|
3549
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": family.plan_path})
|
|
3550
|
+
assert start_item(family, invocation, item_id, paths.project_root).ok is True
|
|
3551
|
+
|
|
3552
|
+
result = update_item(
|
|
3553
|
+
family,
|
|
3554
|
+
invocation,
|
|
3555
|
+
item_id,
|
|
3556
|
+
"workflow_skipped",
|
|
3557
|
+
"session-1",
|
|
3558
|
+
paths.project_root,
|
|
3559
|
+
checkpoint_state={},
|
|
3560
|
+
)
|
|
3561
|
+
status = json.loads((family.state_dir / item_id / "status.json").read_text(encoding="utf-8"))
|
|
3562
|
+
|
|
3563
|
+
assert result.ok is True
|
|
3564
|
+
assert result.data["new_status"] == "skipped"
|
|
3565
|
+
assert result.data["restart_policy"] == "terminal_skipped"
|
|
3566
|
+
assert status["retry_count"] == 0
|
|
3567
|
+
assert status["infra_error_count"] == 0
|
|
3568
|
+
assert status["needs_attention"] is True
|
|
3569
|
+
assert status["skipped_reason"] == "workflow_completion_evidence_invalid"
|
|
3570
|
+
|
|
3571
|
+
|
|
3572
|
+
def test_invalid_existing_checkpoint_skips_terminal_success_with_completion_summary(tmp_path):
|
|
3430
3573
|
from prizmkit_runtime.runner_classification import classify_session
|
|
3431
3574
|
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3432
3575
|
|
|
@@ -3440,11 +3583,11 @@ def test_invalid_existing_checkpoint_blocks_terminal_success_with_completion_sum
|
|
|
3440
3583
|
|
|
3441
3584
|
classification = classify_session(_terminal_success_result(tmp_path), project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
3442
3585
|
|
|
3443
|
-
assert classification.session_status == "
|
|
3586
|
+
assert classification.session_status == "workflow_skipped"
|
|
3444
3587
|
assert classification.reason == "invalid_workflow_checkpoint:invalid_status"
|
|
3445
3588
|
|
|
3446
3589
|
|
|
3447
|
-
def
|
|
3590
|
+
def test_invalid_existing_checkpoint_blocks_runtime_failure_with_head_change(tmp_path):
|
|
3448
3591
|
from prizmkit_runtime.runner_classification import classify_session
|
|
3449
3592
|
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3450
3593
|
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
@@ -3481,7 +3624,7 @@ def test_invalid_existing_checkpoint_blocks_head_change_success(tmp_path):
|
|
|
3481
3624
|
assert classification.reason == "invalid_workflow_checkpoint:malformed_step_id"
|
|
3482
3625
|
|
|
3483
3626
|
|
|
3484
|
-
def
|
|
3627
|
+
def test_invalid_existing_checkpoint_blocks_ambiguous_clean_exit(tmp_path):
|
|
3485
3628
|
from prizmkit_runtime.runner_classification import classify_session
|
|
3486
3629
|
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
3487
3630
|
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|