portfolio-optimiser/docs/2026-09-14-p16-stressrunde-1.md
Kjell Tore Guttormsen cfd9079a4a 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>
2026-09-14 12:47:09 +02:00

354 lines
19 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.

# 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…` |