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.
Files changed (74) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +41 -121
  3. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -6
  4. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +1 -2
  5. package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +150 -0
  6. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +21 -804
  7. package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +4 -6
  8. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +1 -1
  9. package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +4 -6
  10. package/bundled/dev-pipeline/scripts/prompt_framework.py +56 -55
  11. package/bundled/dev-pipeline/scripts/update-bug-status.py +1 -2
  12. package/bundled/dev-pipeline/scripts/update-checkpoint.py +14 -96
  13. package/bundled/dev-pipeline/scripts/update-feature-status.py +1 -2
  14. package/bundled/dev-pipeline/scripts/update-refactor-status.py +1 -2
  15. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +3 -3
  16. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +7 -18
  17. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -6
  18. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +5 -5
  19. package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +1 -1
  20. package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
  21. package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +1 -1
  22. package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +13 -10
  23. package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +1 -1
  24. package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -9
  25. package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +1 -1
  26. package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +1 -1
  27. package/bundled/dev-pipeline/templates/sections/phase-commit.md +1 -1
  28. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +2 -2
  29. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +1 -1
  30. package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +1 -1
  31. package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +1 -1
  32. package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +14 -26
  33. package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -3
  34. package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +13 -10
  35. package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
  36. package/bundled/dev-pipeline/tests/test_checkpoint_state.py +287 -384
  37. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +119 -684
  38. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +17 -40
  39. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +20 -23
  40. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +29 -32
  41. package/bundled/skills/_metadata.json +6 -6
  42. package/bundled/skills/prizmkit/SKILL.md +8 -13
  43. package/bundled/skills/prizmkit/references/workflow-state-protocol.md +61 -80
  44. package/bundled/skills/prizmkit-code-review/SKILL.md +3 -3
  45. package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +61 -80
  46. package/bundled/skills/prizmkit-committer/SKILL.md +3 -3
  47. package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +61 -80
  48. package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +61 -80
  49. package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +61 -80
  50. package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -2
  51. package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +61 -80
  52. package/bundled/skills/prizmkit-test/SKILL.md +217 -187
  53. package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +119 -0
  54. package/bundled/skills/prizmkit-test/references/independent-test-review.md +269 -0
  55. package/bundled/skills/prizmkit-test/references/test-coverage-model.md +193 -0
  56. package/bundled/skills/prizmkit-test/references/test-report-template.md +164 -79
  57. package/bundled/skills/prizmkit-workflow/SKILL.md +15 -23
  58. package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +61 -80
  59. package/package.json +1 -1
  60. package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +0 -476
  61. package/bundled/skills/prizmkit-test/assets/behavior-risk-matrix.schema.json +0 -116
  62. package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +0 -111
  63. package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +0 -66
  64. package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +0 -104
  65. package/bundled/skills/prizmkit-test/references/contract-mock-protocol.md +0 -65
  66. package/bundled/skills/prizmkit-test/references/evidence-protocol.md +0 -208
  67. package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +0 -80
  68. package/bundled/skills/prizmkit-test/references/examples.md +0 -100
  69. package/bundled/skills/prizmkit-test/references/service-boundary-test-catalog.md +0 -228
  70. package/bundled/skills/prizmkit-test/references/test-generation-steps.md +0 -118
  71. package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +0 -110
  72. package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +0 -181
  73. package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +0 -1851
  74. package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +0 -1793
@@ -1,11 +1,9 @@
1
1
  """Tests for generate-bootstrap-prompt.py core functions."""
2
2
 
3
3
  import json
4
- import hashlib
5
4
  import importlib.util
6
5
  import os
7
6
  import re
8
- import time
9
7
  from argparse import Namespace
10
8
  from pathlib import Path
11
9
 
@@ -28,7 +26,6 @@ from generate_bootstrap_prompt import (
28
26
  merge_checkpoint_state,
29
27
  load_active_agent_prompts,
30
28
  validate_rendered,
31
- validate_feature_test_evidence,
32
29
  route_feature_repair,
33
30
  feature_checkpoint_metadata,
34
31
  main as generate_bootstrap_main,
@@ -112,197 +109,6 @@ def test_rendered_prompt_validation_rejects_unresolved_control_placeholders():
112
109
  assert errors == ["Unreplaced placeholders: {{INIT_SCRIPT_PATH}}"]
113
110
 
114
111
 
115
- def scoped_report_text(verdict="PASS", boundary_missing=0,
116
- happy_path_only=0, completion_gate="yes",
117
- validator_result="passed"):
118
- """Build a scoped report with a complete boundary contract."""
119
- return "\n".join([
120
- "# Test Report",
121
- "",
122
- "## Scope",
123
- "- Mode: this-change",
124
- "- Artifact Dir: .prizmkit/specs/123-scope-test/",
125
- "- In-Scope Source Files:",
126
- " - src/feature.js",
127
- "- In-Scope Test Files:",
128
- " - tests/feature.test.js",
129
- "",
130
- "## Boundary Matrix",
131
- "| Module | Interface | Service Type | Happy Path | Request Validation | Auth / Permission / Ownership | Domain Invariants | Collection / Pagination | Date / Time | State Transitions | Dependency Failure | Response Contract | N/A Reasons | Final Status |",
132
- "|--------|-----------|--------------|------------|--------------------|-------------------------------|-------------------|-------------------------|-------------|-------------------|-------------------|-------------------|-------------|--------------|",
133
- "| feature | POST /feature | API | covered | covered | covered | covered | covered | covered | covered | covered | covered | none | boundary-covered |",
134
- "",
135
- "## Boundary Completion Gate",
136
- "- Interfaces total: 1",
137
- "- Boundary-covered: 1",
138
- "- Generated-needs-review: 0",
139
- "- Skipped-with-reason: 0",
140
- f"- Boundary-missing: {boundary_missing}",
141
- f"- Happy-path-only: {happy_path_only}",
142
- f"- Completion gate passed: {completion_gate}",
143
- "",
144
- "## Boundary Validation",
145
- "- Validator command: `python3 validate_boundary_report.py`",
146
- f"- Validator result: {validator_result}",
147
- "- Validator output: Boundary report validation: PASS",
148
- "",
149
- "## Verdict",
150
- verdict,
151
- "",
152
- "## Final Status",
153
- "- All tests passing: yes",
154
- f"- Boundary completion gate passed: {completion_gate}",
155
- "- Ready for commit: yes",
156
- "- Needs human review: None",
157
- "",
158
- ])
159
-
160
-
161
- def write_fresh_scoped_report(project_root, report_path, report_text=None):
162
- """Write marker, in-scope files, and a fresh derived report."""
163
- source = project_root / "src" / "feature.js"
164
- test = project_root / "tests" / "feature.test.js"
165
- marker = project_root / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
166
- source.parent.mkdir(parents=True, exist_ok=True)
167
- test.parent.mkdir(parents=True, exist_ok=True)
168
- marker.parent.mkdir(parents=True, exist_ok=True)
169
- report_path.parent.mkdir(parents=True, exist_ok=True)
170
- source.write_text("export const feature = true;\n", encoding="utf-8")
171
- test.write_text("test('feature', () => {});\n", encoding="utf-8")
172
- marker.write_text("started\n", encoding="utf-8")
173
- time.sleep(0.01)
174
- report_path.write_text(report_text or scoped_report_text(), encoding="utf-8")
175
- return source, test, marker
176
-
177
-
178
- def write_fresh_structured_evidence(project_root):
179
- """Create the smallest protocol-shaped, validator-attested pass package."""
180
- source, test, marker = write_fresh_scoped_report(
181
- project_root,
182
- project_root / ".prizmkit" / "test" / "unused" / "test-report.md",
183
- )
184
- evidence_base = project_root / ".prizmkit" / "test" / "evidence"
185
- environment = {}
186
- plan_inputs = {"feature": "123-scope-test"}
187
- categories = {
188
- "source": [{"path": "src/feature.js", "sha256": hashlib.sha256(source.read_bytes()).hexdigest()}],
189
- "tests": [{"path": "tests/feature.test.js", "sha256": hashlib.sha256(test.read_bytes()).hexdigest()}],
190
- "contracts": [],
191
- "lockfiles": [],
192
- }
193
- target_hashes = {
194
- category: hashlib.sha256(json.dumps(
195
- sorted(records, key=lambda item: item["path"]),
196
- sort_keys=True, separators=(",", ":"),
197
- ).encode()).hexdigest()
198
- for category, records in categories.items()
199
- }
200
- target_hashes["environment"] = hashlib.sha256(b"{}").hexdigest()
201
- target_hashes["plan"] = hashlib.sha256(json.dumps(
202
- plan_inputs, sort_keys=True, separators=(",", ":"),
203
- ).encode()).hexdigest()
204
- inventory = {
205
- "categories": categories,
206
- "plan_inputs": plan_inputs,
207
- }
208
- scope = {"feature": "123-scope-test"}
209
- zero_hash = "0" * 64
210
- identity = {
211
- "baseline_commit": "baseline",
212
- "working_diff_sha256": zero_hash,
213
- "scope_sha256": hashlib.sha256(json.dumps(
214
- scope, sort_keys=True, separators=(",", ":"),
215
- ).encode()).hexdigest(),
216
- }
217
- evidence_id = hashlib.sha256(json.dumps(
218
- identity, sort_keys=True, separators=(",", ":"),
219
- ).encode()).hexdigest()
220
- evidence_root = evidence_base / evidence_id
221
- evidence_root.mkdir(parents=True, exist_ok=True)
222
- (evidence_root / "environment.json").write_text("{}\n", encoding="utf-8")
223
- (evidence_root / "target-inventory.json").write_text(
224
- json.dumps(inventory), encoding="utf-8",
225
- )
226
- (evidence_root / "scope.json").write_text(json.dumps(scope), encoding="utf-8")
227
- verdict = {
228
- "verdict": "TEST_PASS",
229
- "testing_domain_only": True,
230
- "authorizes_commit_or_release": False,
231
- "real_environment_validated": False,
232
- "repairs_business_defects": False,
233
- "code_evidence_replayable": True,
234
- "blockers": [],
235
- "reproduced_failures": [],
236
- }
237
- (evidence_root / "verdict.json").write_text(json.dumps(verdict), encoding="utf-8")
238
- report = scoped_report_text()
239
- (evidence_root / "test-report.md").write_text(report, encoding="utf-8")
240
- stages = []
241
- stage_names = [
242
- "CHANGE_CLASSIFY", "SCOPE_DISCOVER", "CONTRACT_MODEL", "TEST_PLAN",
243
- "INFRA_READY", "TEST_BUILD", "EXECUTE_PROVE", "EVIDENCE_PACKAGE",
244
- "EVIDENCE_VALIDATE",
245
- ]
246
- for name in stage_names:
247
- stages.append({"name": name, "status": "complete", "outputs": []})
248
- manifest = {
249
- "protocol_version": "1.0",
250
- "evidence_id": evidence_id,
251
- "evidence_id_inputs": identity,
252
- "sensitivity": "project-controlled",
253
- "environment_claim": "mocked-code-level-only",
254
- "compatibility": "legacy-test-report-interface-not-supported",
255
- "change_class": "lightweight",
256
- "baseline_commit": "baseline",
257
- "working_diff_sha256": zero_hash,
258
- "target_hashes": target_hashes,
259
- "stages": stages,
260
- "files": [],
261
- "final_verdict": "TEST_PASS",
262
- }
263
- file_records = []
264
- for path in sorted(evidence_root.rglob("*")):
265
- if path.is_file():
266
- relative = path.relative_to(evidence_root).as_posix()
267
- file_records.append({
268
- "path": relative,
269
- "sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
270
- "produced_by": "EVIDENCE_PACKAGE",
271
- })
272
- payload = sorted(
273
- ({"path": item["path"], "sha256": item["sha256"]}
274
- for item in file_records
275
- if item["path"] not in {"verdict.json", "test-report.md"}),
276
- key=lambda item: item["path"],
277
- )
278
- validation = {
279
- "validator": "validate_test_evidence.py",
280
- "validator_sha256": zero_hash,
281
- "payload_sha256": hashlib.sha256(json.dumps(
282
- payload, sort_keys=True, separators=(",", ":"),
283
- ).encode()).hexdigest(),
284
- "result": "passed",
285
- "verdict": "TEST_PASS",
286
- "semantics": "integrity-and-protocol-validation-not-hostile-producer-proof",
287
- "replay_receipt_ids": [],
288
- }
289
- (evidence_root / "validation.json").write_text(json.dumps(validation), encoding="utf-8")
290
- file_records.append({
291
- "path": "validation.json",
292
- "sha256": hashlib.sha256((evidence_root / "validation.json").read_bytes()).hexdigest(),
293
- "produced_by": "EVIDENCE_VALIDATE",
294
- })
295
- manifest["files"] = file_records
296
- (evidence_root / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
297
- pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
298
- pointer.parent.mkdir(parents=True, exist_ok=True)
299
- pointer.write_text(str(evidence_root / "test-report.md"), encoding="utf-8")
300
- report_path = evidence_root / "test-report.md"
301
- now = time.time()
302
- os.utime(marker, (now, now))
303
- os.utime(report_path, (now + 1, now + 1))
304
- return evidence_root
305
-
306
112
 
307
113
  # ---------------------------------------------------------------------------
308
114
  # compute_feature_slug
@@ -601,544 +407,172 @@ class TestProcessModeBlocks:
601
407
 
602
408
  class TestScopedFeatureTestGate:
603
409
  @staticmethod
604
- def _trust_fixture_validator(monkeypatch):
605
- monkeypatch.setattr(
606
- "generate_bootstrap_prompt._evidence_validator_passes",
607
- lambda _project_root, _evidence_root, _validation: True,
410
+ def _artifacts(slug="123-scope-test"):
411
+ return [
412
+ f".prizmkit/specs/{slug}/test-report.md",
413
+ f".prizmkit/specs/{slug}/test-result.json",
414
+ ]
415
+
416
+ @staticmethod
417
+ def _write_result(root, result="TEST_PASS", *, unresolved=None,
418
+ review_required=False, review_scope=None):
419
+ artifact = root / ".prizmkit" / "specs" / "123-scope-test"
420
+ artifact.mkdir(parents=True, exist_ok=True)
421
+ (artifact / "test-report.md").write_text(
422
+ f"# Test Report\n\n## Final Result\n\n- Result: {result}\n",
423
+ encoding="utf-8",
424
+ )
425
+ payload = {
426
+ "schema_version": 1,
427
+ "result": result,
428
+ "report": "test-report.md",
429
+ "main_review_rounds": 1,
430
+ "independent_review": {
431
+ "status": "downgraded",
432
+ "responses": 0,
433
+ "downgrade_reason": "read-only continuation capability unavailable",
434
+ "final_state_rechecked": False,
435
+ },
436
+ "repair_rounds": 0,
437
+ "production_changed": False,
438
+ "review_required": review_required,
439
+ "review_scope": review_scope,
440
+ "unresolved_items": unresolved or [],
441
+ }
442
+ (artifact / "test-result.json").write_text(
443
+ json.dumps(payload), encoding="utf-8",
608
444
  )
609
445
 
610
446
  def test_standard_sections_insert_prizmkit_test_after_review(self):
611
447
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
612
448
  sections = assemble_sections(
613
- "standard",
614
- str(sections_dir),
615
- init_done=True,
616
- is_resume=False,
617
- browser_enabled=False,
449
+ "standard", str(sections_dir), init_done=True,
450
+ is_resume=False, browser_enabled=False,
618
451
  )
619
452
  names = [name for name, _ in sections]
453
+ rendered = "\n".join(content for _, content in sections)
620
454
 
621
- assert "phase-prizmkit-test" in names
622
455
  assert names.index("phase-implement") < names.index("phase-review") < names.index("phase-prizmkit-test")
623
-
624
- rendered = "\n".join(content for _, content in sections)
625
- assert "prizmkit-test-gate.py" not in rendered
626
- assert "{{RUNTIME_HELPER_CMD}} artifact touch .prizmkit/specs/{{FEATURE_SLUG}}/.prizmkit-test-started" in rendered
627
- assert "mkdir -p .prizmkit/specs/{{FEATURE_SLUG}}" not in rendered
628
- assert "when present" not in rendered
629
- assert "Boundary Matrix" in rendered
630
- assert "Boundary Completion Gate" in rendered
631
- assert "Boundary-missing: 0" in rendered
632
- assert "Happy-path-only: 0" in rendered
633
- assert "Validator result: passed" in rendered
634
456
  assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/" in rendered
635
- assert ".prizmkit/bugfix/" in rendered # explicitly forbidden in the gate wording
636
- assert ".prizmkit/refactor/" in rendered # explicitly forbidden in the gate wording
637
- assert "latest_test_report=$(python3" not in rendered
638
- assert "boundary_columns_ok=$(awk" not in rendered
639
- assert "Runs tests using `TEST_CMD` after each task" not in rendered
640
- assert "Run the full test suite" not in rendered
641
- assert "before code review" not in rendered
642
- assert "Precondition: the `prizmkit-test` checkpoint is completed" not in rendered
457
+ assert "test-report.md" in rendered
458
+ assert "test-result.json" in rendered
459
+ assert "TEST_NEEDS_FIXES" in rendered
460
+ assert ".prizmkit/test/evidence" not in rendered
461
+ assert "TEST_FAIL" not in rendered
643
462
 
644
463
  def test_standard_sections_include_central_checkpoint_helper(self):
645
464
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
646
465
  sections = assemble_sections(
647
- "standard",
648
- str(sections_dir),
649
- init_done=True,
650
- is_resume=False,
651
- browser_enabled=False,
466
+ "standard", str(sections_dir), init_done=True,
467
+ is_resume=False, browser_enabled=False,
652
468
  )
653
469
  rendered = "\n".join(content for _, content in sections)
654
470
 
655
471
  assert "### Checkpoint Update Helper" in rendered
656
472
  assert "{{CHECKPOINT_PYTHON_CMD}}" in rendered
657
- assert "python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py" not in rendered
658
473
  assert "--checkpoint-path {{CHECKPOINT_PATH}}" in rendered
659
- assert "--step <step-id-or-skill-name>" in rendered
474
+ assert "--stage-result <optional-domain-result>" in rendered
475
+ assert "--status completed --stage-result REVIEW_PASS" in rendered
476
+ assert "--status completed --stage-result TEST_PASS" in rendered
660
477
  assert "Do not hand-edit `workflow-checkpoint.json` directly." in rendered
661
- assert "pipeline progress mechanism" not in rendered
662
-
663
- def test_lite_sections_include_review_before_prizmkit_test_and_commit(self):
664
- sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
665
- sections = assemble_sections(
666
- "lite",
667
- str(sections_dir),
668
- init_done=True,
669
- is_resume=False,
670
- browser_enabled=False,
671
- )
672
- names = [name for name, _ in sections]
478
+ assert "authoritative_evidence_paths" not in rendered
673
479
 
674
- assert "phase-prizmkit-test" in names
675
- assert "phase-review" in names
676
- assert names.index("phase-implement") < names.index("phase-review") < names.index("phase-prizmkit-test") < names.index("phase-commit")
677
-
678
- def test_checkpoint_orders_prizmkit_test_after_review(self):
480
+ def test_checkpoint_orders_test_after_review_with_terminal_artifacts(self):
679
481
  sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
680
482
  sections = assemble_sections(
681
- "standard",
682
- str(sections_dir),
683
- init_done=True,
684
- is_resume=False,
685
- browser_enabled=False,
483
+ "standard", str(sections_dir), init_done=True,
484
+ is_resume=False, browser_enabled=False,
686
485
  )
687
486
  checkpoint = generate_checkpoint_definition(
688
- sections,
689
- pipeline_mode="standard",
690
- workflow_type="feature-pipeline",
691
- item_id="F-123",
692
- item_slug="123-scope-test",
693
- session_id="F-123-session",
694
- init_done=True,
487
+ sections, pipeline_mode="standard", workflow_type="feature-pipeline",
488
+ item_id="F-123", item_slug="123-scope-test",
489
+ session_id="F-123-session", init_done=True,
695
490
  )
696
491
  skills = [step["skill"] for step in checkpoint["steps"]]
697
-
698
- assert skills.index("prizmkit-implement") < skills.index("prizmkit-code-review") < skills.index("prizmkit-test")
699
492
  test_step = next(step for step in checkpoint["steps"] if step["skill"] == "prizmkit-test")
700
- assert test_step["required_artifacts"] == [
701
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
702
- ".prizmkit/test/evidence/*/manifest.json",
703
- ".prizmkit/test/evidence/*/verdict.json",
704
- ".prizmkit/test/evidence/*/validation.json",
705
- ]
706
-
707
- def test_merge_resets_completed_prizmkit_test_without_report_artifact(self, tmp_path):
708
- existing = {
709
- "steps": [
710
- {
711
- "id": "S01",
712
- "skill": "prizmkit-test",
713
- "status": "completed",
714
- "required_artifacts": [
715
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
716
- ".prizmkit/test/*/test-report.md",
717
- ],
718
- },
719
- {"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "required_artifacts": []},
720
- ],
721
- }
722
- fresh = {
723
- "steps": [
724
- {"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
725
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
726
- ".prizmkit/test/*/test-report.md",
727
- ]},
728
- {"id": "S02", "skill": "prizmkit-code-review", "status": "pending", "required_artifacts": []},
729
- ],
730
- }
731
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
732
- pointer.parent.mkdir(parents=True, exist_ok=True)
733
- pointer.write_text(".prizmkit/test/missing/test-report.md\n", encoding="utf-8")
734
-
735
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
736
-
737
- assert [step["status"] for step in merged["steps"]] == ["pending", "pending"]
738
-
739
- def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path, monkeypatch):
740
- self._trust_fixture_validator(monkeypatch)
741
- evidence_root = write_fresh_structured_evidence(tmp_path)
742
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
743
- pointer.parent.mkdir(parents=True, exist_ok=True)
744
- pointer.write_text(str(evidence_root / "test-report.md") + "\n", encoding="utf-8")
745
- existing = {
746
- "steps": [
747
- {"id": "S01", "skill": "prizmkit-test", "status": "completed", "stage_result": "TEST_PASS", "required_artifacts": [
748
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
749
- ".prizmkit/test/*/test-report.md",
750
- ]},
751
- {"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "stage_result": "REVIEW_PASS", "required_artifacts": []},
752
- ],
753
- }
754
- fresh = {
755
- "steps": [
756
- {"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
757
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
758
- ".prizmkit/test/*/test-report.md",
759
- ]},
760
- {"id": "S02", "skill": "prizmkit-code-review", "status": "pending", "required_artifacts": []},
761
- ],
762
- }
763
-
764
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
765
-
766
- assert [step["status"] for step in merged["steps"]] == ["completed", "completed"]
767
-
768
- def test_merge_resets_completed_prizmkit_test_for_needs_fixes_report(self, tmp_path):
769
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
770
- report.parent.mkdir(parents=True)
771
- report.write_text(scoped_report_text(verdict="NEEDS_FIXES"), encoding="utf-8")
772
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
773
- pointer.parent.mkdir(parents=True, exist_ok=True)
774
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
775
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
776
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
777
- ".prizmkit/test/*/test-report.md",
778
- ]}]}
779
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
780
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
781
- ".prizmkit/test/*/test-report.md",
782
- ]}]}
783
-
784
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
785
-
786
- assert merged["steps"][0]["status"] == "pending"
787
-
788
- def test_merge_resets_completed_prizmkit_test_for_absolute_report_outside_project(self, tmp_path):
789
- project_root = tmp_path / "project"
790
- external_root = tmp_path / "external"
791
- in_project_report = project_root / ".prizmkit" / "test" / "run" / "test-report.md"
792
- external_report = external_root / "report.md"
793
- pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
794
- for path in (in_project_report.parent, external_report.parent, pointer.parent):
795
- path.mkdir(parents=True, exist_ok=True)
796
- report_text = "\n".join([
797
- "# Test Report",
798
- "",
799
- "## Scope",
800
- "- Mode: this-change",
801
- "- Artifact Dir: .prizmkit/specs/123-scope-test/",
802
- "",
803
- "## Verdict",
804
- "PASS",
805
- "",
806
- ])
807
- in_project_report.write_text(report_text, encoding="utf-8")
808
- external_report.write_text(report_text, encoding="utf-8")
809
- pointer.write_text(f"{external_report}\n", encoding="utf-8")
810
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
811
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
812
- ".prizmkit/test/*/test-report.md",
813
- ]}]}
814
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
815
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
816
- ".prizmkit/test/*/test-report.md",
817
- ]}]}
818
-
819
- merged = merge_checkpoint_state(existing, fresh, str(project_root))
820
-
821
- assert merged["steps"][0]["status"] == "pending"
822
-
823
- def test_merge_resets_completed_prizmkit_test_for_sibling_workspace_report(self, tmp_path):
824
- project_root = tmp_path / "project"
825
- sibling_root = tmp_path / "project-sibling"
826
- in_project_report = project_root / ".prizmkit" / "test" / "run" / "test-report.md"
827
- sibling_report = sibling_root / ".prizmkit" / "test" / "run" / "test-report.md"
828
- pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
829
- for path in (in_project_report.parent, sibling_report.parent, pointer.parent):
830
- path.mkdir(parents=True, exist_ok=True)
831
- report_text = scoped_report_text()
832
- in_project_report.write_text(report_text, encoding="utf-8")
833
- sibling_report.write_text(report_text, encoding="utf-8")
834
- pointer.write_text(f"{sibling_report}\n", encoding="utf-8")
835
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
836
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
837
- ".prizmkit/test/*/test-report.md",
838
- ]}]}
839
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
840
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
841
- ".prizmkit/test/*/test-report.md",
842
- ]}]}
843
-
844
- merged = merge_checkpoint_state(existing, fresh, str(project_root))
845
493
 
846
- assert merged["steps"][0]["status"] == "pending"
847
-
848
- def test_merge_resets_completed_prizmkit_test_for_minimal_boundary_matrix(self, tmp_path):
849
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
850
- report.parent.mkdir(parents=True)
851
- report.write_text(
852
- "\n".join([
853
- "# Test Report",
854
- "",
855
- "## Scope",
856
- "- Mode: this-change",
857
- "- Artifact Dir: .prizmkit/specs/123-scope-test/",
858
- "",
859
- "## Boundary Matrix",
860
- "| Interface | Happy Path | N/A Reasons | Final Status |",
861
- "|-----------|------------|-------------|--------------|",
862
- "| POST /feature | covered | none | boundary-covered |",
863
- "",
864
- "## Boundary Completion Gate",
865
- "- Interfaces total: 1",
866
- "- Boundary-covered: 1",
867
- "- Boundary-missing: 0",
868
- "- Happy-path-only: 0",
869
- "- Completion gate passed: yes",
870
- "",
871
- "## Boundary Validation",
872
- "- Validator result: skipped",
873
- "",
874
- "## Verdict",
875
- "PASS",
876
- "",
877
- ]),
878
- encoding="utf-8",
879
- )
880
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
881
- pointer.parent.mkdir(parents=True, exist_ok=True)
882
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
883
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
884
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
885
- ".prizmkit/test/*/test-report.md",
886
- ]}]}
887
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
888
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
889
- ".prizmkit/test/*/test-report.md",
890
- ]}]}
891
-
892
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
893
-
894
- assert merged["steps"][0]["status"] == "pending"
895
-
896
- def test_merge_resets_completed_prizmkit_test_for_missing_boundary_metrics(self, tmp_path):
897
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
898
- report_text = scoped_report_text().replace("- Boundary-missing: 0\n", "").replace("- Happy-path-only: 0\n", "")
899
- write_fresh_scoped_report(tmp_path, report, report_text)
900
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
901
- pointer.parent.mkdir(parents=True, exist_ok=True)
902
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
903
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
904
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
905
- ".prizmkit/test/*/test-report.md",
906
- ]}]}
907
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
908
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
909
- ".prizmkit/test/*/test-report.md",
910
- ]}]}
911
-
912
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
913
-
914
- assert merged["steps"][0]["status"] == "pending"
915
-
916
- def test_merge_resets_completed_prizmkit_test_when_marker_newer_than_report(self, tmp_path):
917
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
918
- _source, _test, marker = write_fresh_scoped_report(tmp_path, report)
919
- now = time.time()
920
- os.utime(report, (now, now))
921
- os.utime(marker, (now + 10, now + 10))
922
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
923
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
924
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
925
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
926
- ".prizmkit/test/*/test-report.md",
927
- ]}]}
928
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
929
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
930
- ".prizmkit/test/*/test-report.md",
931
- ]}]}
932
-
933
- merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
934
-
935
- assert merged["steps"][0]["status"] == "pending"
494
+ assert skills.index("prizmkit-code-review") < skills.index("prizmkit-test")
495
+ assert test_step["required_artifacts"] == self._artifacts()
936
496
 
937
- def test_merge_resets_completed_prizmkit_test_when_in_scope_file_newer_than_report(self, tmp_path):
938
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
939
- source, _test, _marker = write_fresh_scoped_report(tmp_path, report)
940
- now = time.time()
941
- os.utime(report, (now, now))
942
- os.utime(source, (now + 10, now + 10))
943
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
944
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
945
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
946
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
947
- ".prizmkit/test/*/test-report.md",
948
- ]}]}
949
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
950
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
951
- ".prizmkit/test/*/test-report.md",
952
- ]}]}
497
+ def test_merge_keeps_only_consistent_test_pass_artifacts(self, tmp_path):
498
+ self._write_result(tmp_path)
499
+ existing = {"steps": [{
500
+ "id": "S01", "skill": "prizmkit-test", "status": "completed",
501
+ "stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
502
+ }]}
503
+ fresh = {"steps": [{
504
+ "id": "S01", "skill": "prizmkit-test", "status": "pending",
505
+ "required_artifacts": self._artifacts(),
506
+ }]}
953
507
 
954
508
  merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
955
509
 
956
- assert merged["steps"][0]["status"] == "pending"
957
-
958
- def test_merge_resets_completed_prizmkit_test_without_in_scope_tests(self, tmp_path):
959
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
960
- report_text = scoped_report_text().replace("- In-Scope Test Files:\n - tests/feature.test.js\n", "- In-Scope Test Files:\n")
961
- write_fresh_scoped_report(tmp_path, report, report_text)
962
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
963
- pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
964
- existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
965
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
966
- ".prizmkit/test/*/test-report.md",
967
- ]}]}
968
- fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
969
- ".prizmkit/specs/123-scope-test/test-report-path.txt",
970
- ".prizmkit/test/*/test-report.md",
971
- ]}]}
510
+ assert merged["steps"][0]["status"] == "completed"
511
+ assert merged["steps"][0]["stage_result"] == "TEST_PASS"
512
+
513
+ def test_merge_resets_missing_or_non_pass_test_artifacts(self, tmp_path):
514
+ existing = {"steps": [{
515
+ "id": "S01", "skill": "prizmkit-test", "status": "completed",
516
+ "stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
517
+ }]}
518
+ fresh = {"steps": [{
519
+ "id": "S01", "skill": "prizmkit-test", "status": "pending",
520
+ "required_artifacts": self._artifacts(),
521
+ }]}
522
+ missing = merge_checkpoint_state(existing, fresh, str(tmp_path))
523
+ assert missing["steps"][0]["status"] == "pending"
524
+
525
+ self._write_result(tmp_path, "TEST_NEEDS_FIXES", unresolved=["known correction"])
526
+ needs_fixes = merge_checkpoint_state(existing, fresh, str(tmp_path))
527
+ assert needs_fixes["steps"][0]["status"] == "pending"
528
+
529
+ def test_merge_rejects_malformed_or_inconsistent_terminal_projection(self, tmp_path):
530
+ self._write_result(tmp_path)
531
+ result_path = tmp_path / ".prizmkit/specs/123-scope-test/test-result.json"
532
+ payload = json.loads(result_path.read_text(encoding="utf-8"))
533
+ payload["review_required"] = True
534
+ payload["review_scope"] = None
535
+ result_path.write_text(json.dumps(payload), encoding="utf-8")
536
+ existing = {"steps": [{
537
+ "id": "S01", "skill": "prizmkit-test", "status": "completed",
538
+ "stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
539
+ }]}
540
+ fresh = {"steps": [{
541
+ "id": "S01", "skill": "prizmkit-test", "status": "pending",
542
+ "required_artifacts": self._artifacts(),
543
+ }]}
972
544
 
973
545
  merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
974
546
 
975
547
  assert merged["steps"][0]["status"] == "pending"
976
548
 
977
- def test_structured_evidence_accepts_current_matching_package(self, tmp_path, monkeypatch):
978
- self._trust_fixture_validator(monkeypatch)
979
- evidence_root = write_fresh_structured_evidence(tmp_path)
980
-
981
- evidence = validate_feature_test_evidence(
982
- str(tmp_path), ".prizmkit/specs/123-scope-test",
983
- )
984
-
985
- assert evidence is not None
986
- assert evidence["verdict"] == "TEST_PASS"
987
- assert evidence["manifest_path"] == str(
988
- (evidence_root / "manifest.json").relative_to(tmp_path),
989
- )
990
- assert evidence["validation_path"].endswith("/validation.json")
991
-
992
- def test_structured_evidence_requires_authoritative_validator(self, tmp_path, monkeypatch):
993
- write_fresh_structured_evidence(tmp_path)
994
- monkeypatch.setattr(
995
- "generate_bootstrap_prompt._evidence_validator_passes",
996
- lambda _project_root, _evidence_root, _validation: False,
997
- )
998
-
999
- assert validate_feature_test_evidence(
1000
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1001
- ) is None
1002
-
1003
- def test_structured_evidence_rejects_legacy_markdown_only_pass(self, tmp_path):
1004
- report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
1005
- write_fresh_scoped_report(tmp_path, report)
1006
- pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
1007
- pointer.write_text(str(report), encoding="utf-8")
1008
-
1009
- assert validate_feature_test_evidence(
1010
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1011
- ) is None
1012
-
1013
- def test_structured_evidence_rejects_missing_validation(self, tmp_path):
1014
- evidence_root = write_fresh_structured_evidence(tmp_path)
1015
- (evidence_root / "validation.json").unlink()
1016
-
1017
- assert validate_feature_test_evidence(
1018
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1019
- ) is None
1020
-
1021
- def test_structured_evidence_rejects_tampered_verdict(self, tmp_path, monkeypatch):
1022
- self._trust_fixture_validator(monkeypatch)
1023
- evidence_root = write_fresh_structured_evidence(tmp_path)
1024
- verdict_path = evidence_root / "verdict.json"
1025
- verdict = json.loads(verdict_path.read_text(encoding="utf-8"))
1026
- verdict["verdict"] = "TEST_BLOCKED"
1027
- verdict_path.write_text(json.dumps(verdict), encoding="utf-8")
1028
-
1029
- assert validate_feature_test_evidence(
1030
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1031
- ) is None
1032
-
1033
- def test_structured_evidence_rejects_wrong_artifact_scope(self, tmp_path, monkeypatch):
1034
- self._trust_fixture_validator(monkeypatch)
1035
- evidence_root = write_fresh_structured_evidence(tmp_path)
1036
- report_path = evidence_root / "test-report.md"
1037
- report_path.write_text(
1038
- scoped_report_text().replace(
1039
- ".prizmkit/specs/123-scope-test/",
1040
- ".prizmkit/specs/999-other/",
1041
- ),
1042
- encoding="utf-8",
1043
- )
1044
-
1045
- assert validate_feature_test_evidence(
1046
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1047
- ) is None
1048
-
1049
- def test_structured_evidence_rejects_stale_report(self, tmp_path, monkeypatch):
1050
- self._trust_fixture_validator(monkeypatch)
1051
- evidence_root = write_fresh_structured_evidence(tmp_path)
1052
- report = evidence_root / "test-report.md"
1053
- marker = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
1054
- now = time.time()
1055
- os.utime(report, (now, now))
1056
- os.utime(marker, (now + 10, now + 10))
1057
-
1058
- assert validate_feature_test_evidence(
1059
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1060
- ) is None
1061
-
1062
- def test_structured_evidence_rejects_live_inventory_drift(self, tmp_path, monkeypatch):
1063
- self._trust_fixture_validator(monkeypatch)
1064
- write_fresh_structured_evidence(tmp_path)
1065
- source = tmp_path / "src" / "feature.js"
1066
- source.write_text("export const feature = false;\n", encoding="utf-8")
1067
-
1068
- assert validate_feature_test_evidence(
1069
- str(tmp_path), ".prizmkit/specs/123-scope-test",
1070
- ) is None
1071
-
1072
- def test_feature_repair_routing_preserves_review_and_test_boundaries(self):
549
+ def test_feature_result_routing_stops_test_non_pass_without_outer_repair(self):
1073
550
  review = route_feature_repair("REVIEW_NEEDS_FIXES", repair_round=0)
1074
- test_infra = route_feature_repair(
1075
- "TEST_FAIL", "test-infrastructure", repair_round=1,
1076
- )
1077
- production = route_feature_repair("TEST_FAIL", "production", repair_round=1)
551
+ needs_fixes = route_feature_repair("TEST_NEEDS_FIXES", repair_round=1)
552
+ blocked = route_feature_repair("TEST_BLOCKED", repair_round=1)
1078
553
 
1079
554
  assert review["route"] == [
1080
555
  "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
1081
556
  ]
1082
- assert test_infra["route"] == ["prizmkit-implement", "prizmkit-test"]
1083
- assert production["route"] == [
1084
- "prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
1085
- ]
1086
557
  assert review["repair_round"] == 1
1087
-
1088
- def test_feature_repair_routing_blocks_unsafe_and_exhausted_routes(self):
1089
- unknown = route_feature_repair("TEST_FAIL", "unknown", repair_round=1)
1090
- blocked = route_feature_repair("TEST_BLOCKED", "production", repair_round=1)
1091
- exhausted = route_feature_repair("TEST_FAIL", "production", repair_round=3)
1092
-
1093
- assert unknown["blocked"] is True
1094
- assert unknown["route"] == []
558
+ assert needs_fixes["blocked"] is True
559
+ assert needs_fixes["route"] == []
560
+ assert needs_fixes["blocked_reason"] == "test_needs_fixes"
1095
561
  assert blocked["blocked"] is True
1096
- assert blocked["resumable"] is False
1097
- assert blocked["next_stage"] is None
1098
- assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
1099
562
  assert blocked["blocked_reason"] == "test_blocked"
1100
- assert exhausted["blocked"] is True
1101
- assert exhausted["repair_round"] == 3
1102
-
1103
- def test_feature_checkpoint_metadata_records_l1_handoff_fields(self):
1104
- evidence = {
1105
- "manifest_path": ".prizmkit/test/evidence/id/manifest.json",
1106
- "verdict_path": ".prizmkit/test/evidence/id/verdict.json",
1107
- "validation_path": ".prizmkit/test/evidence/id/validation.json",
1108
- }
563
+ assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
1109
564
 
565
+ def test_feature_checkpoint_metadata_has_no_evidence_paths(self):
1110
566
  metadata = feature_checkpoint_metadata(
1111
567
  ".prizmkit/specs/123-scope-test",
1112
- evidence=evidence,
1113
- stage="test",
1114
- status="TEST_FAIL",
1115
- stage_result="TEST_FAIL",
1116
- repair_scope="test-infrastructure",
1117
- repair_round=2,
1118
- next_stage="prizmkit-implement",
1119
- resume_from="prizmkit-implement",
568
+ stage="test", status="TEST_NEEDS_FIXES",
569
+ stage_result="TEST_NEEDS_FIXES", repair_round=0,
570
+ next_stage=None, resume_from=None,
1120
571
  )
1121
572
 
1122
- assert metadata == {
1123
- "schema_version": 1,
1124
- "artifact_dir": ".prizmkit/specs/123-scope-test",
1125
- "orchestrator": "prizmkit-l4",
1126
- "stage": "test",
1127
- "current_stage": "test",
1128
- "status": "TEST_FAIL",
1129
- "stage_result": "TEST_FAIL",
1130
- "repair_scope": "test-infrastructure",
1131
- "repair_round": 2,
1132
- "authoritative_evidence_paths": {
1133
- "manifest": ".prizmkit/test/evidence/id/manifest.json",
1134
- "verdict": ".prizmkit/test/evidence/id/verdict.json",
1135
- "validation": ".prizmkit/test/evidence/id/validation.json",
1136
- },
1137
- "next_stage": "prizmkit-implement",
1138
- "resume_from": "prizmkit-implement",
1139
- "blocked_reason": None,
1140
- "terminal_status": None,
1141
- }
573
+ assert metadata["stage_result"] == "TEST_NEEDS_FIXES"
574
+ assert "authoritative_evidence_paths" not in metadata
575
+ assert metadata["artifact_dir"] == ".prizmkit/specs/123-scope-test"
1142
576
 
1143
577
  # ---------------------------------------------------------------------------
1144
578
  # Active compaction harness rollback
@@ -1605,7 +1039,8 @@ class TestFeaturePromptUnifiedFullGuidance:
1605
1039
  assert "### Plan Self-Check — Orchestrator Direct" in prompt
1606
1040
  assert "### Scoped Feature Test Gate — PrizmKit Test" in prompt
1607
1041
  assert "## Test Failure Recovery Protocol" in prompt
1608
- assert "Boundary Matrix" in prompt
1042
+ assert "test-report.md" in prompt
1043
+ assert "test-result.json" in prompt
1609
1044
  assert "### Browser Verification — MANDATORY" not in prompt
1610
1045
  assert "### TERMINAL STOP — Commit Completed" in prompt
1611
1046
  assert "# Dev-Pipeline Session Bootstrap — Tier 1" not in prompt
@@ -1847,7 +1282,8 @@ class TestHeadlessPromptCleanupF033:
1847
1282
  assert forbidden not in prompt
1848
1283
  if "Feature ID" in prompt:
1849
1284
  assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/123-prompt-cleanup/" in prompt
1850
- assert ".prizmkit-test-started" in prompt
1285
+ assert "test-report.md" in prompt
1286
+ assert "test-result.json" in prompt
1851
1287
  assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1852
1288
  assert "prizmkit-runtime-helper.py" in prompt
1853
1289
  # Anti-Bash guardrails for bugfix/refactor branches (F-039)
@@ -1874,7 +1310,7 @@ class TestHeadlessPromptCleanupF033:
1874
1310
  assert "which playwright-cli" not in prompt
1875
1311
  assert "lsof -ti" not in prompt
1876
1312
  assert "sed -nE" not in prompt
1877
- assert ".prizmkit-test-started" in prompt
1313
+ assert ".prizmkit-test-started" not in prompt
1878
1314
  assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1879
1315
  assert "prizmkit-runtime-helper.py" in prompt
1880
1316
 
@@ -1991,7 +1427,6 @@ class TestFeatureBootstrapShellExtraction:
1991
1427
  assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1992
1428
  assert "python" in prompt.lower()
1993
1429
  assert ".prizmkit/dev-pipeline/scripts/prizmkit-runtime-helper.py" in prompt
1994
- assert "artifact touch .prizmkit/specs/123-prompt-cleanup/.prizmkit-test-started" in prompt
1995
1430
  assert "text final-verdict .prizmkit/specs/123-prompt-cleanup/review-report.md" in prompt
1996
1431
 
1997
1432
  def test_rendered_section_feature_prompts_use_runtime_helper_not_extracted_bash(self, tmp_path):