feat(simulation): GO — demoen kjører levert VEGLYS-bundle, forankret på deres tall [skip-docs]

P3 a-c lukket. Pullen hentet commons 002f000+27cdce9: kriterium 8 grønt (nav-goldens
byte-uendret, målt både med git diff og shasum) og suiten uendret 785 — ingen abort.

Retningen på tallene er SNUDD (P3 b): manus-registerets kostlinjer er skrevet FRA
shared/examples/veglys-fv-soer/cost-baseline.json, ikke ved siden av den, og
baseline_from_scripted_candidate brukes ikke på denne stien — main() leser levert fil.
Overdrivelsen 2100000 er valgt utenfor commons' tall-inventar: målt fraværende fra
bundelen og over målt P90 1769915 (deres anslag var ~1770000). 600000/900000 ville
klarert gaten og aldri utløst Steg 5.

Målingen felte en defekt reserven skjulte: :g slår over i eksponentform ved 7. signifikante
siffer, så levert baseline printet 4.38615e+06. Reservens 300000 har seks siffer og nådde
aldri overgangen. _num erstatter :g begge steder.

Load-bearing MÅLT mot hele suiten, fem mutasjoner alle røde + grønn kontroll:
detach main-wiringen · reverter _num til :g · drift registeret ETT siffer (4386151 —
innenfor 5 %-toleransen, fanget av ingenting i 792 tester bortsett fra den nye) ·
sett flip_key til et token som finnes i bundelen · detach forankringen på bundle-stien.

785 -> 793 passed / 4 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BUjfw4eJdwwnqHSXhfcY6i
This commit is contained in:
Kjell Tore Guttormsen 2026-08-09 21:27:43 +02:00
commit 02ddc6735f
3 changed files with 306 additions and 15 deletions

View file

@ -54,6 +54,9 @@ from portfolio_optimiser.verdicts import (
)
_PROJECT_ID = "BYGG-KONTOR-NORD"
# The delivered domain bundle (commons `002f000`) — the project the demo runs on stage. Kept beside
# the reserve's id rather than replacing it: the reserve stays reachable as the abort path.
_VEGLYS_PROJECT_ID = "VEGLYS-FV-SOER"
# --- P4 pkt. 2: demo stderr discipline -----------------------------------------------------------
# Measured 2026-08-09, the demo wrote six stderr lines: two ``ExperimentalWarning``s from
@ -216,6 +219,38 @@ _CANDIDATES: tuple[ScriptedCandidate, ...] = (
),
flip_key="250000",
),
# VEGLYS-FV-SOER — the DELIVERED bundle (commons `002f000`), and the one on stage. Every number
# below is written FROM `shared/examples/veglys-fv-soer/validator-input.json`, never beside it
# (plan P3 b): this is `baseline_from_scripted_candidate`'s direction REVERSED — the domain team
# shipped the cost lines, so the register copies them, and the derivation helper is not used on
# this path (`main` reads the delivered `cost-baseline.json` off the bundle instead).
#
# The corrected reply IS the delivered IR projection verbatim: affected_items = the WHOLE
# portfolio's annual energy cost (their decision — scoping it to the 2 500 touched points would
# make the RIGHT proposal 38,6 % of its own baseline and the 30 % cap would fell it on stage),
# claimed_saving_nok = 445500, assumptions = the declared energy-price band. The overclaimed one
# differs in exactly ONE field.
#
# 2100000 is chosen OUTSIDE commons' number inventory and above BOTH thresholds: measured absent
# from the bundle (so the correction is caused by the falsification travelling back), and above
# the degenerate cap 0.30 x 4386150 = 1315845 as well as the banded P90. 600000/900000 would have
# CLEARED the gate — the REJECTED line would never appear and demo-criterion 2 would fail quietly.
ScriptedCandidate(
project_id=_VEGLYS_PROJECT_ID,
overclaimed=(
'{"measure":"LED-utskifting av 2 500 eldre HPS-armaturer (114 W -> 70 W)",'
'"affected_items":[{"code":"ENERGI-VEGLYS-EL","quantity":4386150,"unit_cost":1.0}],'
'"claimed_saving_nok":2100000,'
'"assumptions":{"ENERGI-VEGLYS-EL":[0.70,1.40]}}'
),
corrected=(
'{"measure":"LED-utskifting av 2 500 eldre HPS-armaturer (114 W -> 70 W)",'
'"affected_items":[{"code":"ENERGI-VEGLYS-EL","quantity":4386150,"unit_cost":1.0}],'
'"claimed_saving_nok":445500,'
'"assumptions":{"ENERGI-VEGLYS-EL":[0.70,1.40]}}'
),
flip_key="2100000",
),
)
_proposer_reply = scripted_proposer(_CANDIDATES)
@ -226,6 +261,21 @@ _proposer_reply = scripted_proposer(_CANDIDATES)
_COST_BASELINE_FILE = "cost-baseline.json"
_ANCHORED_DIR_NAME = "forankret-reserve"
_RESERVE_PROVENANCE = "tallene er syntetiske — avledet av demo-manuset, ikke levert av et fagmiljø"
# The GO-day honesty sentence (plan P4 pkt. 4). It claims exactly what the bundle's own `_note`
# documents — the cost line is derived from published sources — and nothing about the realization
# rate, which the seed verdict itself marks as BORROWED from lighting-programme literature.
_VEGLYS_PROVENANCE = (
"tallene er levert i kunnskapsbasen — utledet av fagkilder (Håndbok V124, NMFV), "
"ikke av demo-manuset"
)
def _delivered_bundle_dir() -> Path:
"""The delivered VEGLYS bundle, resolved at CALL time via ``shared_root()`` (same seam as
``_default_bundle_dir``). It ships its own ``cost-baseline.json``, so the demo is anchored
WITHOUT ``materialize_anchored_bundle`` that helper exists for the reserve, whose numbers
cannot be given a baseline in place (pull-only subtree + byte-unchanged goldens)."""
return shared_root() / "examples" / "veglys-fv-soer"
def baseline_from_scripted_candidate(candidate: ScriptedCandidate) -> CostBaseline:
@ -598,8 +648,24 @@ def _clip(text: str, limit: int = 92) -> str:
return flat if len(flat) <= limit else flat[: limit - 1] + ""
def _num(value: float) -> str:
"""Render a cost magnitude without exponent notation.
``:g`` what these lines used while the demo ran the reserve switches to scientific notation
at the 7th significant digit, so the DELIVERED baseline printed as ``4.38615e+06``. The reserve's
``300000`` has six digits and never reached the switch: a formatting defect the synthetic numbers
hid and delivered content exposed on the first run. A demo whose headline cost line is unreadable
cannot claim the gate is anchored in real cost lines.
"""
if value == int(value):
return str(int(value))
return f"{value:f}".rstrip("0").rstrip(".")
def _items_line(proposal: SavingsProposal) -> str:
return ", ".join(f"{i.code} {i.quantity:g} x {i.unit_cost:g}" for i in proposal.affected_items)
return ", ".join(
f"{i.code} {_num(i.quantity)} x {_num(i.unit_cost)}" for i in proposal.affected_items
)
def _first_hypothesis(result: RunResult) -> SavingsProposal:
@ -707,7 +773,7 @@ def _baseline_lines(bundle_dir: Path, provenance: str) -> list[str]:
" validatoren regner kun på tallene forslaget selv oppgir",
]
items = ", ".join(
f"{code} {line.quantity:g} x {line.unit_cost:g}"
f"{code} {_num(line.quantity)} x {_num(line.unit_cost)}"
for code, line in sorted(baseline.items.items())
)
return [
@ -730,12 +796,15 @@ def main(argv: list[str] | None = None) -> int: # pragma: no cover - console tr
quiet_expected_round_cap_notice()
work = tempfile.mkdtemp(prefix="po-sim-")
# THE call site (P4 pkt. 0): the demo runs the ANCHORED reserve — the shared bundle plus the
# cost baseline it cannot be given in place. On GO day these two lines point at the delivered
# bundle and state ITS provenance instead; the run path's seam is the same either way.
bundle = materialize_anchored_bundle(Path(work) / _ANCHORED_DIR_NAME)
provenance = _RESERVE_PROVENANCE
result = asyncio.run(simulate_learning_loop(str(bundle), work))
# THE call site (P3, GO): the demo runs the DELIVERED bundle, which ships its own
# `cost-baseline.json` — so the gate is anchored on numbers a domain team wrote, not on numbers
# the script derived. The abort path is these three lines reverted to the anchored reserve
# (`materialize_anchored_bundle` + `_RESERVE_PROVENANCE` + `_PROJECT_ID`); the run path's seam
# is identical either way, which is what made pointing at delivered content a call-site change.
bundle = _delivered_bundle_dir()
provenance = _VEGLYS_PROVENANCE
project_id = _VEGLYS_PROJECT_ID
result = asyncio.run(simulate_learning_loop(str(bundle), work, project_id=project_id))
print("=" * 78)
print("OFFLINE SIMULERING — skriptede agent-svar, INGEN ekte modell.")
@ -750,7 +819,7 @@ def main(argv: list[str] | None = None) -> int: # pragma: no cover - console tr
# Run A walks steps 1-7 of the method; the promotion between the runs IS step 8. Run B is not
# re-numbered — it re-runs the same eight steps, and what the demo needs from it is the ONE
# thing that changed: the marker now reaches the hypothesis prompt.
print(f"\nKJØRING A ({_PROJECT_ID} — fersk kunnskapsbase, ingen tidligere dommer)")
print(f"\nKJØRING A ({project_id} — fersk kunnskapsbase, ingen tidligere dommer)")
for line in _run_trace_lines(
result.run_a, marker=result.marker, marker_in_prompt=result.marker_in_run_a_prompt
):