feat(validator): anchor the deterministic gate to the project's real cost baseline (S4.0)
Every stage of validate_proposal reasoned only about numbers the proposal itself supplied, so an internally-consistent hallucination cleared the whole gate (F3). A new stage 0 reconciles each affected_item against the project's CostBaseline before the CBC solve: an unknown cost code is rejected, and a real code carrying a quantity/unit_cost outside the configured tolerance (5% default, relative to the baseline value) is rejected. Validation, never repair. The baseline argument is OPTIONAL (None = pre-S4.0 behaviour), but both run paths set it: the road path projects project.cost_items, the bundle path loads cost-baseline.json when the bundle ships one. Bundles written before the amendment stay un-anchored, so the commons-owned goldens run byte-identically; a baseline that exists but is malformed still raises on both loaders. F8: the method-specific cap now comes from the METHOD_CAPS registry (measure type -> fraction, injectable) instead of an energy_efficiency string comparison. The baseline format and tolerance semantics were decided locally — the commons amendment (D-A pt. 2) never arrived, exactly as in S3.2. D7 mirroring stays open. Three portfolio fixtures quoted cost codes belonging to OTHER projects; the new gate caught them. They now quote each project's own lines, and the two copied REPLIES tables import the single source instead of drifting from it. Load-bearing measured (tests/test_s40_cost_baseline_loadbearing.py), six mutations all red: detach the reconciliation stage; detach the magnitude tolerance; detach the road wiring; detach the bundle wiring; ignore the injected cap registry; make the optional loader tolerant of malformed content. Control: with the road wiring detached the repaired portfolio fixtures still pass, so they are not masking the seam. 597 -> 612 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdwK7bQ4BZkWH4t8MRDKb4
This commit is contained in:
parent
012adc0a3c
commit
126807aee7
16 changed files with 645 additions and 69 deletions
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
type: project
|
||||
title: "Kontorbygg B — energibaseline med erklærte kostlinjer (repo-lokal fixture)"
|
||||
description: "Syntetisk energibaseline for S4.0-fixturen. Ett kontorbygg, én kandidat-kostpost (ENERGI-TOTAL-EL), og en eksplisitt cost-baseline.json den deterministiske validatoren avstemmer mot."
|
||||
timestamp: 2026-08-03
|
||||
---
|
||||
|
||||
# Kontorbygg B (repo-lokal test-fixture)
|
||||
|
||||
Et lite syntetisk kontorbygg med én dokumentert energibaseline. Til forskjell fra mikro-A erklærer
|
||||
denne bundelen kostlinjene sine maskinlesbart i `cost-baseline.json`, slik at et forslag som siterer
|
||||
en oppdiktet kostkode — eller en ekte kode med oppdiktet størrelse — blir avvist av validatorens
|
||||
avstemmings-stage før løseren i det hele tatt kjøres.
|
||||
|
||||
## Energibaseline
|
||||
|
||||
- Årlig elektrisk energikostnad (`ENERGI-TOTAL-EL`): 180 000 NOK/år (syntetisk), ført som
|
||||
`quantity: 180000` × `unit_cost: 1.0` i `cost-baseline.json`.
|
||||
- Kandidat-tiltak: LED-retrofit av kontorbelysning.
|
||||
- Modellert besparelse: ~18 000 NOK/år (innenfor validatorens feasibelt-område).
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"_note": "SYNTHETIC repo-lokal kostbaseline (S4.0-fixture) — ikke ekte data. Prosjektets FAKTISKE kostlinjer: den deterministiske validatoren avstemmer et forslags affected_items mot disse (okf.load_cost_baseline -> validator._reconcile_against_baseline). Formatet er bestemt LOKALT (commons-amendmentet D-A pkt. 2 kom aldri); D7-speiling er ÅPEN.",
|
||||
"project_id": "BYGG-ENERGI-BASELINE-MIKRO",
|
||||
"items": {
|
||||
"ENERGI-TOTAL-EL": {
|
||||
"quantity": 180000,
|
||||
"unit_cost": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
type: index
|
||||
okf_version: 0.1
|
||||
title: "Bygg-energi baseline-mikro — repo-lokal fixture (S4.0 kostbaseline-forankring)"
|
||||
description: "Minimal repo-lokal OKF-bundle som SHIPPER en cost-baseline.json, så bundle-stiens validator-forankring (S4.0, F3) kan testes ende-til-ende. Speiler bygg-energi-mikro-A, men med kostbaselinen lagt til."
|
||||
tags: [fixture, S4.0, kostbaseline]
|
||||
timestamp: 2026-08-03
|
||||
---
|
||||
|
||||
# Bygg-energi baseline-mikro (repo-lokal test-fixture)
|
||||
|
||||
En **repo-lokal mini OKF-bundle** under pakkens `data/` (ALDRI `shared/` — subtree er PULL-ONLY).
|
||||
Eneste formål: være den ene bundelen som **erklærer sin kostbaseline**, slik at
|
||||
`run_project(bundle_dir=...)` faktisk forankrer den deterministiske gaten mot prosjektets virkelige
|
||||
kostlinjer. De eldre bundlene bærer bevisst INGEN `cost-baseline.json` — de er kontrollen som viser
|
||||
at forankringen er opt-in per bundle (pre-amendment-bundler kjører uendret).
|
||||
|
||||
## Innhold (progressiv disclosure)
|
||||
|
||||
- [bygg-kontor-baseline.md](bygg-kontor-baseline.md) — `type: project` — bygget og energibaselinen
|
||||
som lese-kontekst (og `bundle_citations`-kilde).
|
||||
|
||||
`validator-input.json` er IR-projeksjonen (kandidatens kost-IR), og `cost-baseline.json` er
|
||||
prosjektets FAKTISKE kostlinjer (`code -> {quantity, unit_cost}`) som `okf.load_cost_baseline` leser
|
||||
og validatoren avstemmer `affected_items` mot. De to er bevisst adskilt: IR-projeksjonen er hva
|
||||
noen *foreslår*, baselinen er hva prosjektet *er*.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"_note": "SYNTHETIC repo-lokal IR-projeksjon (S4.0-fixture) — ikke ekte data. Kandidatens kost-IR; project_id matcher det bundle-backede prosjektets id (run._project_from_bundle fail-faster ved mismatch). Kostlinjene her SPEILER cost-baseline.json med vilje: et forslag om denne kandidaten skal avstemme rent.",
|
||||
"project_id": "BYGG-ENERGI-BASELINE-MIKRO",
|
||||
"measure": "LED-retrofit av kontorbelysning",
|
||||
"affected_items": [
|
||||
{
|
||||
"code": "ENERGI-TOTAL-EL",
|
||||
"quantity": 180000,
|
||||
"unit_cost": 1.0
|
||||
}
|
||||
],
|
||||
"claimed_saving_nok": 18000
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue