fix(money): quantize NOK to øre in one order, from one source (kø-p)
Two quantization orders existed and met at exactly one comparison. SavingsLedger quantizes every realized candidate to integer øre and sums the ints; run.py's goal baselines summed Project.total_cost FLOATS across items and projects and quantized the total once. _goal_limit_if_reached compared the former against a threshold derived from the latter — so whether a portfolio pass stops early was decided by two differently-computed sides. Measured divergence: three 60000.005 NOK lines are 18000003 øre quantized first but 18000001 summed first (the float sum drifts to 180000.01499999998). Decision: quantize per cost line, then sum integers. Each CostItem IS a money amount — S4.0 made per-line quantity/unit_cost the validator's ground truth — and integer addition is associative, keeping totals order-independent under the D-D wave model, which the float fold is not. ledger.to_ore is now the framework's one NOK->øre conversion; run.py imports it rather than keeping a private copy (the S4.0 REPLIES precedent). Measuring the mutations found two further gaps, both now closed: the per-project baseline is a SECOND call site whose mutation survived the whole suite, and realize bypassing to_ore with a raw float*100 was caught by nothing. Load-bearing MEASURED (tests/test_money_quantization_loadbearing.py), five mutations all red: detach the portfolio baseline · detach the per-project baseline · reintroduce a private copy in run.py · change the rounding mode · let realize bypass to_ore. 615 -> 621 tests. Honesty boundary: sum_claimed_saving_nok (run.py:_aggregate) is deliberately untouched — a float NOK reporting field that is never quantized and never compared against the ledger, hence outside the ordering defect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WiY53sm8JFqk7NN75g5wRS
This commit is contained in:
parent
9dc3722161
commit
756e8f8259
4 changed files with 267 additions and 15 deletions
18
CLAUDE.md
18
CLAUDE.md
|
|
@ -181,6 +181,24 @@ Python ≥3.10. MAF (`agent-framework-core` 1.9.0). Pakkehåndtering: `uv`. To b
|
|||
Load-bearing MÅLT (`tests/test_portfolio_budget_loadbearing.py` + `tests/test_budget.py`), seks
|
||||
mutasjoner alle røde: detach wave-sjekken · detach pre-call-guarden · detach bølge-reservasjonen ·
|
||||
sjekk run-taket før global kreditering · detach oppstartsnekten · gjør `read_spend` tolerant.
|
||||
- **Pengetall kvantiseres i ÉN orden, fra ÉN kilde (kø-(p)):** `ledger.to_ore` er rammeverkets ENE
|
||||
NOK→øre-konvertering (Decimal, ROUND_HALF_UP), og den brukes **per pengebeløp — deretter summeres
|
||||
HELTALL**. `run.py` importerer den; aldri en egen kopi (to kopier av en penge-konvertering drifter,
|
||||
og en driftet kopi setter de to sidene av en mål-sammenligning på hver sin skala — S4.0s
|
||||
`REPLIES`-presedens). Før dette summerte `run.py`s mål-baseline `Project.total_cost`-FLOATS og
|
||||
kvantiserte totalen ÉN gang, mens `SavingsLedger` summerte per-kandidat-heltall — og de to ordenene
|
||||
møttes i nøyaktig ETT punkt: `_goal_limit_if_reached`, der et prosent-mål avgjør om et
|
||||
porteføljepass stopper tidlig. **Målt divergens:** tre linjer à `60000.005` NOK er `18000003` øre
|
||||
kvantisert først, men `18000001` summert først (float-drift til `180000.01499999998`) — nok til å
|
||||
vippe et mål. **Kvantiser-først valgt fordi hver `CostItem` ER et beløp** (S4.0 gjorde per-linje
|
||||
`quantity`/`unit_cost` til validatorens grunnsannhet), og fordi heltallsaddisjon er assosiativ →
|
||||
rekkefølge-uavhengig under D-D-bølgemodellen, som float-folden ikke er. **To kallsteder, ikke ett:**
|
||||
portefølje- og per-prosjekt-baselinen er separate, og en fiks på bare den ene OVERLEVDE hele suiten
|
||||
(målt). Load-bearing MÅLT (`tests/test_money_quantization_loadbearing.py`), fem mutasjoner alle
|
||||
røde: detach portefølje-baselinen · detach per-prosjekt-baselinen · gjeninnfør en privat kopi i
|
||||
`run.py` · endre avrundingsmodus · la `realize` gå utenom `to_ore`. **Ærlighets-grense:**
|
||||
`sum_claimed_saving_nok` (`run.py:_aggregate`) er BEVISST urørt — et float-NOK-rapportfelt som
|
||||
aldri kvantiseres og aldri sammenlignes mot ledgeren, altså utenfor ordens-defekten.
|
||||
- **Kostnadsdisiplin:** utvikle primært på lokal profil (gratis); Foundry/Azure (privat tenant finnes) kun til målrettet, minimal verifisering; billigste modeller + små syntetiske data + harde token-tak. Ingen tunge test-kjøringer.
|
||||
- **Offline simulering = primært metode-bevis (kostnadsdrevet, erstatter §11.8):** operatøren kjører
|
||||
IKKE MAF mot ekte modell (verken Azure/Foundry eller Ollama — API for begge repoene er for kostbart
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue