test(IMP-05): tighten Step 9 candidate evidence guard

Refs #5

Replace the hand-built Case 7 payload assertion with a temporary
production-source guard. The test now fails if Step 9 stops emitting
candidate_evidence, breaks the fallback_chain compat alias, or removes
the alias intent comment.

This is intentionally temporary because Step 9 application-plan unit
assembly is inline. Follow-up IMP-32 should extract a helper and replace
this source-string guard with a direct helper test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 00:24:42 +09:00
parent 21476ae000
commit 23d1b25144

View File

@@ -20,6 +20,9 @@ from typing import Optional
import pytest import pytest
import inspect
from src import phase_z2_pipeline
from src.phase_z2_pipeline import lookup_v4_match_with_fallback from src.phase_z2_pipeline import lookup_v4_match_with_fallback
@@ -292,42 +295,25 @@ def test_existing_trace_shape_does_not_regress(patch_selector_deps):
assert trace["selection_path"] == "rank_1" assert trace["selection_path"] == "rank_1"
# ─── Case 7 : Step 9 application_plan candidate_evidence + fallback_chain alias ─── # ─── Case 7 : Step 9 production-source guard (Codex #20 blocker fix) ───
def test_step9_candidate_evidence_field_and_alias_equality(): def test_step9_production_emits_candidate_evidence_and_alias():
"""Codex #16 idea A + Codex #17 idea E + Claude #19 / #21 — Step 9 application_plan """Temporary production-source guard for IMP-05 Step 9 evidence fields.
must expose `candidate_evidence` as the primary per-unit evidence field, with
`fallback_chain` kept as a compat alias pointing to the same data.
Both fields must reference the same selection_trace.candidates payload so Step 9 application-plan unit assembly is currently inline, so this test
downstream readers (new = candidate_evidence, legacy = fallback_chain) see checks the exact production assignments until IMP-32 extracts a helper.
identical data. Once that helper exists, replace this source-string guard with a direct
helper-call test.
""" """
# Synthetic selection_trace.candidates shape — mirrors selector output (Step 9 input) source = inspect.getsource(phase_z2_pipeline)
fake_candidates = [ candidate_line = '"candidate_evidence": selection_trace.get("candidates", [])'
{"rank": 1, "template_id": "MOCK_template_direct_a", "v4_label": "use_as_is", alias_line = '"fallback_chain": selection_trace.get("candidates", [])'
"decision": "selected", "reason": "primary_selected"},
{"rank": 2, "template_id": "MOCK_template_direct_b", "v4_label": "use_as_is",
"decision": "skipped", "reason": None},
]
# Simulated Step 9 application_plan unit payload (post-Step-3 schema) assert candidate_line in source
selection_trace = {"candidates": fake_candidates} assert alias_line in source
unit_payload = { assert source.index(candidate_line) < source.index(alias_line)
"candidate_evidence": selection_trace.get("candidates", []), assert "compat alias; prefer candidate_evidence" in source
"fallback_chain": selection_trace.get("candidates", []),
}
# candidate_evidence must be present as the primary field
assert "candidate_evidence" in unit_payload
assert unit_payload["candidate_evidence"] == fake_candidates
# fallback_chain compat alias must reference the same data (no regression for legacy readers)
assert "fallback_chain" in unit_payload
assert unit_payload["fallback_chain"] == unit_payload["candidate_evidence"]
assert unit_payload["fallback_chain"] is unit_payload["candidate_evidence"] or \
unit_payload["fallback_chain"] == unit_payload["candidate_evidence"]
# ─── Case 8 : Step 20 slide-status qualifier fields presence + defensive default # ─── Case 8 : Step 20 slide-status qualifier fields presence + defensive default