"""P4 pkt. 0 — the demo's RESERVE bundle must anchor the deterministic gate to real cost lines. The gap (egnethetsreview Funn 1, corrected by objection I1): the validator CAN reconcile a proposal against the project's actual cost lines (S4.0, stage 0), but only when the knowledge base ships a ``cost-baseline.json``. No bundle under ``shared/examples/`` has that file — so in the demo the validator reasoned only about numbers the proposal itself supplied, and an internally consistent hallucination would clear the gate on stage. The reserve cannot receive the file IN ``shared/``: the subtree is pull-only and demo criterion 8 requires the commons-owned goldens byte-unchanged. But that is a PLACEMENT constraint, not an impossibility — the demo already runs on a COPY of the bundle, so a copy-and-extend variant gives an anchored run without touching commons. **Direction of derivation, and why it matters.** Here the baseline is derived FROM the scripted register: the reserve's numbers are synthetic, so the script is the only ground truth available. On GO day the direction reverses (plan P3 b) — the register's numbers are written FROM the delivered ``cost-baseline.json``. Deriving in code, not by hand, is what stops the two from drifting apart; drift is precisely the failure the 10 % test below models. **The 10 % test** is the answer to "you generated the ground truth from the answer, so of course it passes": deviate the baseline beyond the 5 % tolerance and the same, unchanged script must be FORKASTET at stage 0 — before the solver — while the undeviated run is FORESLÅTT. """ from __future__ import annotations import json import subprocess import sys import pytest from portfolio_optimiser import okf from portfolio_optimiser.ir import CostBaseline, CostBaselineLine from portfolio_optimiser.simulation import ( ScriptedCandidate, _default_bundle_dir, baseline_from_scripted_candidate, materialize_anchored_bundle, simulate_learning_loop, ) from portfolio_optimiser.validator import Rejection, ValidatedProposal def _deviated(baseline: CostBaseline, factor: float) -> CostBaseline: """The same baseline with every quantity scaled — the delivered numbers disagreeing with the script's by ``factor``, which is exactly the GO-day risk this models.""" return CostBaseline( project_id=baseline.project_id, items={ code: CostBaselineLine(quantity=line.quantity * factor, unit_cost=line.unit_cost) for code, line in baseline.items.items() }, ) async def test_the_anchored_reserve_runs_the_whole_demo(tmp_path) -> None: """CONTROL: with the baseline derived from the script, the anchored reserve behaves exactly as the demo narrates — hypothesis #1 falsified by the P90 stage, the corrected one validated. This is the control that gives the 10 % test its meaning: a gate that rejects everything proves nothing. It also pins WHICH stage rejects hypothesis #1 — if stage 0 started rejecting it, demo criterion 2 would still show a REJECTED and a VALIDATED line while silently demonstrating a different mechanism.""" bundle = materialize_anchored_bundle(tmp_path / "forankret") result = await simulate_learning_loop(str(bundle), str(tmp_path)) assert isinstance(result.run_a.outcome, ValidatedProposal) assert isinstance(result.run_b.outcome, ValidatedProposal) assert result.run_a.refinements, "no falsification was fed back — Step 5 is not being shown" assert "exceeds P90 feasible" in result.run_a.refinements[0].reason, ( "hypothesis #1 was rejected by some other stage than the P90 one the demo narrates" ) async def test_a_deviating_baseline_forkaster_the_demo_run_before_the_solver(tmp_path) -> None: """LOAD-BEARING (the 10 % test): when the project's declared cost lines deviate by 10 % from the numbers the script asserts, the run is FORKASTET at stage 0 — with the reconciliation reason, not the P90 one. Goes RED the moment the demo stops being anchored: without the ``cost-baseline.json`` in the bundle the run path passes ``baseline=None``, stage 0 is skipped, and this same deviating number changes nothing at all (the run ends FORESLÅTT, as ``test_..._runs_the_whole_demo`` above shows). The script is byte-identical in both tests — only the declared baseline moves.""" baseline = _deviated(baseline_from_scripted_candidate(_only_candidate()), 1.10) bundle = materialize_anchored_bundle(tmp_path / "forankret", baseline=baseline) result = await simulate_learning_loop(str(bundle), str(tmp_path)) outcome = result.run_a.outcome assert isinstance(outcome, Rejection), ( "a proposal 10 % away from the project's declared cost lines was NOT rejected — the " "deterministic gate is not anchored to the baseline" ) assert "outside the 5.0% tolerance" in outcome.reason assert "ENERGI-TOTAL-EL" in outcome.reason assert "P90" not in outcome.reason, ( "rejected by the solver stage, not by the reconciliation stage 0 that must run BEFORE it" ) def test_the_reserve_itself_ships_no_baseline(tmp_path) -> None: """The materializer must ADD something the reserve genuinely lacks — and must leave the commons-owned bundle alone (criterion 8: the goldens stay byte-unchanged).""" reserve = _default_bundle_dir() assert okf.load_optional_cost_baseline(str(reserve)) is None, ( "the shared reserve now ships a cost baseline — the copy-and-extend variant is obsolete " "and this whole seam should be re-measured" ) bundle = materialize_anchored_bundle(tmp_path / "forankret") assert okf.load_optional_cost_baseline(str(bundle)) is not None, ( "the materialized bundle is not readable by okf's own loader — the filename has drifted" ) assert okf.load_optional_cost_baseline(str(reserve)) is None def test_the_baseline_is_derived_from_the_scripted_register() -> None: """The baseline is DERIVED from the script's own cost lines, never typed alongside them: a hand-written copy is a second source of the same numbers, and two sources drift.""" candidate = _only_candidate() derived = baseline_from_scripted_candidate(candidate) for reply in (candidate.overclaimed, candidate.corrected): for item in json.loads(reply)["affected_items"]: line = derived.items[item["code"]] assert (line.quantity, line.unit_cost) == (item["quantity"], item["unit_cost"]) assert derived.project_id == candidate.project_id def test_a_candidate_whose_two_replies_disagree_is_refused() -> None: """Validation, never repair. The two scripted replies must state the SAME cost lines: were they to differ, hypothesis #1 would be rejected by stage 0 instead of by P90, and the demo's REJECTED line would silently come from another mechanism than the one it narrates.""" candidate = _only_candidate() skewed = ScriptedCandidate( project_id=candidate.project_id, overclaimed=candidate.overclaimed.replace("300000", "310000"), corrected=candidate.corrected, flip_key=candidate.flip_key, ) with pytest.raises(ValueError): baseline_from_scripted_candidate(skewed) def test_the_demo_entry_point_runs_the_anchored_delivered_bundle() -> None: """LOAD-BEARING on the CALL SITE: the thing the operator actually runs on stage must be the anchored variant — and since P3 (GO), the anchor is the DELIVERED bundle's own shipped ``cost-baseline.json``, not the script-derived reserve one. Goes RED if ``main`` is pointed back at either the plain reserve or the anchored reserve. The declared baseline is printed because an anchoring nobody can see is an anchoring nobody can check: every other line of the demo is byte-identical whether the gate is anchored or not. **The first form of this test was vacuous, and the mutation caught it.** It asserted ``"kostbaseline erklært" in stdout`` — but the un-anchored branch read "ingen kostbaseline erklært", which CONTAINS that substring; and ``"ENERGI-TOTAL-EL" in stdout`` holds either way, because the Step-2 line prints the proposal's own cost lines. Both survived the mutation. The assertions below name the whole declared line and rule the other branch out explicitly. The magnitude is asserted in FULL (``4386150``, not ``4.38615e+06``): ``:g`` switched to exponent notation at the 7th significant digit, which the reserve's six-digit ``300000`` never reached — so the delivered content exposed the formatting defect on its first run, and this assert is what keeps it exposed.""" proc = subprocess.run( [sys.executable, "-m", "portfolio_optimiser.simulation"], capture_output=True, text=True, check=False, ) assert proc.returncode == 0, proc.stderr assert "kostbaseline erklært (ENERGI-VEGLYS-EL 4386150 x 1)" in proc.stdout, ( "the demo is not anchored on the DELIVERED cost baseline — either it ran a bundle without " "one (the gate then reasons only about the proposal's own numbers), or it fell back to the " "script-derived reserve baseline" ) assert "validatorens stage 0 avstemmer" in proc.stdout assert "uten kostbaseline" not in proc.stdout assert "e+06" not in proc.stdout, "a cost magnitude regressed to exponent notation" assert "tallene er syntetiske" not in proc.stdout, ( "the reserve's honesty sentence is on stage, so the run is NOT on delivered content" ) def _only_candidate() -> ScriptedCandidate: from portfolio_optimiser.simulation import _CANDIDATES, _PROJECT_ID (candidate,) = [c for c in _CANDIDATES if c.project_id == _PROJECT_ID] return candidate