docs(p16): stressrunde 1 -- four paid runs, four verdicts, the good/bad/ugly

THE UGLY: r761 VALIDATED the falsification arm. a4-indeksregulering -- an approach rule U proves
the base cannot ground -- came back validated at 250 000 NOK with affected_items
[{code: "R761", unit_cost: 10000000}]. The model used the BASE'S OWN NAME as a cost code, and P7's
stage 0b admitted it because the rule is an exact SUBSTRING with no pattern and "R761" occurs
everywhere in a 6.5 MB R761 corpus. The validator (stage 0 skipped, un-anchored), stage 0b and the
checker (approve) all passed it. P7's own row names plain numbers as the one inert class; this adds
a second and worse one -- short, ubiquitous tokens, which unlike a number LOOK like a cost code.

THE BAD: not one of the 26 fasit concepts was opened, in 24 read_file calls across four runs. The
ladder works mechanically and misses professionally.

Also ugly: one directory listing is 27-113x the ceiling S7a-3 binds (n200's krav/N200 is 169 974
chars ~ 56 658 tokens) because the vegnormal hierarchy is FLAT, and it rides every turn -- three of
seven runs died on the token cap, and the deep-hierarchy base (r761) was the CHEAPEST.

Every ugly finding carries a named solution with an estimate. None is built -- the order forbids it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-14 12:47:09 +02:00
commit cfd9079a4a

View file

@ -0,0 +1,354 @@
# P16 — stressrunde 1: the good, the bad and the ugly
Ordre `20260914T091846Z-2971522127-from-.claude`, økt 120, 14.09.2026.
Fire betalte kjøringer mot n100-2023 / n500-2024 / n200-2024 / r761-2025, dømt av en ny
deterministisk dommer. Alle tall her er MÅLT i denne økten mot disk og mot artefaktene
kjøringene etterlot, ikke lest ut av en tidligere rapport.
**Kortversjonen.** Rammeverket kjører ende-til-ende mot fire ekte kunnskapsbaser og navigerer
dem riktig. Ingen av de fire kjøringene traff et eneste av de 26 fasit-konseptene. Tre av fire
avviste alt som ugrunnet; den fjerde **validerte falsifiseringsarmen** — tilnærmingen som ved
konstruksjon ikke kan forankres — og gjorde det ved å bruke basens eget navn som kostkode.
---
## 1. Hva som ble bygget først (DEL A)
`src/portfolio_optimiser/stress.py` — `score_context_set(context_dir, outbox_dir, run_id,
bundle_dir)`. Den leser KUN artefakter som alt finnes: `{run_id}[-{approach}]-proposal.json`,
`-outcome.json` og `{run_id}-debate.json`, pluss settets `mandate.json` / `fasit.json` /
`bundle.txt`. Ingen kjøring fikk et nytt felt.
### 1.1 Ordrens (a)-definisjon var vakuøs, og det ble målt før noe ble bygget på den
Ordren definerer *grounded* som «en `must_cite`-sti er ÅPNET **eller** SITERT». På S2c-stien
stempler `run_project` `citations = bundle_citations(bundle)`**én sitering per konseptfil**.
| Base | Kontekstfiler | Siteringer i stempelet | Fasit-stier «sitert» før noe modellkall |
|---|---|---|---|
| n100-2023 | 446 | 446 | **6 av 6** |
En dommer som fulgte ordren ordrett ville altså vært en gate som bare kan bli grønn — repoets egen
vakuøs-gate-klasse, inne i gaten som ble bygget for å fange den. Derfor: en **sitering** grunner
kun når siteringslista er SMALERE enn basen (et erklært pre-pass-kutt). Begge halvdeler rapporteres
uansett (`opened` / `cited` / `citation_scope`), så avviket er uttalt, aldri stille.
**(b) ble sjekket for SAMME vakuitet og var ren nok til at ordren står:** siteringenes `snippet`
er konsept-BODYER mens `ref`/`title` bor i FRONTMATTER — målt inneholder **0 av 446** n100-bodyer
strengen `Krav 4.1.2—1`. Se likevel § 6.2: målingen på r761 viser at snippet-armen er svak for
KORTE referanser, og `named_in_measure` er den halvdelen som tåler vekt.
### 1.2 Falsifiseringsarmen fikk en kjørbar form (A2)
`po` er ikke et oppslagsverktøy (D-1), så et «ubesvarbart spørsmål» hadde ingen kjørbar form: ingen
kjøresti konsumerte `fasit.json`s `unanswerable`-rader. Samme faktum rir nå en **fjerde bestilt
tilnærming** per sett (`a4-…`) hvis kostlinje basen ikke bærer grunnlaget for, og `fasit.json`
bærer `must_refuse` (`approach_id` / `anchors` / `rationale`) **i stedet for** `unanswerable` — én
form, aldri to kopier av ett faktum. Spørsmålene overlever i `rationale`, som ingenting nøkler på.
Regel U er URØRT og dens kjent-positiv er fortsatt rød.
### 1.3 Mutasjonstabell (DEL A)
Alle elleve røde på sin egen arm, grønn kontroll **1663 passed / 5 skipped**, golden
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` av INNHOLDET =
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f` — ikke git-blob-id-en).
| # | Mutasjon | Røde | Arm |
|---|---|---|---|
| M1 | helbase-siteringer grunner igjen | 2 | (b) + (a) |
| M2 | en åpnet sti grunner ikke | 4 | (a), (d), (f), (k) |
| M3 | (b) dropper snippet-armen | 1 | (e) |
| M4 | hallusinerte siteringsfiler ignoreres | 1 | (f) |
| M5 | hallusinerte koder ignoreres | 1 | (f) |
| M6 | gjettet lesesti forgifter ikke `ferdig` | 1 | (f) |
| M7 | tom utboks gir rent resultat | 1 | (h) |
| M8 | `not_evaluated`-rader utelates | 1 | (j) |
| M9 | kode-lekkasjearmen i `must_refuse` droppes | 1 | (g) |
| M10 | en tom base tolereres | 1 | (i) |
| M11 | Regel U-ens kjent-positiv (P14 M3) | 1 | context-sets |
---
## 2. Gratis-trinnet betalte seg tre ganger (DEL B2)
### FUNN 1 — den dokumenterte kommandoen kunne ikke kjøres
`STATE.md`, `docs/2026-09-12-p14-kontekstsett.md § 4.1` og ordren publiserer alle den samme
kommandoen, som ender `--max-rounds 8 --max-tokens 120000`. **MÅLT: `run.py` tok ingen av dem.**
Alle fire `--live-dry-run` nektet med `unrecognized arguments`. Verre: `main()` sendte **aldri**
`max_rounds`/`max_tokens` videre til `run_project`, så HVER CLI-kjøring noensinne var stille bundet
til `_DEFAULT_MAX_ROUNDS=3` / `_DEFAULT_MAX_TOKENS=100_000`, uten noen operatørflate for å heve
eller senke taket på noe man betalte for. Tre flater beskrev en dør som ikke fantes.
**Fikset før betaling** (B2s egen instruks), commit `5f94c92`: begge flagg DEFAULTER til nøyaktig
de historiske verdiene (utvidelse, aldri bryting), wiret til BEGGE dispatcher (`run_project` OG
`run_portfolio``main()` droppet dem der også) og nektet ved navn i report-modus. Seks armer,
alle røde før fiksen; fire mutasjoner.
### FUNN 2 — `--docs-dir` er påkrevd også når `--bundle-dir` er oppgitt
`run.py:2953` nekter enkeltprosjekt-modus uten `--docs-dir`, selv om `docs_dir` er ubrukt på
bundle-stien. Den dokumenterte kommandoen ville nektet av DEN grunnen også. READMEs egen form
(`--docs-dir <bundle> --bundle-dir <bundle>`) virker. **Målt, rapportert, IKKE fikset.**
### FUNN 3 — annonseringen løy i det øyeblikket flagget fantes
`announce` leste `_DEFAULT_*` direkte. Det var korrekt kun så lenge `main()` ikke kunne gjøre annet.
Målt på den første gratis-turen etter at flaggene landet: samme stdout sa
`Stops at: 3 rounds / 100000 tokens` **to linjer over** `max_rounds=8, max_tokens=120000`.
Annonseringen er det ENE som printes før første betalte kall, og hele jobben dens er å si hva
kjøringen vil gjøre — Fase-3-klassen, innført av nettopp det flagget som annonseres.
**Fikset** (commit `a61a3cc`); M16 (les konstantene igjen) → 1 rød, den armen alene.
### Prediksjonen fra gratis-trinnet, notert FØR betaling
Alle fire dry-runs stoppet rent (rc 0) og sa det samme:
> `Cost baseline: NONE in the bundle — this run is un-anchored`
> `Grounding offer: N distinct identifier(s) and **0 cost line(s)** … every candidate it invents
> will be refused as ungrounded`
| Sett | Identifikatorer | Kostlinjer | Tegn |
|---|---|---|---|
| gate-nordvik (n100) | 435 | **0** | 437 129 |
| tunnel-hauglia (n500) | 272 | **0** | 389 103 |
| fv412 (n200) | 982 | **0** | 1 441 501 |
| kontrakt-sorasen (r761) | **3** | **0** | 6 561 594 |
Predikert utfall: 0 validerte. Se § 4 for hva som faktisk skjedde.
---
## 3. Kjøringene (DEL B3)
Parametrene ble endret underveis, hver gang med årsaken navngitt FØR ny kjøring (B3s regel).
| # | Sett | Base | Runder | Tak | rc | Observert | Veggtid | Maks RSS |
|---|---|---|---|---|---|---|---|---|
| 1 | gate-nordvik | n100 | 8 | 120 000 | **1** | 150 999 | 24,7 s | — |
| 2 | gate-nordvik | n100 | 8 | 500 000 | **1** | 530 881 | 114,0 s | 149 MB |
| 3 | gate-nordvik | n100 | **3** | 600 000 | 0 | 509 310 | 140,1 s | 149 MB |
| 4 | tunnel-hauglia | n500 | 3 | 600 000 | 0 | 255 418 | 73,5 s | 144 MB |
| 5 | fv412 | n200 | 3 | 600 000 | **1** | 604 159 | 121,1 s | 168 MB |
| 6 | fv412 | n200 | **2** | 900 000 | 0 | 523 633 | 176,0 s | 171 MB |
| 7 | kontrakt-sorasen | r761 | 3 | 900 000 | 0 | 104 905 | 218,7 s | **298 MB** |
Totalt betalt: **2 679 305 tokens** over sju kjøringer, hvorav tre døde på taket.
`gpt-4-1-mini`, profile `azure`, `PACE_SECONDS=2`, 0× 429.
**Kostnad — IKKE VERIFISERT.** Ingen faktura er lest. Med listepris for `gpt-4.1-mini`
($0,40/M inn, $1,60/M ut) og et antatt 90/10-forhold inn/ut lander 2,68 M tokens på
**USD 1,4 ≈ NOK 15**. Inn/ut-forholdet er ikke målt — `token_usage` er en totalsum — så tallet
er et anslag med en uttalt antakelse, ikke en måling.
### R761 mot P13s referanse
P13 målte NAVIGASJON av r761 til **7,1 s / 131 MB** (`docs/2026-09-12-p13-okf-pin-r761.md`).
En hel LEVENDE kjøring koster **218,7 s / 298 MB** — 31× tiden og 2,3× minnet. De to måler ikke
det samme (navigasjon mot navigasjon + fire debattrunder + generering + validering), og
sammenligningen sier derfor bare at navigasjonen er en liten del av begge.
---
## 4. Dommen (DEL B4)
`python -m portfolio_optimiser.stress contexts/<sett> --outbox-dir … --run-id …`, fire
`*-verdict.json` skrevet.
| Sett | (a) grunnet | (b) navngitt | (c) hallusinasjoner | a4 / `must_refuse` | ferdig |
|---|---|---|---|---|---|
| gate-nordvik (n100) | **0 / 4** | 0 / 4 | 7 | **PASS** | nei |
| tunnel-hauglia (n500) | **0 / 4** | 0 / 4 | 5 | **PASS** | nei |
| fv412 (n200) | **0 / 4** | 0 / 4 | 6 | **PASS** | nei |
| kontrakt-sorasen (r761) | **0 / 4** | 3 / 4 | 9 | **FAIL** | nei |
Nevnere, alltid: tool_calls 18 / 16 / 12 / 18 · siteringer 1 784 / 810 / 2 266 / 11 024 ·
approach-rader 4 / 3 / 2 / 4 · konsepter i basen 446 / 270 / 1 133 / 2 756.
`validated` totalt: **2 av 20 rader**, begge i r761 — a4 (falsifiseringsarmen) og own-proposal.
---
## 5. THE GOOD
1. **Stigen virker, og den virker på ekte korpus.** Alle fire kjøringer gikk
`list_bundles → read_bundle → read_dir → read_file` uten å bli instruert i det. r761 gikk rett
ned i `R761/6``R761/6/65` — altså brukte den hierarkiet S7a-3 bygde, og betalte **104 905**
tokens for den største basen, mindre enn n100 brukte på den minste.
2. **Falsifisererne biter.** 18 av 20 rader ble avvist, og alle med P7s stadium 0b, med
identifikatoren navngitt i klartekst. Hver eneste avvisningsgrunn var sann.
3. **P8s forankringstilbud predikerte utfallet gratis.** «0 kostlinjer» ble sagt før betaling og
holdt i alle fire kjøringer.
4. **Artefaktene bar beviset.** Dommeren trengte ingen ny instrumentering: `debate.json`,
`proposal.json` og `outcome.json` svarte på alt — også for de tre kjøringene som døde på taket,
der `debate.json` og `runconfig.json` fortsatt lå der.
5. **Alle fire dry-runs stoppet rent** før første modellkall, og gjorde det etter at flaggfunnet
var fikset.
---
## 6. THE BAD
### 6.1 Ikke ett fasit-konsept ble åpnet — 0 av 26, i 24 `read_file`-kall
| Sett | `read_file` | distinkte | fasit ønsket | overlapp |
|---|---|---|---|---|
| gate-nordvik | 10 | 9 | 6 | **0** |
| tunnel-hauglia | 8 | 5 | 8 | **0** |
| fv412 | 4 | 4 | 6 | **0** |
| kontrakt-sorasen | 10 | 6 | 6 | **0** |
Det er hovedresultatet av stressrunden. Navigasjonen fungerer mekanisk og treffer ikke faglig.
Grunnen er lesbar i listingen: konseptfilene heter `id-<uuid>.md`, og `read_dir` gir `name`,
`type`, `title`, `chars`. På n100 må modellen velge blant **446** slike titler i ett svar.
**LØSNING (navngitt, ikke bygget):** gi `directory_listing` en valgfri, *bundet* filtrering på
`req_number`/`title`-delstreng — ett nytt argument på `read_dir`, samme rung, samme gate. Anslag:
én økt, ~6 armer, ingen ny flate i `run.py`. Alternativ b: la `read_bundle` returnere basens egen
`index.md`-body når den er prosa (n-basene har ingen), som ikke hjelper her — derfor a.
### 6.2 (b)s snippet-arm er svak for KORTE referanser — et funn om min egen dommer
r761 ga `named = 3 / 4`, men `named_in_measure` var sann for kun **1** av dem. De to andre ble
båret av snippet-armen, og på r761 er referansene prosessnumre som `12.1` — som forekommer i
mange bodyer i et helbase-sitert stempel. Målingen på n100 (0 av 446 bodyer bærer `Krav 4.1.2—1`)
generaliserer altså ikke.
**LØSNING:** la snippet-armen kun telle når `citation_scope == "narrowed"`, nøyaktig som (a).
Anslag: én linje + to armer. Ikke bygget i denne økten — dette er PMs beslutning, ikke min, fordi
det strammer ordrens egen definisjon en gang til.
### 6.3 En gjettet sti gir modellen en ugjennomsiktig feil
Målt 12 gjettede `read_file`-stier over de fire kjøringene (3 / 2 / 3 / 4). En sti som ikke finnes
reiser `FileNotFoundError`, som funn-99 eksplisitt holder **utenfor** `_RETURNABLE_REFUSALS`, så
modellen får MAFs `"Error: Function failed."` uten grunn. På n200 fyrte MAFs egen
`Maximum consecutive function call errors reached (3). Stopping further function calls for this
request.` — altså ble verktøybruken avskåret midt i en forespørsel.
**Korrigering av min egen første diagnose:** jeg antok først at dette drev budsjettdøden. Det gjorde
det ikke — mønsteret er 2×*hvert* kall (to agenter, samme vandring), ikke en retry-loop.
**LØSNING:** utvid funn-99s returnerte-nekt-form til `read_file` på en sti som ikke finnes
(`REFUSED (BundlePathNotFound): …`), og skriv om den eksisterende armen
`test_a_nonexistent_sibling_is_still_an_os_error`. Anslag: én økt, ~8 armer, og den eksisterende
gaten må skrives om, ikke svekkes.
---
## 7. THE UGLY
### 7.1 Falsifiseringsarmen ble VALIDERT på r761 — og basens eget navn var kostkoden
```
a4-indeksregulering VALIDATED 250000 NOK
measure: "Kutt ved gunstigere indeksregulering av kontraktssummen"
affected_items: [{"code": "R761", "quantity": 1.0, "unit_cost": 10000000.0}]
checker_verdict: approve
p10/p50/p90: 2 876 396 / 3 004 495 / 3 126 462
```
a4 er ved konstruksjon ugrunnbar: rule U bekrefter at `indeksregulering`, `nåverdi`,
`kostnadsestimat`, `markedspris` og `prisstigning` alle er FRAVÆRENDE fra r761. Likevel passerte
den:
* **den deterministiske validatoren** — stadium 0 var hoppet over (uforankret base),
* **P7s stadium 0b** — fordi regelen er `code in grounding`, en eksakt DELSTRENG uten mønster, og
`"R761"` forekommer overalt i et 6,5 MB R761-korpus,
* **checkeren**`approve`.
Modellen fant altså ikke på en kostkode som *lignet* på en ekte: den brukte **basens navn**.
`unit_cost` 10 000 000 er ren oppfinnelse, `p10 ≈ p50 ≈ p90` fordi `assumptions` er tom (degenerert
Monte Carlo), og hele kjeden rapporterte `validated`.
P7-raden uttaler selv at regelen «har intet mønster» og at «rene tall er den ene inerte klassen».
Denne målingen legger til en andre inert klasse: **korte, allestedsnærværende tokens** — og den er
verre, fordi et rent tall ikke *ser* ut som en kostkode mens `R761` gjør det.
**LØSNING (navngitt, ikke bygget), rangert:**
* **(a) minstelengde + treffnevner.** Krev at en identifikator er ≥ N tegn OG at den forekommer
i færre enn M distinkte konsepter — et token som står i 2 756 av 2 756 dokumenter identifiserer
ingenting. Anslag: én økt, ~10 armer, ingen ny flate. Nevneren finnes alt i `delivered`.
* **(b) forankre mot det kjøringen ÅPNET, ikke mot hele korpuset.** `debate_tool_calls` er alt
kaller-eid og skrevet; en identifikator som kun står i dokumenter kjøringen aldri åpnet er ikke
noe forslaget «bygde på». Anslag: én økt, ~8 armer. Strammere enn (a), men endrer hva stadium 0b
betyr — PMs beslutning.
* **(c) `--require-cost-baseline` som default for mandatkjøringer mot uforankrede baser.** Flagget
finnes (F4) og ville nektet alle sju kjøringene før første kall. Det løser ikke 0b, men gjør
«validated» umulig å utstede uforankret. Anslag: en partisjons-rad + en beslutning.
Ingen av dem er bygget her — ordren forbyr det eksplisitt.
### 7.2 Én katalogvisning er opptil 113× taket S7a-3 satte, og den rir hver tur
| Base | Konsepter | Nivåer | Rot | Verste listing | ~tokens | × 1 500-tegns-taket |
|---|---|---|---|---|---|---|
| n100-2023 | 446 | 4 | 118 ch | `krav/N100` 69 250 ch | 23 083 | **46×** |
| n500-2024 | 270 | 4 | 118 ch | `krav/N500` 39 853 ch | 13 284 | 27× |
| n200-2024 | 1 133 | 4 | 119 ch | `krav/N200` 169 974 ch | **56 658** | **113×** |
| r761-2025 | 2 756 | 2 758 | 171 ch | `R761` 110 874 ch | 36 958 | 74× |
S7a-3 binder 1 500 tegn per listing over **de basene gaten kan se** (de tre eksempelbasene), og
uttaler selv at K2s verste nivå er 6 073 tegn. De fire vegnormalbasene er 6,528× dét igjen, fordi
hierarkiet er FLATT: `krav/ → krav/N100/ → 446 filer`. Rung 2 hjelper ikke når korpuset er to nivåer
dypt med hundrevis av blader.
Konsekvensen er ikke teoretisk: **tre av sju kjøringer døde på tokentaket**, og n100 — den minste
basen — trengte over 500 000 tokens ved 8 runder. Den dypt hierarkiske basen (r761) var den
billigste.
**LØSNING (navngitt, ikke bygget):** paginer `read_dir``offset`/`limit` med et bundet vindu og
et `total`-felt, så listingen koster O(vindu) og modellen kan be om mer. Det er S7a-3s egen form
ett hakk videre, og annonsering av avkorting er alt løst der (`index_truncated`). Anslag: én økt,
~8 armer, gaten binder vinduet i TESTEN som i dag. Sekundært: la `okf build` i vegnormal-okf
sub-dele `krav/N100` etter kapittel — men det er en produsent-endring i et annet repo, altså en
coord-sak, ikke vår.
### 7.3 Ordrens eget budsjett var 4,4× for lite, og ingen visste det
`--max-tokens 120000` er publisert tre steder. Den minste basen brukte **509 310**. At ingen visste
det er selve funnet: flaggene fantes ikke, så tallet hadde aldri vært brukt av noe.
---
## 8. Ærlighetsgrenser (uttalt)
* **Én kjøring per sett. Ingen varians er målt.** En andre kjøring av samme sett kan navigere
annerledes; ingenting her sier hvor stabilt utfallet er.
* **a4-armen beviser at ingen validert rad hviler på den — ikke at modellen forsto hvorfor.**
Manuell lesning av de fire a4-utfallene (merket MANUELT): i **n100** skrev modellen ekspertens
etikett ordrett som `measure` og fant på koden `gangfelt_opphoyd` med `unit_cost` 150 000 × 10 —
ingen setning om at basen mangler grunnlag; 0b avviste den. I **n500** og **n200** ble a4 aldri
evaluert (budsjettet var brukt opp før raden), så det finnes ingen tekst å lese. I **r761** skrev
modellen igjen etiketten ordrett og fant på `R761`. **Ingen av de to a4-radene som faktisk ble
evaluert uttalte at basen ikke bærer grunnlaget.** Det er en annen og svakere observasjon enn
«armen bestod» — og for to av fire sett er selv den observasjonen ikke gjort.
* **Parametrene varierer mellom settene** (3/600k, 3/600k, 2/900k, 3/900k). n200 kjørte på 2 runder
mot de andres 3. Tallene i § 4 er derfor ikke strengt sammenlignbare på tvers.
* **Kostnaden er et anslag fra listepris,** ikke lest fra faktura, og inn/ut-forholdet er antatt.
* **Prosjektene er oppdiktet.** Hvert `fasit.json` sier i sitt eget `honesty`-felt hva som er
konstruert: navn, lengder, ÅDT og alle beløp, samt a4 og dens kostkode. Kravene i `must_cite` er
lest ordrett ut av basenes egen frontmatter.
* **`<project_id>` kunne ikke leses av basen.** Ordren ba om «det basens egen IR-projeksjon
erklærer». MÅLT: ingen av de fire basene har `validator-input.json`, så `load_optional_ir_projection`
returnerer `None` (S7b søm 1) og id-en er kaller-oppgitt. Settets eget navn ble brukt.
* **Dommeren leser HVA kjøringen åpnet og siterte, aldri om modellen FORSTO det.**
---
## 9. Verifiseringslogg
| Påstand | Hvordan målt |
|---|---|
| 446/446 siteringer, 6/6 fasit-stier sitert før modellkall | `bundle_citations` mot navigert n100 |
| 0 av 446 n100-bodyer bærer `Krav 4.1.2—1` | telling over `bundle.context_files` |
| `--max-rounds`/`--max-tokens` fantes ikke | `run.py --help` + fire nektede dry-runs |
| `main()` sendte dem aldri videre | lesing av begge `run_project`-kallsteder + `run_portfolio`-kallstedet |
| annonseringen sa 3/100000 mot 8/120000 | samme stdout, gratis dry-run |
| listing-størrelser | `okf.directory_listing` over hvert nivå i hver base |
| token/veggtid/RSS | `/usr/bin/time -l` + `provenance.token_usage` i artefaktene |
| 0 overlapp fasit vs åpnede | `debate.json` `read_file`-stier ∩ `fasit.must_cite` |
| a4 validert med `code: R761` | `*-a4-indeksregulering-proposal.json` + `-outcome.json` |
| rule U holder for alle a4-anchors | `tests/test_context_sets_loadbearing.py::test_c_rule_u…`, grønn |
| suite / golden | `uv run pytest -q` = 1670/5 · `shasum -a 1` = `ea8c534…` |