portfolio-optimiser/docs/2026-09-08-n-bundlene-hypoteseform.md
Kjell Tore Guttormsen 4f23fa2a70 docs(n-bundlene): P4 -- one free N100 run after the P3 fix, (b') is now yes
The live N100 arm without --require-cost-baseline lands the model on the
fasit concept: req_number ("Krav 3.3.1--13") is cited verbatim in 6 of 6
replies, and it is the correct concept, not a stray one. N100 reaches
po's own "ferdig" bar for the first time in this measurement series.
[skip-docs]
2026-09-08 17:31:07 +02:00

729 lines
40 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.

# N100, N200 og N500 re-målt i hypoteseform — det nye utdraget bærer nøkkelen, og po dropper den
> **Ordre `20260908T134013Z-2103630340-from-.claude` (P2).** P1 (`docs/2026-09-08-n-bundlene-konsum.md`,
> `e7ffe9e`+`a26e8f2`) fant at pre-passets utdrag leverte `text` uten `title`/`req_number`, så
> kravnummeret spørsmålet stiller fantes ikke noe sted i modellens kontekst. llm-ingestion-okf lukket
> det (`17c49fc`, `c95d189` — HEAD `171798e`, treet rent), vegnormal-okf re-bygde de tre bundlene med
> proveniens (`feae0c8`, V2-treet). Denne målingen re-kjører de tre armene mot det nye utdraget, med
> `--require-cost-baseline` (operatørvalg D-3), og dømmer etter den NYE «ferdig»-definisjonen
> (operatørvalg 08.09 12:20, D-1: **po er ikke et oppslagsverktøy**).
>
> **Utdraget bærer nå nøkkelen — 14 medlemmer, var 9. Modellen fikk den likevel ikke: po dropper
> feltene TO ganger, ved parsing og ved rendering. Funnet har byttet eier, fra okf til po.**
---
## 0. Hva som ER målt, og hva som IKKE er det
**MÅLT.** Tre nye tre-hasher reprodusert ordrett fra vegnormal-okfs V2-melding. `okf.navigate_bundle`
når 446 / 1 133 / 270 konsepter, 0 ufulgte lenker. Fasit-konseptet på **rang 1 av 8 på alle tre**, med
den flaggløse kommandoen. Utdragets medlemsliste: **14**, med `title`, `req_number`, `sources`,
`source_element_id`, `source_sha256`. Kontraktsjekk **exit 0 × 3**, 15 regler, 0 funn.
`prepass.admit_payload` OK × 3. Klientproben grønn. `--require-cost-baseline` nektet alle tre, rc 1,
**0 modellkall** — på full kjøresti, ikke bare dry-run. `--derive-cost-baseline` nektet også, alle tre.
Tre betalte armer uten flagget, rc 0 × 3, **NOK 0,22 av taket 5, 0 × 429**. Hva som nådde prompten,
felt for felt. Hvert modellsvar ordrett.
**IKKE MÅLT.** **Én kjøring er én kjøring** — tre armer, ett spørsmål hver, én modell
(`gpt-4-1-mini`), ett deployment, ingen gjentakelse. Ingenting her sier noe om varians, og P1 og P2
er to trekk fra samme modell på nesten samme input: at N100 gikk fra `rejected` (P1) til `validated`
(P2) og N200 motsatt vei er ikke tilskrevet noen årsak. At en ANNEN modell ville lest kuttet
annerledes er ikke målt. Ingen av bundlene er faglig gjennomgått — sammenlikningen er mot
konseptkroppen, ikke mot vegnormalen.
**IKKE RØRT.** Ingen fil under `src/`. Ingen Azure-konfig. Ingen fil i vegnormal-okf eller i noen
okf-checkout. Ingen push.
---
## 1. Kjent-positiv per bundle, FØR noe ble betalt
```bash
OKF=~/repos/llm-ingestion-okf # HEAD 171798e, `git status --short` TOMT
B=~/repos/vegnormal-okf/build/ferdig # feae0c8 (V2)
$OKF/.venv/bin/python3 $OKF/tools/okf_consume.py $B/n100-2023 \
--question "Hva krever Krav 3.3.1—13 i N100? Gjengi det sentrale vilkåret." --out payload-n100.json
$OKF/.venv/bin/python3 $OKF/tools/okf_skill.py $B/n100-2023 --out skill-n100
$OKF/.venv/bin/python3 $OKF/tools/okf_contract_check.py --skill skill-n100/SKILL.md --payload payload-n100.json
# ... tilsvarende for n200-2024 og n500-2024, DEFAULT-kommandoen, ingen flagg
```
| kjent-positiv | N100:2023 | N200:2024 | N500:2024 |
|---|---|---|---|
| `sha256-tree` reproduserer V2s ref | **JA** `da6b8204…` | **JA** `c64f36b7…` | **JA** `673a0c2c…` |
| `okf.navigate_bundle` → konsepter | **446** (fasit 446) | **1 133** (fasit 1 133) | **270** (fasit 270) |
| `Bundle.skipped` (ufulgte lenker) | 0 | 0 | 0 |
| considered / withheld / delivered | 446 / 438 / 8 | 1 133 / 1 125 / 8 | 270 / 262 / 8 |
| budsjett brukt av 120 000 | 11 868 | 23 961 | 11 941 |
| **fasit-konseptets rang** | **1 av 8** | **1 av 8** | **1 av 8** |
| `okf_contract_check` | **exit 0**, 15 regler, 0 funn | **exit 0**, 15 regler, 0 funn | **exit 0**, 15 regler, 0 funn |
| `prepass.admit_payload` mot montert base | **OK** | **OK** | **OK** |
| klientprobe `test_foundry_profile_live` | **1 passed** — kjørt FØR armene | (samme kjøring) | (samme kjøring) |
Budsjettforbruket er høyere enn P1s (11 868 mot 8 939 · 23 961 mot 21 102 · 11 941 mot 9 085) —
forventet, og det er selve endringen: hvert utdrag bærer nå fem felt til. okfs egen melding målte
+450,5 B per utdrag på et annet korpus; her er differansen +2 929 / +2 859 / +2 856 B over åtte
utdrag, altså **+366 / +357 / +357 B per utdrag**.
**Budsjett-instrumentets egen kjent-positiv flyttet, som okf varslet:** `expected` 12 563 /
`raw_bytes` 12 227 / `encoding_delta` 336 (var 10 349 / 10 060 / 289), og `measured == expected`
alle tre. En stale verdi ville fått pre-passet til å nekte — den høylytte feilen, ikke en defekt.
**Utdragets medlemmer: 14, var 9.** Ordren forventet ≥ 12.
```
adjudication · bundle_id · bundle_id_inherited · concept_id · rank · req_number · sha256
· source_element_id · source_sha256 · sources · text · text_sha256 · title · trust_tier
```
De fem nye er `title`, `req_number`, `sources`, `source_element_id`, `source_sha256`.
(okfs melding målte 9 → 17 på K2; differansen er at K2 bærer fem `source_*`-lokatorer der
N-bundlene bærer to — prefiks-regelen, ikke en allowlist.)
For N100s fasit-konsept:
```yaml
title: Krav 3.3.1—13 H1 Nasjonal hovedveg, ÅDT < 6 000 og fartsgrense 80 km/t
req_number: Krav 3.3.1—13
sources: [{resource: https://viewers.vegnorm.vegvesen.no/api/nisosts/859984?languageCode=nb,
title: N100:2023}]
source_element_id: id-4b61eee9-a149-42b3-863d-293b8320c15a
source_sha256: c58e8bbc5fa9a5400c111e51b04c05f2cfd9edabd884ef5352a486fdab2cb5ab
```
**Alt P1 etterlyste er der.** Det er derfor resten av dette dokumentet handler om po.
---
## 2. `--require-cost-baseline` NEKTET alle tre — og nekten er gratis
Operatørvalg D-3. Flagget ble kjørt på **full kjøresti**, ikke bare dry-run, og nekten er ordrett:
```
run refused: this run was required to be anchored, but the knowledge base offers no cost baseline:
without one the validator's stage 0 (reconciling each proposed cost line against the project's own)
is skipped and nothing ties a proposed cost line to this project. Ship a cost-baseline.json, or pass
--derive-cost-baseline when the base carries a priced schedule
```
| | N100 | N200 | N500 |
|---|---|---|---|
| `--require-cost-baseline`, full kjøring | **rc 1** | **rc 1** | **rc 1** |
| modellkall før nekten | **0** | **0** | **0** |
| kontroll: samme argv UTEN flagget | **rc 0** | **rc 0** | **rc 0** |
| `cost-baseline.json` i basen | fraværende | fraværende | fraværende |
| `--derive-cost-baseline` (den andre døra nekten navngir) | **rc 1** | **rc 1** | **rc 1** |
Antallet modellkall asserteres, ikke exit-koden alene: **en nekt etter forbruket ser identisk ut ved
exit-koden** (økt 57s regel). rc-0-kontrollen er der fordi en arm som bare kan bli rød ikke beviser
noe.
Den andre dørens nekt er like presis, og den forklarer hvorfor det ikke finnes en tredje vei:
```
no cost table found in bundle '…/n100-2023': no concept file carries a markdown table whose header
names all three of ['code', 'quantity', 'unit_cost']
```
**Målt konsekvens: det finnes ingen forankret form av en N-bundle-kjøring.** En vegnormal er et
kravkorpus, ikke et anbudskorpus — den bærer ingen kostlinjer i det hele tatt, og ingen av po sine
tre baseline-projeksjoner (håndskrevet fil · `baseline_from_project` · `derive_cost_baseline`) kan
lage én av den. F4-flagget gjør altså nøyaktig det det ble bygget for, og svaret er at N-bundlene
ikke er kostnadskjøringer.
**Derfor er de tre betalte armene kjørt UTEN flagget, og det er et uttalt avvik fra ordrens
bokstav.** Ordren ba om tre betalte armer MED flagget; med flagget koster de ingenting og produserer
ingen modellsvar, altså heller ingen (a), (b), (c) eller (e). Nekten er rapportert som resultatet
D-3 ba om (over), og armene som faktisk måler det nye utdraget er kjørt uten det. Det som ellers ville
stått igjen, var en tom måling av den eneste tingen P2 finnes for.
---
## 3. Tre betalte armer
```bash
export PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT=# utledet INLINE fra `az`, aldri i fil
export PORTFOLIO_FOUNDRY_DEPLOYMENT=gpt-4-1-mini
export PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json
export PACE_SECONDS=2
uv run --with tiktoken python scratchpad/nbundler-p2/live_nbundler_p2.py {n100|n200|n500}
# argv: <N100|N200|N500> --profile azure --docs-dir <base> --bundle-dir <base>
# --proposal-review --outbox-dir … --run-id p2-<tag>-free --prepass-payload payload-<tag>.json
```
Innsprutspunktet er `run._default_factory` (Fase 4e). Alle roller LEVENDE.
Deployment urørt (`az … deployment show`): `GlobalStandard`, `capacity` **100**, `gpt-4.1-mini`
`2025-04-14`, `Succeeded`.
| | **N100** | **N200** | **N500** |
|---|---:|---:|---:|
| rc | 0 | 0 | 0 |
| prompter totalt | **6** | **7** | **5** |
| proposer / checker | 5 / 1 | 6 / 1 | 4 / 1 |
| prompt-tokens (o200k) | 7 748 | 16 764 | 6 092 |
| leverandør-input | 12 217 | 25 877 | 9 619 |
| leverandør-output | 824 | 1 248 | 611 |
| **429-svar** | **0** | **0** | **0** |
| parse-feil-artefakt | fraværende | **TIL STEDE (1)** | fraværende |
| verktøykall i debatten | **0** (A-form: verktøyene trukket) | **0** | **0** |
| validator-dom | **validated** | rejected (stage 4, P90) | **validated** |
| dom-nøkkel | `c195117970ce86ef` | `70f3eb3540cd88f7` | `c9740904b1303f65` |
| Steg 5 (reason matet tilbake) | 2 prompter | 2 prompter | 1 prompt |
**Én parse-feil, på N200 — og filens tilstedeværelse ER signalet** (økt 35). Den er ikke en
formatfeil: modellen leverte gyldig JSON i riktig form, og det som falt var en DOMENE-invariant i
`SavingsProposal`:
> `Value error, claimed saving 1500000.0 exceeds affected items' total 900000.0`
Den avledede grammatikken (Fase 1b funn 1b) holdt altså på tre ferske korpora igjen; det som fanget
denne var pydantics egen `claimed <= total`. Funn-1-fangsten (kaller-eid sink, `finally`) leverte
teksten VERBATIM, som er hele grunnen til at setningen over kan siteres i det hele tatt.
---
## 4. Hovedfunnet: nøkkelen finnes nå, og po dropper den to ganger
P1s funn 1 var okfs. **Det er lukket.** Det som står igjen er po sitt, og det er to uavhengige tap
på samme sti.
**Tap 1 — ved PARSING.** `prepass.PrepassExcerpt` arver `_Permissive`, som er
`ConfigDict(extra="ignore")`. Payloadens 14 medlemmer blir til 7 i objektet po bygger:
```python
>>> sorted(gold.model_dump())
['adjudication', 'bundle_id', 'concept_id', 'sha256', 'text', 'text_sha256', 'trust_tier']
```
`title`, `req_number`, `sources`, `source_element_id` og `source_sha256` finnes ikke lenger.
**Tap 2 — ved RENDERING.** `prepass._data_blocks` renderer fire ting per utdrag: `concept_id`,
`adjudication`, `trust_tier` og `text`. Selv om feltene hadde overlevd parsing, ville de ikke nådd
prompten. DATA-blokka for fasit-konseptet, ORDRETT:
```
--- BEGIN DATA krav/N100/id-4b61eee9-a149-42b3-863d-293b8320c15a (adjudication: unknown, trust_tier: unverified) ---
## Krav
Eventuell kryssing mellom gang- og sykkelveg og veg skal være planskilt ved ÅDT > 4 000.
--- END DATA krav/N100/id-4b61eee9-a149-42b3-863d-293b8320c15a ---
```
**En første måling av dette var KONFUNDERT, og ble felt før noe ble bygget på den.** Et naivt
delstreng-søk fant `req_number` i 2 av 6 prompter og `source_element_id` i 2 av 6 — begge falske:
strengen `Krav 3.3.1—13` står i prompten fordi den er en del av SPØRSMÅLET
(«cut computed for: Hva krever Krav 3.3.1—13 i N100?»), og `id-4b61eee9-…` fordi den er en delstreng
av `concept_id`. Ingen av dem kommer fra utdragets nye felt. Det er repoets egen regel om at en
assert aldri skal stå på en delstreng to grener deler, anvendt på en måling i stedet for en test.
| når prompten faktisk? | N100 | N200 | N500 |
|---|---|---|---|
| utdragets `text` | **JA** | **JA** | **JA** |
| `concept_id` (UUID-formet) | **JA** | **JA** | **JA** |
| `req_number` som FELT | **NEI** | **NEI** | **NEI** |
| `title` | **NEI** | **NEI** | **NEI** |
| `sources` / `resource`-URL | **NEI** | **NEI** | **NEI** |
| `source_element_id` som FELT | **NEI** | **NEI** | **NEI** |
| `source_sha256` | **NEI** | **NEI** | **NEI** |
**Konsekvensen er direkte observerbar i modellens egne ord.** N200s proposer skriver:
> «…to optimize the geotechnical investigations and ground assessments already in the regulatory
> planning phase (**as per the krav with ID 03418c46-ad07-4678-bae1-08f441f38903**)»
Modellen siterer en UUID fordi UUID-en er den eneste identifikatoren den kan se — den står i
DATA-avgrenseren. Kravnummeret, som er det et menneske ville sitert, er i payloaden og når aldri
fram. **Rangeringen finner riktig dokument, produsenten leverer nå nøkkelen, og konsumenten kaster
den.**
---
## 5. Målene (a)(e) + (b), med nevner
### (a) Svarte modellen med det sentrale vilkåret i fasit-kravet?
| | N100 | N200 | N500 |
|---|---|---|---|
| nøkkelord fra fasit-kroppen | **3 av 3** (`planskilt` 13×, `ÅDT` 14×, `4 000` 3×) | **0 av 6** | **0 av 5** |
| fasit-setningen ORDRETT | **3 ganger** | 0 | 0 |
| **(a)** | **JA** | **NEI** | **NEI** |
Uendret fra P1, og mekanismen er den samme, målt og ikke gjettet: modellen ble bedt om et
kostnadsbesparende tiltak. På N100 ER fasit-kravet kostnadsformet (en terskel som lar deg sløyfe en
dyr konstruksjon), så «svar på oppgaven» og «svar på spørsmålet» sammenfaller. På N200 og N500 gjør
de det ikke, og modellen fulgte oppgaven den fikk — den resonnerte om grunnundersøkelser og om
fjernstyrte bommer, begge fra ANDRE leverte konsepter i kuttet.
### (b) Navnga modellen konseptet den bygde på?
Ordrens nye mål, og det som P2 finnes for.
| | N100 | N200 | N500 |
|---|---|---|---|
| `req_number` ordrett i svaret | **0** | **0** | **0** |
| `title` ordrett | 0 | 0 | 0 |
| fasit-`concept_id` ordrett | 0 | 0 | 0 |
| `source_element_id` som EGET felt | 0 | 0 | 0 |
| `resource`-URL / `nisosts` | 0 | 0 | 0 |
| leverte konsepter navngitt i det hele tatt | **0 av 8** | **1 av 8** (ikke fasit) | **1 av 8** (ikke fasit) |
| **(b)** | **NEI** | **NEI** | **NEI** |
**`req_number` er sitert 0 ganger i 18 modellsvar** — og det kan den ikke være, siden feltet aldri
nådde prompten (§ 4). (b) måler derfor po sin renderer, ikke modellens vilje: ingen implementasjon
av modellen kunne bestått denne raden slik koden står.
**Proveniens sitert: nei × 3.** Verken `source_element_id` eller `resource`-URL-en forekommer i noe
svar. Det er samme årsak.
### (c) Hallusinerte den et kravnummer eller en verdi?
| | N100 | N200 | N500 |
|---|---|---|---|
| kravnummer-formede tokens i svaret | **0** | **0** | **0** |
| tall i prosa som ikke er i delivered | **0** | **0** | **0** |
| konsept-id-referanser som ikke er levert | **0** | **0** | **0** |
| **(c)** | **0** | **0** | **0** |
Ett treff undersøkt og forkastet som instrumentfeil: N100s `4,000` er modellens engelske tusenskille
av det leverte `4 000`. N200s og N500s tall-fragmenter (`03418`, `4678`, `0000`, `4715`, …) er biter
av ekte, leverte konsept-UUID-er som modellen skrev i prosa.
**Kjent-negativ for instrumentet:** første tall-regex fant 0 tokens i N200s og N500s prosa, altså
kunne den ikke ha funnet en hallusinasjon heller. Den ble skjerpet til den fant de ekte tallene
(N100 `4 000` fra kroppen), og målingen over står på den skjerpede.
**Det strukturerte FORSLAGET dikter fortsatt opp kostkoder**, som i P1, og av samme strukturelle
grunn (ingen baseline, og oppgaven krever et tall):
| | kode | finnes i basen |
|---|---|---|
| N100 | `planskilt_kryssing` | **0 av 450 filer** |
| N200 | `03418c46-ad07-4678-bae1-08f441f38903` | 2 av 1 137 — en EKTE konsept-id brukt som kostkode |
| N500 | `RCB01` | **0 av 274 filer** |
**Forskjellen fra P1 er verdt å si:** P1s `03423b12` var en oppdiktet identifikator formet som en
ekte, og den ble VALIDERT. Denne gangen er ingen oppdiktet kode identifikator-formet — de to
oppdiktede er generiske kostlinje-etiketter. Det er ikke en forbedring noen bygget; det er et annet
trekk fra samme modell. **Begge kjøringene ville vært nektet av `--require-cost-baseline`** (§ 2),
og det er den delen som ikke er et sammentreff.
### (d) Kostnad
Listepris, Azure Retail Prices API (`api-version=2023-01-01-preview`, `currencyCode='NOK'`,
`armRegionName=eastus`), **hentet på nytt 08.09**, metere `gpt 4.1 mini Inp/Outp glbl Tokens`:
input NOK 0,003734 / 1K, output NOK 0,014936 / 1K.
| Kjøring | Prompter | Input | Output | NOK |
|---|---:|---:|---:|---:|
| N100 | 6 | 12 217 | 824 | 0,058 |
| N200 | 7 | 25 877 | 1 248 | 0,115 |
| N500 | 5 | 9 619 | 611 | 0,045 |
| klientprobe | 1 | ~20 | ~5 | ~0,000 |
| **SUM** | **19** | **47 733** | **2 688** | **NOK 0,22** |
**NOK 0,22 — 4,4 % av taket på NOK 5, og under ordrens tak på NOK 1. 429-svar: 0.**
De tre nekt-armene kostet **NOK 0,00** (0 modellkall).
### (e) Nevner-vokabularet
**Ikke brukt i noen arm.** `[sourced-not-sufficient]`, `[unread]`, `[sourced]`, `[inferred]` og
`[unsupported]` står **0 ganger i 18 modellsvar**. Som i P1: alle tre armene FANT noe å foreslå i
kuttet, så ingen var i posisjonen markøren finnes for. Det er en ubesvart nevner, ikke et bevis for
at markøren ikke virker.
---
## 6. «Ferdig»-dommen, etter den NYE definisjonen
Operatørvalg 08.09 12:20 (D-1). **Oppslagsspørsmålet «Hva krever Krav X?» er IKKE lenger po sitt
kriterium** — oppslag er Claude Code sin jobb via okf sin C1-oppskrift
(`llm-ingestion-okf/docs/2026-09-08-claude-code-skill-vilkaarlig-bundle.md`, som måler fire spørsmål
over to bundler med fire pass og null oppfunne tall). po sitt kriterium er hypoteseformen:
**ferdig i po = (a) modellen bygger hypotesen på riktig fasit-konsept ELLER nekter forankret
· (b) navngir konseptet · (c) 0 hallusinasjoner.**
| | (a) | (b) | (c) | **ferdig** |
|---|---|---|---|---|
| **N100:2023** | **JA** (fasit-setningen ordrett ×3) | **NEI** | **0** | **NEI** |
| **N200:2024** | NEI | **NEI** | **0** | **NEI** |
| **N500:2024** | NEI | **NEI** | **0** | **NEI** |
**0 av 3 — og den bindende raden er nå (b), på alle tre.**
Det er en annen situasjon enn P1s 0 av 3, og forskjellen er hele poenget:
- **P1: nøkkelen fantes ikke.** For to av tre bundler var kravnummeret fraværende fra modellens
kontekst i det hele tatt. Ingen konsument kunne gjort noe.
- **P2: nøkkelen finnes, og po kaster den.** Alle tre payloadene bærer `req_number`, `title` og en
hentbar `resource`-adresse. `prepass.PrepassExcerpt` ignorerer dem, og `prepass._data_blocks`
renderer dem ikke. **(b) er derfor ikke en modell-dom — det er en po-dom**, og den kan ikke bli
ja for noen modell før de to linjene endres.
Den andre halvdelen av (a) — «ELLER nekter forankret» — er verdt å lese nøyaktig: med
`--require-cost-baseline` nekter alle tre (§ 2), men da finnes det ingen hypotese å navngi et
konsept for, så (b) er umålbar og dommen ville vært ufullstendig snarere enn ja. **En N-bundle kan
ikke samtidig være forankret og produsere en hypotese**, fordi et kravkorpus ikke bærer kostlinjer.
Det er ikke en defekt i noen av de tre repoene; det er hva slags korpus en vegnormal er.
### Hvem eier hva som mangler
**llm-ingestion-okf eier ingenting her lenger.** P1s funn 1 er lukket og verifisert i denne
målingen: utdraget bærer `title`, `req_number`, `sources`, `source_element_id` og `source_sha256`,
rangeringen står på rang 1 × 3, kontraktsjekken går exit 0 med 15 regler, og budsjett-instrumentets
egen kjent-positiv er oppdatert i takt med at § 8 flyttet.
**vegnormal-okf eier ingenting.** V2-treet reproduserer sine egne hasher, nevnerne lukker, 0 ufulgte
lenker × 3, og hvert konsept bærer en adresse okf kan bære videre.
**portfolio-optimiser eier begge de gjenstående postene.** (1) `prepass`-sømmen dropper fem felt to
ganger. (2) Kjøreformen: A-formens oppgave er fortsatt hardkodet (`run.py:1243`) og `--explore` er
fortsatt nektet med `--prepass-payload` — men etter D-1 er dét ikke lenger en mangel, det er en
avgrensning operatøren har tatt stilling til.
### Anbefaling til operatøren (beslutningen er din)
1. **La po bære utdragets nye felt gjennom til prompten.** To linjer: navngi feltene på
`PrepassExcerpt` (eller les dem via `model_extra`), og la `_data_blocks` sette `req_number` og
`title` i DATA-avgrenseren ved siden av `concept_id`. Kostnaden er målt: **+366 B per utdrag**
er allerede betalt i payloaden, og rendringen legger til titalls tegn per utdrag. Dette er det
ENESTE som kan gjøre (b) nåbar, og det er en rød test og en søm — ikke en beslutning.
2. **Ikke gjør N-bundlene til kostnadskjøringer.** `--require-cost-baseline` og
`--derive-cost-baseline` nekter begge, målt, og grunnen er strukturell. Hvis en N-kjøring skal
forankres, må baselinen komme fra prosjektet — ikke fra normalen.
3. **`--require-cost-baseline` bør fortsatt brukes på N-kjøringer der et TALL skal telle.** Den
nektet tre kjøringer som ellers ville stemplet `validated` over kostkoder som ikke finnes i noen
base (§ 5c). Prisen er null.
4. **Etter (1) er kriteriet på nytt målbart for under NOK 0,25.** Samme tre armer, samme spørsmål.
---
## 7. Funn — rapportert, ikke fikset
1. **po dropper utdragets nye felt to ganger** (§ 4). Eier: po. `PrepassExcerpt` er
`extra="ignore"`; `_data_blocks` renderer fire ting. Nevner: 5 av 14 medlemmer når aldri
prompten, og `req_number` er sitert 0 ganger i 18 svar. **Dette er (b)s eneste årsak.**
2. **Ingen forankret form finnes for en N-bundle** (§ 2). Eier: ingen — det er en egenskap ved
korpustypen. Begge dørene nekter, målt, med rc-0-kontroll.
3. **To av tre validerte forslag bruker oppdiktede kostkoder** (§ 5c). Eier: po (bruksmåte).
`planskilt_kryssing` 0 av 450, `RCB01` 0 av 274. F4-flagget nekter dem, gratis.
4. **Én parse-feil på N200** (§ 3). Ikke en formatfeil — en domene-invariant i `SavingsProposal`
(`claimed 1 500 000 > total 900 000`). Rapportert fordi P1 målte null på alle tre, og fordi
artefaktets tilstedeværelse er signalet.
5. **Nevner-vokabularet ble ikke brukt i noen arm** (§ 5e). Uendret fra P1, samme ubesvarte nevner.
6. **En delstreng-måling av «nådde feltet prompten» er konfundert** (§ 4). Både `req_number` og
`source_element_id` finnes i prompten av HELT andre grunner (spørsmålslinja og `concept_id`).
Nevnt fordi neste måling vil gjøre samme feil om den ikke er skrevet ned.
---
## 8. Ærlighets-grenser, uttalt
- **Én kjøring er én kjøring.** Tre armer, ett spørsmål hver, ingen gjentakelse, én modell.
P1 og P2 er to trekk fra samme modell på nesten samme input, og de er UENIGE om utfallet på to av
tre armer (N100 rejected → validated, N200 validated → rejected). Ingen årsak er tilskrevet; det
er nettopp dét varians ser ut som når nevneren er 1.
- **(b) måler po, ikke modellen.** Feltet når aldri prompten, så ingen modell kunne bestått raden.
Å skåre den som en modell-svakhet ville vært å bruke definisjonen som gjemmeplass.
- **(a) på N100 er ikke bevis for at kjeden svarer på oppslag.** Det er bevis for at fasit-teksten
nådde modellen og ble brukt, og på N100 sammenfaller de to spørsmålene ved et sammentreff i
kravets innhold. Sammentreffet er identifisert, ikke skjult — og etter D-1 er oppslag uansett
ikke po sitt kriterium.
- **(c) = 0 gjelder svaret.** Det strukturerte forslaget dikter opp kostkoder, som er strukturelt
påkrevd i en uforankret kjøring. Skillet er uttalt i § 5c, ikke skjult av definisjonen.
- **AVVIK fra ordren, uttalt:** de tre betalte armene er kjørt UTEN `--require-cost-baseline`.
Med flagget koster de null og måler null (§ 2). Nekten er rapportert som D-3s resultat, og armene
som måler det nye utdraget er kjørt uten det.
- **`--cost-vocabulary`, `--k` og `--rarity-weight` ble IKKE brukt.** Fasit sto på rang 1 med
default-kommandoen på alle tre, så betingelsen for opt-in-flaggene inntraff aldri.
- **Kuttet er verifisert mot den monterte basen** (`admit_payload` × 3), så payloaden kan ikke ha
levert bytes basen ikke holder. Det er en gate, ikke en tillitserklæring til produsenten.
- **Ingen av bundlene er faglig gjennomgått.** Sammenlikningen i (a) er mot konseptkroppen slik den
står, ikke mot vegnormalen.
- **`sources`-adressen er ikke hentet av po.** vegnormal-okf målte at URL-en returnerer bytes som
er bytelike med `source_sha256`; den målingen er deres, ikke gjentatt her.
---
## 9. Verifiseringslogg
| # | Påstand | Slik den ble verifisert |
|---|---|---|
| 1 | okf HEAD `171798e`, treet rent | `git -C ~/repos/llm-ingestion-okf log --oneline -1` + `status --short` (tom) |
| 2 | Tre V2-hasher reproduserer | `payload.bundle.ref` mot vegnormal-okfs melding, 3 av 3 ordrett |
| 3 | 446 / 1 133 / 270 konsepter, 0 skipped | `okf.navigate_bundle(...).context_files`, po sin egen kode |
| 4 | Fasit på rang 1 × 3 | indeks av fasit-id-en i `payload.excerpts`, flaggløs kommando |
| 5 | Utdraget har 14 medlemmer | `sorted(payload['excerpts'][0].keys())`, 3 av 3 |
| 6 | Kontraktsjekk exit 0 × 3 | `okf_contract_check.py --skill … --payload …`, 15 regler, 0 funn |
| 7 | `admit_payload` OK × 3 | `prepass.admit_payload(p, bundle_dir=…, resolved_id=…)` |
| 8 | Klientproben grønn | `uv run pytest tests/test_foundry_profile_live.py -q` → 1 passed |
| 9 | Deployment urørt | `az … deployment show` → GlobalStandard, 100, `2025-04-14` |
| 10 | Flagget nekter × 3, 0 modellkall | full kjøring, `assert not records` i måleskriptet, rc 1 |
| 11 | rc-0-kontroll uten flagget | samme argv uten `--require-cost-baseline` → rc 0, 3 av 3 |
| 12 | `--derive-cost-baseline` nekter × 3 | rc 1 + nekt-teksten navngir de tre påkrevde kolonnene |
| 13 | po dropper feltene ved parsing | `sorted(excerpt.model_dump())` → 7 nøkler; `_Permissive` er `extra="ignore"` |
| 14 | po dropper dem ved rendering | `prepass.render_context(...)`, DATA-blokka sitert ordrett |
| 15 | Delstreng-målingen var konfundert | de to «treffene» lokalisert til spørsmålslinja og `concept_id` |
| 16 | (a) N100 ja | fasit-setningen ORDRETT 3 ganger; `planskilt`/`ÅDT`/`4 000` alle til stede |
| 17 | (a) N200/N500 nei | 0 av 6 / 0 av 5 nøkkelord fra hver fasit-kropp i noe svar |
| 18 | (b) nei × 3 | `req_number`/`title`/`concept_id`/`element_id`/URL: 0 treff i 18 svar |
| 19 | (c) = 0 | kravnummer-formede tokens: 0; tall i prosa mot delivered, ett treff forkastet |
| 20 | Instrumentet for (c) virker | skjerpet til det fant `4 000` fra kroppen; første form fant 0 av 0 |
| 21 | Kostkodene | `grep -rl` i hver base med nevner; fasit-id-en som kjent-positiv (2 treff) |
| 22 | Parse-feilen på N200 | `p2-n200-free-parse-failures.json` finnes; feilteksten sitert ordrett |
| 23 | Prisene | Azure Retail Prices API hentet på nytt 08.09 |
| 24 | 0 × 429 | `retries`-telleren i alle 18 poster |
---
## 10. P3 (ordre `20260908T141941Z`) — po bærer utdragets nye felt gjennom til prompten
PM-valg 08.09 14:20Z, på denne målingens egen anbefaling (§ 6): **ja, to linjer, rød test først.**
### 10.1 Reproduksjon (steg 1, uendret fra § 4)
```python
>>> sorted(json.load(open("scratchpad/nbundler-p2/payload-n500.json"))["excerpts"][0].keys())
['adjudication', 'bundle_id', 'bundle_id_inherited', 'concept_id', 'rank', 'req_number',
'sha256', 'source_element_id', 'source_sha256', 'sources', 'text', 'text_sha256', 'title',
'trust_tier'] # 14 medlemmer, i payloaden
>>> sorted(prepass.load_prepass_payload("scratchpad/nbundler-p2/payload-n500.json").excerpts[0]
... .model_dump().keys())
['adjudication', 'bundle_id', 'concept_id', 'sha256', 'text', 'text_sha256', 'trust_tier']
# 7 medlemmer, etter parsing
>>> "req_number" in prepass.render_context(...)
False # aldri i prompten (§ 4)
```
### 10.2 Feltform, valgt med måling
Spørsmålet ordren stiller: hvilken form overlever en ukjent `source_foo`-nøkkel fra en FRAMTIDIG
produsent, uten kodeendring her? okfs egen melding (§ 4, sitert) kaller `source_*` en **prefiks-
regel, ikke en allowlist** — K2 bærer fem slike lokatorer der N-bundlene bærer to. To former ble
sammenliknet mot nøyaktig det spørsmålet:
- **Navngitte felt** (`source_element_id: str | None`, `source_sha256: str | None`, …): en tredje
`source_*`-nøkkel krever et NYTT felt på `PrepassExcerpt` og en ny rendringslinje — en
kodeendring, nøyaktig det prefiksregelen sier man IKKE skal måtte gjøre.
- **`model_extra`** (`extra="allow"``PrepassExcerpt` ALENE, lest tilbake via et
`source_`-prefikssøk i `source_locators()`): en ny `source_*`-nøkkel havner unavngitt i
`model_extra` og plukkes opp av SAMME søk, null kodeendring.
**Valgt: `model_extra` for `source_*`-familien.** `title`, `req_number` og `sources` er derimot
navngitte felt — de er SS-8-deklarerte, entallige medlemmer (aldri en voksende familie), og en
`model_extra`-lesning av dem ville kastet bort pydantics egen validering for ingen gevinst.
Testen `test_a_future_source_star_key_survives_without_a_code_change`
(`tests/test_prepass_excerpt_fields_loadbearing.py`) legger til en TREDJE, aldri navngitt
`source_page_number`-nøkkel og viser at den når `source_locators()` uendret.
### 10.3 Fiksen: to steder, ikke to linjer
Anbefalingen i § 6 anslo "to linjer"; målt var det to STEDER (parsing + rendering), hver med mer
enn én linje fordi `sources` trengte en egen typet klasse (`PrepassSource`, med `resource` og en
valgfri `title`) og `source_locators()` trengte en egen metode:
1. **Parsing** (`PrepassExcerpt`): `title: str | None`, `req_number: str | None`,
`sources: tuple[PrepassSource, ...] | None`, alle default `None` (de fleste payloader i dette
repoet er fra FØR P2s produsent-endring og bærer ingen av dem — et påkrevd felt ville refusert
hver payload skrevet før i dag). `model_config = ConfigDict(extra="allow")` — kun på DENNE
klassen, `_Permissive`s `extra="ignore"` står urørt for `PrepassBundle`/`PrepassBudget`/osv.
2. **Rendering** (`_excerpt_header`, ny funksjon `_data_blocks` nå kaller): `req_number` og
`title` legges til i parentesen ved siden av `adjudication`/`trust_tier` NÅR de finnes;
`sources[0].resource` (adressen) og hver `source_*`-lokator (generisk, via
`source_locators()`) legges til deretter. **Kjent-negativ:** når ingen av de nye feltene
finnes, er sløyfa en no-op og headeren er BYTE-IDENTISK med før P3
(`test_a_p1_form_payload_renders_the_header_exactly_as_before`,
`test_a_p1_form_payload_render_seed_also_unchanged`) — pinnet LITERALT, ikke med en
delstreng-assert, fordi en lekkende tom-klausul (f.eks. en etterlatt `, `) ikke ville vist seg
i et substring-søk.
`render_seed` deler `_data_blocks` med `render_context` (ko-(p)); en fiks som bare traff den ene
armen ville latt utforsknings-døra ligge stille bak debatt-døra —
`test_render_seed_also_carries_the_new_fields` gater begge.
### 10.4 Rødt → grønt → mutasjon rød
Ny fil: `tests/test_prepass_excerpt_fields_loadbearing.py`, 10 arm. FØR fiksen: **8 røde, 2
grønne** (de to kjent-negative armene er trivielt grønne før fiksen òg — de er kontroller, ikke
mål). ETTER fiksen: **10 av 10 grønne.**
**Mutasjon** (revert `_excerpt_header`/`_data_blocks` til § 4s form, midlertidig, aldri committet):
3 av 10 røde — nøyaktig de tre rendrings-armene
(`test_the_data_block_renders_req_number_title_and_the_address`,
`test_the_data_block_renders_a_future_locator_too`, `test_render_seed_also_carries_the_new_fields`)
— parsing-armene forblir grønne (mutasjonen rørte ikke `PrepassExcerpt`), som beviser at de to
halvdelene av fiksen har HVER SIN uavhengige gate. Fiksen restaurert fra en kopi (`shasum -a 1`
verifisert identisk før/etter), aldri `git checkout`.
### 10.5 Suite, golden, lint
**1521 passed, 5 skipped** (var 1511 — 10 nye), `pytest -q`, 272 s. `ruff check src tests` og
`ruff format --check src tests`: rene (94 funn i `scratchpad/` er FØR-eksisterende, utracket,
utenfor scope — samme avgrensning som K3-ordrenes `ruff check src tests tools`). `mypy src`: 0
funn, 37 filer. Golden `demo-transcript.stdout` UENDRET: `shasum -a 1` = `ea8c534…` — forventet,
`simulation.py` importerer ikke `prepass` i det hele tatt.
### 10.6 Én betalt N100-kjøring med `--require-cost-baseline` — og (b) er STRUKTURELT ikke dekket
Samme oppsett som § 3 (`live_nbundler_p2.py n100 --require-cost-baseline`), med den FIKSEDE koden.
```
KJENT-POSITIV n100: navigate_bundle=446 (fasit 446) OK
run refused: this run was required to be anchored, but the knowledge base offers no cost baseline: …
===== ARM n100 [req] (rc=1) =====
MODELLKALL: 0 (gaten MAA fyre foer foerste kall)
```
**rc=1, 0 modellkall, NOK 0,00.** Dette REPRODUSERER § 2s funn ordrett — `--require-cost-baseline`
nekter N100 FØR første modellkall, fordi en vegnormal ikke bærer kostlinjer (strukturelt, uendret
av denne fiksen). **Konsekvens: (b) kan IKKE måles i denne armen** — det oppstår aldri en
hypotese å navngi et konsept for, uansett hvor godt utdraget bærer `req_number`/`title`. Dette er
IKKE en regresjon fra fiksen; det er § 2s "en N-bundle kan ikke samtidig være forankret og
produsere en hypotese" gjentatt på den fiksede koden.
**(b) er derfor "ikke dekket" i DENNE armen — strukturelt, ikke som en feil ved fiksen.**
Fiksens EGEN korrekthet er verifisert der den kan verifiseres gratis: offline, mot payloadens
faktiske felt (§ 10.110.4). En live gjenmåling av (b) på den FRIE armen (uten flagget, slik § 3
kjørte N100) ville kostet et nytt betalt kall utenfor denne ordrens scope (ordren navngir
eksplisitt `--require-cost-baseline`-armen som den ENE betalte kjøringen); det er ikke gjort her.
### 10.7 Funn — rapportert, ikke fikset
7. **(b) er umålbar under `--require-cost-baseline` for enhver N-bundle** (§ 10.6). Samme
struktur som funn 2 i § 7, nå bekreftet på den fiksede koden. Ingen handling — F4 gjør nøyaktig
det den ble bygget for.
8. **En live gjenmåling av (b) på den frie N100-armen (uten flagget) er ikke gjort** her — ordren
navngir `--require-cost-baseline` som den ene betalte kjøringen, og en fri kjøring ville vært
en ny, ubedt betalt handling.
## 11. P4 (ordre `20260908T151403Z`) — den frie N100-armen, ETTER fiksen: (b) = JA
PM-valg 08.09 15:14Z, på § 6 punkt 4 og § 10.7 funn 8s egen anbefaling: funn 8s hull lukkes med
ÉN betalt, flaggfri N100-kjøring. Ingen `src/`-endring; ny kjørefil KUN i `scratchpad/`
(`live_p4_free.py`, en kopi av `live_nbundler_p2.py`s frie arm med eget run-id/filnavn, for å ikke
overskrive P2s eksisterende `n100-free-*`-evidens).
### 11.1 Repro (steg 1, gratis)
```python
>>> len(json.load(open("scratchpad/nbundler-p2/payload-n100.json"))["excerpts"])
14 # medlemmer i excerpts[0], uendret
```
```
--- BEGIN DATA krav/N100/id-4b61eee9-…-c15a (adjudication: unknown, trust_tier: unverified,
req_number: Krav 3.3.1—13, title: Krav 3.3.1—13 H1 Nasjonal hovedveg, ÅDT < 6 000 og
fartsgrense 80 km/t, source: https://viewers.vegnorm.vegvesen.no/api/nisosts/859984?…
```
`req_number` og `title` står ORDRETT på `BEGIN DATA`-avgrenserens EGEN linje (§ 7 funn 6: målt her
på avgrenseren, ikke på hele prompten, som § 7 selv advarte var konfundert).
**Klientprobe FØR armen, begge env-variabler satt:** `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` (utledet
INLINE fra `az cognitiveservices account show --name po-foundry-ktg --resource-group
portfolio-optimiser-rg`, PROSJEKT-formen `…/api/projects/po-project`) + `PORTFOLIO_FOUNDRY_DEPLOYMENT`
= `gpt-4-1-mini`. `uv run pytest tests/test_foundry_profile_live.py -q`**1 passed** (ikke skippet).
### 11.2 Den betalte kjøringen
`uv run --with tiktoken python scratchpad/nbundler-p2/live_p4_free.py`, profil `azure`,
`gpt-4-1-mini`, `PACE_SECONDS=2`, `PORTFOLIO_MODEL_MAP=scratchpad/major2-live/model_map.json`,
`--run-id p4-n100-free`, UTEN `--require-cost-baseline` og UTEN `--derive-cost-baseline`. Rå
resultat:
```
KJENT-POSITIV n100: navigate_bundle=446 (fasit 446) OK
rang 1 = krav/N100/id-4b61eee9-…-c15a req_number='Krav 3.3.1—13'
===== ARM p4-n100-free (rc=0) =====
N100: Rejection (no expert verdict given; verdict key=ca7cd8e99c6cc2d9)
proposal review offered, never consulted (no candidate validated)
--- ledger ---
checker prompter=1 in=4693 out=99
proposer prompter=5 in=10716 out=648
prompter totalt: 6 429-retries: 0
```
**rc=0, 6 modellkall (5 proposer, 1 checker), ingen 429.** `--proposal-review` var satt (skriptet
manus svarer `approve` på hvert spørsmål, uten at noe forslag noensinne når validatoren — checkeren
avviste ikke, men ingen kandidat ble VALIDERT, se § 11.4).
### 11.3 Modellsvarene, ordrett (utdrag)
Proposer, forsøk 1:
> A concrete cost-saving measure for the N100 project could be: **Avoid planskilt (grade-separated)
> crossings between pedestrian/cycle paths and roads when the ÅDT (Average Daily Traffic) is 4,000
> or less.** Rationale based on **Krav 3.3.1—13** from the N100 knowledge base: The requirement
> states that "Eventuell kryssing mellom gang- og sykkelveg og veg skal være planskilt ved
> ÅDT > 4 000." …
Checker:
> The N100 excerpt **Krav 3.3.1—13** clearly states that crossing between pedestrian/bicycle paths
> and roads must be grade separated (planskilt) only if ÅDT > 4,000. … VERDICT: APPROVE
### 11.4 (b)/(a)/(c) — med nevner, samme rader som § 5
| | N100 (P4, fri, ETTER fiksen) |
|---|---|
| `req_number` (`Krav 3.3.1—13`) ordrett | **6 av 6 svar** |
| `title` ordrett (hele strengen) | 0 |
| fasit-`concept_id` ordrett | 0 |
| `source_element_id` som eget felt | 0 |
| `resource`-URL/`nisosts` | 0 |
| leverte konsepter navngitt i det hele tatt | **1 av 8 — OG DET ER FASIT-KONSEPTET** (ulikt P2s N200/N500, hvor det navngitte konseptet IKKE var fasit) |
| **(b)** | **JA** |
| | N100 (P4) |
|---|---|
| fasit-setningen ORDRETT (`"Eventuell kryssing … ÅDT > 4 000."`) | **1 gang** (proposer, forsøk 1) |
| `planskilt` / `ÅDT` / `4 000`-formet tall | 10 / 10 / 11 |
| **(a)** | **JA** |
| | N100 (P4) |
|---|---|
| kravnummer-formede tokens i svaret UTOVER `Krav 3.3.1—13` | **0** (regex `Krav\s+[\d.]+—\d+` finner ETT distinkt token på tvers av alle 6 svar) |
| tall i prosa som ikke er i det leverte | 0 (`4,000`/`4 000` er samme fasit-tall, engelsk tusenskille — § 5c-instrumentfeilen gjentatt, ikke en ny hallusinasjon) |
| konsept-id-referanser som ikke er levert | 0 |
| **(c)** | **0** |
**Kjent-negativ for instrumentet, kjørt FØR dommen:** samme spørring (`"Krav 3.3.1—13"` i teksten)
mot P2s `n100-free-records.json` (FØR fiksen, 6 svar) gir **0** — spørringen er ikke konfundert.
**Det strukturerte forslaget dikter fortsatt opp kostkoder** (`CS-GS1`, `planSkiltCrossing`,
`GRD-CRS-01` — ingen finnes i basen), av samme strukturelle grunn som § 5c: ingen baseline, og
`--derive-cost-baseline` ble bevisst IKKE brukt her (ordrens scope). Uendret funn, ikke en ny defekt.
**«Ferdig»-raden for N100, etter § 6-definisjonen:** (a) JA · (b) **JA** (var NEI i P1/P2) ·
(c) 0. **N100 = FERDIG.** Første gang en av de tre N-bundlene når «ferdig» i po sin hypoteseform —
P3s fiks var det som gjorde det nåbart, og denne kjøringen er beviset på at det ER nådd, ikke bare
at koden er riktig offline.
**Er (b) MODELL-dom, ikke po-dom:** feltet sto i DATA-avgrenseren (§ 11.1); at modellen faktisk
brukte det — siterte `req_number` ordrett i 6 av 6 svar, og bygde hypotesen på nøyaktig det
kravet fasiten peker på — er noe modellen gjorde, ikke noe po garanterte. Oppgaven prompten stiller
er fortsatt A-formens hardkodede `"Find a cost-saving measure for {project.id}."`
(`run.py:1243`, § 6 «Hvem eier hva») — ikke et spørsmål om Krav 3.3.1—13 spesifikt; at modellen
likevel fant og navnga akkurat det kravet, i et kutt på 8 av 446, er dømmekraft utenfor det po kan
ta æren for.
### 11.5 Kostnad
Samme listepris som § 5d (input NOK 0,003734/1K, output NOK 0,014936/1K):
| Kjøring | Prompter | Input | Output | NOK |
|---|---:|---:|---:|---:|
| N100 (P4, fri) | 6 | 15 409 | 747 | 0,069 |
| klientprobe | 1 | ~20 | ~5 | ~0,000 |
| **SUM** | **7** | **15 429** | **752** | **NOK 0,07** |
**NOK 0,07 — under ordrens tak på NOK 0,25. 429-svar: 0.**
### 11.6 Funn — rapportert, ikke fikset
9. **Ingen nytt funn som okf eier.** Utdraget bar begge feltene korrekt (§ 11.1); at modellen brukte
dem er en modell-egenskap, ikke en utdragsform-defekt. Ingen `coord-send` til llm-ingestion-okf.
10. **`--proposal-review` fikk ingen kandidat å vise** — checkeren avviste aldri, men heller ingen av
de fem proposer-forsøkene ble VALIDERT av den deterministiske validatoren (ingen baseline, se
§ 11.4s kostkode-funn), så reviewer-døra sto åpen uten noe å spørre om. Uendret struktur fra
P1/P2, ikke en ny defekt.