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

20 KiB
Raw Blame History

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 tiktokentiktoken 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

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_bundlesread_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_projects 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

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øyresultater 6 527 (87 %) 6 527 (87 %) 6 527 (93 %)
tekst (fakta, plan, ledger-historikk) 965 (13 %) 901 (12 %) 470 (7 %)
verktøyargumenter 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_projects 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.jsontool_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.pyFalse 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).