portfolio-optimiser/docs/2026-09-09-p8-forankringstilbudet.md
Kjell Tore Guttormsen 455d611660 feat(run,generate): a run says what its delivered input can ground, before it spends an attempt [skip-docs]
P7 is right and landed, but re-measuring it exposed a consequence no row stated: with the gate
live, 29 of 29 cost codes in 13 of 13 delivered proposals fall across the three free recordings
(PM's denominator; 14 of 14 in 8 proposals on the PARSEABLE one -- the five blobs that separate
the numbers are refused by pydantic's `claimed <= total` and never reach stage 0b). All 29 were
invented, so the gate is right; but a gate that always refuses is as useless as one that never
does.

The cause is that the PROMPT asks for something the input cannot supply. `_build_messages`
requires each affected_item to "restate a cost line as the project's price schedule already
carries it", while K2's delivered input carries 9 occurrences / 2 distinct code-shaped tokens --
`SHA-01`/`SHA-10`, both document numbers off a page footer -- and `derive_cost_baseline` refuses
the base outright. There is no cost line in it to restate.

`GroundingOffer(chars, identifiers, cost_lines)` reports it. The PAIR is the diagnosis: "50
identifiers, 0 cost lines" says what neither number says alone. A REPORT, never a gate -- it
blocks nothing, because a blocking requirement IS `--require-cost-baseline` (F4/D-3, opt-in,
untouched), and `_ground_against_input` is untouched.

The callsite is MEASURED, not chosen: `generate.py` composes the grounding per attempt, after
`await _fetch_parsed`, so a report there could only speak once an attempt had been paid for;
`run.py` binds both halves above the `--live-dry-run` cut and before the first `debate.run`, so
the FREE trip says it. `delivered` is bound ONCE and the same variable feeds the report and
`_evaluate`; the report composes THROUGH `_grounding_text`, the gate's own composer.

A pattern is admissible here and not in the gate, and that is the difference between a report and
a falsifier: an unknown form is a token left uncounted -- an under-count, never a false rejection.
The forms are transcribed from the measurement; bare numbers are excluded with the number
(46 394 / 2 117 in K2). `grounding_offer_notice` is the ONE renderer and is silent when the run
CAN anchor -- omission, never an empty row.

Load-bearing MEASURED (tests/test_grounding_offer_loadbearing.py, 12 arms), nine mutations all
red against the WHOLE suite + green control 1570/5 (from 1558/5, strict superset, 0 removed) and
the golden byte-unchanged (shasum -a 1 of the CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f).

Measurement: docs/2026-09-09-p8-forankringstilbudet.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 17:50:02 +02:00

14 KiB
Raw Blame History

P8 — den leverte inputen bærer ingen kostlinjer, så ingen forankret proposal kan oppstå

Ordre 20260909T142951Z-365478643-from-.claude, økt 110. HEAD ved start: 277bb95. Alt i dette dokumentet er målt gratis — null modellkall, NOK 0,00.

§ 0 Hva som ER målt og hva som IKKE er det

Målt: hva de tre gratis opptakene faktisk leverte og hvor mange av kodene P7-gaten feller; hva den LEVERTE inputen i hvert korpus po har liggende kan tilby som lovlig affected_item-kode; og at rapporten som nå bygges sier null der tilbudet er null og positivt der det er positivt.

Ikke målt: at rapporten endrer noe LEVENDE. Ingen betalt kjøring er gjort, og ingen er bestilt. At en modell som får se linja oppfører seg annerledes er ikke vist — samme klasse som structured-output-grensen. Rapporten er dessuten skrevet på grunnlag av tre opptak fra ÉN modell og ETT deployment.

Ikke bygget, med vilje: prosa-skanningen (operatørbeslutning A) og --require-cost-baseline som default (operatørbeslutning, F4/D-3). Begge står uendret.

§ 1 Premissene (i)(viii) — hver verifisert selv

# Premiss (PM) Mitt utfall
(i) HEAD 277bb95, git ls-remote origin main = eb41374, upushet 2 BEKREFTET ordrett
(ii) To utrackede: presentasjons-HTML (parallell økt) + scratchpad/ BEKREFTET, intet tredje
(iii) 1558 passed / 5 skipped, ruff rent, mypy rent (37 filer), golden ea8c534… BEKREFTET alle fire
(iv) 13 forslag / 29 koder / 29 ugrunnet BEKREFTET på PMs nevner, med et avvik — se § 2
(v) K2 leverer 9/2 distinkte kodeformede (SHA-01/SHA-10, dokumentnumre); N100 0 kodeformede, 17/8 kravnumre BEKREFTET ordrett
(vi) Ved uttømte forsøk returneres siste Rejection, ingen krasj BEKREFTET: generate.py returnerer GenerationResult(outcome=last_ruling, …) etter forsøksløkka
(vii) --require-cost-baseline er opt-in og skal ikke bli default RESPEKTERT, urørt
(viii) (A) og (B) er operatørens RESPEKTERT, ikke avgjort her

§ 2 Konsekvensen — og AVVIKET mot PMs tall

PMs tall er reprodusert nøyaktig på PMs nevner, men den nevneren teller blobs som aldri NÅR P7-gaten. Begge tall er sanne om hver sin ting, og begge gir 100 %:

Opptak Rå JSON-blobs Koder Ugrunnet Parsebare til IR Koder Ugrunnet
p6-Aopen-records.json 2 4 4 2 4 4
Aopen-records.json 8 22 22 3 7 7
p4-n100-free-records.json 3 3 3 3 3 3
Totalt 13 29 29 (100 %) 8 14 14 (100 %)

De fem blobene som skiller tallene avvises av en annen og TIDLIGERE falsifiserer — pydantics claimed_saving_nok <= affected items' total — og når derfor aldri stadium 0b. Det er ikke en svakhet i PMs måling; det er to nevnere om to ulike hendelser. Grunnlaget er det mest sjenerøse som finnes: unionen av ALLE prompter i opptaket. Selv der er ingen kode grunnet.

Et instrument-forbehold, skrevet ned fordi det først ga feil svar: en rå SavingsProposal.model_validate_json avviser alle 13, fordi svarene bærer WIRE-formen av assumptions (et array) som _normalise_assumptions folder tilbake. En måling som stoppet der ville rapportert 0 forslag — null fordi instrumentet ikke kunne lese formen, ikke fordi formen manglet.

§ 3 Årsaken — prompten ber om noe inputen ikke kan levere

generate._build_messages sier ordrett: «Each entry in affected_items must restate a cost line as the project's price schedule already carries it». Målt på record 0 (turen som bar bundelen):

  • K2 (P6: 96 567 tegn; S7c: 145 281 tegn) → 9 forekomster / 2 distinkte kodeformede, og begge er SHA-01 / SHA-10. Konteksten er entydig: Oppdragsnr.: 52308329 Dokumentnr.: SHA-01 Versjon: 01 — en sidefot i en SHA-plan — og Dokumentnavn: SHA-10 - … Restrisikorapport. Dokumentnumre, ikke kostlinjer.
  • N100 (10 569 tegn) → 0 kodeformede, men 17 forekomster / 8 distinkte kravnumre på Krav X.Y.Z—N-form (em-dash U+2014).

Modellen ble bedt om å gjengi en kostlinje som ikke finnes, og fabrikkerte den. Dette rimer med funn 4 (økt 107): K2s prisskjema bærer ingen mengdefortegnelse, 0 av 92 rader navngir kode/mengde/enhetspris.

§ 4 Tilbudet per korpus — målt med den SHIPPEDE funksjonen

Teksten er komponert nøyaktig som run_project komponerer P7s grunnlag ("\n".join([context, bundle_grounding]), deretter gjennom generate._grounding_text). «Kostlinje» er po sin EGEN definisjon (okf.derive_cost_baseline, MAJOR-4) — ikke en ny heuristikk oppfunnet her.

Korpus Konsepter Grunnlag (tegn) Distinkte identifikatorer Kostlinjer derive_cost_baseline
K2 s7c + payload-open 630 1 991 597 50 0 REFUSED
K2 s7c + payload-default 630 1 970 415 50 0 REFUSED
K2 s7a2 (peker-armen, P6) 629 1 894 500 50 0 REFUSED
N100 n100-2023 446 462 041 435 0 REFUSED
N200 n200-2024 1 133 1 500 962 982 0 REFUSED
N500 n500-2024 270 408 220 272 0 REFUSED
shared/veglys-fv-soer 5 30 038 3 1 REFUSED (men fila finnes)
shared/tunnel-hauglia 5 36 546 2 1 REFUSED (men fila finnes)
shared/bygg-energi-mikro 4 11 019 2 0 REFUSED

Klassene, målt. K2s 50 distinkte kodeformede er tegningsnumre (B-20-00-00, F-20-00-01, V-73-20-01-01), dokumentnumre (SHA-01, RIM-02, RIA-01, NOT-01), stoff- og standardreferanser (PCB-7, PAH-16, DALI-2) og forskriftsnumre (FOR-2011-12-06-1357). Ingen av dem er en kostkode. N-korpusene bærer kravnumre og UUID-er (N200: 2 290 distinkte UUID-er), ingen kostkoder.

Er et forankret forslag i det hele tatt MULIG? For K2: NEI, og tallet er 0 — derive_cost_baseline nekter basen, og ingen av de 50 identifikatorene er en kostlinje. Bare 2 av 50 når i det hele tatt prompten. For N-korpusene: NEI på kostlinje — en vegnormal bærer ingen — men JA på identifikator: 435 / 982 / 272 distinkte kravnumre er sitérbare, og 8 av dem sto i N100-prompten.

Rene tall er farligst og bæres videre uendret: 46 394 forekomster / 2 117 distinkte i K2 (P7 § 2). Regelen er inert mot dem og feiler ÅPENT. Ikke bygget om på.

Gammel default. Alt over er målt på K2-bundle-20260903. okf melder (innboks 20260909T134141Z, reply-expected: no, lukket med coord-done) at deres default-bygg sluttet å emittere den 2026-09-08, at gjeldende default er 436 konsepter / 832 filer, og at de leser formfunnet som fortsatt stående fordi endringen treffer FORMEN, ikke innholdet. Ingen ny bundle er bygget eller konsumert her — deres endring er ikke pushet, og en re-måling på den nye defaulten er en senere, separat ordre.

§ 5 Hva som er bygget — en RAPPORT, ikke en gate

generate.GroundingOffer(chars, identifiers, cost_lines) + generate.grounding_offer(...), kalt fra run.py, rendret av run.grounding_offer_notice.

Valget av kallsted, med grunnen (ordrens eget krav). Begge kandidater ble lest først. generate.py komponerer grunnlaget per forsøk, ETTER await _fetch_parsed(messages) — en rapport derfra kan først tale når ett forsøk allerede er betalt, altså nøyaktig det ordren ber den om å komme foran. run.py binder begge halvdeler ved run.py:1228, over --live-dry-run-kuttet og før første debate.run. Valgt: run.py. Tellefunksjonen bor likevel i generate.py, ved siden av _grounding_text den måler — å skille dem ville gitt to steder å bli uenige på.

De fire kravene:

  1. BLOKKERER ikke. En kjøring med null tilbud kjører som før. Et blokkerende krav ER --require-cost-baseline, som premiss (vii) fredet.
  2. Måler den EKSAKTE teksten. grounding_offer komponerer GJENNOM _grounding_text — samme funksjon P7s gate bruker — og run.py binder delivered én gang og gir samme variabel til både rapporten og _evaluate. To komposisjoner av én tekst er fri til å være uenige (kø-(p)); her er de identiske ved konstruksjon.
  3. Når utfallet operatøren leser. RunResult.grounding_offer, DryRunReport.grounding_offer og én linje på stdout i begge CLI-armene.
  4. Gjenbruker P7s sømmer. Ingen ny domstype ved siden av Rejection/ValidatedProposal. _ground_against_input er URØRT.

Identifikator-formene er TRANSKRIBERT fra målingen i § 4, ikke valgt: [A-ZÆØÅ]{1,8}[-_]\d… (K2s 50) og Krav X.Y.Z—N (N-korpusenes dominerende form). At et mønster er tillatt HER og ikke i _ground_against_input er selve skillet mellom en rapport og en gate: en form rapporten ikke kjenner er et token den unnlater å telle, altså en under-telling — aldri en falsk avvisning. Rene tall er BEVISST utelatt, med tallet (46 394 / 2 117): å telle dem ville gjort hver rapport positiv og målingen inert — repoets kardinalklasse, en gate som bare kan bli grønn.

Rendereren er ÉN, og den tier når kjøringen KAN forankre en kostlinje — omisjon, aldri en tom rad (cost_baseline_notices regel). Den bærer begge tall, fordi paret er diagnosen: «0 kostlinjer» alene leses som en gjentakelse av cost_baseline_notice, «50 identifikatorer» alene leses som gode nyheter.

§ 6 Kjent-positiv og kontroll

Kjent-positiven (bindende): N100s input bærer Krav 3.3.1—13 (em-dash U+2014; P7 premiss (vii): 6 av 6 prompter). Rapporten sier positivt tilbud der, og armen er paret med en kontroll på at strengen faktisk STÅR i fixturen — en rapport som fant null fordi den lette etter ingenting ville ellers bestått.

Kontrollen på samme materiale: K2s to genererings-prompter, der tilbudet er null i begge tall. Uten den beviser en grønn kjent-positiv ingenting.

Fixturvalget, uttalt. Testene leser P7s egne SPORede fixturer under tests/fixtures/p7-grounding/. De leverte KUTTENE (10 kB N100, 96 kB K2) er ikke sporet: å committe verbatim anbudstekst og standardtekst inn i et repo som publiseres på open/ er en publiseringsbeslutning som tilhører operatøren, ikke denne ordren. 8-/435-distinkt-tallene står derfor i § 4 som MÅLINGER; det testene asserterer er EGENSKAPEN, på tekst repoet allerede shipper.

§ 7 Mutasjonstabellen

Grønn kontroll 1570 passed / 5 skipped (fra 1558/5; +12 node-ider, 0 fjernet, målt med comm mot en liste bygget fra HEAD). Golden demo-transcript.stdout BYTE-UENDRET, shasum -a 1 av INNHOLDET = ea8c534773acdbe41ae68f2c55724d69aaf8be4f (ALDRI git-blob-id-en). Hver mutasjon kjørt mot HELE suiten, én per kall, restaurert fra scratchpad/p8/mut-backup/ med shasum -c, aldri git checkout.

# Mutasjon Røde
M1 rapporten teller alltid null 2 (kjent-positiven blant dem)
M2 rapporten teller alltid positivt 7 (K2-kontrollen først)
M3 rapporten bygges fra delivered alene, ikke gjennom _grounding_text 1 (krav (2)-armen alene)
M4 rapporten når aldri RunResult 3
M5 rapporten når aldri DryRunReport 3
M6 rendereren skriver alltid linja 2 (omisjonen er selv gatet, på begge flater)
M7 detach dry-run-utskriften 1
M8 detach fullkjørings-utskriften 1
M9 rene tall telles som tilbud 2

Ni mutasjoner, alle røde. Ingen grønn mutasjon, altså ingen uvitnet søm i denne leveransen.

§ 8 Honesty limits

  • Alt er målt OFFLINE på tre opptak fra ÉN modell og ETT deployment. Ingen betalt kjøring bekrefter at rapporten endrer noe levende, og at en modell som ser linja velger annerledes er ikke vist (structured-output-grensens klasse).
  • Rapporten BLOKKERER ikke. En kjøring med null tilbud kan fortsatt brenne tre forsøk på et forslag som ikke kan bli forankret. Det er VALGT, ikke oversett: et blokkerende krav er --require-cost-baseline, og F4/D-3 la den beslutningen hos operatøren.
  • Identifikator-formene er et mønster, og et mønster kan mangle en form. Retningen er under-telling, aldri falsk avvisning — men et korpus med en tredje form vil rapportere lavere enn det tilbyr, til noen måler den formen.
  • cost_lines er len(baseline.items), lest av SAMME baseline-binding _grounding_text tar som sin tredje kilde. Den gjentar altså forankringen som et ANTALL. Den står her fordi paret er diagnosen, ikke fordi antallet er en ny kjensgjerning.
  • Portefølje-armen er BEVISST ikke wiret. run_portfolio skriver ingen slik linje; bundle_id_notices avgjørelse, ikke cost_baseline_notices. Asymmetrien står her fordi stillhet om den er det eneste gale svaret.
  • Den hostede flaten er urørt. Feltet er i ingen av hostings tre sett, så Fase 4es to halvdeler står uendret.
  • Tilbudsmålingen er gjort på okf sin GAMLE default-bundle (K2-bundle-20260903). En re-måling på den nye (436 konsepter / 832 filer) er en senere, separat ordre.
  • (A) prosa-skanningen og (B) blindsone-valget forblir OPERATØRENS. Målingen her informerer (B) — den handler om TILBUDET i inputen, ikke om hvilket utdrag modellen VELGER — men den avgjør den ikke.

§ 9 Reproduksjon

uv run pytest -q tests/test_grounding_offer_loadbearing.py
uv run python -m portfolio_optimiser.run BYGG-KONTOR-NORD \
  --docs-dir shared/examples/bygg-energi-mikro \
  --bundle-dir shared/examples/bygg-energi-mikro --live-dry-run
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER \
  --docs-dir shared/examples/veglys-fv-soer \
  --bundle-dir shared/examples/veglys-fv-soer --live-dry-run

Den første basen har ingen kostbaseline og skriver tilbudslinja; den andre har én og tier. Måleskriptene ligger i scratchpad/p8/ (utracket).