portfolio-optimiser/docs/2026-09-02-read-bundle-kontekstkostnad.md
Kjell Tore Guttormsen 9e44afeefb docs: name the mechanism behind the untouched-debate proof, and re-measure all three bases
The report's strongest evidence is that the debate's three context copies are
byte-identical before and after. It was established by comparing the per-prompt
proposer/checker token lists, NOT by the probe's carries flag -- which after the
change correctly reports no, because the listing's text is not in the debate's
prompts. A flag that flips for the right reason is not a proof of sameness; the
token counts are. Said once, where the claim is made.

And bygg's copy count was carried over from the middle-slice probe that the
section immediately below declares broken. All three bases are now re-measured
with the corrected ASCII probe; bygg confirms five copies and the same
exploration total. A cell sourced from an instrument you yourself retired is
what that paragraph exists to refuse.
2026-09-03 01:30:09 +02:00

158 lines
11 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.

# S2c / MAJOR-3 — `read_bundle`s kontekstkostnad: målingen
**Ordre:** `20260902T151931Z-250257273-from-.claude`. Bindende ordretekst: ordreutkast M3 i
[`docs/2026-09-02-misjonsreview-v2.md`](2026-09-02-misjonsreview-v2.md) § 7.
**Regelen ordren gir: MÅL FØRST, så ÉN søm.** Denne fila er tallene med nevner — skrevet og
committet FØR noe ble endret, og utvidet med ETTER-tabellen i sømmens egen commit.
## 0. Instrumentet, og hvorfor det er troverdig
Efemert, i `/tmp/claude-s2c/` — utenfor repoet, som forrige gang (`misjonsreview-v2` § 8): et
måleinstrument i `tests/` ville vært en beslutning om å beholde det, og ordren ba om en måling,
ikke en ny produksjonsflate. Tokens telles med `tiktoken`s `o200k_base`, kjørt med
`uv run --with tiktoken` (`tiktoken` er fortsatt IKKE en prosjekt-avhengighet).
**Kjent-positiv kontroll FØR noe ble målt** (Verifiseringsloven ansikt 4 — en spørring skal bevises
å KUNNE finne): instrumentet ble kjørt mot de tre eksempelbundlene og reproduserte commons' egne
publiserte fasittall for `okf.bundle_context` **eksakt** — 3 861 / 10 406 / 12 595. Et instrument
som ikke treffer en kjent fasit måler seg selv.
**Prompten måles som tekst + `function_call` + `function_result`, ALDRI `.text` alene.** Den
skriptede klientens egen `sink` samler kun `.text`, og en prompt hvis hele nyttelast er et
verktøyresultat måles da til noen få tegn. Den fella er dokumentert i `misjonsreview-v2` vedlegg A
og er unngått her ved en egen wrapper på `ScriptedChatClient._inner_get_response`.
**Kjøringen er ekte, ikke rekonstruert:** hver rad er én `run.main([... --explore ...])` gjennom
CLI-døra, med navigatøren skriptet som `list_bundles → read_bundle → tekst` (trinn-manuset fra økt
67, MAJOR-1 b). At navigatøren faktisk ÅPNET basen er lest av kjøringens eget artefakt
(`{run_id}-exploration.json`s `tool_calls`), ikke antatt.
**Ærlighetsgrense, uttalt:** kopi-telleren er en 160-tegns skive fra MIDTEN av `read_bundle`s
nyttelast (samme sonde som `misjonsreview-v2` § 4). Skiva finnes i dag i BEGGE de to strengene
kjøringen sender — utforskningens `function_result` OG debattens `okf.bundle_context` — fordi de er
den samme strengen. Kolonnene under skiller dem derfor på ROLLE, som er det eneste som skiller
dem: `manager`/`navigator`/`hypothesiser` er utforskningen (sømmen denne ordren rører),
`proposer`/`checker` er debatten (som ordren forbyr å røre).
## 1. FØR — hva `read_bundle` koster, og hvor mange prompts det rir med i
Per base, én CLI-kjøring med `--explore`:
| Base | `read_bundle`-nyttelast | Prompts totalt | Prompt-tokens totalt | Utforsknings-kopier | Tokens i dem | Debatt-kopier | Tokens i dem |
|---|---:|---:|---:|---:|---:|---:|---:|
| `bygg-energi-mikro` | 12 005 tegn / **3 861 tok** | 15 | 35 773 | **5** | 22 013 | 3 | 11 738 |
| `veglys-fv-soer` | 32 201 tegn / **10 406 tok** | 19 | 88 881 | **5** | 54 623 | 3 | 31 376 |
| `tunnel-hauglia` | 39 583 tegn / **12 595 tok** | 19 | 106 519 | **5** | 65 698 | 3 | 37 943 |
**De fem utforsknings-kopiene, navngitt** (tunnel-tall): navigatørens egen prompt etter kallet
(12 770) · managerens tre progress-ledger-/final-prompts (13 527 / 13 549 / 13 075) ·
hypotesiserens ene tur (12 777). Ett `read_bundle`-kall gjør hele basen til et `function_result`,
og det resultatet rir med i **hver senere prompt i samme samtale** — deltakerne deler
samtalehistorikk, så ingen av dem betaler for å be om det.
**Selve nyttelasten, som andel av alt:** 5 kopier × nyttelast = 19 305 / 52 030 / **62 975** tokens,
altså **54 % / 59 % / 59 %** av ALLE prompt-tokens i kjøringen. Debattens tre kopier (11 738 /
31 376 / 37 943 = 33 % / 35 % / 36 %) kommer fra `run.py`s `okf.bundle_context` og er en EGEN
beslutning (PM) — de er med i tabellen som nevner og som ETTER-kontroll, ikke som mål.
**Nevneren for «hvor mange prompts»:** 15 / 19 / 19 prompts i alt, hvorav 8 bærer konteksten.
Tallet 5 gjelder DETTE manuset (én navigatør-tur, én hypotesiser-tur, tre manager-prompts).
`misjonsreview-v2` § 4 målte 7× på bibliotek-armen med et annet manus (to hypotesiser-turer, fire
navigatør-prompts); begge er sanne om sitt eget manus, og ingen av dem er en påstand om hva en
LEVENDE manager gjør. Multiplikatoren er en egenskap ved samtalehistorikken, ikke ved manuset:
den er antall prompts etter kallet, og den er ≥ 1 uansett.
## 2. Hva basene faktisk inneholder — premisset for formen
| Base | Filer navigert | Konseptfiler | `type: verdict` | Rot-`index.md` (body) |
|---|---:|---:|---:|---:|
| `bygg-energi-mikro` | 6 | 4 | 1 | 1 884 tegn |
| `veglys-fv-soer` | 7 | 5 | 1 | 3 646 tegn |
| `tunnel-hauglia` | 7 | 5 | 1 | 4 763 tegn |
**Ett premiss felt før noe ble bygget på det:** «basens indeks er selve navigasjonsprosaen, så den
hører hjemme i `read_bundle`». Tunnelbasens rot-indeks er alene **4 763 tegn ≈ 1 400 o200k-tokens**
— altså nesten hele ordrens tak på 1 500 for HELE kallet. Å legge den inn ville brukt opp budsjettet
på et felt katalogen alt gir et bundet utdrag av (`_CATALOGUE_EXCERPT_CHARS`), og som fortsatt er
nøyaktig ett `read_file(id, "index.md")` unna. `read_bundle` bærer derfor konseptlista og ikke
indeksbodyen.
## 3. Hva prefiks-caching ville kjøpt — notert, IKKE bygget
Ordren ber om notatet, ikke mekanismen. Regnet av prompt-prefiksene i `misjonsreview-v2` § 4
(ikke av en fakturering — leverandørens faktiske cache-treff er UMÅLT): ≈ 60 % av debattens og
≈ 40 % av utforskningens prompt-tokens er prefiks-cachebart som koden står i dag, uten én
kodeendring, fordi prefiksene allerede er byte-stabile. Caching virker på KOPIENE; et
kontekstbudsjett virker på GRUNNTALLET de multipliserer. De to utelukker ikke hverandre, og
denne ordren gjør bare det siste.
## 4. Verifiseringslogg (FØR-halvdelen)
| # | Påstand | Kommando → resultat |
|---|---|---|
| 1 | Instrumentet treffer en kjent fasit | `uv run --with tiktoken python /tmp/claude-s2c/measure.py <repo>` → 3 861 / 10 406 / 12 595 = commons' publiserte tall |
| 2 | Kjøringen ÅPNET basen (ikke bare listet den) | `measure-exploration.json`s `tool_calls``list_bundles`, `read_bundle(<base>)` |
| 3 | Prompt-størrelse inkluderer verktøyresultatet | wrapper på `_inner_get_response` serialiserer `contents` (`function_call`/`function_result`), ikke `.text` |
| 4 | Nevner | 15 / 19 / 19 prompts per kjøring; 8 bærer konteksten i alle tre |
| 5 | Indeksbodyen alene sprenger nesten hele taket | `len(navigate_bundle(tunnel).index_summary)` → 4 763 tegn ≈ 1 400 tok |
---
## 5. ETTER — samme instrument, samme kommandoer, samme baser
`read_bundle` returnerer nå katalogformen: én oppføring per konseptdokument (`name`, `type`,
`title`, `chars`), med `read_file` som neste trinn.
| Base | Nyttelast før → etter | Kopier i utforsknings-prompts | Nyttelast × kopier | Utforsknings-prompts totalt | Hele kjøringen |
|---|---|---:|---|---:|---|
| `bygg-energi-mikro` | 3 861 → **163 tok** | 5 → 5 | 19 305 → **815** | 23 726 → **5 434** (77 %) | 35 773 → 17 481 (51 %) |
| `veglys-fv-soer` | 10 406 → **237 tok** | 5 → 5 | 52 030 → **1 185** | 56 314 → **5 742** (90 %) | 88 881 → 38 309 (57 %) |
| `tunnel-hauglia` | 12 595 → **259 tok** | 5 → 5 | 62 975 → **1 295** | 67 415 → **5 973** (91 %) | 106 519 → 45 077 (58 %) |
**Ordrens eget kriterium, verifisert direkte:** `read_bundle` over tunnelbasen er **748 tegn / 259
o200k-tokens** — under taket på 1 500. Målt forhold 2,89 tegn/token for denne norske markdownen;
det er dét som lar gaten bounde TEGN uten å gjette (se testens docstring, som uttaler avviket).
**«Ikke utløs»-beviset er en MÅLING, ikke en forsikring.** Ordren forbyr å røre `okf.bundle_context`,
nav-goldenene og `run.py`. Debattens tre kontekstkopier kommer fra nettopp den sømmen, og de er
**byte-identiske før og etter** i alle tre baser — 12 047 / 32 567 / 39 104 prompt-tokens, hver
enkelt prompt uendret. Det er et sterkere utsagn enn «jeg rørte den ikke». **Mekanismen, sagt én
gang:** identiteten er fastslått ved å sammenligne de per-prompt `proposer`/`checker`-tokentallene
FØR og ETTER, ikke ved sondens `carries`-flagg — flagget svarer på «bærer denne prompten
`read_bundle`s nyttelast», og etter endringen er svaret naturlig nei for debatten, siden lista ikke
finnes i debattens prompts. Et flagg som skifter av riktig grunn er ikke et bevis på uendrethet;
tokentallene er. I tillegg: hele suiten
grønn (**1 195 passed / 5 skipped**, mot 1 189/5 før — supersett, 0 fjernet), tre byte-fasiter OK,
og golden `demo-transcript.stdout` UENDRET (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`).
**Et fravær som var et instrumentfeil, ikke et faktum (Verifiseringsloven ansikt 4).** Den første
ETTER-kjøringen rapporterte **0 kopier** på veglys og tunnel. Det var ikke sant: sonden var en
160-tegns skive fra MIDTEN av nyttelasten, og den nye nyttelasten er kort nok til at midten treffer
norske tegn, som prompten serialiserer escaped (`å`) mens sonden holdt dem rå. Sonden ble byttet
til et konseptfilnavn (ASCII, ordrett i begge), og svaret ble 5 — samme tall som før endringen.
Et negativt resultat fra en ødelagt spørring er ikke en måling. **Alle tre basene er re-målt med
den rettede sonden** (ikke bare de to som viste feilen): en tabellcelle som stammet fra et
instrument man selv har erklært ødelagt, er nøyaktig dét dette avsnittet advarer mot.
## 6. Ærlighetsgrenser, uttalt
1. **Dette er ikke «59 % kostnad».** Det målte utsagnet er at `read_bundle`s EGET bidrag faller fra
5 × 12 595 til 5 × 259 tokens på tunnelbasen. En navigatør som deretter åpner *k* dokumenter
betaler *k* `read_file`-resultater, og en som åpner ALT betaler omtrent de samme bytene — bare
per kall. Gevinsten er at den betaler for det den valgte, og at hvert resultat rir fra SITT eget
kall og framover i stedet for at alt rir fra det første.
2. **At en LEVENDE modell velger BEDRE med en liste enn med hele konteksten er IKKE bevist.** Samme
klasse som structured-output-grensen: det som er bevist er formen og prisen, ikke dømmekraften.
3. **Multiplikatoren 5 gjelder dette manuset.** Se § 1.
4. **Debattens 3× står urørt** og er PM-ens beslutning, ikke denne ordrens.
5. **Prefiks-caching er notert (§ 3), ikke bygget** — ordren ba om notatet.
## 7. Verifiseringslogg (ETTER-halvdelen)
| # | Påstand | Kommando → resultat |
|---|---|---|
| 6 | Gaten er rød uten sømmen | 7 mutasjoner mot HELE suiten, alle røde: se CLAUDE.md-raden |
| 7 | Grønn kontroll | `uv run pytest -q` → 1 195 passed / 5 skipped (før: 1 189/5) |
| 8 | Golden uendret | `PYTHONIOENCODING=utf-8 uv run python -m portfolio_optimiser.simulation \| shasum``ea8c5347…` |
| 9 | Debatten urørt | ETTER-kjøringens `proposer`/`checker`-prompts identiske med FØR-kjøringens, per prompt |
| 10 | Lint + typer | `uv run ruff check .` → All checks passed; `uv run mypy src` → no issues, 35 files |