# Misjonsreview v2 — bruksscenarioet målt, ikke bygget (2026-09-02) **Ordre:** `20260902T113744Z-1245330375-from-.claude` (PM-plan § 4 / S2). **Økt 72.** **Reviewer:** Fable 5.1 (high, uten advisor) — derfor kommer hvert tall under fra en kommando som står i § 8, og ingen tall er sitert fra `STATE.md`. **Mandat: MÅL, IKKE BYGG.** Ingen fil under `src/` eller `tests/` er endret av denne økten (`git status --short` før og etter: identisk — kun den ucommittede F15-diffen `tests/test_maf_version_guard.py` 28+/15−, som ordren ba meg se og ikke røre, pluss to fremmede utrackede stier). **Baseline:** [syretesten 25.08](2026-08-25-syretest-vei-ab.md) · [katalogkostnaden 26.08](2026-08-26-katalogkostnaden.md) · [misjonsreview 25.08](2026-08-25-fable-misjonsreview.md). Golden `demo-transcript.stdout` **byte-uendret** (`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`, målt `shasum` ved øktstart). Suiten: **1085 tester samlet** (`pytest --co`), 120 testfiler. **Bruksscenarioet (operatørens ord 02.09):** «basert på et helt sett av prosjektdokumenter + OKF bundles + MCP servere og en konkret oppgave skal portfolio-optimiser gjennom dialog og lengre analyser komme fram til forslag til besparelser i prosjektet, og med tilbakemeldinger komme med forbedrede forslag. Det er også viktig at portfolio-optimiser er så effektiv som mulig i forhold til bruken av tokens.» **Eksponerings-grense (arvet, holdt):** repoet pusher til `open/`. Rapporten bærer tall, stier, kommandoer og egne probestrenger. Ingen bundle-innhold er gjengitt; fra produsentens golden-bundle siteres kun frontmatter-NØKLER. --- ## 0. Sammendrag — én linje per målepunkt | # | Spørsmål | Svar | Kjernetall | |---|---|---|---| | M1 | Åpner navigatøren en base etter `444fea7`? | **Fra CLI-døra: nei, og det KAN den ikke** (konstant tekst per rolle kan ikke emittere et verktøykall). **Fra biblioteket: ja, alle fire verktøy, på alle fire baser.** | CLI: 0 verktøykall, 0 approaches, 1 runde × 4 baser. Bibliotek: `list_bundles`/`read_bundle`/`read_file`/`quick_validate` 1/1/1/1 × 4 baser, 1 rutet approach hver | | M2 | Blir FORSLAGET bedre av `--plan-review revise`, eller bare planen? | **Bare planen — og planen operatøren viser er ULESELIG:** `` på terminalen, i `{run_id}-exploration.json` og i den parkerte `{run_id}-plan-review.json` | Feedback nådde 5 manager-prompts, 0 hypotesiser-prompts; mandat identisk i begge armer; 3 av 4 CLI-artefakter byte-identiske | | M3 | Hvor lander feedback på et LEVERT forslag? | **I NESTE kjøring** (innboks → `VerdictStore` → hypotese-prompt), bevist med kontroll. **Ingen vei til «forbedret forslag i samme kjøring» for et menneske** — kun validatorens egen avvisning sløyfes tilbake | Run B: ekspertteksten i proposerens prompt; kontroll uten innboks: 0. In-run: 1 refinement, 4 proposer-kall | | M4 | Tokenprofil per fase | **Bundle-konteksten re-sendes 3× i debatten og 7× i utforskningen = 73–93 % av alle prompt-tokens.** Ledgeren offline er 8 tok/kall (skriptet) — ekte usage finnes kun fra 14.08 (15 306) | Tunnel: debatt 40 605 tok (93 % kontekst), utforskning 103 649 tok (85 %); tre største poster navngitt i § 4 | | M5 | Klar for K2? | **Nei — og det største hullet er ikke `adjudication`:** ingen kode lager `validator-input.json`/`cost-baseline.json` fra et ingestert korpus, og uten dem nekter pipelinen | Produsentens segmenterte v0.2-golden navigeres (3 filer, 0 hopp), `adjudication` leses som streng, 0 konsumenter i `src/` | | M6 | Vises MCP-kallet i provenance? Egress-linjen? | **Ja, begge — målt mot en EKTE stdio-subprosess** (første gang på kjørestien; B4-testen bruker en stand-in) | `Contacts: prisregister (lookup_unit_price)` · `external_calls=[{prisregister, lookup_unit_price}]` i resultat OG outbox · serverens svar i neste prompt | **Rangert:** 1 BLOCKER · 4 MAJOR · 4 MINOR · 3 NICE (§ 7). BLOCKER-en er ny og felles for F4 (økt 63) og U12 (økt 64): begge dørene stiller mennesket et spørsmål det ikke kan lese, og de fire assertene som pinner planteksten (tre testfiler) er grønne fordi de bare krever `plan != ""`. --- ## 1. M1 — `--explore` etter `444fea7` **Oppsett.** Fire baser: `shared/examples/{bygg-energi-mikro, veglys-fv-soer, tunnel-hauglia}` + `~/repos/vegnormal-okf/build/B-n200-2024-gren-1-1-importert` (grenbasen katalogmålingen 26.08 brukte; 10 filer). Kontrakt: `max_rounds=6, max_tokens=100000, max_stall_count=2, max_reset_count=1, max_plan_revisions=0, enable_plan_review=false`. Instrumentet (§ 8, vedlegg A) teller hvert verktøykall ved å wrappe `FunctionTool.func` ETTER konstruksjon — verktøylisten agentene får er uendret — og hvert modellkall per rolle. **Arm A — operatørdøra** (`run.main` med `--explore --explore-config --scripted-replies`, én konstant streng per rolle, som er den ENESTE offline-formen CLI-en tilbyr): | Base | rc | Modellkall (manager/proposer/checker) | Verktøykall | Artefakt: runder / `quick_validations` / `tokens_spent` | Notis | |---|---:|---|---:|---|---| | bygg-energi-mikro | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | «concluded after 1 round(s); **0 approach(es)**» | | veglys-fv-soer | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme | | tunnel-hauglia | 0 | 8 (4/3/1) | **0** | 1 / 0 / 32 | samme | | B-n200-2024-gren-1-1 (PROJECT_ID=PROBE) | 1 | 4 (4/0/0) | **0** | 1 / 0 / 32 | utforskningen FULLFØRER, så `run refused: IR projection not found in bundle: 'validator-input.json'`; `m1a-exploration.json` skrevet fra `finally` ✅ | To ting er målt her, og de er ulike: (i) `444fea7` lukket krasjen — rc er 0/1 med ren nekt, ingen traceback (4/4); (ii) sløyfa er **vakuøs ved konstruksjon** på denne døra: en `ScriptedChatClient` returnerer tekst og kan aldri emittere et `function_call`, så navigatøren KAN ikke åpne en base, og manageren — som svarer én konstant ledger — sier seg ferdig etter én runde. Dette er nøyaktig det 25.08-rapporten forutså og `444fea7`s commit-melding selv sa («fortsatt delvis vakuøs»). Re-målt: sant. **Artefaktet kan ikke svare på M1s spørsmål.** `{run_id}-exploration.json` har nøklene `completed, plan_reviews, quick_validations, rounds, run_id, stop, tokens_spent` — navigatørens tre verktøy har ingen sink og etterlater ingen rad. Bare `quick_validate` spores (`ExplorationTrace.quick_validations`, `explore.py:277`). «Åpnet navigatøren en base?» er i dag ubesvarbart fra artefaktet, uansett klient. **Arm B — bibliotekdøra med en klient som EMITTERER verktøykall** (`RecordingClient`, vedlegg A; navigatør-manus: `list_bundles` → `read_bundle(id)` → `read_file(id, "index.md")` → tekst; hypotesiser-manus: `quick_validate(id, IR-json)` → `HYPOTHESIS:`-linje; manager: stadienøklet med tre ledgere navigator→hypothesiser→satisfied): | Base | Verktøykall (`list_bundles`/`read_bundle`/`read_file`/`quick_validate`) | Modellkall (mgr/nav/hyp) | Runder | Approaches (label, `bundle_id`) | `quick_validate`-dom | Navigatørens prompt-tokens per kall | |---|---|---|---:|---|---|---| | bygg | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (`probe-direction-B`, `bygg-energi-mikro`) | validated, **anchored=false**, P10/P50/P90 68 543 / 95 444 / 121 057 | 3 → 144 → 4 031 → 4 783 | | veglys | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `veglys-fv-soer`) | validated, anchored=true | 3 → 122 → 10 553 → 11 869 | | tunnel | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `tunnel-hauglia`) | validated, anchored=true | 3 → 148 → 12 767 → 14 429 | | B-n200-gren-1-1 | **1/1/1/1** | 12 (6/4/2) | 3 | 1 (…, `B-n200-2024-gren-1-1-importert`) | validated, anchored=false, **P10 = P50 = P90 = 300** | 3 → 137 → 2 476 → 3 050 | Katalogresultatet (`index_excerpt`) står i navigatørens NESTE prompt i 4/4 baser (målt på innholdet, ikke på `.text`). Sømmen bærer altså et verktøykall ende til ende — også mot en regelverksbase uten prosjekt. **Det er første gang dette er målt på utforskningsstien;** 25.08 og økt 56 kunne bare kalle verktøykroppene direkte. **MINOR-funn på veien:** `quick_validate` sier `validated` med P10 = P50 = P90 når kandidaten mangler `assumptions` — alle 512 MC-samples identiske, den stokastiske falsifisereren er inert, og dommen flagger det ikke. Det er samme defektklasse CLAUDE.md beskriver for `response_format`-skjemaet, nå på det rådgivende verktøyet (kandidaten var min egen probe uten bånd, men en levende hypotesiser vil også utelate bånd). ## 2. M2 — `--plan-review revise`: planen eller forslaget? **Bibliotek-armen med DISKRIMINATOR.** Skriptet manager svarer én plan uten feedback og en ANNEN plan når feedback-nøkkelen `NIGHT-SETBACK-7731` står i prompten; hypotesiseren svarer `night-setback` når nøkkelen står i DENS prompt, ellers `led-retrofit`. Da er mandatet forskjellig hvis og bare hvis feedbacken nådde hypotesiseren. | Arm | Reviews (index → beslutning) | Manager-kall | Feedback-nøkkel i prompts (rolle: antall) | Hypotesiserens prompt | Mandat | |---|---|---:|---|---|---| | approve | 0 → approve | 5 | — | 19 tegn (`shape one direction`) + 375 tegn instruksjoner | `[led-retrofit]` | | revise, approve | 0 → revise (feedback ordrett), **1 → approve** | 7 | **manager: 5** (replan ×2, ledger ×2, final) · **hypothesiser: 0** | 19 tegn | `[led-retrofit]` | Manageren replanla (2 ekstra kall, indeks 1 finnes — F4s diskriminator holder). Men **planteksten kringkastes aldri til deltakerne**: hypotesiseren får KUN managerens `instruction_or_question` (19 tegn), så den eneste veien fra feedback til forslag går gjennom en LEVENDE managers neste instruksjon. Offline kan det hoppet ikke måles — det er en ærlighetsgrense, ikke en defekt. Det som ER målt: med skriptet manager er mandatet identisk, altså forslaget identisk. **CLI-armen (ekte stdin, ekte artefakter, `--outbox-dir`):** begge rc 0. | Artefakt | approve vs revise | |---|---| | `m2-runconfig.json` | identisk | | `m2-own-proposal-proposal.json` | identisk | | `m2-own-proposal-outcome.json` | identisk | | `m2-exploration.json` | **forskjellig** — kun `plan_reviews` | **BLOCKER-1 — planen mennesket skal godkjenne er en objekt-repr.** Terminalen skrev: ``` PLAN REVIEW #1 --- the plan the exploration would run --- --- progress so far --- None Answer "approve" to sign it off, or "revise ": ``` Samme streng står i `m2-exploration.json` → `plan_reviews[*].plan`, og i den PARKERTE `park1-plan-review.json` → `plan` (U12-døra, målt med `--checkpoint-dir`): eksperten som skal svare dager senere får `''`. **Rotårsak, lest i kilden:** `MagenticPlanReviewRequest.plan` er typet `Message` (`_magentic.py:846`); `explore.py:1395/1404/1418/1478` gjør `str(review.plan)`, og `Message` har ingen `__str__` (målt: `type(m).__str__ is object.__str__` → `True`; `m.text` gir planteksten). Hjelperen som gjør det riktig finnes alt i samme fil: `_plan_text` (`explore.py:966`), brukt for span-eventene. **Hvorfor gaten er grønn (fire asserts, tre filer):** `test_plan_review_cli_door_loadbearing.py:161` asserterer `plan != ""`, `:180` asserterer `reviews[0]["plan"][:40] in out` — repr-strengen står jo i stdout; `test_async_plan_review_loadbearing.py:408` asserterer truthiness; `test_explore_loadbearing.py:564` `!= ""`. Repoets vakuøs-gate-klasse, **tolvte gang**, og denne gangen på begge HITL-dørene samtidig. En planreview ingen kan lese er en review ingen kan svare på; F4s og U12s målbilde «be om svar, bruke svarene» er dermed ikke nåbart for et menneske i dag. ## 3. M3 — feedback på et LEVERT forslag Kommandoene er `run_project`-kall med instrumentet (vedlegg A); alle fire skritt på `bygg-energi-mikro`. | Skritt | Kommando/handling | Målt | |---|---|---| | 1. Kjøring A uten ekspert | `run_project(..., outbox_dir=…, run_id="m3a")`, ingen `verdict_input` | `result.verdict is None` ✅ · `verdict_key = ee44c58e0c0d258a` · `m3a-outcome.json.verdict_id = ee44c58e0c0d258a` · proposer 3 kall, checker 1 | | 2. Eksperten svarer | `capture_verdict(_features_of(outcome.proposal), "rejected", "EKSPERT-MARKOR-7731: …")` → `write_verdict(inbox, v)` | `v.id == verdict_key` ✅ → fila `ee44c58e0c0d258a.json` | | 3. Kjøring B, FERSK store | `run_project(..., verdict_dir=inbox, store=VerdictStore([]))` | markøren står i **proposerens** prompt (3 kall) · checker: nei | | 3b. Kontroll | samme uten `verdict_dir` | markør i **0** prompts | | 4. In-run maskin-feedback | proposer overdriver (270 000) til «REJECTED by the deterministic validator» står i prompten | `refinements = ["claimed saving 270000 exceeds P90 feasible 121057"]`, proposer **4** kall, utfall `ValidatedProposal` | **Hvor den lander i dag:** `run.py:644-647` (`load_verdicts_from_dir` → `store.add`) FØR Steg-1-folden → `generate._build_messages(context=…)` i NESTE kjøring. Outboxen bærer nøkkelen (`RunResult.verdict_key`, F2) så en ukommentert kjøring er dømbar — bevist over. **Finnes en vei til «forbedret forslag i SAMME kjøring»?** For maskinen: ja — validatorens `Rejection.reason` → `_build_messages(prior_rejection=…)` (`generate.py:320-325`), bundet av `max_attempts=3` + `meter.tick_round`. For et MENNESKE: **nei.** `grep -n "prior_" generate.py` gir kun `prior_rejection`; checker-kritikken sløyfes ikke tilbake (uttalt grense siden Steg 5); eneste menneske-søm i sløyfa er `plan_reviewer`, som fyrer FØR hypotesene finnes; hosting `_response_payload` (`hosting.py:195-203`) legger ut `verdict_id` og stopper. Bruksscenarioets «med tilbakemeldinger komme med forbedrede forslag» er derfor en to-kjørings-sløyfe med dager mellom, aldri en dialog om det forslaget som ligger på bordet. Minste søm og vokter: MAJOR-2 (§ 7). ## 4. M4 — tokenprofil per fase **Instrument, med ærlighetsgrense først.** `BudgetMiddleware` leser `usage_details.total_token_count` (`budget.py:246`), og den skriptede klienten rapporterer **8 tokens per svar** (`tokens_per_reply=8`) — ledger-summene under (56–96) er derfor KALLTELLINGER × 8, ikke forbruk. Eneste ekte usage repoet har er den levende kjøringen 14.08: **15 306 tokens** (`docs/2026-08-14-…:221`), tolv runder med parse-feil, ingen utforskning. Profilen under er derfor **o200k_base-tokens av alt modellen VILLE fått sendt** (tekst + `function_call` + `function_result`), målt per kall med instrumentet fra § 8 — samme instrument som reproduserte commons' fasittall 25.08 (3 861 / 12 595 / 10 406 for de tre eksempelbundlene; `bundle_context` re-målt i dag til nøyaktig de tallene). **Debatt + generering (CLI-armen, M1 A):** | Base | Sum prompt-tok | debatt(proposer) ×2 | debatt(checker) ×1 | utforskning(manager) ×4 | generering ×1 | Kontekst re-sendt | |---|---:|---:|---:|---:|---:|---| | bygg (ctx 3 861) | 14 283 | 7 868 (55 %) | 3 986 (28 %) | 2 243 (16 %) | 186 (1 %) | 3× = **81 %** | | veglys (ctx 10 406) | 34 001 | 20 984 (62 %) | 10 556 (31 %) | 2 243 (7 %) | 218 (1 %) | 3× = **92 %** | | tunnel (ctx 12 595) | 40 605 | 25 376 (62 %) | 12 759 (31 %) | 2 243 (6 %) | 227 (1 %) | 3× = **93 %** | **Utforskning med verktøykall (bibliotek-armen, M1 B):** | Base | Sum prompt-tok | manager-ledger ×3 | hypothesiser ×2 | navigator ×4 | manager-final ×1 | plan+facts ×2 | Kontekst re-sendt | |---|---:|---:|---:|---:|---:|---:|---| | bygg | 36 919 | 12 064 (33 %) | 9 786 (27 %) | 8 961 (24 %) | 5 292 (14 %) | 816 (2 %) | 7× = **73 %** | | veglys | 86 013 | 26 262 (31 %) | 23 984 (28 %) | 22 547 (26 %) | 12 404 (14 %) | 816 (1 %) | 7× = **85 %** | | tunnel | 103 649 | 31 394 (30 %) | 29 116 (28 %) | 27 347 (26 %) | 14 976 (14 %) | 816 (1 %) | 7× = **85 %** | | B-n200-gren (ctx 2 307) | 24 761 | 8 532 (34 %) | 6 254 (25 %) | 5 666 (23 %) | 3 493 (14 %) | 816 (3 %) | 7× = **65 %** | «Kontekst re-sendt» = antall prompts som inneholder en 160-tegns skive fra midten av `okf.bundle_context(base)` × kontekstens tokens, delt på summen. Én `read_bundle` gjør hele konteksten til et `function_result`, og det resultatet rir med i **hver** senere prompt: navigatørens egne (2), begge hypotesiserens (2, fordi de deler samtalehistorikk), og alle managerens ledger-/final-prompts (3). Hele utforskningen koster **8,2 × kontekststørrelsen** (tunnel: 103 649 / 12 595), debatten **3,2 ×**. **De tre største postene, navngitt:** 1. **Debattens tre turer bærer hele `bundle_context` hver** — 81–93 % av en CLI-kjørings prompt-tokens (proposer ×2 + checker ×1). Kilde: `workflow.py` GroupChat, konteksten i deltakernes melding, ikke i `instructions` (målt `instructions_chars` 61/307). 2. **Managerens progress-ledger-prompts bærer hele samtalen inkl. verktøyresultater** — 3 × 5 766 (bygg) / 3 × 15 450 (tunnel) tokens, 30–34 % av utforskningen. MAF-Magentic-atferd (`_magentic.py`), ikke vår kode. 3. **Hypotesiseren arver navigatørens verktøyresultater** — 2 × 4 795 / 2 × 14 441 tokens, 25–28 %. **Hva caching og kontekstbudsjett ville kjøpt — regnet fra prompt-prefiksene, ikke antatt:** | Par | Felles prefiks (tok) | Av | Lesning | |---|---:|---|---| | debatt proposer #1 vs #2 (bygg / tunnel) | **3 877 / 12 612** | 3 877 / 12 612 | hele første tur er et re-sendt prefiks (100 %) | | debatt proposer #1 vs checker | 3 877 / 12 612 | 3 986 / 12 759 | 97–99 % delt | | manager ledger #1 vs #2 | 215 | 751 / 5 547 (bygg) | før første verktøyresultat deles nesten ingenting | | manager ledger #2 vs #3 (bygg / tunnel) | **5 010 / 14 656** | 5 766 / 15 450 | 87–95 % delt | | hypothesiser #1 vs #2 | 4 795 / 14 441 | 9 786 / 29 116 | 49–50 % | Anslag, per base tunnel: **leverandør-prefiks-caching** (uendret kode, bare byte-stabile prefikser, som allerede er tilfellet) kunne treffe ≈ 2 av 3 kontekstkopier i debatten (≈ 25 200 av 40 605 = 62 %) og ≈ 2 av 3 ledger-kopier i utforskningen (≈ 29 300 av 103 649 = 28 %); hypotesiserens andre kall ≈ 14 400 (14 %). Samlet ≈ **40 % av utforskningen og ≈ 60 % av debatten** er prefiks-cachebart i dag. **Kontekstbudsjett** virker på multiplikatorens grunntall: et tak på `read_bundle` av samme form som katalogtaket (`_CATALOGUE_EXCERPT_CHARS`) — f.eks. konseptliste + `read_file` per konsept i stedet for hele `bundle_context` — kutter alle 7 kopiene samtidig; ved tunnel er hver kopi 12 595 tokens. **Ikke målt her:** hva en levende manager faktisk kaller og hvor mange runder den bruker; tallene over er én navigatør-tur + én hypotesiser-tur. ## 5. M5 — klar for K2? **Leseprøve mot produsentens nyeste form** (`~/repos/llm-ingestion-okf/examples/ ingest-golden-segmented-okf-v0-2/expected-bundle`, den eneste committede bundelen med `adjudication`-nøkkelen; produsent-HEAD `62b6192`, tag `v0.5.0a2`+104). Konsumentens funksjoner, kalt direkte: | Funksjon | Resultat | Lesning | |---|---|---| | `okf.navigate_bundle` | 3 kontekstfiler, 0 verdicts, **0 hopp**; nestede `krav/index.md` og `krav/1-1/index.md` fulgt | Navigasjonen tåler den segmenterte formen ✅ | | `okf.parse_frontmatter(konsept)` | nøkler `adjudication, bundle_id, generated, ingested_at, parent, segment_id, source_file, source_offset, source_sha256, title, type`; `adjudication → 'proposed'`, `source_offset → '[94, 176]'` (streng) | Nøkkelen er LESBAR som streng; ingen tolker den | | `okf.parse_frontmatter(index.md)` | `{okf_version: 0.2, bundle_id: b-golden-segmented-okf-v0-2}` | Rot-`bundle_id` er der; konsumenten bruker basenavnet (`explore.py:666`, `run.py:1512`) | | `okf.bundle_context` | 132 tokens; `adjudication: proposed` forekommer **1** gang — som index-LABEL-prosa | Tilstanden lekker inn som tekst, ikke som tilstand | | `explore.list_bundles` | `{"id": "expected-bundle", …, "cost_baseline": false, "documents": 3}` | Katalogen fungerer ✅ | | `okf.load_ir_projection` | **`FileNotFoundError: IR projection not found in bundle: 'validator-input.json'`** | **Pipelinen nekter** (samme nekt som M1 A på grenbasen) | | `verdicts.seed_store_from_bundle` | 0 | — | | `grep -rn adjudication src/ tests/` | **0 treff** (kjent-positiv `bundle_id`: 71) | Ingen konsument | | `grep -rn concept_id src/ tests/` | **0 treff** | Ingen konsept-identitet i konsumenten | **Hva som mangler for «hele veien», konkret:** | # | Gap | Fil/funksjon i dag | Eier / status | |---|---|---|---| | 1 | **IR-projeksjon + kostbaseline fra et ingestert korpus.** `validator-input.json` er håndskrevet per prosjekt (README:146 «one file the ingest layer does not write for you»; `docs/kunnskapsbase-for-en-kjoring.md:84`). K2 er anbudsdokumenter (PDF/DOCX/XLSX) — ingen kode leser kostlinjer ut av dem | `run._project_from_bundle` (`run.py:437`) → `okf.load_ir_projection` (`okf.py:433`) fail-fast; `okf.load_optional_cost_baseline` (`:411`) | **Ingen eier, ingen ordre.** Det tyngste enkelthullet — uten det stopper syretesten der M1 A stoppet på grenbasen | | 2 | **`adjudication`-konsument** (PM B2: fravær = `unknown`, aldri `absent`) | ingen; nærmeste form er B4 Steg 7 `evidence_for(path, key="verified")` med `EvidenceState` | **Bestilt** i S3 amendment B (Steg 7b), K4c-vokter `tests/test_falsification_verdict_loadbearing.py`. Ikke bygget | | 3 | **(`bundle_id`, `concept_id`)-identitet** | `explore._bundle_index` + `run.py:1512` (basename, to kopier); `mandate.Approach.bundle_id` default `""`; `verdicts._mint_id` (`:86`) korpus-blind; `VerdictStore.add` (`:305`) first-write-wins stille | **Bestilt** S3 amendment D / B4 Steg 9–10 (`reconcile_bundle_id`, `collisions`). `concept_id` finnes ikke som begrep i konsumenten — S3 B1 gir den, men ingen kode i dag nøkler en dom på et konsept | | 4 | **Flerkilde-`sources` + K5-terskel** | `parse_frontmatter` holder lister verbatim som streng | B4 Steg 2–7 / S3 amendments A + C | | 5 | **Ny profil `SEGMENTED_OKF_V0_2`** | installert `llm_ingestion_okf` **v0.3.2** har moduler `connectors, errors, manifest, materialize, render` — **ingen `profiles.py`**; profilen finnes kun i produsent-HEAD | Gjelder KUN Door A (`ingest.materialize`) i dette repoet; LESING trenger ingen pin. STATE (økt 70): «IKKE bump `llm-ingestion-okf`-pinnen» — står ikke i veien for K2-lesing | | 6 | **Office → tekst** | — | Produsent (S1), utenfor dette repoet | **Konklusjon M5:** når S1 leverer et K2-bundle, kan portfolio-optimiser **navigere det og katalogisere det** i dag, men **ikke kjøre pipelinen** — og hindringen er ikke `adjudication`, det er at ingen lager prosjektets tallfiler. Det er et scenario-hull ingen av de tre planene (PM, B4, S3) navngir med eier. ## 6. M6 — MCP under kjøring, mot en ekte stdio-server **Server:** `/tmp/claude-s2/mcp_stub.py` — `FastMCP("prisregister")`, ett verktøy `lookup_unit_price(code) -> "unit price for {code}: 1.0 NOK/kWh (STUB-SERVER-7731)"`. Konfig: `{"servers":[{"name":"prisregister","transport":"stdio","command":<.venv python>,"args":[stub], "allowed_tools":["lookup_unit_price"],"timeout_seconds":30}]}`. | Arm | Kommando | Egress-linje (stdout) | `provenance.external_calls` | Serverens svar i neste prompt | |---|---|---|---|---| | A — CLI, tekst-agenter | `python -m portfolio_optimiser.run BYGG-KONTOR-NORD … --mcp-config mcp.json --scripted-replies … --outbox-dir … --run-id m6a` (subprosess) | **`Contacts: prisregister (lookup_unit_price)`** ✅, rc 0 | `m6a-proposal.json`: **`[]`** — agentene kalte aldri (kan ikke, tekst) | — | | B — bibliotek, proposer som KALLER | `run_project(..., mcp_servers=load_mcp_config(cfg), outbox_dir=…, run_id="m6b")` med `RecordingClient`-manus `[call lookup_unit_price(code=ENERGI-TOTAL-EL), IR-json]` | (ikke CLI) | **`[{"server": "prisregister", "tool": "lookup_unit_price"}]`** i `result.provenance` OG i `m6b-proposal.json` ✅ | **ja** (`STUB-SERVER-7731` i proposerens 2. prompt); serverloggen viser `CallToolRequest` | Dette er første måling av kjørestien (`run_project` → `build_mcp_tools` → `MCPStdioTool` → `AsyncExitStack` → `ToolCallRecorder`) mot en **ekte subprosess**; `test_b4_mcp_call_trace_loadbearing.py` bruker en in-process stand-in med vilje, og `test_ingest_golden_mcp.py` måler INGEST-transporten, ikke denne. Attribusjonen (`server="prisregister"`) kom fra vår egen konfig-indeks, som designet. **To MINOR-funn i samme kjøring:** (a) genereringskallet (`generate.py:460` `chat_client.get_response(messages, options={"response_format": …})`) går utenom `Agent` og har **ingen verktøy** — et `function_call`-svar der blir en parse-feil, så prisregisteret kan brukes i debatten men ikke i steget som skriver forslags-JSON-en (indirekte via debatt-outputen som kontekst); (b) `m6b-parse-failures.json` fanget det svaret som `"text": ""` — «verbatim»-fangsten mister et svar som var et funksjonskall, altså nøyaktig beviset den finnes for. **Instrument-lærdom, uttalt:** første forsøk på arm A krasjet med `io.UnsupportedOperation: fileno` fordi jeg fanget stderr in-process (`redirect_stderr`) og `stdio_client` gir barnet `sys.stderr`. Det er min instrumentfeil, ikke repoets — men det betyr at en `capsys`-basert test av denne stien ville feilet på samme måte; subprosessen er målingen (P4-presedensen, igjen). --- ## 7. Funn, rangert ### BLOCKER **BLOCKER-1 — Planreviewen viser et menneske `` i stedet for planen, på alle tre flater.** `explore.py:1395/1404/1418/1478` `str(review.plan)` på en MAF `Message` uten `__str__`. Terminal (F4), `{run_id}-exploration.json.plan_reviews[*].plan`, og den parkerte `{run_id}-plan-review.json.plan` (U12). Fire asserts i tre testfiler grønne på `!= ""`/truthiness/`[:40] in out`. Konsekvens: begge HITL-dørene er uøvbare for et menneske; en ekspert som svarer `approve` på en repr signerer blindt. Beviset ligger i § 2 og i `/tmp/claude-s2/out/m2/result.json` + `m2-park/outbox/park1-plan-review.json` (efemert; kommandoene i § 8 gjenskaper det). **Ordreutkast B1 (Opus 5 / high, én økt, Iron Law):** > Oppgave: planteksten som vises og lagres i plan-reviewen skal være `Message.text`, aldri > `str(Message)`. Bruk den eksisterende `_plan_text` (`explore.py:966`) på alle fire stedene; ingen > ny hjelper. **Test først, RØD:** i `tests/test_plan_review_cli_door_loadbearing.py` skjerpes T2 til > å asserte at den skriptede managerens plantekst (en unik streng i `_REPLIES["manager"]`-planen — > scenariet må gi manageren et stadienøklet svar slik `simulation._exploration_manager_reply` gjør, > ellers finnes ingen plantekst å finne) står i stdout OG i artefaktets `plan`, og at strengen > `"Message object at"` IKKE står noe sted; i `tests/test_async_plan_review_loadbearing.py` samme > assert på `pending_plan_reviews(...)[0].plan` og på `{run_id}-plan-review.json`. Mutasjon: revert > til `str(review.plan)` → begge røde; kontroll: hele suiten grønn, golden byte-uendret. **Ikke > utløs:** ingen endring i Magentic-wiring, `checkpoint_storage`, `_ALLOWED_CHECKPOINT_TYPES` eller > F15-diffen; ingen push. ### MAJOR **MAJOR-1 — Offline-generalprøven av utforskningen er vakuøs ved konstruksjon, og artefaktet kan ikke se det.** (M1) `--scripted-replies` tar én konstant streng per rolle → 0 verktøykall, 0 approaches, 1 runde på 4/4 baser; `{run_id}-exploration.json` har ingen rad for navigatørens verktøy. En operatør som «beviser så mye som mulig gratis før det dyre trinnet» kan i dag ikke bevise at navigatøren åpner noe, og etter en betalt kjøring kan ingen lese om den gjorde det. > **Ordreutkast M1 (Opus 5 / high, to halvdeler i én økt):** (a) `ExplorationTrace.tool_calls` > (navn + `bundle_id`-argument, aldri resultatet) fylt av en `FunctionMiddleware` på > utforskningsagentene — speil av `mcp_tools.ToolCallRecorder`, én kopi av mønsteret — og skrevet av > `trace_payload` ved siden av `quick_validations`; (b) `_load_scripted_replies` godtar for > utforskningsrollene en LISTE av trinn der et trinn kan være `{"call": "", "args": {…}}`, og > `scripted_factory` bygger da en klient som emitterer `function_call` (formen målt i denne rapporten, > vedlegg A). **Test først:** `tests/test_scripted_explore_door_loadbearing.py` — CLI-kjøring med > manus `list_bundles → read_bundle → tekst` gir `tool_calls == [list_bundles, read_bundle]` i > artefaktet (RØD uten (a) og uten (b) hver for seg); kontroll: konstant-streng-manus gir `[]`, og > golden byte-uendret. **Ikke utløs:** ingen endring i verktøykroppene eller katalogtaket; ikke > `run.py`s debatt-roller; ingen push. **MAJOR-2 — Ingen dør for menneskelig feedback på det forslaget som ligger på bordet.** (M3) Feedback lander i neste kjøring (bevist), maskinens egen avvisning sløyfes tilbake (bevist), men bruksscenarioets «med tilbakemeldinger komme med forbedrede forslag» er en to-kjørings-sløyfe. > **Ordreutkast M2 (Opus 5 / high, Voyage-brief anbefalt — berører `run.py`, `generate.py`, > `outbox.py`, CLI):** `run_project(..., proposal_reviewer: ProposalReviewer | None)` — kalles ETTER > validatorens utfall med (forslag, dom, provenance) og svarer `approve` eller `revise(feedback)`; > `revise` mater `generate._build_messages(prior_feedback=…)` (parallell til `prior_rejection`, kun > teksten, aldri forrige JSON) inn i ett nytt bundet forsøk under EKSISTERENDE `max_attempts` + > `meter.tick_round` — ingen ny løkke; hvert svar registreres verbatim på `RunResult.expert_revisions` > og i outboxen; CLI-dør `--proposal-review` speiler `--plan-review` (EOF = feil, aldri godkjenning); > hosting NEKTER (synkron). **Test først:** `tests/test_proposal_review_loop_loadbearing.py` — en > `revise` gir et ANDRE genereringskall hvis prompt bærer feedbacken ordrett og et annet utfall enn > en alltid-godkjenn-reviewer (diskriminatoren, F4-formen); EOF raiser; taket holder (revise ×N > stopper på `max_attempts`); kontroll uten reviewer: byte-identisk kjøring og golden uendret. > **Ikke utløs:** ingen endring i dom-myntingen (F2), Steg 7-innboksen, `capture_verdict` eller > checker-gaten. **MAJOR-3 — Bundle-konteksten re-sendes 3× i debatten og 7× i utforskningen; 73–93 % av alle prompt-tokens.** (M4) Tre poster navngitt i § 4; ≈ 40 % (utforskning) / ≈ 60 % (debatt) er prefiks-cachebart som koden står, resten krever et kontekstbudsjett på `read_bundle`. > **Ordreutkast M3 (Fable 5.1 eller Opus 5 / high — MÅL FØRST, så én søm):** (1) mål med instrumentet > i vedlegg A hva `read_bundle` koster som `function_result` per base og hvor mange prompts det rir > med i; (2) bygg `read_bundle` om til samme form som katalogen: konseptliste (`name`, `type`, > `title`, lengde) med `read_file` som neste trinn, tak i TESTEN ikke i koden > (`test_catalogue_cost_loadbearing.py`-formen); (3) gate: `tests/test_read_bundle_cost_loadbearing.py` > — `read_bundle` over tunnel-basen < 1 500 o200k-tokens, kontroll at én konseptfil alene sprenger > taket, og at `read_file` fortsatt gir hele fila. **Ikke utløs:** `okf.bundle_context` selv (den > bærer debattens kontekst og goldenene), nav-goldenene, `run.py`. Debattens 3× er GroupChat-formen > og en egen beslutning (kontekst i `instructions` vs melding endrer ikke tokens, bare cache-treff). **MAJOR-4 — K2 kan ikke kjøres «hele veien» fordi ingen lager prosjektets tallfiler, og ingen plan eier det.** (M5) `validator-input.json` + `cost-baseline.json` er håndskrevne; `_project_from_bundle` nekter uten dem (målt to ganger i dag). `adjudication`/identitet ER bestilt (S3), dette er det ikke. > **Ordreutkast M4 (Fable 5.1 / high — MÅL, IKKE BYGG, til PM):** inventar av K2 (86 filer): hvilke > bærer kostlinjer (XLSX/tabeller i PDF), hvilken form (kode, mengde, enhetspris), og om > `ir.SavingsProposal`/`CostBaseline` kan fylles fra dem uten skjønn. Utfall: EN eier-beslutning — > produsent-profil som emitterer `cost-baseline.json` (llm-ingestion-okf), eller et konsument-steg > `okf.derive_cost_baseline(bundle)` med vokter `tests/test_cost_baseline_derivation_loadbearing.py`. > **Ikke utløs:** ingen kode før eieren er valgt; ingen endring i `ir.py`. ### MINOR **MINOR-1** — `quick_validate` sier `validated` med P10 = P50 = P90 når kandidaten mangler `assumptions` (M1 B, grenbasen: 300/300/300); den inerte MC-en flagges ikke i dommen. **MINOR-2** — parse-feil-fangsten registrerer et `function_call`-svar som `"text": ""` (M6 B). **MINOR-3** — MCP-verktøy er nåbare i debatten, ikke i genereringskallet (`generate.py:460`, ingen `tools`) — by design, men ikke uttalt noe sted. **MINOR-4** — Planteksten kringkastes aldri til deltakerne (hypotesiserens prompt = 19 tegn instruksjon); effekten av en planrevisjon på forslaget hviler 100 % på managerens neste `instruction_or_question`. Ærlighetsgrense som bør stå i `--plan-review`-hjelpeteksten. ### NICE (positivt målt) **NICE-1** — Verktøysømmen bærer ende til ende, også mot en regelverksbase uten prosjekt (M1 B, 4/4). **NICE-2** — Innboks-sløyfa virker med kontroll og nøkkel-samsvar (M3), og `finally`-artefaktene skrives også når pipelinen nekter (M1 A grenbasen, 1 fil). **NICE-3** — Egress-deklarasjon og B4-recorder holder mot en ekte stdio-subprosess (M6). --- ## 8. Verifiseringslogg Instrumentet ligger i `/tmp/claude-s2/` (efemert, utenfor repoet — ordren forbød produksjonskode, og et måleinstrument i `tests/` ville vært en beslutning om å beholde det). Vedlegg A gjengir kjernen så tallene kan reproduseres. | # | Påstand | Kommando → resultat | |---|---|---| | 1 | Treet urørt; F15-diff sett | `git status --short` → ` M tests/test_maf_version_guard.py`, `?? docs/presentasjon-…html`, `?? scratchpad/` (før og etter); `git stash list \| wc -l` → **0** (STATE sa «stash») | | 2 | Golden | `PYTHONIOENCODING=utf-8 uv run python -m portfolio_optimiser.simulation \| shasum` → `ea8c5347…` | | 3 | Nevner tester | `uv run pytest --co -q \| tail -1` → 1085; `ls tests/*.py \| wc -l` → 120 | | 4 | M1 A/B × 4 baser | `uv run python /tmp/claude-s2/m1.py /tmp/claude-s2/out/m1-` → `result.json` (tall i § 1) | | 5 | Artefaktnøkler | `python -c 'import json;print(sorted(json.load(open("…/m1a-exploration.json"))))'` → 7 nøkler, ingen `tool_calls` | | 6 | M2 bibliotek + CLI | `uv run python /tmp/claude-s2/m2.py shared/examples/bygg-energi-mikro BYGG-KONTOR-NORD /tmp/claude-s2/out/m2`; park: `python -m portfolio_optimiser.run … --checkpoint-dir … --run-id park1` → `park1-plan-review.json.plan` = repr | | 7 | `Message` uten `__str__` | `uv run python -c "from agent_framework import Message; m=Message(role='assistant',contents=['PLAN: x']); print(str(m)[:40], m.text, type(m).__str__ is object.__str__)"` → `` + re-sendt-skript (§ 4); `bundle_context`-tokens re-målt = commons' fasit | | 12 | Ekte usage finnes kun 14.08 | `grep -n token_usage docs/2026-08-14-fase1b-forste-levende-kjoring.md` → `:221 token_usage: 15 306` | | 13 | M5 leseprøve | `uv run python - <