docs(shared): bygg-energi mikro-eksempel — OKF-bundle + golden + load-bearing test

Persistent dev-fixture for energieffektivisering (energiledelse/M&V), valgt for
sin lærings-overflate: gapet mellom modellert besparelse (validatoren regner) og
faktisk realisert besparelse i drift (eksperten kjenner) — det ExpeL skal lære.

Ett kontorbygg, ett LED-retrofit-tiltak. OKF-bundle (index/project/hypothesis/
methodology/reference/verdict) bærer kontekst-laget; verdict-led-fro.md koder
realiseringsgraden (RR ≈ 0,82, forankret i National Grid SBS 2010) som ExpeL-frø.

Energi mappet inn i den EKSISTERENDE kost-IR-en uendret (affected = byggets totale
energikostnad, claimed = modellert besparelse ~10 % < 30 %-cap), så validatoren
kjører som-den-er — src/ urørt. golden.json fryser de seeded percentilene; testen
beviser at fixturen er konsumerbar (validerer, ikke Rejection), ikke bare til stede.

Domenetall verifisert mot primærkilder (EVO/IPMVP, DOE/NREL UMP, CPUC, fire
evalueringsstudier); norsk energipris mot SSB Q1 2026. README + shared/README
oppdatert (eksempel finnes, ikke lenger "planned"). Suite 121/4, ruff+mypy rene.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHR8iKxJRxDiDfNw8HZmWE
This commit is contained in:
Kjell Tore Guttormsen 2026-06-29 09:42:13 +02:00
commit cbc7a22c78
11 changed files with 457 additions and 3 deletions

View file

@ -12,8 +12,10 @@ The result will never fit any single customer 100%. The goal is a **~90% genuine
## Docs
- [`docs/plan/2026-06-26-maalbilde-agentic-loop.md`](docs/plan/2026-06-26-maalbilde-agentic-loop.md) — target picture: the agentic cost-saving loop + OKF knowledge architecture (north star).
- [`docs/research/2026-06-23-prior-art-platform.md`](docs/research/2026-06-23-prior-art-platform.md) — prior-art & platform research (incl. implementation register §15).
- [`docs/plan/2026-06-23-incremental-plan.md`](docs/plan/2026-06-23-incremental-plan.md) — incremental delivery plan.
- [`docs/plan/2026-06-23-incremental-plan.md`](docs/plan/2026-06-23-incremental-plan.md) — incremental delivery plan (deterministic backbone).
- [`shared/`](shared/) — framework-neutral shared core (concept + example OKF knowledge bundles), reused unchanged by both reference implementations.
## Stack

View file

@ -16,8 +16,11 @@ so the only thing that differs is the agent framework itself.
- [`CONCEPT.md`](CONCEPT.md) — the business concept, written for a non-specialist
(e.g. a business developer at another company).
- *(planned)* the method specification, the example knowledge bundles (OKF / LLM-wiki),
the expert-reviewer persona, and the golden-suite of expected validator outcomes.
- [`examples/bygg-energi-mikro/`](examples/bygg-energi-mikro/) — the first example knowledge
bundle (OKF / LLM-wiki): one office building, one LED-retrofit measure, with a seed expert
verdict encoding the realization gap and a golden-suite of expected validator outcomes. A
small **dev fixture** for exercising the agentic loop; a realistic full-scale example comes later.
- *(planned)* the method specification and the expert-reviewer persona.
## Rules

View file

@ -0,0 +1,40 @@
---
type: project
title: "Kontorbygg Nord"
description: "Fiktivt næringsbygg (kontor) brukt som mikro-eksempel. Energibaseline og rammer for ett effektiviseringstiltak."
resource: BYGG-KONTOR-NORD
tags: [kontorbygg, energibaseline, naeringsbygg]
timestamp: 2026-06-29
---
# Kontorbygg Nord (BYGG-KONTOR-NORD)
Fiktivt kontorbygg. Tallene er **illustrative men forankret i typiske norske verdier**
ikke et ekte bygg. En produksjons-deployer erstatter denne med en ekte kunnskapsbase.
## Energibaseline
| Størrelse | Verdi | Merknad |
|---|---|---|
| Oppvarmet bruksareal (BRA) | ~2 500 m² | [I] illustrativt |
| Totalt elforbruk | **300 000 kWh/år** | [I]; ~120 kWh/m²/år — typisk norsk kontor |
| Herav belysning | ~54 000 kWh/år (~18 %) | 200 armaturer × 90 W × 3 000 t/år |
| Variabel energikostnad | **1,00 NOK/kWh** ekskl. mva | [V-forankret] kraftpris + nettleie-energiledd + elavgift |
**Energiprisen** (1,00 NOK/kWh) er den marginale variable kostnaden et spart kWh faktisk
unngår, ekskl. mva (næring trekker fra mva). Sammensetning, forankret i SSB Q1 2026:
kraftpris tjenesteytende næringer ~0,801,12 NOK/kWh + nettleie energiledd ~0,100,13 +
elavgift 0,0713. Den varierer kraftig med prisområde (NO4 ~0,13 vs NO2 ~0,96 i kraftpris
alene) og sesong — derfor er den **konfigurerbar**, og usikkerheten håndteres i Monte
Carlo-steget (band 0,701,40 NOK/kWh). Se [kilder-realiseringsgap.md](kilder-realiseringsgap.md).
## Rammer (constraints)
- Tiltak vurderes **inne i** dette prosjektet (ikke på tvers av en portefølje).
- Budsjett og tekniske rammer eies av deployer; her holdes de minimale.
- Bygget driftes i normal kontortid; belysning styres delvis på timeplan (relevant for
realiseringsgapet — se [verdict-led-fro.md](verdict-led-fro.md)).
## Kandidat-tiltak
- [tiltak-led-retrofit.md](tiltak-led-retrofit.md) — LED-retrofit av belysning.

View file

@ -0,0 +1,21 @@
{
"_note": "Golden-suite (forventet utfall) for bygg-energi-mikro. To deler: (1) 'validator' fryser den deterministiske (seeded, _MC_SEED=20260624) outputen av validate_proposal pa validator-input.json — en regresjon som fanger utilsiktede endringer i validator/MC. Den MENINGSFULLE assertionen er validates=true (claimed < P90). (2) 'learning_surface' koder realiseringsgapet validatoren IKKE kan regne — ExpeL-froet fra verdict-led-fro.md. expected_actual = realization_rate * modelled.",
"validator": {
"outcome": "ValidatedProposal",
"validates": true,
"claimed_saving_nok": 30000,
"nominal_feasible": 90000.0,
"p10": 68543.08886748762,
"p50": 95443.98966314227,
"p90": 121057.08845805985,
"_percentile_meaning": "OVRE FEASIBLE GRENSE (30% av samplet energikostnad), IKKE LED-besparelsens fysiske band. Se tiltak-led-retrofit.md, 'Mapping til validatoren'."
},
"learning_surface": {
"modelled_saving_nok": 30000,
"realization_rate": 0.82,
"expected_actual_saving_nok": 24600,
"gap_source": "hours-of-use-overestimation",
"context_key": "kontorbygg; HOU-kilde=timeplan-stipulert",
"_meaning": "Det validatoren IKKE kan regne fra parameterne. ExpeL-frøet loopens steg 1 skal hente og anvende. Se verdict-led-fro.md."
}
}

View file

@ -0,0 +1,41 @@
---
type: index
title: "Bygg-energi mikro-eksempel — kontorbygg, LED-retrofit"
description: "Minimal OKF-bundle for ett kontorbygg med ett energieffektiviseringstiltak (LED-belysningsretrofit). Utviklings-fixture for den agentiske loopen: kontekst → hypotese → deterministisk validering → ekspert-dom → læring."
tags: [energieffektivisering, M&V, IPMVP, mikro-eksempel, fixture]
timestamp: 2026-06-29
---
# Bygg-energi mikro-eksempel
En **mikro OKF-bundle** (Open Knowledge Format): ett kontorbygg, ett kandidat-tiltak.
Den er liten med vilje — formålet er **rask småskala-testing og validering** av den
agentiske loopen gjennom hele utviklingsløpet. Et **realistisk fullskala-eksempel**
bygges senere (målbilde §8); dette er forløperen.
> Dette er en framework-nøytral artefakt (null kode-avhengighet). Den deles uendret
> mellom MAF- og Claude-SDK-implementasjonene. Se [shared/README.md](../../README.md).
## Hvorfor energieffektivisering
Domenet ble valgt for sin **lærings-overflate**: et reelt, dokumentert gap mellom det
en deterministisk validator kan *regne* (modellert besparelse fra parametere) og det en
fagekspert *kjenner* (faktisk realisert besparelse i drift). Det gapet — realiseringsgraden
— er det lærings-sløyfa (ExpeL) skal lære. Se [verdict-led-fro.md](verdict-led-fro.md).
## Innhold (progressiv disclosure)
- [bygg-kontor-nord.md](bygg-kontor-nord.md) — `type: project` — bygget og energibaseline.
- [tiltak-led-retrofit.md](tiltak-led-retrofit.md) — `type: hypothesis` — kandidat-tiltaket
(LED-retrofit) med alle parametere for den modellerte besparelsen.
- [metode-ipmvp-a.md](metode-ipmvp-a.md) — `type: methodology` — M&V-metoden (IPMVP Option A).
- [kilder-realiseringsgap.md](kilder-realiseringsgap.md) — `type: reference` — verifisert
litteratur om realiseringsgrad og dets årsaker.
- [verdict-led-fro.md](verdict-led-fro.md) — `type: verdict` — frøsatt ekspert-dom som koder
realiseringsgapet. **ExpeL-frøet** loopens steg 1 henter fra.
## Hvordan den kjøres i dag
`validator-input.json` er IR-projeksjonen den **eksisterende deterministiske validatoren**
konsumerer uendret (energitiltaket mappet inn i kost-IR-en); `golden.json` fryser det
forventede utfallet. Se [tiltak-led-retrofit.md](tiltak-led-retrofit.md) §«Mapping til validatoren».

View file

@ -0,0 +1,46 @@
---
type: reference
title: "Realiseringsgrad og energy performance gap — verifisert litteratur"
description: "Kildebelagte tall for gapet mellom modellert og faktisk realisert besparelse, og dets systematiske årsaker. Grunnlaget for verdict-frøets realiseringsgrad."
tags: [realization-rate, performance-gap, M&V, evaluering, kilder]
timestamp: 2026-06-29
---
# Realiseringsgrad (realization rate) — verifisert litteratur
**Realiseringsgrad (RR)** = faktisk evaluert besparelse (ex-post) ÷ modellert/påstått
besparelse (ex-ante). RR < 1 betyr at drift leverte mindre enn modellen lovte. Avviket
kalles *energy performance gap*. Alle tall under er verifisert mot primærkilde [V].
| Nivå | Funn | Kilde |
|---|---|---|
| Program (regulatorisk default) | Default gross RR **0,90** for kWh/kW/therm; ex-ante «generally over-estimated» | CPUC Resolution E-4952 |
| Program (lys, drift lavere) | Operational adjustment ned til **81,1 %** (metrede driftstimer 15 % lavere); coincidence factor **0,566** vs antatt 1,0 | National Grid SBS 2010 (DNV KEMA) |
| Program (lys, drift høyere) | Hours-of-Use RR **106,5 %**; coincidence 72,2 % — gapet går **begge veier** | Massachusetts Impact Evaluation 2010 |
| Parameter (driftstimer) | Metret **3 053 t/år** vs antatt **3 772 t/år** (≈19 % lavere); CV ≈ 0,5 | Efficiency Maine 2021 |
| Portefølje | Commercial lighting **98 %** vs residential **61 %** vs total **93 %** | LADWP Retrospective FY15/1619/20 |
| Bygg (grønne næringsbygg) | Predikert besparelse **1,53×** realisert; ~⅓ av LEED-bygg bruker mer energi | "Mind the energy performance gap", ScienceDirect |
| Måleterskel | Besparelse bør overstige **~10 % av baseline** for å skilles pålitelig fra støy | FEMP/RDH M&V-veiledning |
## Systematiske årsaker (hvorfor faktisk < modellert) [V]
1. **Driftstimer / Hours-of-Use** — dominerende. Timeplan-baserte estimat (det Option A
stipulerer) treffer sjelden metret brenntid (3 053 vs 3 772).
2. **Baseline- og værjustering** — over-predikert baseline blåser opp absolutt besparelse.
3. **Coincidence / diversity factor** — for effekt(kW): andel last under nett-topp ~0,570,72, ikke 1,0.
4. **HVAC interactive effects** — mindre spillvarme → endret kjøle-/varmebehov; «too small to measure», stipuleres.
5. **In-service rate, drift & persistens** — ikke alt installeres/forblir; styringer overstyres; degradering.
6. **Måleusikkerhet** — under ~10 %-terskelen drukner signalet i støy.
7. **Rebound / atferd** — mer lys på, lengre, fordi det «koster mindre».
## Kilder (URL)
- EVO IPMVP Generally Accepted M&V Principles (okt. 2018): https://evo-world.org/images/corporate_documents/IPMVP-Generally-Accepted-Principles_Final_26OCT2018.pdf
- DOE/NREL Uniform Methods Project, Ch. 2 Commercial & Industrial Lighting (NREL 68558): https://docs.nrel.gov/docs/fy17osti/68558.pdf
- Massachusetts Impact Evaluation of 2010 Prescriptive Lighting: https://ma-eeac.org/wp-content/uploads/Impact-Evaluation-of-2010-Prescriptive-Lighting-Installations-Final-Report-6-21-13.pdf
- National Grid SBS 2010 Prescriptive Lighting (DNV KEMA): https://www.nationalgridus.com/media/pdfs/our-company/eereports/2014-ngrid-sbs-impact-eval-final-prot.pdf
- Efficiency Maine Retail & Distributor Lighting 2021: https://www.efficiencymaine.com/docs/Retail-and-Distributor-Lighting-Final-Impact-Evaluation-Report-2021.pdf
- LADWP Retrospective Impact Evaluation FY15/1619/20: https://www.ladwp.com/sites/default/files/2024-01/LADWP%20Retrospective%20Report%20FINAL%20V4.pdf
- CPUC Resolution E-4952: https://docs.cpuc.ca.gov/publisheddocs/published/g000/m232/k459/232459122.pdf
- "Mind the energy performance gap" (ScienceDirect): https://www.sciencedirect.com/science/article/abs/pii/S0921344918303860
- SSB Elektrisitetspriser (kraftpris tjenesteytende næringer, Q1 2026): https://www.ssb.no/energi-og-industri/energi/statistikk/elektrisitetspriser

View file

@ -0,0 +1,40 @@
---
type: methodology
title: "IPMVP Option A — Retrofit Isolation, Key Parameter Measurement"
description: "M&V-metoden for å verifisere besparelsen fra ett isolert tiltak ved å måle nøkkelparameteren (effekt) og estimere resten (driftstimer)."
methodology: IPMVP
option: A
tags: [IPMVP, M&V, EVO, retrofit-isolation]
timestamp: 2026-06-29
---
# M&V-metode: IPMVP Option A
**IPMVP** (International Performance Measurement and Verification Protocol) er
konsensus-rammeverket for å måle og verifisere energibesparelser, eid og vedlikeholdt av
**EVO** (Efficiency Valuation Organization). Kjerneinnsikten som begrunner hele
lærings-sløyfa står eksplisitt i protokollen [V]:
> *"Savings cannot be directly measured, because savings represent the absence of energy use."*
Besparelse er en **kontrafaktisk** størrelse — det finnes ingen måler for «det som ikke ble
brukt». Den *beregnes*: `Baseline-energi Rapporterings-energi ± justeringer` (IPMVP Eq. 1).
## De fire opsjonene (EVO, offisielle navn) [V]
- **Option A — Retrofit Isolation: Key Parameter Measurement.** Måler nøkkelparameteren
(typisk effekt) på det berørte utstyret; øvrige parametere (typisk driftstimer) *estimeres*.
- **Option B — Retrofit Isolation: All Parameter Measurement.** Måler alle relevante parametere.
- **Option C — Whole Facility.** Besparelse fra byggets hovedmåler, med rutinejustering (vær/produksjon).
- **Option D — Calibrated Simulation.** Besparelse via simuleringsmodell kalibrert mot måledata.
## Hvorfor Option A for dette tiltaket [V]
EVOs egen tabell bruker nettopp et **lysarmatur-retrofit** som den kanoniske Option A-saken:
effekt før/etter måles (billig, presist), mens **driftstimer stipuleres** fra byggets
timeplan. Det gjør Option A enklest og billigst for ett isolert tiltak.
**Kritisk for lærings-overflaten:** parameteren Option A tillater å *estimere* — driftstimer
— er nøyaktig der realiseringsgapet oppstår. Den stipulerte timeplanen treffer sjelden den
faktiske, metrede brenntiden. Se [verdict-led-fro.md](verdict-led-fro.md) og
[kilder-realiseringsgap.md](kilder-realiseringsgap.md).

View file

@ -0,0 +1,66 @@
---
type: hypothesis
title: "LED-retrofit av kontorbelysning"
description: "Bytte 200 lysrørarmaturer (3-rørs T8 troffer, ~90 W) til LED-paneler (~40 W). Kandidat-tiltak med modellert besparelse og usikkerhet."
resource: BYGG-KONTOR-NORD
measure_id: LED-RETROFIT-01
tags: [LED, belysning, retrofit, ECM]
timestamp: 2026-06-29
---
# Tiltak: LED-retrofit av kontorbelysning
Bytte av 200 lysrørarmaturer (2×4 fluorescerende troffer) til LED-paneler. Dette er det
vanligste enkelt-ECM-et (Energy Conservation Measure) og IPMVPs egen kanoniske
Option A-illustrasjon — se [metode-ipmvp-a.md](metode-ipmvp-a.md).
## Parametere
| Parameter | Verdi | Status | Kilde/forankring |
|---|---|---|---|
| Antall armaturer | 200 | [I] | mikro-skala valgt |
| Effekt før (T8 troffer m/ ballast) | 90 W | [V] | 3×32 W ≈ 9096 W m/ ballastfaktor |
| Effekt etter (LED-panel) | 40 W | [V] | kommersielt 2×4 LED-panel ~40 W |
| Reduksjon per armatur (ΔW) | 50 W | beregnet | 90 40 |
| Driftstimer (HOU) | 3 000 t/år | [I] | forankret i metret 3 053 t (Efficiency Maine) |
| Variabel energipris | 1,00 NOK/kWh | [V-forankret] | se [bygg-kontor-nord.md](bygg-kontor-nord.md) |
## Modellert besparelse (ex-ante)
Lysligning (DOE/NREL Uniform Methods Project, Eq. 3):
`kWh = Σ (W_før W_etter) × antall × HOU / 1000`
> ΔW = 90 40 = **50 W/armatur**
> kWh/år = 50 × 200 × 3 000 / 1 000 = **30 000 kWh/år**
> kr/år = 30 000 × 1,00 = **30 000 NOK/år**
HVAC-interaktiv effekt (effektivt lys → mindre spillvarme → endret kjøle-/varmebehov,
UMP Eq. 6) er ~+5 % i elektrisk kjølte bygg, men **utelatt fra kjernetallet** her
(konservativt; den lille interaktive justeringen er en ex-post-vurdering eksperten kan
legge til). Modellert kjernebesparelse: **30 000 kWh/år ≈ 30 000 NOK/år**.
## Usikkerhet (for Monte Carlo P10/P50/P90)
Den dominerende usikkerheten i en *energibesparelse* ligger i driftstimer (HOU), ikke
prisen — men den eksisterende validatorens Monte Carlo varierer enhetspris. I denne
mikro-mappingen brukes derfor **prisbandet 0,701,40 NOK/kWh** som usikkerhetsakse
(region/sesong, jf. [bygg-kontor-nord.md](bygg-kontor-nord.md)). Den fysiske HOU-usikkerheten
og — viktigere — den *systematiske* HOU-skjevheten håndteres i verdict-laget, ikke her.
## Mapping til validatoren (hvorfor `validator-input.json` ser ut som den gjør)
Den eksisterende deterministiske validatoren er en *feasibility-gate* (`claimed ≤ 30 % av
affected total`, Monte Carlo over enhetspris) bygd for kostnadskutt. Energitiltaket mappes
inn **uendret**:
- `affected_items = [{code: "ENERGI-TOTAL-EL", quantity: 300000 kWh/år, unit_cost: 1.00 NOK/kWh}]`
→ byggets **totale** årlige energikostnad (300 000 NOK). LED-besparelsen er ~10 % av den,
godt innenfor 30 %-cap-en.
- `claimed_saving_nok = 30000` → den modellerte LED-besparelsen.
- `assumptions = {"ENERGI-TOTAL-EL": [0.70, 1.40]}` → prisbandet for Monte Carlo.
**Ærlig begrensning:** validatorens P10/P50/P90 betyr her «øvre feasible grense» (30 % av
samplet energikostnad), *ikke* «LED-besparelsens fysiske band». Det er bevisst — den
domenetro besparelses-modelleringen og realiseringsgapet hører hjemme i verdict-laget
([verdict-led-fro.md](verdict-led-fro.md)), som er nettopp det lærings-sløyfa skal lære.
En energi-bevisst validator (ΔW × antall × HOU) er senere fase-arbeid, ikke dette fixturet.

View file

@ -0,0 +1,16 @@
{
"_note": "IR-projeksjon (ir.SavingsProposal) for det eksisterende deterministiske validatoren. Energitiltaket er mappet inn i kost-IR-en UENDRET: affected_items = byggets totale arlige energikostnad; claimed_saving_nok = modellert LED-besparelse (~10% av total, innenfor 30%-cap); assumptions = energipris-band (NOK/kWh) for Monte Carlo. Se tiltak-led-retrofit.md, seksjon 'Mapping til validatoren'.",
"project_id": "BYGG-KONTOR-NORD",
"measure": "LED-retrofit av 200 lysrorarmaturer (90 W -> 40 W) i kontorlokaler",
"affected_items": [
{
"code": "ENERGI-TOTAL-EL",
"quantity": 300000,
"unit_cost": 1.0
}
],
"claimed_saving_nok": 30000,
"assumptions": {
"ENERGI-TOTAL-EL": [0.70, 1.40]
}
}

View file

@ -0,0 +1,56 @@
---
type: verdict
title: "Ekspert-dom (frø): LED-retrofit — godkjent med realiseringskorreksjon"
description: "Frøsatt ekspert-dom som koder realiseringsgapet for LED-tiltaket. ExpeL-frøet loopens steg 1 henter fra: modellert besparelse korrigeres ned med realiseringsgraden eksperten kjenner fra drift."
resource: BYGG-KONTOR-NORD
measure_id: LED-RETROFIT-01
decision: approved_with_adjustment
realization_rate: 0.82
modelled_saving_nok: 30000
expected_actual_saving_nok: 24600
gap_source: hours-of-use-overestimation
context_key: "kontorbygg; HOU-kilde=timeplan-stipulert"
provenance: "frø — AI-forfattet, forankret i National Grid SBS 2010 (RR 0,81); erstattes av ekte HITL i produksjon"
tags: [verdict, realization-rate, ExpeL-seed, HITL]
timestamp: 2026-06-29
---
# Ekspert-dom (frø): LED-retrofit
> **Dette er et frø**, ikke en ekte dom. I simulering gir en ekspert-persona slike dommer;
> i produksjon gir et menneske dem via samme mappe-grensesnitt. Frøet er forankret i
> verifisert litteratur ([kilder-realiseringsgap.md](kilder-realiseringsgap.md)), ikke
> oppdiktet. Det er **provenance-merket** og promoteres til wikien kun fordi en (simulert)
> ekspert har godkjent det (målbilde, steg 8 gated).
## Dommen
**Beslutning:** godkjent — med realiseringskorreksjon.
Den modellerte besparelsen (**30 000 NOK/år**) er teknisk korrekt fra parameterne, og
validatoren bekrefter at den er innenfor feasibelt område. Men i drift realiseres erfaringsvis
**~82 %** av en timeplan-stipulert LED-besparelse i kontorbygg → forventet faktisk
besparelse **≈ 24 600 NOK/år**.
## Begrunnelse (det validatoren ikke kan regne)
Realiseringsgapet her er **ikke** spredning i parameterne — det er en *systematisk skjevhet*
mellom de stipulerte parameterne og driftsvirkeligheten:
- **Driftstimer overvurdert (dominerende):** den antatte timeplanen (3 000 t) ligger over
faktisk metret brenntid — naturlig dagslys, tomme rom, sensorstyring. Jf. National Grid
(RR-bidrag 0,81 fra 15 % lavere metrede timer) og Efficiency Maine (3 053 vs 3 772 t).
- **In-service rate < 1:** ikke alle 200 armaturer er nødvendigvis montert/i drift ved
evaluering.
- **Atferd/persistens:** rebound (mer lys på fordi det «er gratis») og overstyrte styringer.
Du kan **ikke** regne deg til RR = 0,82 fra `{200, 90 W, 40 W, 3 000 t, 1,00 NOK/kWh}`.
Skjevheten er epistemikk parameterne ikke bærer — den finnes bare i akkumulert drifts-erfaring.
## Lærings-signalet (ExpeL)
Korreksjonen er **kontekstbetinget**: `context_key = "kontorbygg; HOU-kilde=timeplan-stipulert"`.
Neste kjøring, gitt en lignende hypotese i samme kontekst, skal hente denne dommen og
justere den modellerte ex-ante-besparelsen mot forventet ex-post (≈ 0,82×) — uten å vente
på 12 måneders måling. **Det er denne dataflyten — verdict inn i neste hypotese — loopens
steg 1 må wire (målbilde §5, §7). Dette frøet er testankeret.**

View file

@ -0,0 +1,123 @@
"""Bygg-energi mikro-eksempel — fixture-konsumerbarhet (shared/examples/bygg-energi-mikro).
The energi micro-fixture is a *dev fixture* exercised through the whole build. These tests
prove it is actually consumable, not merely present:
1. the IR projection (``validator-input.json``) drives the EXISTING deterministic validator to
a ``ValidatedProposal`` (not a ``Rejection``) energi mapped into the cost-IR unchanged;
2. that validator output is frozen against ``golden.json`` (seeded ``_MC_SEED=20260624``);
3. the OKF bundle is well-formed (every file declares ``type``; index cross-links resolve);
4. the seed verdict encodes a realization gap (RR < 1) consistent with the golden the anchor
the later step-1 ExpeL wiring becomes load-bearing against.
No ``yaml`` dependency: frontmatter is parsed with a minimal key:value reader.
"""
from __future__ import annotations
import json
import re
from pathlib import Path
import pytest
from portfolio_optimiser.ir import AffectedItem, SavingsProposal
from portfolio_optimiser.validator import Rejection, ValidatedProposal, validate_proposal
BUNDLE_DIR = Path(__file__).resolve().parents[1] / "shared" / "examples" / "bygg-energi-mikro"
_EXPECTED_TYPES = {
"index.md": "index",
"bygg-kontor-nord.md": "project",
"tiltak-led-retrofit.md": "hypothesis",
"metode-ipmvp-a.md": "methodology",
"kilder-realiseringsgap.md": "reference",
"verdict-led-fro.md": "verdict",
}
def _parse_frontmatter(path: Path) -> dict[str, str]:
"""Minimal YAML-frontmatter reader: the ``---``-delimited leading block as key:value
strings. Enough for ``type`` and the verdict's scalar fields; list values (``tags: [...]``)
are kept verbatim and unused."""
lines = path.read_text(encoding="utf-8").splitlines()
if not lines or lines[0].strip() != "---":
return {}
fm: dict[str, str] = {}
for line in lines[1:]:
if line.strip() == "---":
break
key, sep, val = line.partition(":")
if sep:
fm[key.strip()] = val.strip()
return fm
def _proposal_from_input() -> SavingsProposal:
raw = json.loads((BUNDLE_DIR / "validator-input.json").read_text(encoding="utf-8"))
return SavingsProposal(
project_id=raw["project_id"],
measure=raw["measure"],
affected_items=[AffectedItem(**a) for a in raw["affected_items"]],
claimed_saving_nok=raw["claimed_saving_nok"],
assumptions={k: tuple(v) for k, v in raw["assumptions"].items()},
)
def _golden() -> dict:
return json.loads((BUNDLE_DIR / "golden.json").read_text(encoding="utf-8"))
def test_validator_input_validates() -> None:
"""The core: the energi tiltak, mapped into the cost-IR, passes the blocking validator —
a ``ValidatedProposal``, never a ``Rejection`` (claimed 30k < P90 feasible)."""
result = validate_proposal(_proposal_from_input())
assert isinstance(result, ValidatedProposal)
assert not isinstance(result, Rejection)
assert result.proposal.claimed_saving_nok < result.p90
assert result.p10 <= result.p50 <= result.p90
def test_validator_output_matches_golden() -> None:
"""Regression: the deterministic (seeded) percentiles match the frozen golden."""
result = validate_proposal(_proposal_from_input())
assert isinstance(result, ValidatedProposal)
g = _golden()["validator"]
assert g["validates"] is True
assert result.nominal_feasible == pytest.approx(g["nominal_feasible"])
assert result.p10 == pytest.approx(g["p10"])
assert result.p50 == pytest.approx(g["p50"])
assert result.p90 == pytest.approx(g["p90"])
def test_bundle_files_declare_type() -> None:
"""Every OKF bundle file carries the one required field (``type``) with the expected value."""
for name, expected_type in _EXPECTED_TYPES.items():
fm = _parse_frontmatter(BUNDLE_DIR / name)
assert fm.get("type") == expected_type, f"{name}: type={fm.get('type')!r}"
def test_index_crosslinks_resolve() -> None:
"""Intra-bundle cross-links in ``index.md`` resolve to existing files (our own example
must have no broken links, even though OKF consumers must tolerate them)."""
index = (BUNDLE_DIR / "index.md").read_text(encoding="utf-8")
targets = re.findall(r"\]\(([^)]+\.md)\)", index)
internal = [t for t in targets if "/" not in t]
assert internal, "expected intra-bundle .md links in index.md"
for target in internal:
assert (BUNDLE_DIR / target).exists(), f"index links to missing {target}"
def test_verdict_seed_encodes_realization_gap() -> None:
"""The seed verdict encodes a realization gap (RR < 1), and the golden learning-surface is
internally consistent (expected_actual = RR x modelled). This is the anchor the later
step-1 ExpeL wiring is made load-bearing against."""
fm = _parse_frontmatter(BUNDLE_DIR / "verdict-led-fro.md")
rr = float(fm["realization_rate"])
assert 0.0 < rr < 1.0, "a realization gap means RR strictly below 1"
ls = _golden()["learning_surface"]
assert ls["realization_rate"] == pytest.approx(rr)
assert ls["expected_actual_saving_nok"] == pytest.approx(
ls["realization_rate"] * ls["modelled_saving_nok"]
)