portfolio-optimiser/docs/2026-09-15-p19-stressrunde-3.md
Kjell Tore Guttormsen fc26d4f0a0 docs(p19): stress round 3 -- five paid runs, and the premise the order rested on
DEL E, and the whole report. Environment measured before the paid arms: the
Foundry endpoint resolved INLINE from az, the client probe green (not skipped),
and a free --live-dry-run on all four sets first. Parameters are P18's,
unchanged for comparability.

The order's A1 premise was felled before anything was built on it: the stress
command carries no --explore, the two are refused together, and none of the
nine round-1/2 outboxes holds an exploration artefact -- so a demand only the
hypothesiser could carry would have been inert in exactly the paid runs this
order commissions. A2's own sentence points at a tool, and that is what made
round 3 measurable: a LIVE model called declare_requirement in 5 of 5 runs,
and used the read_dir filter 4 to 38 times per run against 0 in rounds 1-2.

The headline moved and barely: fasit concepts OPENED 0/26, 0/26, then 1/32.
That is movement, and it is one document.

Four findings remain, each with a named solution and an estimate. The first
already has one built: --require-cost-baseline, measured 15.09, refuses the
exact run that validated a REQUIREMENT number as a cost code -- before the
first model call, at NOK 0. Whether it becomes the stress round's default is
the operator's call, not this session's.

Honesty limits stated: B3 never fired live (it is proved on two replayed
artefacts), the prose-code fall is not isolated to it, sorasen-04's spend is
unknown because the artefact carrying it was never written, one variance pair
is not a sample, and no invoice has been read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 03:31:34 +02:00

325 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# P19 — kravet som binder, ordet som ikke er en kode, sporet som sier hvordan
**Økt 122, 15.09.2026.** Ordre `20260914T221206Z-3427310140-from-.claude`.
Commits `c84e8bf` (A) · `d74f32d` (B) · `4c6084e` (C+D) · denne rapporten (E).
Kontroll **1744 passed / 5 skipped** (fra 1699/5 ved P18, strengt supersett, 0 fjernet).
Golden `tests/golden/demo-transcript.stdout` BYTE-UENDRET gjennom hele arbeidet:
`shasum -a 1` av INNHOLDET = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` (aldri git-blob-id-en).
---
## 0. Et premiss i ordren ble felt FØR noe ble bygget på det
Ordrens **A1** legger kravet i `_INSTRUCTIONS[HYPOTHESISER_ROLE]` ALENE, og **E1** gjentar P18s
stresskommando ordrett. De to kan ikke begge være sanne:
| målt 15.09 | resultat |
| --- | --- |
| stresskommandoen i E1 | `--mandate`, **ingen `--explore`** |
| `--explore` + `--mandate` | NEKTES ved navn (økt 57) |
| `{run_id}-exploration.json` i de ni runde-1/2-utboksene | **0 av 9** |
Hypotesisereren kjører altså **aldri** i en stressrunde. En forpliktelse bare den kan bære ville
vært strukturelt inert i nøyaktig de betalte kjøringene ordren bestiller — og **A3** («kravet når
forslaget») ville vært unåbar sammen med den.
**A2s egen setning løser det:** nekten skal gå til modellen «som en tur den kan rette (samme
mekanisme som `quick_validate`s nekt), ikke som en `raise`». `quick_validate` ER et verktøy.
`declare_requirement` bor derfor i `navigator_tools`, altså hos BEGGE roller som navigerer:
utforskningen, og siden S2c debatten. Én instruksjon, én nekt, én record, to dører.
**Dette er ikke en omtolkning som ble bekreftet i etterkant — det er dét som gjorde runde 3 målbar:**
en levende modell kalte verktøyet i **5 av 5** kjøringer.
---
## 1. DEL A — kravet som binder
`declare_requirement(bundle_id, path, ref)` finnes **kun når kalleren gir begge sinkene**
(`opened` + `requirements`), og dét er hva som holder hvert pre-P19-kallsted byte-identisk. Én sink
uten den andre NEKTES ved konstruksjon: en logg som ikke ser hva som ble åpnet ville akseptert
enhver erklæring. `opened` er den SAMME lista `ExplorationToolRecorder` fyller — en alias, aldri en
kopi — så nekten leser kjøringens EGEN lesetrace.
Merket hypotese bærer `requirement` som PÅKREVD nøkkel: utelatt er en hard feil, eksplisitt `null`
er lovlig og krever `why_none`, halvnavngitt nektes. Et MYNTET forslag bærer feltet; et FRØ får det
aldri (§ C.6 dør 1). `_build_messages` skriver linja kun når feltet finnes.
**Mutasjoner (4, alle røde mot HELE suiten, grønn kontroll 1711/5):**
| # | mutasjon | røde |
| --- | --- | --- |
| A-i | `requirement` valgfri igjen | 1 |
| A-ii | nekten sjekker ikke mot åpnede stier | 1 |
| A-iii | A3-linja fyrer ubetinget | 1 |
| A-iv | dommeren teller mot hele basen | 1 |
**ORDRENS SPÅDDE SIGNATUR FOR A-iii BLE FALSIFISERT.** A5 sier golden må bli rød. Den er
byte-uendret, og grunnen er strukturell: demoen kjører UTEN mandat, så `_build_messages`' approach-
gren tas aldri på golden-stien. Vitnet er byte-identitets-halvdelen av arm (g), som asserterer at en
prompt uten krav er tegn for tegn den samme som før.
**A-iv STO GRØNN FØRST — repoets vakuøs-gate-klasse, TJUEFJERDE gang.** Armen drev `_attributable`
mens treffet regnes ut på KALLSTEDET i `score_context_set`. Den driver nå hele dommeren mot en
erklæring som ER i basen men IKKE er fasitens, med en positiv kontroll.
---
## 2. DEL B — ordet som ikke er en kode
**Kjent-positiv, MÅLT offline mot basene kjøringene faktisk fikk:**
| artefakt | kode | før | etter |
| --- | --- | --- | --- |
| `tunnel-hauglia-2027-02-a4` | `impulsventilator` | validated | **rejected** — «offers 391 identifiers of its own» |
| `fv412-…-02-a1` | `bituminøst bærelag` | validated | **rejected** — «offers 1359» |
**B1 — tilbudet per base, før/etter de to nye formene:**
| base | dok | tegn | før | etter |
| --- | ---: | ---: | ---: | ---: |
| n100-2023 | 446 | 436 799 | 435 | 578 |
| n200-2024 | 1 133 | 1 441 170 | 982 | 1 512 |
| n500-2024 | 270 | 388 773 | 272 | 391 |
| **r761-2025** | 2 756 | 6 561 263 | **3** | **2 332** |
**Den FØRSTE formen ble utvidet i samme slengen, og dét er en måling.** B2 gjorde de samme formene
til `prose`/`identifier`-avgjørelsen, og repoets EGEN `ENERGI-TOTAL-EL` matchet ingen av dem (form 1
krevde siffer etter separatoren) — klassifisereren kalte altså en ekte kostkode prosa, og den nye
gaten nektet den. **En gate får bare ta feil i retningen som slipper for mye inn.** Form 2 fikk et
valgfritt `_<n>`-suffiks fordi ÉN av de 26 fasit-referansene er `Krav 3.3.2—1_1`.
**Kjent-negativ:** 26 av 26 fasit-referanser klassifiseres som identifikatorer.
**Ærlighets-grense, målt og gitt sin EGEN arm:** `42.5` og `12.1` er typografisk identiske og ingen
regel skiller dem, så formen teller begge. P8s eksisterende «bare tall telles ikke»-arm er derfor
SNEVRET til heltall (K2s målte klasse, 46 394 forekomster), og desimal-tvetydigheten står i en
navngitt arm i stedet for i en docstring.
**Re-dom av alle ni runde-1+2-utbokser: 26 av 36 koder er prosa** (runde 1: 11 av 15 · runde 2: 15
av 21).
**Mutasjoner (4, alle røde, grønn kontroll 1734/5):** B-i gaten uten tilbuds-vilkåret (**16**, spredt
over syv eldre testfiler) · B-ii prosessnummer-formen droppet (10) · B-iii `prose` rapporteres aldri
(1) · B-iv gaten detachet (2).
---
## 3. DEL C + DEL D — sporet, forbruket og stoppgrunnen
`ToolCall` bærer nå `filter`/`offset`/`limit`. `_number_argument` er en SØSKEN av `_string_argument`:
en modell kan sende `limit` som `10` eller `"10"`, og en leser som kjente én form ville rapportert et
paginert kall som upaginert.
**P18s FUNN 4 VAR FEIL SOM FORMULERT.** `provenance.token_usage` har stått på hvert `-proposal.json`
siden S3.4; det som manglet var en LESER. Rettelsen er lagt inn som datert tilføyelse i
`docs/2026-09-14-p18-stressrunde-2.md` § 7, UNDER det opprinnelige avsnittet — en rapport som retter
seg selv i stillhet er ikke en rapport.
**Det som genuint manglet er `{run_id}-coverage.json`.** `stop_reason` kommer fra en KALLER-EID SINK,
ikke fra `in_flight`, og dét er en måling: `_evaluate_mandate` SVELGER `BudgetExceeded` så snart noe
er produsert, så `run_project`s egen `in_flight` ser den aldri.
**Mutasjoner (4, alle røde, grønn kontroll 1744/5):** C-i vindus-argumentene registreres ikke (2) ·
D-i coverage skrives med tom grunn (1) · D-ii kjøringen skriver den aldri (2) · D-iii dommeren
slutter å lese forbruket (1).
**D-i STO GRØNN FØRST — vakuøs-gate-klassen, TJUEFEMTE gang.** Armen kalte `write_coverage` selv og
VALGTE dermed grunnen den så asserterte på. Bare en kjøring et tak faktisk kappet kan skille de to;
armen driver nå `run_project` med `max_rounds=1` (én runde betaler første approach, den andre er den
taket kutter).
---
## 4. DEL E — stressrunde 3 (fem betalte kjøringer)
Miljø: `az cognitiveservices account show --name po-foundry-ktg --resource-group
portfolio-optimiser-rg` INLINE → PROSJEKT-formen `…/api/projects/po-project`;
`PORTFOLIO_FOUNDRY_DEPLOYMENT=gpt-4-1-mini`; `PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json`;
`PACE_SECONDS=2`. **Klientprobe FØR armene: `tests/test_foundry_profile_live.py` → 1 passed** (ikke
skippet). Gratis `--live-dry-run` på alle fire først, alle rc 0.
Parametrene er P18s (`--max-rounds 3 --max-tokens 600000`), uendret for sammenlignbarhet.
### 4.1 Rådata, alle tre runder gjennom SAMME dommer
| kjøring | grunnet | req_hit | named | halluc | prosa | filter | paged | tokens | stopp | a4 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | --- |
| fv412-01 | 0 | 0 | 0 | 3 | 3 | 0 | 0 | 523 633 | absent | ✅ |
| gate-01 | 0 | 0 | 0 | 4 | 4 | 0 | 0 | 509 310 | absent | ✅ |
| sorasen-01 | 0 | 0 | 1 | 5 | 3 | 0 | 0 | 104 905 | absent | ❌ |
| tunnel-01 | 0 | 0 | 0 | 3 | 1 | 0 | 0 | 255 418 | absent | ✅ |
| fv412-02 | 0 | 0 | 0 | 5 | 5 | 0 | 0 | 44 468 | absent | ✅ |
| gate-02 | 0 | 0 | 0 | 3 | 2 | 0 | 0 | 35 406 | absent | ✅ |
| gate-03 | 0 | 0 | 0 | 4 | 2 | 0 | 0 | 89 911 | absent | ✅ |
| sorasen-02 | 0 | 0 | 1 | 4 | 4 | 0 | 0 | 73 627 | absent | ✅ |
| tunnel-02 | 0 | 0 | 0 | 5 | 2 | 0 | 0 | 45 642 | absent | ❌ |
| **fv412-04** | 0 | 0 | 0 | 4 | **1** | **38** | 5 | 287 883 | rounds | ✅ |
| **gate-04** | 0 | 0 | 0 | 4 | **1** | **13** | 2 | 140 695 | rounds | ✅ |
| **gate-05** | 0 | 0 | 0 | 4 | **0** | **10** | 4 | 51 257 | rounds | ✅ |
| **tunnel-04** | **1** | 0 | 0 | 4 | **0** | **4** | 4 | 59 372 | rounds | ❌ |
| **sorasen-04** | — | — | — | — | — | — | — | — | rounds | — |
`sorasen-04` ble **REFUSERT av dommeren** (`EmptyMeasurement`): kjøringen døde på rundetaket
(`rounds limit=12 observed=13`) etter 11 parse-feil — modellen foreslo `claimed_saving_nok: 0` elleve
ganger — og etterlot intet forslags-artefakt. Dommeren nekter å rapportere «0 hallusinasjoner» om en
tom utboks. **`{run_id}-coverage.json` ble likevel skrevet, med `stop_reason: "rounds"`** — det er
nøyaktig kjøringen DEL D2 finnes for.
### 4.2 Hovedtallet: fasit-konsepter ÅPNET
| runde | åpnet | nevner |
| --- | ---: | ---: |
| 1 | **0** | 26 |
| 2 | **0** | 26 |
| **3** | **1** | **32** (gate kjørte to ganger) |
Treffet er `krav/N500/id-bfb0edb4-…` i `tunnel-hauglia-2027-04`, som gjorde a1 `grounded=True` for
første gang i tre runder. **Det er bevegelse, og det er lite.** 1 av 32 er ikke et resultat noen
skal bygge en påstand på.
### 4.3 Det verktøyet FAKTISK gjorde
**En LEVENDE modell kalte `declare_requirement` i 5 av 5 kjøringer** (12 ganger hver). Eksempel fra
r761: `{"bundle_id": "vegnormal-r761-2025", "path": "R761/kapittel/4-3/id-7c6d5921-….md",
"ref": "4.3"}`. Den brukte også filteret tungt (`filter='krav'`, `'bindende'`, `'bind'`) — 4 til 38
filtrerte kall per kjøring, mot **0 i runde 1 og 2**, der sporet ikke kunne se det.
**Men `requirement_hit` er 0 i 5 av 5:** ikke én erklæring traff fasitens konsepter. Modellen
navngir et krav, leser det først (nekten tvinger det), og velger likevel feil dokument.
`declare_requirement` flyttet altså **hvorvidt** et krav navngis, ikke **hvilket**.
### 4.4 Varians: 04 mot 05 på samme sett
`gate-nordvik-2027-04` og `-05` er samme sett, samme base, samme parametre:
| | 04 | 05 |
| --- | ---: | ---: |
| tokens | 140 695 | 51 257 (**2,7×**) |
| verktøykall | 30 | 18 |
| filtrerte kall | 13 | 10 |
| åpnede dokumenter | 6 | 2 |
| erklærte krav | 2 | 1 |
| utfall | 4 rejected | 4 rejected |
**Utfallet er identisk, forbruket 2,7×.** Én kjøring per sett er ikke et utvalg, og det er den
viktigste enkeltsetningen i denne seksjonen.
---
## 5. Hva hver del kjøpte — målt, ikke tilskrevet
| del | målbar effekt |
| --- | --- |
| A | `declare_requirement` kalt av en levende modell i **5/5**; `requirement_hit` **0/5** |
| B | r761s tilbud **3 → 2 332**; to runde-2-artefakter `validated → rejected` offline; prosa-koder per kjøring **15 → 01** |
| C | filtrerte kall lesbare for første gang: **0 → 4…38** |
| D1 | forbruket lesbart per kjøring for første gang (rettelse av P18 funn 4) |
| D2 | `sorasen-04` er den eneste kjøringen i tre runder som SIER hvorfor den stoppet |
**Ikke tilskrevet:** at prosa-kodene faller fra 15 til 01 er IKKE bevist å være B3s fortjeneste.
B3 fyrte **aldri** i runde 3 — hver avvisning kom fra P7 («appears nowhere in the input»), som
fyrer FØRST. Modellene produserte koder som ikke står i basen i det hele tatt. At de samtidig
sluttet å produsere prosa-koder som STÅR der, er en observasjon om fem kjøringer, ikke en effekt
som er isolert.
---
## 6. Gjenstående stygt — hvert funn med en navngitt løsning
### F1. Et KRAVNUMMER blir akseptert som en kostkode
`tunnel-hauglia-2027-04` sitt `a4-enhetspris-ventilator` VALIDERTE på koden **`10.4`** — et
kravnummer fra N500, ikke en kostlinje. Det er grunnet (står i basen), det har en identifikator-form,
og B3 slipper det gjennom. Formene kan ikke skille «identifikator for et KRAV» fra «identifikator for
en KOSTLINJE», fordi en base uten prisskjema ikke har noen av de siste.
`must_refuse`-armen faller derfor for andre runde på rad på nøyaktig dette settet.
**LØSNING (finnes allerede, ikke aktivert): `--require-cost-baseline`.** MÅLT 15.09: med flagget
nekter nøyaktig denne kjøringen **før første modellkall**, med rc 1 og NOK 0. F4 gjorde det opt-in
med vilje (hver commons-eid golden er uforankret), og **valget om å gjøre det til stressrundens
default er operatørens**. Anslag: 0 kodelinjer, én rad i kjørekommandoen.
**ALTERNATIV LØSNING (bygges, ~1 økt):** la `code_forms` skille en tredje verdi `requirement`
en kode som matcher form 2 eller 3 OG står som `req_number` i basens frontmatter er et krav, ikke
en kostlinje — og la 0b nekte den når kjøringen er uforankret. Risiko: r761s prosessnumre ER både
krav og oppgjørsposter, så regelen ville nektet nøyaktig det korpuset den er mest relevant for.
**Anbefaling: `--require-cost-baseline` først, mål så om alternativet fortsatt trengs.**
### F2. Modellen navngir et krav, men ikke det riktige
`requirement_hit` 0 av 5. Nekten tvinger fram en LESNING, ikke en RELEVANS.
**LØSNING (~1 økt):** la `declare_requirement` returnere kravets egen `title`/`req_number` fra
frontmatter i svaret, og la mandatets `success_criteria` nå hypotese-prompten — i dag når den bare
annonseringen. Anslag: to sømmer, én ny gate, ingen ny flate. **Ikke bygget her: det er en ny
beslutning om hva som skal inn i prompten, ikke en fiks av noe målt ødelagt.**
### F3. Rundetaket kapper hver kjøring
`stop_reason: rounds` i **5 av 5**. `own-proposal` ble aldri evaluert i noen kjøring i noen runde.
Taket er `max_rounds * 4 = 12` genererings-runder, og fire bestilte approaches bruker minst fire av
dem — flere når en parse-feil brenner en.
**LØSNING (0 kodelinjer):** `--max-rounds 5` gir 20 runder. Kostnaden er lineær i antall approaches,
ikke i korpuset. **Operatørbeslutning, fordi den øker regningen.**
### F4. `claimed_saving_nok: 0` brenner et helt budsjett
r761 brant 11 av 12 runder på at modellen foreslo null besparelse. Pydantic nekter `> 0`, og
`_fetch_parsed` prøver på nytt med samme prompt.
**LØSNING (~0,5 økt):** mat `ValidationError`-grunnen inn i neste forsøks prompt — Steg 5s mekanisme
finnes allerede for validator-avvisninger (`prior_rejection`), men en PARSE-feil går ikke den veien.
Anslag: én ny parameter på `_build_messages`, én gate.
---
## 7. Kostnad — anslag med uttalt antakelse
Målt forbruk, runde 3: **539 207 tokens** over de fire kjøringene som etterlot et artefakt.
`sorasen-04` er **ikke målt** (den døde før noe forslag ble skrevet, så ingen `token_usage` finnes) —
og dét er en ærlig luke, ikke en null.
| runde | kjøringer | målte tokens |
| --- | ---: | ---: |
| 1 | 4 | 1 393 266 |
| 2 | 5 | 289 054 |
| 3 | 4 (+1 umålt) | **539 207** |
**Runde 3 er dyrere enn runde 2**, og det er forventet: modellen navigerer nå mye mer (438 filtrerte
kall mot 0). **ANTAKELSE, ikke måling:** ved samme listepris som P18s anslag (289 k ≈ NOK 2) er
runde 3 ≈ **NOK 4**. Ingen faktura er lest.
---
## 8. Verifisering
| sjekk | resultat |
| --- | --- |
| `uv run pytest -q` | **1744 passed / 5 skipped** |
| `shasum -a 1 tests/golden/demo-transcript.stdout` (INNHOLD) | `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` |
| `uv run ruff check src tests` | All checks passed |
| `uv run ruff format --check src tests` | 210 files already formatted |
| `uv run mypy src` | Success: no issues found in 38 source files |
| mutasjoner A/B/C+D | 4 + 4 + 4 = **12, alle røde mot HELE suiten** |
| klientprobe før betalte armer | `test_foundry_profile_live.py` 1 passed |
| gratis dry-run, fire sett | rc 0 alle fire |
---
## 9. Ærlighets-grenser
* **1 av 32 fasit-konsepter er ikke et resultat.** Det er bevegelse fra 0, og fem kjøringer.
* **Varians ikke målt utover ett par.** 04/05 på gate viser 2,7× forbruksforskjell ved identisk
utfall; ett par er ikke et utvalg.
* **B3 fyrte aldri levende.** Gaten er bevist på to REPLAYEDE artefakter, ikke på en kjøring der den
faktisk avgjorde utfallet.
* **`prose_codes` falt uten at årsaken er isolert.** Se § 5.
* **`sorasen-04`s forbruk er ukjent** — artefaktet som bærer tallet ble aldri skrevet.
* **Ingen faktura lest.** Alle kronebeløp er anslag fra listepris.
* **Formene er transkribert fra FIRE korpus.** Et femte kan bære en femte form; en ukjent form
klassifiseres som prosa, og feilretningen er derfor nekt — dét er hva generalitetsvernet
(tilbud ≥ 1) og baseline-unntaket finnes for.
* **`token_usage` er kjøringens ENE teller** — den skiller ikke debatt fra generering.