portfolio-optimiser/docs/2026-09-02-read-bundle-kontekstkostnad.md
Kjell Tore Guttormsen ce7f687717 docs: read_bundle's context cost measured with a denominator, before any change
S2c / MAJOR-3, order 20260902T151931Z-250257273. The order's rule is MEASURE
FIRST, so the numbers land as their own commit before the seam is touched.

Instrument validated against a known positive before use: it reproduces
commons' own published bundle_context fasit exactly (3 861 / 10 406 / 12 595).
Prompts are measured as text + function_call + function_result -- .text alone
undercounts a prompt whose whole payload is a tool result.

Measured, per CLI --explore run: one read_bundle result rides in FIVE
exploration prompts (navigator 1, manager 3, hypothesiser 1), which is 54-59
percent of every prompt-token in the run. The debate's three copies come from
run.py's okf.bundle_context and are a separate decision; they are in the table
as denominator and as the after-control, never as the target.

One premise felled before building on it: the tunnel base's root index body is
4 763 chars alone, nearly the whole 1 500-token ceiling, so read_bundle carries
the concept listing and not the index body -- which the catalogue already
excerpts and read_file still returns whole.
2026-09-02 23:52:21 +02:00

97 lines
6.5 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 |