"""Step 3 tests — non-tautological top-K retrieval + REAL-SessionContext two-arg injection. The true match shares the *structured* similarity fields with the query but uses different description text; the decoys share surface text but differ structurally. The injection test uses a REAL ``agent_framework.SessionContext`` (not a single-arg fake), exercising the genuine two-arg ``extend_instructions(source_id, instructions)`` GA signature — retiring the Critical Fase 1 risk. Pattern: tests/spikes/test_d_verdictstore.py + real SessionContext. """ import pytest from agent_framework import SessionContext from portfolio_optimiser.verdicts import ( ExpeLContextProvider, ProposalFeatures, Verdict, VerdictStore, capture_verdict, ) _QUERY = ProposalFeatures( affected_codes=frozenset({"05.2", "03.1"}), measure_type="scope_reduction", claimed_saving_nok=220_000, # bucket [100k, 500k) description="asphalt base course reduction near school", ) def _store_with_true_match_and_decoys() -> tuple[VerdictStore, str]: true_match = Verdict( id="TRUE", proposal_features=ProposalFeatures( affected_codes=frozenset({"05.2", "03.1"}), # same codes measure_type="scope_reduction", # same measure type claimed_saving_nok=200_000, # same magnitude bucket description="zzz totally unrelated wording alpha beta", # DIFFERENT text ), decision="approved", rationale="prior scope reduction on the same codes was approved", ) decoy_low = Verdict( id="DECOY-LOW", proposal_features=ProposalFeatures( affected_codes=frozenset({"09.1"}), measure_type="rate_renegotiation", claimed_saving_nok=50_000, description="asphalt base course reduction near school", # same words as query ), decision="rejected", rationale="surface-text decoy", ) decoy_high = Verdict( id="DECOY-HIGH", proposal_features=ProposalFeatures( affected_codes=frozenset({"21.2"}), measure_type="material_substitution", claimed_saving_nok=700_000, description="asphalt base course reduction extra words", ), decision="rejected", rationale="surface-text decoy", ) return VerdictStore(verdicts=[decoy_low, true_match, decoy_high]), "TRUE" def test_retrieve_finds_structural_match_over_text_decoys() -> None: store, true_id = _store_with_true_match_and_decoys() hits = store.retrieve(_QUERY, k=3) assert hits[0].id == true_id # structural match ranks #1 despite different wording def test_retrieve_is_deterministic() -> None: store, _ = _store_with_true_match_and_decoys() assert [h.id for h in store.retrieve(_QUERY, k=3)] == [ h.id for h in store.retrieve(_QUERY, k=3) ] def test_retrieve_rejects_non_positive_k() -> None: store, _ = _store_with_true_match_and_decoys() with pytest.raises(ValueError): store.retrieve(_QUERY, k=0) def test_capture_verdict_mints_stable_id() -> None: a = capture_verdict(_QUERY, "approved", "ok") b = capture_verdict( ProposalFeatures( affected_codes=frozenset({"03.1", "05.2"}), # same set, different order measure_type="scope_reduction", claimed_saving_nok=220_000, description="DIFFERENT surface wording entirely", # text excluded from the id ), "approved", "ok", ) assert a.id == b.id # structurally identical -> stable id assert len(a.id) == 16 async def test_before_run_populates_real_sessioncontext_two_arg() -> None: store, true_id = _store_with_true_match_and_decoys() provider = ExpeLContextProvider(store, _QUERY, k=2) # A REAL SessionContext (not a single-arg fake) — exercises the genuine GA two-arg # extend_instructions(source_id, instructions) signature. ctx = SessionContext(input_messages=[], instructions=[]) await provider.before_run(agent=None, session=None, context=ctx, state={}) assert any(true_id in instr for instr in ctx.instructions)