MAF measured their own tree and corrected our claim that they "round one monetary figure": their projection rounds nothing (value_report.py:48, 0 hits for round()); the one operation is a NOK->ore quantisation at the ledger edge (ledger.py:204-214, Decimal + ROUND_HALF_UP). Rule 1 as we wrote it therefore solved a divergence that did not exist and opened one that did not — it condemned their deliberate, documented quantisation as "rendering". Adopted their proposed split as rules 1a/1b. Then measured here, which REFUTED our own first hypothesis: we assumed float accumulation was the divergence. It is not — 0 of 20 000 random portfolios diverge on 2-decimal NOK input. The real mechanism is where quantisation sits relative to summation, and it only wakes on sub-ore input: 66.4% of totals diverge, worst 6 ore over 28 rows, and a designed 100 x 0.005 case diverges by a factor of 2. Sub-ore input is reachable in BOTH stacks — neither ir.py:42 (claimed_saving_nok) nor ledger.py:42 (amount_nok) constrains decimals; both are float = Field(gt=0, allow_inf_nan=False). So two stacks that both obey 1a+1b can still return different totals on identical input, which is exactly what A5 exists to close. Hence new rule 1c: the amendment MUST fix one quantisation point — reject finer-than-ore claims at the contract edge, or quantise once on the total — and both stacks must land on the same choice. Doc-only; no source touched. Suite 627 passed. Nothing patched unilaterally: shared/ is pull-only and the amendment is not in commons. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MQu2xxwedckjU56byu1aUG
183 lines
12 KiB
Markdown
183 lines
12 KiB
Markdown
# D-A-bolken — godkjent 2026-07-25, forslag til amendment-tekst (D7-siden)
|
||
|
||
> **Hva dette er:** operatøren godkjente 2026-07-25 HELE D-A-bolken som én amendment —
|
||
> de fire spec-rettelsene (C-F2/C-F3/C-F4/C-F5), brief-§7 (projeksjons-kontrakten over
|
||
> hovedboken), og C-P1-kollisjonen løst ved **re-scoping** (ikke ved å amendere det frosne
|
||
> §3 Steg 1-prinsippet). Dette dokumentet er D7-repoets bidrag til teksten: spec-anker,
|
||
> foreslått normativ ordlyd, og hva vi har MÅLT om kompatibilitet.
|
||
>
|
||
> **Hva dette IKKE er:** beslutningen selv. Den protokollføres i hovedrepoets delte kø.
|
||
> `shared/` er en PULL-ONLY subtree — amendment-teksten kan ikke committes herfra; den må
|
||
> landes i commons og hentes av BEGGE søsken. Ordlyden under er et forslag, ikke en vedtatt
|
||
> tekst.
|
||
|
||
## 0. Prosedyre (uendret mal)
|
||
|
||
amendment i commons → **koordinert** `git subtree pull` i BEGGE repo (samme commons-commit)
|
||
→ begge suiter grønne → deretter én byggsesjon per punkt, failing test først (§11).
|
||
Ingen av søsknene retter ensidig — heller ikke A5s kvantiseringspunkt, som er en LIVE, målt
|
||
divergens (66,4 % av totaler ved sub-øre-inndata; se A5).
|
||
|
||
## A1 — Validator: den nominale grensen må gate (C-F2 → sesjon C2.7)
|
||
|
||
**Anker:** §3 Steg 4 pkt. 2 beregner «maximum feasible saving ... 0.30 × Σ quantity·unit_cost»,
|
||
men pkt. 4 blokkerer KUN over `p90`. §7.1 har ingen invariant som binder usikkerhetsbåndet til
|
||
`unit_cost`, og båndet forfattes av modellen selv.
|
||
|
||
**Foreslått ordlyd (to tillegg):**
|
||
- §3 Steg 4, nytt pkt.: «A claim above `nominal_feasible` MUST yield the same distinct
|
||
rejection type as pkt. 4, regardless of the percentiles. The structural block is evaluated
|
||
against BOTH bounds; the deterministic bound is never advisory.»
|
||
- §7.1, ny IR-invariant: «When an item carries an uncertainty band, `low ≤ unit_cost ≤ high`
|
||
MUST hold; a band that excludes the unit cost is a schema violation, not an input.»
|
||
|
||
**Kompatibilitet (målt på D7-siden, ikke antatt — `review-2026-07.md` §1 C-F2):** golden-claim
|
||
30 000 ≤ nominal 90 000 (passerer); alle ikke-tomme bånd i repoets fixtures omslutter
|
||
`unit_cost` ([0.8,1.2]∋1.0 · [0.70,1.40]∋1.0 · (0.70,1.50)∋1.0; resten tomme). Verken
|
||
fasit-eksemplet eller eksisterende testdata brytes.
|
||
|
||
## A2 — Kostbaseline: claim må avstemmes mot noe utenfor modellens egne tall (C-F3 → C4.0)
|
||
|
||
**Anker:** ingenting i bundle-formatet bærer en kostbaseline. §3 Steg 4 pkt. 1 regner kun på
|
||
selv-deklarerte linjer, så en diktet kostlinje passerer validering (kjørt bevis: `XX-DIKTET`,
|
||
1e6 × 10 → claim 2,9 MNOK stemplet `validated`).
|
||
|
||
**Foreslått ordlyd:** «A bundle MAY carry a cost baseline projection (`cost-baseline.json`).
|
||
When present, every `affected_items` code MUST reconcile against it, fail-closed: an unknown
|
||
code or a quantity above the baseline's is a rejection of the same distinct type as §3 Steg 4
|
||
pkt. 4. **A run that calls a model MUST have a baseline** — a bundle without one MUST be
|
||
refused at startup (§10), never run unreconciled.»
|
||
|
||
**Åpent underspørsmål amendmenten MÅ svare på:** skal baselinen være obligatorisk for ALLE
|
||
kjøringer, eller kun for kjøringer som kan bruke penger? Vårt forslag er formulert som det
|
||
siste, med vilje: fasit-bundelen (`shared/examples/bygg-energi-mikro/`) har ingen
|
||
`cost-baseline.json`, så et ubetinget krav ville endret fasit-bytene og dermed §7-fasiten for
|
||
begge stacker. Feilscenarioet C-F3 beskriver er dessuten live-stien. Velger amendmenten
|
||
likevel «obligatorisk overalt», må fasit-endringen planlegges eksplisitt i begge repo.
|
||
|
||
## A3 — Ledende `/` i OKF-lenker skal forstås, ikke hoppes over (C-F4 → C2.8)
|
||
|
||
**Anker (verbatim, §3 Steg 1):** «Targets containing a path separator are out-of-bundle and
|
||
MUST be skipped.» OKF-spec-en anbefaler bundle-rot-relativ `/`-form — spec-en er altså
|
||
spec-tro implementert mot en spec-feil.
|
||
|
||
**Foreslått ordlyd:** «A target with a LEADING `/` is bundle-root-relative and MUST be
|
||
resolved against the bundle root. Any other path separator remains out-of-bundle and MUST be
|
||
skipped. Resolution stays boundary-checked against the bundle directory, fail-closed — the
|
||
leading slash changes the base, never the boundary.»
|
||
|
||
**Merknad til hovedrepoet:** MAF-planen har i dag ingen byggsesjon for F4-implementasjonen på
|
||
sin side; den bør legges til når D-A lander, ellers divergerer stackene på et punkt
|
||
amendmenten nettopp harmoniserer.
|
||
|
||
## A4 — Seeding må nøkles på dommens EGNE features (C-F5 → C3.2, folder inn R-8)
|
||
|
||
**Anker (verbatim, §3 Steg 1):** «Seeding: every `type: verdict` file in the bundle becomes a
|
||
store entry keyed on those candidate features» — «those» er bundelens ENE IR-projeksjon, så
|
||
dommer om ulike tiltak får identisk nøkkel.
|
||
|
||
**Foreslått ordlyd:** «A seeded verdict MUST be keyed on ITS OWN candidate features when its
|
||
frontmatter carries them (the promotion gate, §6, writes them); the bundle's IR projection is
|
||
the fallback for hand-authored seeds that do not. Two verdicts about different candidates MUST
|
||
NOT share a retrieval key.»
|
||
|
||
**Nyanse (D7-målt):** id-halvdelen av MAFs F5-funn er allerede løst her — `verdict_id` leses
|
||
verbatim fra frontmatter, load-bearing-bevist. Det er feature-halvdelen som står igjen.
|
||
|
||
## A5 — Projeksjons-kontrakten over hovedboken (brief-§7 — meldt uavhengig fra BEGGE stacker)
|
||
|
||
**Anker:** ingen. Method-spec-en nevner ikke hovedbok eller verdirapport i det hele tatt
|
||
(verifisert: 0 treff på «ledger»). Spec-en sier hva som skal produseres, ikke hva en
|
||
projeksjon over resultatene må garantere — derfor traff begge søsken samme hull samme døgn og
|
||
måtte ta samme tre skjønnsvalg.
|
||
|
||
**Formen vi foreslår:** kontrakten bindes til projeksjonens ARITMETIKK og fullstendighet, ikke
|
||
til hovedbokens filformat. Det holder brief pkt. 6 åpen (formatene forblir stack-lokale til en
|
||
eventuell konvergens via verktøykassa) og lukker likevel divergensen som gjør «samme
|
||
kapabilitet» forenlig med ulike totaler på identiske inndata.
|
||
|
||
**Foreslått ordlyd (tre regler, én per skjønnsvalg):**
|
||
1. **Runding og kvantisering** (REVIDERT 2026-07-25 etter MAFs målte korreksjon + egen måling
|
||
— se «A5-premisset korrigert» under; erstatter den opprinnelige ettsetnings-regelen):
|
||
- a. *Projeksjons-aritmetikk:* «Monetary figures MUST NOT be rounded in the projection —
|
||
neither per row nor on the total. Derived non-monetary ratios MAY be rounded, at a fixed
|
||
documented precision.»
|
||
- b. *Enhets-kvantisering ved persisterings-kanten:* «A stack MAY quantise monetary input to
|
||
a fixed minor unit ONCE, at the point of persistence, at a documented precision. This is
|
||
a representation choice, NOT a rendering concern, and it is explicitly permitted.»
|
||
- c. *Kvantiseringspunktet* (NYTT — uten dette er a+b ikke tilstrekkelig): «Where a stack
|
||
quantises, the contract MUST fix quantisation relative to summation: either input is
|
||
constrained to the minor unit at the contract edge (a finer-grained claim is a rejection),
|
||
or quantisation happens once on the total. Per-row quantisation of unconstrained input is
|
||
NOT interchangeable with quantisation of the sum.»
|
||
2. **Null kvalifiserende rader:** «A report MUST be produced even when no row qualifies. Every
|
||
figure the expert layer has not supplied MUST be UNMARKED (null), never 0 — a zero that
|
||
means "unjudged" is a false claim (§1).»
|
||
3. **Renhet:** «The projection MUST NOT restate provenance fields. It is a view over the
|
||
layers it reads; provenance is asserted once, by the run that produced it (§9).»
|
||
|
||
**A5-premisset korrigert (2026-07-25) — vår opprinnelige formulering var feil, og korreksjonen
|
||
flytter divergensen.** Vi skrev «MAF runder ett monetært beløp». MAF målte i sitt eget tre og
|
||
korrigerte oss: projeksjonen deres runder INGENTING (`build_value_report`, `value_report.py:48`
|
||
summerer heltalls-øre; `grep -n "round(" value_report.py` → 0 treff). Den ene monetære
|
||
operasjonen er en NOK→øre-**kvantisering** ved hovedbok-kanten (`ledger.py:204-214`, `Decimal`
|
||
+ `ROUND_HALF_UP`, dokumentert som «happens HERE and only here … to avoid binary-float error»).
|
||
Det er en enhetskonvertering ved persistering, ikke en runding i en projeksjon. Vi tar
|
||
korreksjonen: den opprinnelige regel 1 løste en divergens som ikke fantes (begge stacker runder
|
||
allerede intet i projeksjonen) og åpnet én som ikke fantes (andre setning fordømte MAFs
|
||
bevisste, dokumenterte kvantisering som «rendering»). Regel 1a+1b over er MAFs foreslåtte
|
||
splitt, ordrett i intensjon.
|
||
|
||
**Men splitten er ikke tilstrekkelig — målt her, og den avkreftet vår egen første hypotese.**
|
||
Vi antok at divergensen lå i float-akkumulering (vi summerer `float`-NOK, MAF heltalls-øre).
|
||
Målt: **0 av 20 000** tilfeldige porteføljer (2–40 rader, 2-desimal NOK) ga ulik total. Float
|
||
er ikke mekanismen, og vi rapporterer det selv om det svekker vår egen opprinnelige lesning.
|
||
Den reelle mekanismen er **hvor kvantiseringen står i forhold til summeringen**, og den bare
|
||
våkner når inndata bærer finere presisjon enn øre:
|
||
|
||
| inndata | MAF (kvantiser per rad, summer heltall) | D7 (summer fullt, kvantiser til slutt) |
|
||
|---|---|---|
|
||
| `[0.004, 0.004, 0.004]` | `0+0+0` = **0 øre** | `0.012` → **1 øre** |
|
||
| `100 × 0.005` | **100 øre** | **50 øre** — faktor 2 |
|
||
| 20 000 tilfeldige, 2-desimal | *ingen divergens* — **0/20 000** | *ingen divergens* — **0/20 000** |
|
||
| 20 000 tilfeldige, sub-øre | **66,4 % divergerer** — verste avvik 6 øre over 28 rader | (samme kjøring, motparten) |
|
||
|
||
*Reproduksjon:* `random.seed(20260725)`, 2–40 rader, `uniform(0.01, 500000.0)`; MAF-siden
|
||
modellert etter deres siterte `ledger.py:204-214` (`Decimal(str(v))*100`, `ROUND_HALF_UP`).
|
||
Målt mot vårt tre ved `457a0f4`. De to første radene er deterministiske, ikke seed-avhengige.
|
||
|
||
Og sub-øre-inndata er **nåbart, ikke hypotetisk, i begge stacker**: verken vår
|
||
`claimed_saving_nok` (`ir.py:42`) eller `amount_nok` (`ledger.py:42`) har noen
|
||
desimal-begrensning — begge er `float = Field(gt=0, allow_inf_nan=False)`. To stacker som
|
||
begge lyder 1a+1b kan altså fortsatt gi ULIK total på identiske inndata, som er nøyaktig det
|
||
A5 finnes for å lukke. Derfor regel 1c: amendmenten må velge ETT kvantiseringspunkt — enten
|
||
avvis finere-enn-øre-claims ved kontraktskanten, eller kvantiser én gang på totalen — og begge
|
||
stacker må lande på samme valg. **Ingen av delene kan patches herfra.**
|
||
|
||
**Status:** divergensen er fortsatt LIVE, men den er nå navngitt og målt i stedet for antatt.
|
||
Vi runder ingen kronebeløp (kun deriverte andeler, 6 siffer — `_SHARE_DIGITS`,
|
||
`valuereport.py:61`, verifisert). Regel 2 og 3 er
|
||
allerede D7s oppførsel (`_corrected`, `valuereport.py:182-194`: `pending` og
|
||
`adjusted_unquantified` gir `None`, aldri 0 — og `rejected` gir 0.0 fordi NULL er dommen, ikke
|
||
et fravær av den; `report_payload`, `valuereport.py:424`) og pkt. 3 er også MAFs valg.
|
||
|
||
## A6 — C-P1: semantisk henting re-scopes (INGEN spec-endring)
|
||
|
||
**Anker (verbatim, §3 Steg 1):** «Ranking is **structural, never textual** — surface text MUST
|
||
NOT contribute to similarity.» Den delte D-C-planen (cosine over tekst) kolliderer direkte.
|
||
|
||
**Vedtatt retning:** re-scoping, ikke amendment. Det frosne prinsippet består; semantisk
|
||
henting bygges på strukturelle kjennetegn alene. Konsekvens for planene i begge repo: rad 13
|
||
(K17) forblir GATED på D-C, men gaten er nå «D-C re-scopet», ikke «D-C som skrevet».
|
||
|
||
## Verifisering (hvordan vi vet at bolken faktisk har landet)
|
||
|
||
1. Commons har amendment-commiten; BEGGE repo har hentet SAMME commit (`git log shared/` viser
|
||
samme squash-hash i begge).
|
||
2. Begge suiter grønne rett etter pull, FØR noen byggsesjon starter — amendmenten alene skal
|
||
ikke rødne noe (den utvider spec-en; A1s stramming er målt kompatibel, A2s valg avgjør om
|
||
fasit må endres).
|
||
3. Deretter én sesjon per punkt, failing test først: C2.7 (A1) · C4.0 (A2) · C2.8 (A3) ·
|
||
C3.2 (A4) · verdirapport-kontrakten (A5, treffer K11-koden her).
|
||
4. A5 er ikke ferdig før BEGGE stacker produserer samme total på identiske inndata — det er
|
||
testen som beviser at divergensen faktisk er lukket, ikke at ordlyden finnes.
|