"""Step-1 load-bearing seam (målbilde §5 / §7): a prior expert verdict MUST reach the next hypothesis prompt. The diagnosis (fot-i-bakken, verified in code): ExpeL retrieval was computed AFTER generation and written into a discarded ``SessionContext`` — so the seed verdict could not influence any hypothesis (``context_providers`` = 0). Fase 2a wires it: before ``generate_via_llm``, the candidate's prior verdicts are folded into the generation context. These two tests are a load-bearing PAIR, not a smoke test (per the Fase-2 green-but-dead trap): - the positive test asserts the seed verdict's realization signal ("0.82") IS in the generation prompt — it goes RED the moment the fold is detached; - the empty-store test asserts the signal is ABSENT without a seed — proving the signal's presence is CAUSED by the seam, not incidental to the fixture. """ from __future__ import annotations from pathlib import Path from portfolio_optimiser.run import run_project from portfolio_optimiser.validator import ValidatedProposal from portfolio_optimiser.verdicts import VerdictStore, seed_store_from_bundle BUNDLE_DIR = Path(__file__).resolve().parents[1] / "shared" / "examples" / "bygg-energi-mikro" # A VALID BYGG-KONTOR-NORD proposal: affected total = 300000 x 1.0; no assumptions -> degenerate # Monte Carlo P90 = 0.30 x 300000 = 90000 >= claimed 30000 -> validates on the first attempt. _ENERGY_REPLY = ( '{"measure":"LED-retrofit av kontorbelysning","affected_items":' '[{"code":"ENERGI-TOTAL-EL","quantity":300000,"unit_cost":1.0}],"claimed_saving_nok":30000}' ) _VERDICT_INPUT = {"decision": "approved", "rationale": "expert reviewed (sim)"} # Two load-bearing markers, by design: # - the seed verdict's MINTED id (16-char content hash ``format_fewshot`` emits as ``- [] ...``); # it cannot appear in the bundle's raw text, so it isolates the STRUCTURAL ExpeL path. # - the realization rate "0.82". Fase 2b replaced keyword chunk-stuffing with OKF-navigation that # EXCLUDES ``type: verdict`` from the bundle context, so the signal now reaches a prompt ONLY via # the gated ExpeL fold. Pre-2b (``docs_dir == bundle_dir``) chunk-stuffing leaked "0.82" from the # verdict + golden files into the debate prompt regardless of the fold — which the empty-store # control below asserts is no longer true (it goes RED against the pre-2b chunk-stuffing path). _SEED_ID = seed_store_from_bundle(str(BUNDLE_DIR)).verdicts[0].id _REALIZATION_SIGNAL = "realiseringsgrad=0.82" # the exact ``_verdict_rationale`` fewshot string def _generation_prompts(sink: list[str]) -> list[str]: """The generation-call prompts (``generate._build_messages`` embeds 'SavingsProposal'), isolated from the debate-round prompts also captured in the sink.""" return [p for p in sink if "SavingsProposal" in p] async def test_prior_verdict_reaches_hypothesis_prompt(make_recording_client_factory) -> None: """LOAD-BEARING: with the energi bundle's seed verdict in the store, the realization signal reaches the hypothesis-generation prompt. Detach the fold in ``run.py`` and this goes red.""" store = seed_store_from_bundle(str(BUNDLE_DIR)) assert store.verdicts, "precondition: the bundle seeds exactly one verdict" factory, recorded = make_recording_client_factory(_ENERGY_REPLY) result = await run_project( "BYGG-KONTOR-NORD", "local", docs_dir=str(BUNDLE_DIR), bundle_dir=str(BUNDLE_DIR), verdict_input=_VERDICT_INPUT, store=store, client_factory=factory, ) assert isinstance(result.outcome, ValidatedProposal) gen_prompts = _generation_prompts(recorded) assert gen_prompts, "the generation call must have happened" assert any(_SEED_ID in p for p in gen_prompts), ( "the seed verdict did not reach the hypothesis prompt — the ExpeL->prompt fold is detached" ) # Fase 2b: the realization SIGNAL itself (not just the id) is now a clean marker — it can only # reach the prompt via the gated fold, since OKF-navigation excludes the verdict from context. assert any(_REALIZATION_SIGNAL in p for p in gen_prompts), ( "the realization signal did not reach the hypothesis prompt via the ExpeL fold" ) async def test_empty_store_leaves_no_signal_in_prompt(make_recording_client_factory) -> None: """CAUSALITY CONTROL: the same run with an EMPTY store carries no realization signal into the prompt — proving the signal above is caused by the seam, making the positive test load-bearing (not merely green against the fixture's mere presence).""" factory, recorded = make_recording_client_factory(_ENERGY_REPLY) await run_project( "BYGG-KONTOR-NORD", "local", docs_dir=str(BUNDLE_DIR), bundle_dir=str(BUNDLE_DIR), verdict_input=_VERDICT_INPUT, store=VerdictStore(verdicts=[]), client_factory=factory, ) assert all(_SEED_ID not in p for p in recorded), ( "no seed verdict in the store, yet its id appeared in a prompt — " "the assertion is not actually load-bearing" ) # Fase 2b LOAD-BEARING: with no fold, the realization rate must appear in NO prompt. This goes # RED against the pre-2b chunk-stuffing path (which leaked "0.82" from the verdict + golden files # into the debate prompt) and only passes once OKF-navigation excludes the verdict from context. assert all("0.82" not in p for p in recorded), ( "the realization rate leaked into a prompt without a seed verdict — bundle context is still " "chunk-stuffing the verdict/golden files (Fase 2b OKF-navigation not in effect)" )