portfolio-optimiser/docs/2026-08-25-syretest-vei-ab.md
Kjell Tore Guttormsen a1f8522bdf feat(explore): katalogkallet koster O(baser), ikke O(korpus) (ORDRE 20260825T213645Z)
list_bundles returnerte hele rot-indeksens body for HVER konfigurert base samtidig,
pluss ett JSON-objekt per ufulgt kryss-lenke. Begge vokser med korpuset, saa prisen
paa aa finne ut HVILKE baser som finnes ble satt av hvor mye de INNEHOLDER - progressiv
disclosure snudd paa hodet.

Maalt med o200k_base, instrumentet foerst validert mot commons' egne fasittall:
  tre flate Vegnormal-baser   112 116 -> 362 tokens   (-99,7 %)
  171 grenbaser               124 942 -> 21 448       (-82,8 %)
Grenformen (vegnormal-okf 8145c23) lukket bundle-siden og gjorde katalogsiden verre,
noeyaktig som det repoet forutsa.

Et premiss ble felt FOER noe ble bygget paa det: "indeksbodyen forteller hva basen
handler om" er usant for maskin-importerte baser - grenbasenes index.md er en ren
lenkeliste uten frontmatter og prosa, saa feltet var dyrt OG innholdsloest der.

Fast vindu (200 tegn), aldri en andel av basen. Avkorting annonseres som FELT
(index_truncated), og en base som passer blir ikke merket avkortet. En ufulgt lenke
overlever som ANTALL; per-lenke-detaljen blir liggende der den er handlingsbar.
Hele indeksen er fortsatt ett read_file(id, "index.md") unna.

Taket (500 tegn/base) bor i TESTEN, ikke i explore.py.

Load-bearing MAALT: tests/test_catalogue_cost_loadbearing.py, 7 armer, ni mutasjoner
alle roede mot HELE suiten + groenn kontroll 1066/5 og golden demo-transcript.stdout
byte-uendret (ea8c534773acdbe41ae68f2c55724d69aaf8be4f).

Ogsaa: MINOR-1 i syretest-rapporten rettet - flatheten er Doer C sin
(llm-ingestion-okf importer.py, §6-index-blokka), ikke vegnormals emitterform.
Verifisert mot kilden, ikke mot meldingen.

Maaling: docs/2026-08-26-katalogkostnaden.md
2026-08-26 14:45:16 +02:00

18 KiB
Raw Blame History

Syretesten vei A/B — de tre Vegnormal-basene gjennom portfolio-optimiser

Ordre: 20260825T111038Z-1174613178-from-.claude (programplanens spor 3, gap G12). Dato: 2026-08-25 (økt 60). Mandat: MÅL, IKKE BYGG. Ingen fil under src/ er endret; uv run pytest -q1021 passed / 5 skipped (166 s), identisk med tallet før økten.

Eksponerings-grense (ordrens harde krav, holdt): dette repoet pusher til open/. Rapporten bærer derfor kun tall, stier, kommandoer og egne observasjoner. Ingen bundle-fil er kopiert, og ikke én linje kravtekst fra et konsept er lest inn eller gjengitt — alle konsept-tall under er lengdemålinger, ikke innhold.

Stopp-betingelsen var oppfylt: multi-base-ordren 20260825T080753Z-103813595 lå i orders/archive/ (commit 18af86e + 785261f) da økten startet. Multi-base-formen er lest slik den faktisk landet (run.py, explore.py, mandate.py, README), ikke slik planens § C.7 omtalte den.


1. Sammendrag — én setning per målepunkt

# Punkt Status Kjernetall
1 Katalogen (index_summary, konsepter, cost-baseline, verdicts) MÅLT 446 / 1017 / 270 konsepter; 0 av 3 har cost-baseline.json; 0 av 3 har validator-input.json; 0 type: verdict-filer i alle tre
2 Kontekstkostnad (bundle_context(navigate_bundle(...))) MÅLT 93 422 / 250 785 / 85 937 o200k_base-tokens — sum 430 144 mot 3861/12595/10406 for de tre eksempelbundlene (instrumentet reproduserte commons' tall eksakt)
3 Dry-run med tre baser i multi-base-formen MÅLT — og formen finnes ikke fra CLI-en Fire --bundle-dir gir exit 0 og en kjøring mot ÉN base (siste vinner, stille); bibliotekdøra run_mandate_across_bundles ruter korrekt men feiler i dispatch: FileNotFoundError @ okf.py:433
4 Offline-simuleringen med samme oppsett MÅLT Golden demo-transcript.stdout BYTE-UENDRET (ea8c534773acdbe41ae68f2c55724d69aaf8be4f), stderr 4 linjer; demoen kan ikke peke på en Vegnormal-base (samme okf.py:433)
5 --explore med full seks-felts --explore-config MÅLT — delvis vakuøst, som ordren forutså Sløyfa fullfører offline mot tre baser og returnerer et rutet mandat (bundle_id='B-n100-…', stop=None); men 0 verktøykall og 0 quick_validate — navigatoren åpnet aldri en base. Fra CLI-en er utforskningen ikke kjørbar offline i det hele tatt (KeyError: 'navigator')
6 G14: navigasjon inn i nestede index.md IKKE PRØVBAR HER — nevner oppgitt 0 nestede index.md av 1733 konsepter i alle tre basene (0 underkataloger); kjent-positiv kontroll nav-golden-hierarchy/bundle finner 2 nestede index og 2 dypt-nådde kontekstfiler, så instrumentet kan se dem

2. Tabellen (punkt 1 + 2)

Kommando bak hver rad: okf.navigate_bundle(d)okf.bundle_context(bundle), med tiktoken.get_encoding("o200k_base") (kjørt via uv run --with tiktoken; tiktoken er ikke lagt til som prosjekt-avhengighet).

Base Konsepter Kontekstfiler index_summary (tegn / tokens) bundle_context tegn bytes o200k_base-tokens verdicts cost-baseline validator-input skipped links
B-n100-2023-uten-sources-importert 446 446 51 214 / 28 289 221 916 226 430 93 422 0 nei nei 0
B-n200-2024-uten-sources-importert 1017 1017 116 879 / 64 764 616 179 626 034 250 785 0 nei nei 0
B-n500-2024-uten-sources-importert 270 270 30 974 / 17 197 240 714 245 251 85 937 0 nei nei 0
Sum, tre baser 1733 1733 199 067 / 110 250 1 078 809 1 097 715 430 144 0 0
kontroll: veglys-fv-soer 6 5 3 646 / — 32 201 32 884 10 406 1 ja ja 1
kontroll: tunnel-hauglia 6 5 4 763 / — 39 583 40 475 12 595 1 ja ja 1
kontroll: bygg-energi-mikro 5 4 1 884 / — 12 005 12 270 3 861 1 nei ja 1

Instrumentet er validert mot kjent fasit (Verifiseringsloven ansikt 4): de tre kontrollradene reproduserer commons' egne tall — 3861 / 12 595 / 10 406 — eksakt. Uten den kontrollen ville Vegnormal-tallene vært en måling ingen visste kunne treffe.

Ordrens tall bekreftet mot ground truth før noe ble bygget på dem: 447 / 1018 / 271 .md-filer på disk = 446 / 1017 / 270 konsepter + index.md i hver. Hver index.md har nøyaktig like mange lenker som det er konsepter (446 / 1017 / 270), alle unike, alle fulgt — skipped = 0.

Det manageren faktisk ser. Ett list_bundles()-kall (explore.py:419-438) returnerer hele index_summary for alle baser samtidig:

tegn bytes o200k_base-tokens
list_bundles() over de tre basene 201 196 201 196 112 116

Dette er ett verktøykall, og det er katalogverktøyets eneste form.

Hva tallene sier om .claudes § 9.1-analyse

.claude sin docs/okf-bundle-prosessen.md § 9.1 («en fil uten lenke finnes ikke; alt som lenkes leses helt») er bekreftet mot et ekte korpus, og den er kostbar her: alle 1733 konsepter er lenket fra rot-index.md, skipped = 0, og «leses helt» betyr 430 144 tokens for de tre basene. Progressiv disclosure gir ingen lettelse på denne bundle-formen, fordi importformen legger alt på ett nivå — se funn MINOR-1.


3. Funn

BLOCKER-1 — kontekstkostnaden gjør en live utforskning mot disse basene ugjennomførbar som de står

list_bundles() = 112 116 tokens i ett kall; read_bundle("B-n200-…") = 250 785 tokens. ExplorationContract.max_tokens (explore.py:70) er ledgeren BudgetMiddleware håndhever, og et enkelt katalogkall bruker mer enn et normalt tak. En 128k-modell kan ikke ta N200 i det hele tatt.

Dette er ikke en defekt i rammeverket — det er korpusets form møtt av § 9.1-kontrakten. Men det er den harde grensen for vei A/B live, og den var ikke målt før i dag. Fil:linje: src/portfolio_optimiser/explore.py:419-438 (list_bundles), :444-445 (read_bundle).

Oppdatert 2026-08-26 (økt 65, ordre 20260825T213645Z-9019120455): katalog-halvdelen er lukket. list_bundles() over de samme tre basene koster nå 362 tokens (fra 112 116), og over alle 171 grenbaser 21 448 (fra 124 942). read_bundle-halvdelen ble lukket på korpussiden av vegnormal-okf 8145c23 (grener som egne baser). Måling og gate: docs/2026-08-26-katalogkostnaden.md. Setningen over står som den ble målt 25.08 — den er historikk, ikke en gjeldende tilstand.

MAJOR-1 — gjentatt --bundle-dir forkastes STILLE; kjøringen ser ut som multi-base og er det ikke

Ordrens pkt. 3 forutsatte at CLI-en tar tre baser. Målt:

uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER \
  --docs-dir shared/examples/veglys-fv-soer \
  --bundle-dir <N100> --bundle-dir <N200> --bundle-dir <N500> \
  --bundle-dir shared/examples/veglys-fv-soer --live-dry-run
→ EXIT 0, "VEGLYS-FV-SOER: LIVE-DRY-RUN OK (…)"

Exit 0. De tre Vegnormal-basene ble droppet uten ett ord. Bytter man rekkefølgen slik at en Vegnormal-base står sist, feiler samme kommando i stedet (live-dry-run refused: IR projection not found in bundle: 'validator-input.json') — altså siste --bundle-dir vinner, som er argparse sin default når action="append" mangler.

Dette er repoets egen defektklasse, anvendt på operatørflaten: hosting-whitelisten nekter ukjente felt ved navn nettopp fordi stille dropping er uleselig utenfra, og multi-base-invarianten avviser en andre bundle_dirrun_project-signaturen fordi den ville tvunget «et stille velg-en». Her er det et stille velg-en — bare i argv i stedet for i signaturen.

Fil:linje: src/portfolio_optimiser/run.py:1581-1583 (add_argument("--bundle-dir", default=None…), ingen action="append"), konsumert run.py:2070 (bundle_dirs=(args.bundle_dir,)) og run.py:607-610. Minste ærlige rettelse (ikke bygget — ordren er MÅL, IKKE BYGG): nekt et gjentatt --bundle-dir ved navn, på linje med de åtte eksisterende utforskningsnektene. STATE fører allerede «repeterbart --bundle-dir» som en åpen operatørbeslutning fra økt 58; denne målingen sier at inntil den er tatt, er stillheten selv problemet — ikke fraværet av funksjonen.

MAJOR-2 — --explore --scripted-replies krasjer med rå traceback: KeyError: 'navigator'

Den ene offline-døra CLI-en har til --explore er ubrukelig. _SCRIPTED_ROLES = ("proposer", "checker") er debattens to roller; utforskningen trenger i tillegg navigator, hypothesiser og manager. _load_scripted_replies er eksplisitt fail-fast for de to den kjenner («a missing role would otherwise surface as a KeyError deep inside scripted_factory's lookup, mid-run») — og så inntreffer nøyaktig det den advarer mot, for de tre den ikke kjenner:

File ".../explore.py", line 576, in fresh_exploration_workflow
    client_factory(role),
File ".../simulation.py", line 470, in factory
    reply = replies[role]
KeyError: 'navigator'

Ingen run refused:-linje, ingen rc-1 med forklaring — en traceback, som er den kanalen økt 57 betalte for å holde konfigurasjonsfeil UTE av.

Positivt målt i samme kjøring: finally-blokka holdt. {run_id}-exploration.json ble skrevet selv om kjøringen krasjet, med "completed": false og "stop": null — nøyaktig det completed-feltet finnes for.

Fil:linje: src/portfolio_optimiser/run.py:1531 (_SCRIPTED_ROLES), :1559-1564 (fail-fast-listen), krasjer i src/portfolio_optimiser/simulation.py:470. Merk: simulation.scripted_exploration_factory (simulation.py:713-736) dekker allerede alle fem rollene. Sømmen finnes; CLI-en når den bare ikke.

MAJOR-3 — regelverksbaser kan ikke være baser i multi-base-dispatchen (arkitektonisk, ikke en bug)

Bibliotekdøra run_mandate_across_bundles ble målt direkte med de tre basene og et mandat med én approach per base:

STEG 1  route_by_bundle → B-n100…: ['a0']   B-n200…: ['a1']   B-n500…: ['a2']     ✅ korrekt partisjon
STEG 2  run_mandate_across_bundles → FileNotFoundError @ okf.py:433
        "IR projection not found in bundle: 'validator-input.json'"

Partisjonen virker perfekt. Dispatchen gjør det ikke, fordi run.py:1491 leser hver bases prosjekt fra den basens egen IR-projeksjon — som er selve multi-base-invariantens designvalg («en kaller-oppgitt konstant kunne uansett bare vært riktig for én base av N»).

Konsekvensen er den viktigste innsikten i hele syretesten: multi-base betyr N prosjekter, ikke 1 prosjekt × N referansebaser. Vegnormal-basene er regelverk — de har verken prosjekt eller kostbaseline, og skal ikke ha det. Ordrens mentale modell («ett veglysprosjekt + tre normalbaser som kontekst») er en annen form, og den finnes allerede — bare ikke i dispatchen:

Dør bundle_dirs betyr Passer regelverk?
run.run_mandate_across_bundles (run.py:1388) N prosjektbaser → N kjøringer Nei — krever validator-input.json per base
explore.explore (explore.py:~840) N lesekilder for navigator/hypothesiser Ja — målt, se under

Fil:linje: src/portfolio_optimiser/okf.py:431-433, kalt fra run.py:1491 (dispatchen), run.py:609 (enkeltkjøringen) og simulation.py (demoen) — alle tre feiler på samme sted.

MINOR-1 — importformen legger alt på ett nivå, så progressiv disclosure gir null lettelse

0 underkataloger, 0 nestede index.md, 1733 av 1733 konsepter lenket direkte fra rot. Navigasjonen har ingenting å utsette; hele korpuset er ett flatt nivå. Dette er en egenskap ved kilden, ikke ved okf.py.

Rettet 2026-08-26 (økt 65): tilskrivelsen var feil, og vegnormal-okf har rett. Flatheten er Dør C sin, ikke vegnormals emitterform. Verifisert mot kilden, ikke mot deres melding: llm-ingestion-okf src/llm_ingestion_okf/importer.py (§6-index-blokka) kaller link_in_index(bundle, entry.path.name, _index_label(entry.concept_path)) per merget oppføring — altså én flat lenke i rot-index.md for hvert konsept, uansett hvor nestet konseptstien er. Vegnormals emitter skriver allerede et tonivåtre. Setningen over sto uendret som «vegnormal-okf sin importform» til dette punktet.

Dette var også hele grunnen til BLOCKER-1: med nestede indekser kunne manageren åpnet én gren om gangen. Løsningen ble en annen — grener som egne baser (vegnormal-okf 8145c23), med den målte begrunnelsen at basegrensen er der OKF-navigasjonen stopper, så ingen indeksstruktur INNE i en base senker prisen på å åpne den.

NICE-1 — read_bundle nekter ukjent base ved navn, som lovet

read_bundle("finnes-ikke") → ExplorationError: unknown knowledge base 'finnes-ikke';
                             configured: B-n100-2023-…, B-n200-2024-…, B-n500-2024-…

Nekten navngir det konfigurerte settet. Fil:linje: explore.py:390-393.


4. Hva punkt 5 faktisk viste — og hvor grensen for offline går

Ordren ba om at vakuiteten skulle måles, ikke antas. Målt, med scripted_exploration_factory (alle fem roller) mot de tre basene:

Arm Utfall
B — hypotese uten bundle_id, tre baser HypothesisParseError @ explore.py:732 — «a marked hypothesis must name its knowledge base when several are configured». Multi-base-nekten fyrer korrekt mot et ekte korpus.
C — hypotese med bundle_id, tre baser stop=None, 1 approach, bundle_id='B-n100-2023-uten-sources-importert', 2 ledger-runder, 6 modell-prompts. Sløyfa fullfører og produserer et rutet mandat.

Og så det ærlige forbeholdet, som er poenget:

  • quick_validate-kall: 0
  • Prompt-strøm-forekomster av basenavnene: N100 2, N200 0, N500 0 — begge fra instruksjonene, ingen fra et verktøyresultat.

Navigatoren åpnet aldri en base. Den scriptede klienten returnerer tekst og emitterer ingen verktøykall — nøyaktig den grensen økt 56s måling allerede slo fast, nå bekreftet mot et eksternt korpus. Lesesømmen selv er derimot bevist mot disse basene, ved direkte kall (samme kontroll økt 56 måtte innføre da den oppdaget at armen lå utenfor gaten): list_bundles() → 3 baser, read_bundle() → 221 916 / 616 179 / 240 714 tegn.

Konklusjon for punkt 5, uten pynt: plumbingen er bevist ende-til-ende mot ekte eksterne baser — ruting, nekt, mandatform, artefaktskriving. Verdien er ikke bevist, og kan ikke bli det offline. Syretesten trenger en levende modell. Det er et funn, ikke en feil.


5. Hva som MÅ til for en live-kjøring

Målt i denne økten, ikke antatt:

# Mangler Målt tilstand Konkret
1 Kontekstbudsjettet list_bundles() = 112 116 tokens; read_bundle(N200) = 250 785 Den harde blokkeringen. Enten en modell med svært stort vindu, eller — mer realistisk — en bundle-form med nestede indekser slik at manageren kan åpne én gren. Kilden eies av vegnormal-okf.
2 Foundry-endepunkt PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT ikke satt, FOUNDRY_PROJECT_ENDPOINT ikke satt Én av de to må eksporteres. Låst av operatørbeslutningen om DisableLocalAuth (ordre 20260821T094949Z, docs/2026-08-18-vurdering-azure-omdoeping.md). Ikke rørt her.
3 Modell-map PORTFOLIO_MODEL_MAP ikke satt; src/portfolio_optimiser/data/model_map.json bærer REPLACE-WITH-FOUNDRY-DEPLOYMENT for alle azure-roller resolve_model("azure", r) nekter for alle fem roller, inkl. manager/navigator/hypothesiser. Under local faller alle fem til qwen3:4b via default — utforskningsrollene er fortsatt ikke eksplisitt mappet (kjent ærlighets-grense fra økt 56).
4 En prosjektbase 0 av 3 Vegnormal-baser har validator-input.json eller cost-baseline.json Kjøringen trenger et prosjekt å optimere. Vegnormal-basene er regelverket det optimeres innenfor. Riktig oppsett: --bundle-dir <prosjektbase> for pipelinen + de tre normalbasene som explore(bundle_dirs=…)-lesekilder — men det krever MAJOR-1 løst, siden CLI-en i dag sender én base til begge.
5 Offline-generalprøve KeyError: 'navigator' MAJOR-2 må lukkes før en betalt kjøring, ellers er første live-kjøring også første gjennomkjøring. Repoets egen måleprotokoll: bevis så mye som mulig gratis, så en feil er attribuerbar.

Rekkefølge, uten å foregripe operatørens valg: 5 → 1 → 2/3 → 4. Punkt 5 er gratis, punkt 1 avgjør om vei A/B i det hele tatt er mulig med denne bundle-formen, og punktene 23 koster penger og er Azure-gatet.


6. Kommandologg

Hver tabellverdi over stammer fra én av disse, kjørt i denne økten:

# pkt 1: konsepter, index-lenker, verdicts, cost-baseline
find <base> -name '*.md' | wc -l ; grep -oE '\]\([^)]+\)' <base>/index.md | wc -l
grep -lE '^type: *verdict' <base>/*.md | wc -l

# pkt 1+2: navigasjon, kontekst, tokens (instrument validert mot commons' tre fasittall)
uv run --with tiktoken python  # okf.navigate_bundle / okf.bundle_context / o200k_base

# pkt 3: CLI, fire --bundle-dir, begge rekkefølger
uv run python -m portfolio_optimiser.run VEGLYS-FV-SOER --docs-dir … --bundle-dir … --live-dry-run

# pkt 3: bibliotekdøra
python  # mandate.route_by_bundle + run.run_mandate_across_bundles

# pkt 4: golden-regresjon
uv run python -m portfolio_optimiser.simulation | shasum   # ea8c534773acdbe41ae68f2c55724d69aaf8be4f

# pkt 5: CLI-en, og deretter explore() direkte med alle fem roller scriptet
uv run python -m portfolio_optimiser.run … --explore … --explore-config … --scripted-replies …

# pkt 6: nestede index, med nav-golden-hierarchy som kjent-positiv kontroll

# regresjon
uv run pytest -q   # 1021 passed, 5 skipped, 166.00s