portfolio-optimiser/docs/2026-09-03-syretest-s7a-k2.md
Kjell Tore Guttormsen 98e648310c docs(s7a): SS 5s "deler aarsak" var en paastand, ikke en maaling - og maalingen felte tallet
Rapporten paasto at de tre stoerste postene "deler aarsak: alle tre baerer
navigatoerens read_file". Det var aldri maalt. Ved aa maale det kom TO ting fram.

1. Aarsaken stemmer, og er naa dekomponert: verktoeyRESULTATENE utgjoer 87-93 % av
   hver av de tre stoerste promptene (6 527 tok), og enkeltvis er det read_file
   4 043 (62 %), read_bundle 2 354 (36 %), list_bundles 130 (2 %). read_file er
   4 043 baade i prompten og maalt isolert, saa det som rir med er hele
   returverdien - ikke en forkortet form.

2. MITT EGET INSTRUMENT VAR FEIL. Sonden summerte Message.text OG hvert
   Content.text, men Message.text ER sammenkjedingen av tekst-innholdet - altsaa
   ble tekstdelen talt to ganger. Totalen 45 643 er forkastet; riktig tall er
   40 320 over 13 prompter (manager 61 %, navigator 23 %, hypotesiser 16 %), og
   de tre stoerste er 7 532 / 7 468 / 7 037 = 55 %, ikke 8 572 / 8 444 / 7 582.
   Feilen var IKKE synlig i totalen. Den ble synlig foerst da sammensetningen ble
   brutt ned - som er hele grunnen til at en total ingen har dekomponert er en
   total ingen har kontrollert. Begge instrumentfeilene staar naa i SS 0.

Ogsaa: SS 1c skilte ikke maalt faktum fra min tolkning av hva 1 500-tegns-taket
"er". Faktumet staar (K2s read_bundle er 6 244 tegn mot en gate skrevet for
3-dokuments baser); lesningen av hva taket er ment aa binde tilhoerer den som
eier gaten.

Ingen produksjonskode. Doc-gatene gronne (25 passed).

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

388 lines
20 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.

# Syretest S7a — K2 → kunnskapsbase → utforskning → revise
**Dato:** 2026-09-03 · **Ordre:** `20260902T233842Z-2955810469-from-.claude` ·
**Grunnlag:** `~/.claude/docs/helhetlig-plan-okf-og-portfolio-optimiser.md` § 0, § 5, § 9.1
Dette er en **måling**. Ingen produksjonskode er endret; hele måleoppsettet ligger utenfor treet
(`scratchpad/s7a/`, utracket) og hver tabell under er produsert av en kommando som står i teksten.
**Ingen validert besparelse er forventet eller funnet** — K2 er pre-award og prisskjemaet er
uutfylt (plan § 9.1). Det som måles er navigasjonen, dialogen, revise-sløyfa, falsifiseringen mot
proveniens og tokenprofilen.
---
## 0. Instrumentet, validert mot en kjent positiv FØR bruk
Alle tokentall er `tiktoken` `o200k_base` over den fulle prompt-blobben (tekst + `function_call` +
`function_result`), kjørt med `uv run --with tiktoken``tiktoken` er fortsatt ikke en
prosjektavhengighet.
| Kjent-positiv sjekk | Publisert fasit | Målt nå |
|---|---|---|
| `read_bundle` over `tunnel-hauglia` | 259 tok (`docs/2026-09-02-read-bundle-kontekstkostnad.md`) | **259 tok** |
| `bundle_context` over `tunnel-hauglia` | 12 595 tok (samme) | **12 595 tok** |
Instrumentet reproduserer begge eksakt. Først etter det er K2-tallene under lest som tall.
**To instrumentfeil ble fanget underveis, og begge er skrevet ned fordi de er samme klasse som
misjonsreview v2 rapporterte.** (i) Første prompt-sonde leste `repr(Content)` og fikk
`<agent_framework._types.Content object at 0x…>` for **hver** melding — en prompt som så tom ut
mens den bar 108 000 tegn. Sonden henter nå `text`/`result`/`arguments` ut av hvert `Content`.
(ii) Den rettede sonden summerte så `Message.text` **og** hvert `Content.text`, altså tekstdelen
to ganger. Den feilen ble ikke synlig i totalen — den ble synlig først da § 5s sammensetning ble
målt, og det er hele grunnen til at sammensetningen ble målt i det hele tatt: en total ingen har
brutt ned er en total ingen har kontrollert.
---
## 1. Kunnskapsbasen slik den faktisk er levert
Bygget av `llm-ingestion-okf` 02.09 (`tools/okf_corpus_run.py`, steg 17) til `/tmp/k2-trinn1-bundle`;
kopiert til `scratchpad/s7a/k2-bundle` og målt der (`/tmp` er flyktig).
| Egenskap | Målt |
|---|---|
| Konseptfiler (`context_files`) | **39** |
| Filer totalt (`files`) | 40 (39 + `index.md`) |
| `type: verdict`-lag | 0 |
| Ufulgte kryss-lenker (`skipped`) | **0** |
| `bundle_id` / opphav | `k2-bundle` / **`mount-derived`** |
| `index.md`-body | 6 044 tegn, ingen frontmatter, ren lenkeliste |
| `log.md` (OKF § 7) | **finnes ikke** |
**Ordrens `N=43, merged 39` er delvis verifiserbart.** `merged = 39` er målt direkte (39
konseptfiler). `N = 43` er produsentens tall og kan **ikke** verifiseres fra bundelen: den bærer
ingen `log.md` og ingen manifest-oppføring som teller inn-dokumentene. Det står som produsentens
påstand, ikke som en måling gjort her.
### `adjudication`-nøkkelen: fraværende, med nevner
```
cd scratchpad/s7a/k2-bundle && grep -l '^adjudication:' *.md | wc -l # 0
grep -l '^type:' *.md | wc -l # 39 (kjent-positiv kontroll)
```
Frontmatter-folketellingen over alle 39, med sifre i nøkkelnavnet tatt med:
| Nøkkel | Antall (av 39) |
|---|---|
| `type`, `title`, `source_file`, `source_sha256`, `ingested_at`, `generated`, `derived` | 39 |
| `references` | 20 |
| **`adjudication`** | **0** |
| **`verified`** | **0** |
| **`sources`** | **0** |
**Ordrens premiss om «den nye profilen (`adjudication`)» holder ikke mot det som ble levert.**
Bundelen bærer ingen `adjudication`-nøkkel og ingen provenienss-nøkkel i det hele tatt. Det er et
faktum om bundelen, ikke om leseren — se § 4, der gaten bevises å kunne si ja.
### Navigasjonskostnad, per kall
| Kall | Tegn | o200k-tokens |
|---|---|---|
| `list_bundles` (1 base) | 352 | **127** |
| `read_bundle("k2-bundle")` | 6 244 | **2 312** (39 oppføringer) |
| `read_file` × 39, sum | — | **684 236** |
| `read_file`, median / min / maks | — | 6 709 / 541 / **121 462** |
| `bundle_context` (formen før MAJOR-3) | — | **682 303** |
Tre ting følger av tabellen.
**(a) MAJOR-3 er det som gjør K2 navigerbar i det hele tatt.** Den gamle `read_bundle`-kroppen
returnerte `bundle_context` — 682 303 tokens for denne basen, altså mer enn noe kontekstvindu
tar, og den ville ridd med i hver senere prompt. Den nye formen koster 2 312. Reduksjonen er
**99,7 %**, og forskjellen her er ikke «billigere», den er «mulig mot umulig».
**(b) Katalogtaket holder på et 39-dokuments korpus.** `list_bundles` koster 127 tokens, og
`index_truncated` kommer tilbake `true` — de 6 044 tegnene i indeksen blir til et 200-tegns
utdrag. Det bekrefter det `docs/2026-08-26-katalogkostnaden.md` forutså for maskin-importerte
baser: utdraget er nesten innholdsløst (første lenkelinje), men det er *bundet*, og hele indeksen
er ett `read_file(id, "index.md")` unna.
**(c) `read_bundle` er O(dokumenter), og på 39 dokumenter er det 2 312 tokens.** Det målte
faktumet: `tests/test_read_bundle_cost_loadbearing.py` setter taket til 1 500 **tegn** mot
3-dokuments baser, og K2s `read_bundle` er 6 244 tegn. Det er ikke et brudd — gaten måler sine
egne baser — men tallet er verdt å legge fram for den som eier gaten. Hva taket *er ment* å
binde, er deres avgjørelse, ikke min lesning her.
**(d) To dokumenter sprenger § 5s tokenkriterium hvis de leses hele.** Kriteriet er «ingen
enkeltpost > 100 000 tok uten en navngitt grunn». To av 39 passerer det som `read_file`:
| Dokument | Tokens |
|---|---|
| `inbox-del-ii-bilag-3-1-milj-teknisk-rapport-med-tiltaksplan.md` | 121 462 |
| `inbox-del-ii-bilag-9-1-avtale-som-tiltransporteres-norconsult-as.md` | 119 909 |
Grunnen er navngitt her: begge er enkeltdokumenter fra korpuset som ikke er delt opp i konsepter.
De ble **ikke** lest i kjøringene under — men en navigatør som velger dem, betaler det.
---
## 2. Dialogen: én offline utforskning med verktøyspor
```bash
PYTHONIOENCODING=utf-8 uv run python -m portfolio_optimiser.run K2-STANGE \
--docs-dir scratchpad/s7a/k2-bundle --bundle-dir scratchpad/s7a/k2-bundle \
--explore "Finn kostnadsbesparelser i Stange skole-anbudet" \
--explore-config scratchpad/s7a/explore-config.json \
--scripted-replies scratchpad/s7a/scripted-replies.json \
--outbox-dir scratchpad/s7a/outbox --run-id s7a-offline
```
MAJOR-1s trinn-manus er brukt: navigatøren får `{"call": …}`-steg, manageren én tekst per stadium
(fakta, plan, tre ledgere, sluttsvar). Uten manager-manuset avslutter kjøringen etter én runde uten
å gi noen deltaker en tur — det er MAJOR-1s egen måling, og den er grunnen til at manageren er
skriptet i stadier.
`{run_id}-exploration.json`:
| Felt | Målt |
|---|---|
| `completed` | `true` |
| `stop` | `null` |
| `tool_calls` | **`list_bundles``read_bundle(k2-bundle)``read_file(k2-bundle)`**, i kallrekkefølge |
| `rounds` | 3, alle med `speaker_known: true` og en ekte deltaker som `next_speaker` |
| `approaches` (i mandatet) | 1 — `hypothesis-1 — SENTINEL-ALFA prisskjema-post 82` |
**Plan § 5s dialog-rad er oppfylt og målt:** ≥ 1 `read_bundle`/`read_file`-kall er logget i
artefaktet. Kjøringen er ikke-vakuøs i den forstand MAJOR-1 definerte: tre runder, kjent taler
hver gang, tre verktøykall, ett mandat.
### Kjøringen ender likevel rc = 1, og det er S7bs skarpeste funn
```
run refused: IR projection not found in bundle: 'validator-input.json'
```
Utforskningen fullførte, mandatet ble kunngjort — og så nektet pipelinen. Bundelen bærer ingen
`validator-input.json`, så `run_project`s bundle-arm kan ikke slå opp prosjektet. Artefaktet
overlevde fordi det skrives fra en `finally`, som er nøyaktig hva den konstruksjonen finnes for.
Dette er et **mer spesifikt** S7b-gap enn «K2 har ingen priser»: selv med priser ville kjøringen
stoppet her. Ingesterings-profilen emitterer ingen IR-projeksjon. **Kryss-repo-funn for
`llm-ingestion-okf`.**
### Levende kjøring: ikke kjørt
Ordrens escape-klausul fyrer. Målt, ikke antatt:
| Variabel / fil | Tilstand |
|---|---|
| `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` | tom/usatt |
| `FOUNDRY_PROJECT_ENDPOINT` | tom/usatt |
| `PORTFOLIO_MODEL_MAP` | tom/usatt |
| `OPENAI_API_KEY` / `AZURE_OPENAI_API_KEY` | tom/usatt |
| `data/model_map.json` | finnes ikke |
`az account show` svarer riktignok med en konto, men Fase 4e målte at AZURE-armen slår opp et
deployment-navn i modell-mappet **før** noen klient bygges — uten mappet er en innlogget CLI
ikke nok. **Levende kjøring ikke gjort, mangler nøkkel og modell-map.** Ingen konflikt med
STATE-ens `IKKE RØR AZURE` oppsto.
---
## 3. Revise-sløyfa
```bash
printf 'revise SENTINEL-BRAVO: …\napprove\n' | uv run python -m portfolio_optimiser.run K2-STANGE \
… --explore-config scratchpad/s7a/explore-config-review.json --plan-review \
--outbox-dir scratchpad/s7a/outbox-review --run-id s7a-review
```
| Egenskap | Målt |
|---|---|
| Plan-reviewer stilt | **2** (`#1` besvart `revise`, `#2` besvart `approve`) |
| Manageren replanla mellom dem | ja — plan `#1` 1 115 tegn, plan `#2` 1 132 tegn, ulik tekst |
| Tilbakemelding i artefaktet | **ordrett**, `SENTINEL-BRAVO: …` |
| `"object at"` i artefakt eller stdout | **0 forekomster** (BLOCKER-1 bekreftet lukket på begge dørene) |
| `current_progress` | `"(no progress ledger yet)"` — ikke ordet `None` (PM-tillegg 4 bekreftet lukket) |
### Mandat-diffen er TOM, og grunnen er instrumentet — ikke systemet
Mandatet bygges av hypotesiserens `HYPOTHESIS:`-merkede turer. Under et **konstant** manus sier
hypotesiseren det samme uansett hva revisjonen sa, så før/etter er identiske ved konstruksjon. Å
rapportere den tomme diffen som et funn ville vært å lese et instrumentresultat som et faktum om
verden. Det som *kan* måles offline er **kjeden**: hvilke prompter bærer operatørens
tilbakemelding? Med to ASCII-sentineler (norske tegn serialiseres escaped i prompten og ville gitt
falske nuller — samme felle misjonsreview v2 rapporterte):
| Ledd | Bærer | Målt |
|---|---|---|
| 1. Operatørens revise-tekst → managerens prompt | `SENTINEL-BRAVO` | **6 av 8** manager-kall |
| 1b. Samme tekst → deltakernes prompt | `SENTINEL-BRAVO` | **0 av 4** navigatør, **0 av 1** hypotesiser |
| 2. Managerens post-revise-output → hypotesiserens prompt | `SENTINEL-CHARLIE` i `instruction_or_question` | **1 av 1** |
| 2b. Samme, via plan-TEKSTEN | revidert plan-streng | **0 av 1** hypotesiser, **0 av 4** navigatør |
| 3. Endret hypotese ut av deltakeren | — | **ikke målbar offline** |
**Mekanismen, uttalt:** operatørens tilbakemelding når **manageren**, aldri deltakerne direkte.
Den når deltakerne bare gjennom det manageren selv skriver i neste rundes
`instruction_or_question` — og det er bevist diskriminerende: sentinelen manageren skrev *etter*
revisjonen står i hypotesiserens prompt, mens plan-teksten (verken den opprinnelige eller den
reviderte) gjør det ikke.
Sløyfa er altså hel, men leddet som faktisk former hypotesen er managerens **omskriving** av
tilbakemeldingen. Under et skriptet manus er den omskrivingen min, ikke en modells. **At en revise
endrer hva hypotesiseren foreslår er derfor ikke bevist her, og kan ikke bevises offline** — det
krever en levende manager. Det er S7bs andre åpne krav.
---
## 4. Falsifisering mot proveniens (K5-terskelen)
Ordren ber om et K2-konsept med `adjudication: proposed`. **Det finnes ikke: 0 av 39.** Terskelen
er derfor kjørt på det korpuset faktisk bærer, mot en påstand om den ene prisede posten:
> «Post 82 *Prosjektering (tiltransport av prosjekterende)* i Bilag 7 Prisskjema er priset til
> 5 647 500 NOK og er derfor en kandidat for kostnadskutt.»
| | K2 slik den er levert | Kjent-positiv kontroll (patchet kopi) |
|---|---|---|
| `adjudication_for` | `unknown` | `adjudicated` |
| `(state, reason, items_seen)` | `(absent, None, 0)` | `(present, None, 1)` |
| `trust_tier` | `None` | `human-reviewed` |
| `admits_falsification` | **`False`** | **`True`** |
Over alle 39, med nevner: `adjudication` `unknown` 39/39 · `evidence.state` `absent` 39/39 ·
`tier` `None` 39/39 · `admits_falsification` `False` **39/39**.
**Kontrollen er bærende.** Uten den er «alle 39 diskontert» ikke til å skille fra «funksjonen
diskonterer alltid» — repoets vakuøs-gate-klasse. Kontrollen er en kopi under
`scratchpad/s7a/k2-control/` der ett konsept har fått
`verified: { by: human:…, at: … }` + `adjudication: adjudicated`; gaten flipper `False → True`.
Den leverte bundelen er aldri skrevet til.
**Dommen, slik skillen krever den:** `undecided`, aldri `survived`. Diskonteringen sies eksplisitt
med trippelen, ordrett fra `evidence_notice`:
```
provenance absent in …/inbox-del-ii-bilag-7-prisskjema.md; items_seen=0
```
Og med skillens egen regel: en gjendrivelse som ikke navngir en gjendriver er ingen gjendrivelse —
her finnes ingen kilde som klarerer terskelen, så «vi kan ikke bekrefte» må aldri skrives om til
«påstanden er gal».
---
## 5. Tokenprofil
Ekte `usage` finnes ikke: `ScriptedChatClient` rapporterer 8 tokens per svar, så artefaktets
`tokens_spent` (88 offline, 104 med review) er **syntetisk** og sier ingenting om kostnad. Profilen
under er derfor instrument-målt over de faktiske prompt-blobbene i revise-kjøringen.
> **Instrumentfeil funnet og rettet før dette tallet ble stående.** Første sonde summerte
> `Message.text` **og** deretter hvert `Content.text` — men `Message.text` *er* sammenkjedingen av
> meldingens tekst-innhold, så tekstdelen ble talt to ganger. Feilen kom for dagen først da
> sammensetningen under ble målt, ikke da totalen ble lest. Tallene i denne seksjonen er fra den
> rettede sonden (`m5_prompt_composition.py`); det forkastede tallet var 45 643.
**13 prompter, 40 320 o200k-tokens.**
| Rolle | Prompter | Tokens | Andel |
|---|---|---|---|
| manager | 8 | 24 465 | 61 % |
| navigator | 4 | 9 251 | 23 % |
| hypothesiser | 1 | 6 604 | 16 % |
De tre største postene — til sammen 22 037 tokens, **55 % av hele kjøringen**:
| # | Post | Tokens |
|---|---|---|
| 1 | manager-kall 6 (ledger etter at navigatøren hadde lest prisskjemaet) | 7 532 |
| 2 | manager-kall 5 (samme, runden før) | 7 468 |
| 3 | manager-kall 7 (sluttsvar) | 7 037 |
**Hva de består av — målt, ikke antatt.** Hver av de tre er brutt ned per innholdstype:
| Del | manager #6 | manager #5 | manager #7 |
|---|---|---|---|
| verktøy**resultater** | 6 527 (87 %) | 6 527 (87 %) | 6 527 (93 %) |
| tekst (fakta, plan, ledger-historikk) | 965 (13 %) | 901 (12 %) | 470 (7 %) |
| verktøy**argumenter** | 40 (0 %) | 40 (0 %) | 40 (0 %) |
Og de 6 527 tokenene enkeltvis, i den rekkefølgen navigatøren hentet dem:
| Verktøyresultat | Tokens i prompten | Andel av de 6 527 |
|---|---|---|
| `list_bundles` | 130 | 2 % |
| `read_bundle` | 2 354 | 36 % |
| **`read_file` (Bilag 7 Prisskjema)** | **4 043** | **62 %** |
`read_file`-resultatet er altså 4 043 tokens i prompten og 4 043 tokens målt isolert — samme tall,
så det som rir med er hele returverdien, ikke en forkortet form. (`read_bundle` er 2 354 i prompten
mot 2 312 isolert; differansen er JSON-innrammingen meldingen legger rundt.)
**Mekanismen er den samme som MAJOR-3 målte, ett trinn ned på stigen.** Utforskningens deltakere
deler **én** samtalehistorikk, så ett `read_file` betales av hver senere prompt i kjøringen. Her
utgjør de tre verktøyresultatene 8793 % av hver av de tre største postene, og prisskjemaet alene
er 62 % av dem. Ingen enkeltpost passerer § 5s 100 000-tak i denne kjøringen; de to dokumentene som
*ville* gjort det (§ 1d) ble ikke åpnet.
---
## 6. Hva som mangler for S7b (validert besparelse)
Tre krav, alle målt her, i den rekkefølgen de blokkerer:
**1. Kunnskapsbasen mangler en IR-projeksjon.** `run_project`s bundle-arm nekter med
`IR projection not found in bundle: 'validator-input.json'` (§ 2). Uten den stopper kjøringen etter
utforskningen uansett hva prisskjemaet inneholder. Ingesterings-profilen emitterer den ikke.
**Kryss-repo-krav mot `llm-ingestion-okf`** — ingen brief i dette repoet kan lukke det.
**2. Prisskjemaet er uutfylt, og MAJOR-4 nekter — korrekt.** Kjørt mot det ekte K2-prisskjemaet:
```
CostBaselineDerivationError: no cost table found in bundle 'scratchpad/s7a/k2-bundle':
no concept file carries a markdown table whose header names all three of
['code', 'quantity', 'unit_cost']
```
Kontroll: samme funksjon mot `tests/fixtures/k2-prisskjema-SYNTETISK` gir **3 kostlinjer**, så
avlederen virker — nekten er en egenskap ved K2, ikke ved koden.
Formen er målt presist: K2s Bilag 7 er **ett** ark (`## Prissammenstilling`), en pandoc SIMPLE table
med kolonneparet `Post … SUM`. Ordet `Enhetspris` forekommer **0 ganger** i hele filen, og
`Mengde`/`Timepris` står bare i prosa — de tilhørende cellene er tomme, fordi totalentreprenøren
skal fylle dem. Den **eneste** prisede raden i hele skjemaet er
`82 Prosjektering (tiltransport av prosjekterende) = 5 647 500`.
S7b trenger altså den syntetisk utfylte kopien plan § 9.1 anbefaler. MAJOR-4 har allerede formen:
`okf.derive_cost_baseline` (eier B) og fixturparet
`tests/fixtures/k2-prisskjema-SYNTETISK` / `…-uprisert-SYNTETISK`, med
`--derive-cost-baseline` som CLI-døra.
**3. `adjudication` mangler i hele korpuset (0/39).** K5-terskelen diskonterer derfor hvert konsept
(§ 4). En validert besparelse kan formelt sett fortsatt produseres — terskelen gjelder
falsifiseringsdommen, ikke validatoren — men den kan ikke *hvile* på noe i basen. **Kryss-repo-krav
mot `llm-ingestion-okf`**, samme klasse som punkt 1.
Ett krav er **ikke** blokkerende og bør ikke telles med: at en revise endrer hypotesen (§ 3). Det
krever en levende manager, ikke en endring i noe av dette.
---
## 7. Verifiseringslogg
| # | Påstand | Kommando → resultat |
|---|---|---|
| 1 | Instrumentet reproduserer publisert fasit | `uv run --with tiktoken python scratchpad/s7a/m1_navigation.py` → tunnel `read_bundle` 259, `bundle_context` 12 595 |
| 2 | 39 konsepter, 0 `adjudication`, kjent-positiv 39 `type` | `grep -l '^adjudication:' *.md \| wc -l` → 0; `grep -l '^type:'` → 39 |
| 3 | `N=43` ikke verifiserbart | ingen `log.md` i bundelen (`Path(base)/'log.md').exists()``False`) |
| 4 | Navigasjonstall | samme skript → § 1-tabellen |
| 5 | Verktøysporet | `outbox/s7a-offline-exploration.json``tool_calls` = 3, i rekkefølge |
| 6 | Pipelinen nekter på manglende IR | stderr → `IR projection not found in bundle` |
| 7 | Levende ikke kjørbar | alle fem env-variabler tomme; `data/model_map.json` finnes ikke |
| 8 | Revise → replan → nytt spørsmål | `outbox-review/s7a-review-exploration.json` → 2 `plan_reviews`, ulik `plan` |
| 9 | Ingen `object at` | `grep -c 'object at'` over artefakter + stdout → 0 |
| 10 | Kjeden, tre ledd | `uv run --with tiktoken python scratchpad/s7a/m3_revise_chain.py` → § 3-tabellen |
| 11 | K5 diskriminerer | `uv run python scratchpad/s7a/m4b_falsification.py``False` vs `True` |
| 12 | MAJOR-4 nekter mot ekte K2, godtar syntetisk | `uv run python scratchpad/s7a/m6_derive.py` → feilmelding vs 3 kostlinjer |
| 13 | Tokenprofil + sammensetning | `uv run --with tiktoken python scratchpad/s7a/m5_prompt_composition.py` → § 5-tabellene |
**Ikke verifisert her:** at en levende modell kaller verktøyene eller velger godt (samme grense som
structured-output-raden); at `N=43` stemmer; at leverandørens caching treffer prefiksene; K2s
Prisskjema slik en utfylt versjon faktisk ville sett ut (fixturen er syntetisk).
**Premisser som ble felt av målingen, og som er rapportert i stedet for omgått:** ordrens
«ny profil med `adjudication`» (0/39), ordrens punkt 4 (ingen `proposed`-konsept finnes), og
antakelsen om at mandat-diffen ville vise revisjonens virkning (tom ved konstruksjon).