prizmkit 1.1.129 → 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.
Files changed (88) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/dev-pipeline/.env.example +1 -1
  3. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
  4. package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
  5. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
  6. package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
  7. package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
  8. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +15 -1
  9. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +398 -6
  10. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +61 -3
  11. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +64 -4
  12. package/bundled/dev-pipeline/scripts/prompt_framework.py +156 -7
  13. package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
  14. package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
  15. package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
  16. package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
  17. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
  18. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +20 -14
  19. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +21 -4
  20. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +18 -0
  21. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
  22. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +5 -15
  23. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
  24. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
  25. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +16 -0
  26. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +26 -23
  27. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +12 -8
  28. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +1 -1
  29. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
  30. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
  31. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +404 -24
  32. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +132 -0
  33. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
  34. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +411 -18
  35. package/bundled/dev-pipeline/tests/test_unified_cli.py +3 -3
  36. package/bundled/skills/_metadata.json +62 -25
  37. package/bundled/skills/app-planner/SKILL.md +4 -3
  38. package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
  39. package/bundled/skills/bug-planner/SKILL.md +12 -27
  40. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
  41. package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
  42. package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
  43. package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
  44. package/bundled/skills/feature-planner/SKILL.md +27 -22
  45. package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
  46. package/bundled/skills/feature-workflow/SKILL.md +94 -121
  47. package/bundled/skills/prizmkit/SKILL.md +130 -94
  48. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
  49. package/bundled/skills/prizmkit-code-review/SKILL.md +114 -84
  50. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +1 -1
  51. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
  52. package/bundled/skills/prizmkit-committer/SKILL.md +131 -47
  53. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
  54. package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
  55. package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
  56. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
  57. package/bundled/skills/prizmkit-init/SKILL.md +5 -5
  58. package/bundled/skills/prizmkit-plan/SKILL.md +132 -105
  59. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
  60. package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
  61. package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
  62. package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
  63. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
  64. package/bundled/skills/prizmkit-test/SKILL.md +28 -6
  65. package/bundled/skills/prizmkit-test/references/examples.md +1 -1
  66. package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
  67. package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
  68. package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
  69. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
  70. package/bundled/skills/recovery-workflow/SKILL.md +62 -241
  71. package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
  72. package/bundled/skills/recovery-workflow/references/detection.md +28 -52
  73. package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
  74. package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
  75. package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
  76. package/bundled/skills/refactor-planner/SKILL.md +12 -57
  77. package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
  78. package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
  79. package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
  80. package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
  81. package/bundled/skills/refactor-workflow/SKILL.md +98 -139
  82. package/bundled/templates/project-memory-template.md +4 -4
  83. package/package.json +1 -1
  84. package/src/index.js +4 -0
  85. package/src/prompts.js +10 -3
  86. package/src/scaffold.js +1 -1
  87. package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
  88. package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
 
7
- from prizmkit_runtime.checkpoint_state import load_checkpoint_state, read_checkpoint_cursor
7
+ from prizmkit_runtime.checkpoint_state import load_checkpoint_state, read_checkpoint_cursor, validate_checkpoint_data
8
8
  from update_checkpoint import update_checkpoint
9
9
 
10
10
 
@@ -138,7 +138,7 @@ def test_update_checkpoint_rejects_ambiguous_skill_without_mutation(tmp_path):
138
138
 
139
139
  def test_update_checkpoint_exact_id_updates_one_duplicate_skill_step(tmp_path):
140
140
  path = _write(tmp_path / "workflow-checkpoint.json", _checkpoint([
141
- _step("S01", "prizmkit-committer", "Commit First", "pending"),
141
+ _step("S01", "prizmkit-committer", "Commit First", "completed"),
142
142
  _step("S02", "prizmkit-committer", "Commit Second", "pending", "S01"),
143
143
  ]))
144
144
 
@@ -147,7 +147,7 @@ def test_update_checkpoint_exact_id_updates_one_duplicate_skill_step(tmp_path):
147
147
 
148
148
  assert result["ok"] is True
149
149
  assert result["step_id"] == "S02"
150
- assert data["steps"][0]["status"] == "pending"
150
+ assert data["steps"][0]["status"] == "completed"
151
151
  assert data["steps"][1]["status"] == "completed"
152
152
 
153
153
 
@@ -162,3 +162,362 @@ def test_update_checkpoint_invalid_checkpoint_fails_without_mutation(tmp_path):
162
162
  assert result["ok"] is False
163
163
  assert "invalid_status" in result["error"]
164
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
+ }
203
+
204
+
205
+ def test_semantic_completion_requires_family_sequence_verdicts_and_structured_evidence(tmp_path, monkeypatch):
206
+ monkeypatch.setattr(
207
+ "prizmkit_runtime.checkpoint_state._resolve_evidence_validator",
208
+ lambda _root: tmp_path / "validator.py",
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)
232
+
233
+ state = load_checkpoint_state(checkpoint, project_root=tmp_path)
234
+
235
+ assert state.valid is True
236
+ assert state.all_completed is True
237
+ assert state.semantic_complete is True
238
+ assert state.semantic.evidence_valid is True
239
+ assert state.semantic.terminal_status == "WORKFLOW_COMPLETED"
240
+
241
+
242
+ def test_completed_steps_without_stage_verdicts_are_not_semantic_completion(tmp_path):
243
+ skills = [
244
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
245
+ "prizmkit-retrospective", "prizmkit-committer", "completion-summary",
246
+ ]
247
+ payload = _family_checkpoint("feature-pipeline", skills)
248
+ payload["steps"][1].pop("stage_result")
249
+ payload["steps"][2].pop("stage_result")
250
+ payload["feature_state"]["terminal_status"] = "WORKFLOW_COMPLETED"
251
+
252
+ state = validate_checkpoint_data(payload, project_root=tmp_path)
253
+
254
+ assert state.valid is True
255
+ assert state.all_completed is True
256
+ assert state.semantic_complete is False
257
+ assert state.semantic.error_code == "incomplete_semantic_evidence"
258
+
259
+
260
+ def test_family_sequence_mismatch_cannot_complete(tmp_path):
261
+ paths = _write_test_evidence(tmp_path)
262
+ skills = [
263
+ "prizmkit-implement", "prizmkit-test", "prizmkit-code-review",
264
+ "prizmkit-retrospective", "prizmkit-committer", "completion-summary",
265
+ ]
266
+ payload = _family_checkpoint(
267
+ "feature-pipeline",
268
+ skills,
269
+ semantic={"authoritative_evidence_paths": paths, "terminal_status": "WORKFLOW_COMPLETED"},
270
+ )
271
+ payload["steps"][1]["authoritative_evidence_paths"] = paths
272
+
273
+ state = validate_checkpoint_data(payload, project_root=tmp_path)
274
+
275
+ assert state.semantic_complete is False
276
+ assert state.semantic.sequence_valid is False
277
+ assert state.semantic.error_code == "invalid_stage_sequence"
278
+
279
+
280
+ def test_stale_or_mismatched_test_evidence_cannot_complete(tmp_path):
281
+ paths = _write_test_evidence(tmp_path, verdict="TEST_FAIL")
282
+ skills = [
283
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
284
+ "prizmkit-retrospective", "prizmkit-committer", "completion-summary",
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:]:
359
+ step["status"] = "pending"
360
+ step.pop("stage_result", None)
361
+ path = _write(tmp_path / "workflow-checkpoint.json", payload)
362
+
363
+ result = update_checkpoint(
364
+ str(path),
365
+ "prizmkit-code-review",
366
+ "failed",
367
+ semantic_update={"stage_result": "REVIEW_NEEDS_FIXES", "repair_round": 0},
368
+ )
369
+ data = json.loads(path.read_text(encoding="utf-8"))
370
+
371
+ assert result["ok"] is True
372
+ assert data["feature_state"]["terminal_status"] == "WORKFLOW_BLOCKED"
373
+ assert data["feature_state"]["blocked_reason"] == "unsafe_repair_scope"
374
+ assert "retry_count" not in data["feature_state"]
375
+
376
+
377
+ def test_update_checkpoint_enforces_three_outer_repair_rounds(tmp_path):
378
+ payload = _family_checkpoint(
379
+ "feature-pipeline",
380
+ [
381
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
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:]:
388
+ step["status"] = "pending"
389
+ step.pop("stage_result", None)
390
+ path = _write(tmp_path / "workflow-checkpoint.json", payload)
391
+
392
+ result = update_checkpoint(
393
+ str(path),
394
+ "prizmkit-code-review",
395
+ "failed",
396
+ semantic_update={"stage_result": "REVIEW_NEEDS_FIXES", "repair_scope": "production"},
397
+ )
398
+ data = json.loads(path.read_text(encoding="utf-8"))
399
+
400
+ assert result["ok"] is True
401
+ assert data["feature_state"]["repair_round"] == 3
402
+ assert data["feature_state"]["terminal_status"] == "WORKFLOW_BLOCKED"
403
+ assert data["feature_state"]["blocked_reason"] == "repair_round_limit"
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
440
+
441
+
442
+
443
+ def test_update_checkpoint_rejects_test_pass_when_validator_replay_fails(tmp_path, monkeypatch):
444
+ paths = _write_test_evidence(tmp_path)
445
+ payload = _family_checkpoint(
446
+ "feature-pipeline",
447
+ [
448
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
449
+ "prizmkit-retrospective", "prizmkit-committer", "completion-summary",
450
+ ],
451
+ )
452
+ payload["steps"][2]["status"] = "in_progress"
453
+ for step in payload["steps"][3:]:
454
+ step["status"] = "pending"
455
+ step.pop("stage_result", None)
456
+ checkpoint = tmp_path / "workflow-checkpoint.json"
457
+ _write(checkpoint, payload)
458
+ monkeypatch.setattr(
459
+ "prizmkit_runtime.checkpoint_state._resolve_evidence_validator",
460
+ lambda _root: tmp_path / "validator.py",
461
+ )
462
+ monkeypatch.setattr(
463
+ "prizmkit_runtime.checkpoint_state._evidence_validator_passes",
464
+ lambda *_args: False,
465
+ )
466
+
467
+ result = update_checkpoint(
468
+ str(checkpoint),
469
+ "prizmkit-test",
470
+ "completed",
471
+ semantic_update={
472
+ "stage_result": "TEST_PASS",
473
+ "authoritative_evidence_paths": paths,
474
+ },
475
+ )
476
+
477
+ assert result["ok"] is False
478
+ assert "validator-attested" in result["error"]
479
+
480
+
481
+
482
+ def test_update_checkpoint_rejects_test_before_review_pass(tmp_path):
483
+ payload = _family_checkpoint(
484
+ "feature-pipeline",
485
+ [
486
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
487
+ "prizmkit-retrospective", "prizmkit-committer", "completion-summary",
488
+ ],
489
+ )
490
+ payload["steps"][1]["status"] = "skipped"
491
+ payload["steps"][1].pop("stage_result", None)
492
+ payload["steps"][2]["status"] = "pending"
493
+ for step in payload["steps"][3:]:
494
+ step["status"] = "pending"
495
+ step.pop("stage_result", None)
496
+ checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
497
+
498
+ result = update_checkpoint(str(checkpoint), "prizmkit-test", "in_progress")
499
+
500
+ assert result["ok"] is False
501
+ assert "REVIEW_PASS" in result["error"]
502
+
503
+
504
+ def test_update_checkpoint_rejects_finalization_before_test_pass(tmp_path):
505
+ payload = _family_checkpoint(
506
+ "feature-pipeline",
507
+ [
508
+ "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
509
+ "prizmkit-retrospective", "prizmkit-committer", "completion-summary",
510
+ ],
511
+ )
512
+ payload["steps"][2]["status"] = "skipped"
513
+ payload["steps"][2].pop("stage_result", None)
514
+ payload["steps"][3]["status"] = "pending"
515
+ for step in payload["steps"][4:]:
516
+ step["status"] = "pending"
517
+ step.pop("stage_result", None)
518
+ checkpoint = _write(tmp_path / "workflow-checkpoint.json", payload)
519
+
520
+ result = update_checkpoint(str(checkpoint), "prizmkit-retrospective", "in_progress")
521
+
522
+ assert result["ok"] is False
523
+ assert "TEST_PASS" in result["error"]