portfolio-optimiser-claude/docs/2026-07-25-d-a-amendment-forslag.md
Kjell Tore Guttormsen ffb0503da3 docs(portfolio): A5 premise corrected — the quantisation POINT is the live divergence
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
2026-07-25 19:58:04 +02:00

12 KiB
Raw Blame History

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 (240 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.0121 øre
100 × 0.005 100 øre 50 øre — faktor 2
20 000 tilfeldige, 2-desimal ingen divergens0/20 000 ingen divergens0/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), 240 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.