# Review-paritet — portfolio-optimiser-claude (2026-07-16) > **Reviewer:** Fable 5 (xhigh) som orkestrator + seks Opus 4.8 xhigh-subagenter (spec- > konformitet, sikkerhet/injeksjon, SDK+model-map, testkvalitet/detach, ærlighet §1, > korrekthet). **Mandat:** full review + paritetsgap-kartlegging under operatør-mandatet FULL > MAF-PARITET (2026-07-16). Ingen live-fil endret; alle kjørbare eksperimenter i throwaway- > kopier (scratchpad) med import-isolasjon verifisert, eller rene in-memory-kall mot > live-moduler (read-only). Ingen API-kall, ingen nettverk (unntatt SDK-agentens > dokumentasjons-oppslag mot PyPI/Anthropic, eksplisitt tillatt). > > **Ny funn-serie R (kolliderer ikke med C-serien i [review 2026-07-10](review-2026-07.md)).** > Paritetsmatrisen + sesjonsplanen ligger i > [`2026-07-16-sesjonsplan-paritet.md`](2026-07-16-sesjonsplan-paritet.md); operatør-køen i > [`2026-07-16-beslutningsbrief.md`](2026-07-16-beslutningsbrief.md). ## 0. Baseline (verifisert 2026-07-16, ikke sitert fra STATE) | Påstand | Resultat | Belegg | |---|---|---| | Testsuite | **265 passed** (1.68s) — STATE-tallet stemmer | `uv run pytest -q` (kjørt) | | Lint / format / typer | ruff clean (40 filer) · ruff format clean · mypy clean (16 filer, strict) | kjørt | | Push-status | HEAD `b46f32c` == `origin/main`; `open/main` @ `42f320b` (to review/guard-commits foran open — bevisst ikke speilet); `commons/main` @ `84191c6` | `git rev-parse` alle | | Subtree-status | `shared/` 2 commons-commits bak (docs-only: `.gitignore` + guard-adopsjonsdoc — INGEN spec-endring) | subtree-diff `HEAD:shared`↔`commons/main` | | Commons-amendment (D-F/D-G) | **IKKE landet** i commons | samme diff | | okf-toolkit-repo | **finnes ikke** (lokalt + Forgejo) | `ls` + `git ls-remote` → not found | | Detach-stikkprøver (4/4) | ALLE RØDE i kopi, revert→grønn, arbeidstre urørt | testkvalitetsagent, §3 | | SDK-premisser | alle attributter holder mot installert 0.2.110 + t.o.m. nyeste 0.2.120 | SDK-agent, §2 | | S10-artefakter | `runs/s10/` urørt (fasit) — kun lest | ikke re-kjørt | **Konklusjon baseline:** ingen BLOCKER. STATE-ens tall er korrekte. De nye funnene under er robusthets-/korrekthetsdefekter (mest alvorlige R-2/R-3, begge MEDIUM) + paritetsgap + konformitetsgapet R-1 (§11-raden «Closed loop» ubundet). --- ## 1. Status på eksisterende funn (C-serien, review 2026-07-10) Alle re-verifisert; ingen har endret premiss. Stikkprøvene under er kjørt på nytt, ikke sitert. | C-funn | Status 2026-07-16 | Belegg (re-verifisert) | |---|---|---| | C-F2 (nominal gater aldri) | **ÅPEN, bekreftet** | `validator.py:53` beregner `nominal_feasible`, `:73` bruker den kun i avvisnings-*meldingen*; eneste numeriske gate er p90 (`:68`) | | C-F3 (ingen kostbaseline) | **ÅPEN, bekreftet** | `ir.py:43-48` sjekker kun claim ≤ Σ quantity·unit_cost; diktede kostlinjer validerer | | C-F4 (`/`-lenker skippes) | **ÅPEN, spec-tro** | `okf.py:112` skip; spec §3 Steg 1 pinner regelen | | C-F5 (seeding-nøkling) | **ÅPEN med nyanse** | features fra `load_validator_input(bundle_dir)` (`experience.py:132`), ikke per verdict-fil; id-halvdel løst (verbatim `verdict_id` `:147`). Ny konkret drop/count-effekt: **R-8** | | C-F7 (inbox-injeksjon) | **ÅPEN, kjørt-bevist på nytt** | injeksjons-`rationale` OG `decision` når prompt verbatim; `decision` tillater newline som bryter linjeformatet (sikkerhetsagent §a). Ny run-path-nyanse: **R-9** | | C-N1 (method-spec-vakt mangler) | **ÅPEN, bekreftet** | ingen `test_method_spec_loadbearing.py`; §12-kompletthet dermed ikke håndhevet (spec §12 lover det) | | C-N2 (Steg-7 kjøresti-inngang) | **ÅPEN, bekreftet** | `merge_inbox_into_store` refereres kun fra tester (grep); `run_s10.py` har ingen `--inbox` | | C-N3 (løs SDK-pin) | **ÅPEN, bekreftet** | `pyproject.toml:8` `>=0.2` uten øvre grense; «verifisert mot 0.2.110» kun i docstrings, ingen kjørbar versjonssjekk. Anbefalt pin nå: `>=0.2.111,<0.3` (§2) | | C-N4 (budsjett post-charge / USD per-kall) | **ÅPEN, bekreftet** | `charge_tokens` etter `client.complete` (`loop.py:90-91`); per-kall-USD via `max_budget_usd`, ingen run-total | | C-S1 (REJECT-substring-presedens) | **ÅPEN, spec-pinnet** | `_REJECT_PATTERN` først med presedens (`loop.py:188-191`) | | C-S2 (max_rows etter innlesing) | **ÅPEN, bekreftet + utvidet** | `_read_csv` (`ingest.py:191-195`) OG `_read_sql` `fetchall()` (`:244-248`) — se **R-7** | | C-S3 (promotion RMW ikke-atomisk) | **ÅPEN, dokumentert MVP-grense** | `promotion.py:92-103` | | C-P1 (D-C ↔ frossen §3 Steg 1) | **ÅPEN, delt** | står til D-A-bolken (beslutningsbrief pkt. 1); `test_surface_text_never_contributes` håndhever regelen i dag | --- ## 2. Nye funn (R-serien, rangert) ### R-1 [MEDIUM · konformitet] §11-raden «Closed loop» er ubundet — `test_simulation_loadbearing.py` mangler - **Belegg:** method-spec §11 (`method-spec.md:408`) navngir sømmen «the two-run simulation's marker crosses runs [via] promotion (or fails to cross with it)». Filen finnes ikke; ingen test importerer både `promote` og en run-komposisjon (grep, spec-konformitetsagent). Den nærmeste — `test_step8_promotion_loadbearing.py:143` — binder `promotion→seed→fold` (markør i prompt), IKKE en to-kjørings-simulering med negativ kontroll. `test_step7` binder to-kjørings-flippen, men via **inbox**-stien, ikke promotering. - **Feilscenario:** hvis promoterings-stien slutter å lukke lærings-loopen på run-nivå (promotert verdict seedes ikke inn i en senere kjøring), går INGEN test rød. Dette er §11s eneste udekkede rad utover C-N1s spec-vakt. - **Distinkt fra C-N1** (spec-integritet, §11 rad 11) og **C-N2** (Steg-7 kjøresti-inngang, D7-egen kjørbarhet). R-1 er §11 rad 8. - **Fiks:** ny `tests/test_simulation_loadbearing.py` + tynn deterministisk to-run-driver (`simulation.py`, ScriptedClient) → sesjon **K4** (byggbar nå, forutsetter C2.0-inngangen). ### R-2 [MEDIUM · korrekthet] `Infinity` beseirer den deterministiske validatoren — KJØRT-BEVIST (også av orkestrator) - **Belegg:** Pydantic v2 har `allow_inf_nan=True` som default, så `float('inf')` tilfredsstiller `unit_cost: Field(gt=0)` (`ir.py:27`) og de ubegrensede `assumptions`-båndene (`ir.py:37`); `json.loads` godtar det ikke-standard JSON-tokenet `Infinity`. - **Kjørt bevis (orkestrator, mot live-modul, read-only):** `unit_cost: Infinity` → `SavingsProposal` konstrueres, `validate_proposal` → `ValidatedProposal(validates=True, nominal_feasible=inf, p90=inf)`. Samme via `assumptions: {"A":[1.0, Infinity]}` → `validates=True, p90=inf`. **NaN avvises korrekt** (kontroll: `ValidationError`) — kun `inf` slipper gjennom. - **Feilscenario:** validatoren er spec-ens obligatoriske, blokkerende anker mot swarm-selvbekreftelse (§4). Ett `Infinity`-token fra proposer-modellen — nåbart på kjørestien via `loop._parse_candidate` — får ethvert claim til å klarere porten vakuøst (`claimed > inf` er alltid False). - **Fiks:** `allow_inf_nan=False` på de numeriske IR-feltene (eller eksplisitt finitthetssjekk av `quantity`/`unit_cost`/`claimed_saving_nok` + bånd-endepunkter). Ingen beslutnings- avhengighet, golden uendret (golden-tallene er finite) → sesjon **C2.6** (byggbar nå). ### R-3 [MEDIUM · korrekthet/robusthet] Tom kilde-CSV krasjer ingest MIDT i materialisering → delvis bundle - **Belegg (kjørt av korrekthetsagent; mekanisme re-verifisert av orkestrator):** `_render_table([])` kaster `ValueError: not enough values to unpack` (`ingest.py:176`, `header, *data = rows`). `_read_csv` returnerer `[]` for 0-byte fil (`ingest.py:188-197`; `data_rows=0` → ingen max_rows-brudd fanger det). - **Feilscenario:** en ekstraksjon med tom CSV → krasj INNE i `materialize`s skrive-løkke, ETTER at stemplede filer er fjernet og tidligere ekstraksjoner skrevet, FØR `_update_index` → bundle etterlatt delvis materialisert (bekreftet: `ingest-first.md` skrevet, ingen `index.md`, gamle stemplede filer allerede slettet). SQL-stien er immun (header alltid til stede). - **Fiks:** vokt tom `rows` med klar fail-fast FØR disk-mutasjon (eller behandle som tom tabell) → sesjon **K2.9** (byggbar nå). ### R-4 [LOW–MEDIUM · korrekthet] UTF-8 BOM korrumperer stille første CSV-header - **Belegg (kjørt av korrekthetsagent; encoding re-verifisert av orkestrator: 5× `encoding="utf-8"`, ingen `utf-8-sig` i `ingest.py`):** Excel/Windows-CSV med ledende BOM → første header-celle blir `'code'` i stedet for `'code'`; korrupt header flyter verbatim inn i markdown-tabell + concept-fil. Ingen krasj — stille datakorrupsjon. - **Fiks:** les CSV med `utf-8-sig` → sesjon **K2.9**. ### R-5 [LOW–MEDIUM · robusthet] Malformert men EKSISTERENDE lenket concept-fil avbryter ALL bundle-navigasjon - **Belegg (kjørt av korrekthetsagent):** `parse_concept_file(resolved)` i `navigate_bundle` (`okf.py:118`) kaster `ValueError` for en lenket fil som eksisterer men mangler frontmatter/`type` — fanges ikke. Robusthetsregelen (skip broken/escaping links) dekker kun ikke-eksisterende / utenfor-bundle mål. - **Feilscenario:** én dårlig generert/kuratert fil tar ned hele lese-konteksten: `bundle_context`, `build_citations` OG `seed_store_from_bundle` (seeding avbrytes helt i stedet for å hoppe over den ene fila). Kan være tiltenkt fail-fast — **asymmetrien mot skip-broken-links fortjener en eksplisitt beslutning** (tolerér-og-hopp vs fail-fast), ikke stille divergens. - **Fiks:** beslutt semantikk + bind med test → sesjon **K2.9** (med eksplisitt valg dokumentert). ### R-6 [LOW · sikkerhet, latent] `inbox.write_verdict` mangler id-grammatikk → path-traversal ved skriving - **Belegg (kjørt av sikkerhetsagent; konstruksjon re-verifisert av orkestrator):** `VerdictDocument.id` er kun `Field(min_length=1)` (`inbox.py:46`) — ingen grammatikk, i motsetning til `source.id`/`extraction.id` (`_ID_RE`, `ingest.py:63-68,100-105`). `write_verdict` skriver `inbox_dir / f"{verdict.id}.json"` (`inbox.py:96`). Konstruksjon med `id="../../escaped"` aksepteres verbatim (orkestrator-kjørt) → skrivemål utenfor inbox. - **Feilscenario:** i dag ALLTID mintet id (`from_candidate`→`mint_verdict_id`, 16 hex) → ikke nåbar med fiendtlig input nå. Latent hvis en fremtidig sti skriver en verdict med untrusted id. Defensiv asymmetri beviser glippen: `promotion._filename_token` behandler samme id som fiendtlig (saniterer + hash-fallback, `promotion.py:34-40`). - **Fiks:** samme grammatikk/sanering før `write_verdict` eller på `VerdictDocument.id` → foldes inn i **C2.5** (inbox-herding). ### R-7 [LOW–MODERAT · robusthet] `_read_sql` materialiserer hele resultatsettet før `max_rows` (nytt tilfelle av C-S2) - **Belegg (kjørt av sikkerhetsagent):** `cursor.fetchall()` (`ingest.py:244`) FØR `len(data) > max_rows` (`ingest.py:247`); 1000-rads tabell med `max_rows=10` → alle 1000 i minne før cap-en hever feil. Samme feilmodus som C-S2 i `_read_csv`. - **Feilscenario:** robusthet/DoS-mot-seg-selv (begge kilder førstepart-scopet), ikke angriper-drevet i dag. Fail-fast i UTFALL (aldri stille trunkering) — akseptabel MVP-grense. - **Fiks (valgfri):** `LIMIT max_rows+1` / `fetchmany` for SQL, streaming-tell for CSV → **K2.9** eller notér som akseptert MVP-grense. ### R-8 [LOW · korrekthet, knyttet til C-F5] `seed_store_from_bundle` over-rapporterer antall; seeds uten `verdict_id` kollapser stille - **Belegg (kjørt av korrekthetsagent):** alle seedede verdicts deler SAMME features (bundle-IR-projeksjonen), så flere seed-filer UTEN `verdict_id` minter samme id → first-write-wins dropper alle utenom første, men funksjonen returnerer FIL-antallet (`experience.py:132,145-153`). To `type: verdict`-filer, ulik decision/rationale, ingen `verdict_id` → `seeded=2` returnert, `len(store)==1`; det andre læringssignalet tapes. `run_s10.py:86` printer det inflaterte tallet. - **Feilscenario:** konkret manifestasjon av C-F5s feature-halvdel + en count-ærlighetsfeil (rapportert antall ≠ faktisk lagret). Løses av per-verdict-nøkling (C3.2); count-ærligheten bør fikses uansett. - **Fiks:** C3.2 løser roten; legg en count-korreksjon (returner faktisk lagret antall, eller logg drops) i C3.2s scope. ### R-9 [MERKNAD · sikkerhet — presiserer C-F7/guard-plan, ikke ny defekt] Fold-som-injeksjonsbærer NÅR kjørestien via bundle-seeding - **Belegg (kjørt av sikkerhetsagent):** mens inbox-injeksjonen (C-F7) ikke er wiret til noen kommando (C-N2), ER samme injeksjonsbærer wiret via den ANDRE folde-kilden: `run_s10.py:73` → `seed_store_from_bundle` leser `type: verdict`-frontmatter og folder den inn. En forgiftet `promoted-verdict-*.md` i bundelen nådde folded prompt på run-stien (kjørt: `- aaaa [approved]: IGNORE PRIOR INSTRUCTIONS ... INJECT-MARKER-SEED`). Seed-stien er single-line-begrenset (frontmatter-parser), så inbox er strengt verre — men *fold-som-bærer* treffer den faktiske kjørestien. - **Konsekvens for guard-planen:** dette er nøyaktig trust-grensen guard-planens §2 rad 5/6 beskriver (mottatt/delt bundle med forgiftet promotert verdict). Bekrefter at G3 (`okf.import_bundle` ved delt/mottatt bundle) er riktig plassert, og at promoterings- gaten (§6, fail-closed, kun ekspert-godkjent) er den nåværende beskyttelsen. **Ingen ny kodeendring kreves nå** — men guard-planens markørlinje bør nevne bundle-seed-stien eksplisitt som den wirede fold-bæreren (i dag nevner den inbox). Notert i STATE-oppdatering. ### R-10 [LOW · testdekning] Run-path-orkestrering og SDK-meldingshåndtering er offline-ubundet - **Belegg (testkvalitetsagent, coverage i kopi, 90% total):** `run_s10.py` 0% (`main()`- orkestreringen, inkl. `BudgetExceeded`-stopp-grenen `:99-114` som docstringen selv kaller en tidligere regresjon «stopped here with NO record»); `sdk_client.py` 63% (meldingshåndteringen i `_complete_async` — tekst-aggregering, error-fail-paths, usage/kost-ekstraksjon, `_total_tokens` fail-closed, `max_budget_usd_per_call<=0`-guard — kjøres aldri fordi `test_sdk_isolation`s `fake_query` yield-er tom strøm). - **Feilscenario:** en regresjon i stopp-artefakt-persisteringen eller usage-ekstraksjonen går ubemerket. `run_s10.py` er fasit-sti (skal ikke endres), men logikken KAN bindes offline bak en klient-seam på den NYE kjøresti-inngangen (C2.0) uten å røre fasit-stien. - **Fiks:** budsjett-stopp-binding på C2.0-stien (R-10a); syntetisk-strøm-test for `sdk_client._complete_async` (R-10b) → foldet inn i **C2.0** og **C2.5** (se sesjonsplan). ### Lavere / kant (korrekthetsagent + spec-agent, kort — hver notert, ikke egen sesjon) - **assumptions-bånd uten grense** (`ir.py:37`): negative bånd-verdier godtas → skjev MC (negative p10/p50, kjørt). Løses av samme finitthets-/gyldighets-hardening som R-2 (C2.6). - **tom-streng modell-id passerer `ModelMapContract`** (`contracts.py:43`, ingen `min_length`) → `resolve_model` kan returnere `""`. PLAUSIBEL, lav. Foldes inn i C2.6/contracts-hardening. - **promotion filnavn-token-kollisjon** (`promotion.py:34-40`): to distinkte usikre id-er → samme token → overskriv/dedup. Kun nåbar med ikke-mintede id-er. PLAUSIBEL, kant. - **anker/query-krysslenker droppes stille** (`okf.py:24` `_CROSSLINK_PATTERN` krever `.md)` umiddelbart) → `(x.md#sec)` navigeres aldri. LAV, spec-tro (regelen er `\]\(([^)]+\.md)\)`). - **ikke-SELECT SQL → `cursor.description is None` → TypeError** (`ingest.py:243`). Konfig-feil, kryptisk melding. PLAUSIBEL, lav. - **`check_turn_safety_net` er en død vokter** (`loop.py:111`): turns ≤ 2·max_rounds < 2·max_rounds+2, så den kan aldri utløses gitt løkkegrensene. Ikke en defekt — men §8s «turn-count termination safety net above [the round cap]» er dermed en no-op i praksis; notér i C2.6 eller egen liten opprydding. - **OBS-2 (spec-agent):** `DataSourceContract.docs_dir` valideres (`contracts.py:36`) men `run_s10` navigerer `args.bundle` direkte (`run_s10.py:63,69`) — konsistent i dag, men et fremtidig avvik passerer stille. LAV. --- ## 3. Testkvalitet + §11 load-bearing (detach-stikkprøver) **4/4 detach-eksperimenter RØDE i throwaway-kopi** (testkvalitetsagent), hver revert→grønn, arbeidstre urørt (`git status` = kun `?? .claude/`, HEAD `b46f32c`): | # | Detach (i kopi) | Forventet | Faktisk | |---|---|---|---| | A | checker-override kortsluttet (`loop.py apply_checker_gate`) | `test_checker_gate_loadbearing` rød | **RØD, 3 failed** | | B | inbox-merge → `pass` (`inbox.py`) | `test_step7_async_loop_loadbearing` rød | **RØD, 2 failed** | | C | verbatim-id re-mintet (`experience.py seed`) | `test_step8_promotion_loadbearing` rød | **RØD, 2 failed** | | D | `okf_type` fjernet fra ingest-spec-KOPI | `test_ingest_spec_loadbearing` rød | **RØD, 1 failed** | Baseline i kopi 265 passed; etter alle reverts 265 passed re-bekreftet. Metoden holder på de stikkprøvde sømmene (paritet med 2026-07-10s 11/11 på disse fire). **Presisjons-lærdom (detach D):** ingest-spec-vaktens assertion er substring-hvor-som-helst, ikke tabellspesifikk — å fjerne KUN §12-tabellraden ville ikke detache (feltet står også i felt-definisjonen). Dette må C1.1s method-spec-vakt ta høyde for (dokumentér begrensningen eller scope assertion til §12-blokken). **Testkvalitetssveip:** ingen vakuøse assertions (ingen `assert True`/`xfail`/`skip`/TODO); ScriptedClient (`tests/_scripted.py`) bekreftet SDK-import-ren (importerer kun `typing.Callable` + `loop.ModelReply`; `loop.py` drar ingen SDK); suiten deterministisk uten nøkkel/nett (ingen ambient `os.environ`/socket/api_key; eneste `setenv` er lokal sqlite-fixture-sti). Dekningshull: **R-10** (run_s10 0%, sdk_client 63%). --- ## 4. SDK-bruk + modell-map-gyldighet (SDK-agent, med kilder) - **Alle SDK-attributt-premisser i `sdk_client.py` holder mot installert 0.2.110** — verifisert mot pakkekilden (`.venv/.../claude_agent_sdk/types.py`): `AssistantMessage.error/.model`, `ResultMessage.usage/.total_cost_usd/.is_error/.subtype/.errors`, `ClaudeAgentOptions.max_budget_usd/.setting_sources/.system_prompt/.max_turns/.model/.tools`, `TextBlock.text`, `query`. `setting_sources=[]`-isolasjonspåstanden og `system_prompt=None`→`--system-prompt ""`-serialiseringen er ordrett dokumentert/verifisert ned til CLI-argumentet (`subprocess_cli.py:227-228`). - **Nyeste SDK: 0.2.120 (15. juli 2026)**; installert 0.2.110. **Ingen breaking changes** på de brukte attributtene 0.2.110→0.2.120 (hovedsakelig bundlede CLI-bumps). **0.2.111 (6. juli) fikser feil som treffer nettopp denne klientens leseløkke** (NDJSON-parser mistet whitespace på linjer >64 KiB; TypeError-krasj når `content` er ren streng; zombie-subprosess ved cancellation). Kilder: [pypi.org/project/claude-agent-sdk](https://pypi.org/project/claude-agent-sdk/), [GitHub releases](https://github.com/anthropics/claude-agent-sdk-python/releases), hentet 2026-07-16. - **Anbefalt pin: `claude-agent-sdk>=0.2.111,<0.3`** (C-N3) + versjonsvakt-test (C2.5). - **Modell-map:** `claude-haiku-4-5-20251001` (default/proposer/checker) er **gjeldende** i offisiell modell-oversikt, IKKE deprecated, og **fortsatt billigst egnet** ($1/$5 MTok; eneste rimeligere er retired Haiku 3.5; ingen Haiku nyere enn 4.5). Kostnadsdisiplinen (Haiku 4.5 for en eventuell live-kjøring) holder. Kilder: [platform.claude.com models](https://platform.claude.com/docs/en/about-claude/models/overview), [pricing](https://platform.claude.com/docs/en/about-claude/pricing), hentet 2026-07-16. `ModelMapContract` matcher fil-strukturen; ingen manglende/ubrukte roller. - **Presisjonsnoter:** `test_sdk_isolation.test_the_system_prompt_is_empty` hevder i docstringen `--system-prompt ""`-serialisering men asserterer bare `system_prompt is None` (grønn-men-død for akkurat den claimen — påstanden er korrekt, testen dekker den ikke); runtime-feltnavnene i `usage`-dicten (`input_tokens` etc.) kan ikke bekreftes uten et forbudt `query()`-kall, men koden degraderer fail-closed til `None` ved avvik. --- ## 5. Spec-konformitet (spec-agent) - **~70 normative krav KONFORME** (implementert + bundet av test). Nøkkelbelegg: §3 Steg 1 navigasjon `okf.py:93-119` + verdict-eksklusjon `:131`; fold/seeding `experience.py:60-171`; Steg 4 validator `validator.py:50-83` + checker-gate `loop.py:186-212`; §6 promotion `promotion.py:48-103`; §8 budsjett `budget.py:35-57`; §10 startup `contracts.py:98-116`; ingest verdict-reservasjon `ingest.py:114-122`, provenance-lag `:279-297`. - **Konformitetsgap:** §1 konformans-pkt 3 (hver load-bearing søm har detach-test) er 9/11 — C-N1 (spec-integritet) + R-1 (closed loop) mangler; §12-kompletthet er derfor ikke håndhevet (spec §12 lover at spec-integritetstesten gjør det). - **Konformt-men-uvoktet (folder inn i C-N1):** §1 ærlighetsregelen, §4.3 persona-prosa- nøytralitet og §11 spec-integritet er alle konforme NÅ, men uten automatisk vakt — drift ville være usynlig. Nettopp lasten C-N1 skal bære. - **ingest §8 Network gate:** N/A i D7-scope (http uimplementert/nektet ved manifest-validering; file+sql lokale → ingen egress-sti). Bekreftet av sikkerhetsagentens egress-grep (0 treff). **§12-felt konsumert av src-logikk** (for C1.1s vaktdesign): `decision`, `rationale`, `id`/`verdict_id`, `proposal_features`, `affected_codes`, `measure_type`, `claimed_saving_nok`, `description`, `marker`, `project_id`, `measure`, `affected_items`, `code`, `quantity`, `unit_cost`, `assumptions`, `validates`, `nominal_feasible`, `p10`, `p50`, `p90`, `realization_rate`, `expected_actual_saving_nok`, `type`, `approved`, `rejected`, `approved_with_adjustment`. **Kun golden/test-konsumert (ikke lest av src):** `outcome`, `modelled_saving_nok`, `gap_source`, `context_key`. Anbefaling: vakten dekker ALLE 25 (mirror av ingest-vakten), med bevissthet om at de fire siste er fasit-/test-konsumert. --- ## 6. Ærlighetsregel §1 (docs) Verifisert av orkestrator mot faktiske artefakter, OG uavhengig bekreftet av ærlighets-agenten (samme konklusjon: én myk overclaim, ingen harde, ingen umerkede stand-ins, ingen utdaterte tall; agenten rekonsilierte S10-tallene byte for byte og la til p10 68 543 / p90 121 057). **Ingen OVERCLAIM funnet** (utover den ene myke F1/C-N2 under). Docs er gjennomgående ærlige (§1). Verifiserte claims: | Claim | Verifisert mot | Status | |---|---|---| | «265 tests, offline uten API-nøkkel» (`README.md:16-18,153`; `CHANGELOG`) | `uv run pytest` (265 passed uten nøkkel/nett) | ÆRLIG | | S10-tall: «$0.127514 · 36 791/150 000 tokens · 2 of 12 rounds» (`README.md:132-138`) | `runs/s10/usage.json` (`cost_usd 0.127514`, `tokens_used 36791`, `max_tokens 150000`, `rounds_used 2`, `max_rounds 12`) — matcher byte for byte | ÆRLIG | | «everything else is deterministic and offline; nothing here claims more live behaviour than that one documented run» (`README.md:140-141`) | S10 er eneste live-kjøring; resten offline | ÆRLIG (eksplisitt §1-selvbegrensning) | | «http/MCP … D7 does not implement it … no network egress path» (`README.md:118-123`, `extending.md:128-155`) | egress-grep 0 treff (sikkerhetsagent); http avvist fail-fast | ÆRLIG (gjentatt, presist hedget) | | «`create_sdk_mcp_server`/`@tool` … not used anywhere in this repo» (`extending.md:152-155`) | `ingest.py` ren stdlib | ÆRLIG | | ScriptedClient som stand-in | `_scripted.py:1-7` merket §1 | ÆRLIG | | ingest «zero model calls, imports nothing from SDK» (`README.md:100-102`) | `ingest.py:5`; import-grep | ÆRLIG | **Én honesty-nær sak (ikke overclaim, men upresis):** README-ens inbox-avsnitt (`README.md:66-68`) «a later run ingests it tolerantly and merges it before the fold» er sant om KONTRAKTEN, men ingen leverbar kjøresti komponerer den i dag (C-N2 — `merge_inbox_into_store` kalles kun fra tester). Formuleringen impliserer en kjørbar «later run» som ikke finnes som kommando. Lukkes av C2.0 (kjøresti-inngangen gjør claimet sant om en skipbar sti). Merk tilliggende nyanse fra R-9: fold-bæreren ER wiret via bundle-seeding på run-stien — så «merges it before the fold» er delvis sant (seed-folden kjører; inbox-folden gjør ikke). **Presis fiks (ærlighets-agentens anbefaling):** ordet «run» i `README.md:67` er det eneste som bærer overclaimet — kvalifisér til f.eks. «the async-loop seam — proven by `test_step7` in a two-run simulation — ingests … before the fold», ELLER (bedre) la C2.0 gjøre setningen bokstavelig sann ved å levere kjøresti-inngangen. C2.0-scopet oppdaterer allerede README-inbox-avsnittet. **Ærlighetsregelens egen svakhet (konformitet):** ingen automatisk vakt håndhever §1 på docs/spec (C-N1 mangler) — dagens ærlighet er ekte, men drift ville være usynlig. C1.1 lukker spec-siden; en docs-ærlighets-test (K12s README-claims-mot-CLI-sjekk) lukker README-siden. --- ## 7. Verifiseringslogg | Påstand | Kilde/kommando | |---|---| | 265 passed / ruff / mypy clean | `uv run pytest -q` (1.68s); `ruff check .`; `ruff format --check .`; `mypy src` (2026-07-16) | | HEAD/remote-sync | `git rev-parse HEAD origin/main open/main commons/main`; `git fetch` alle | | commons-amendment ikke landet / toolkit finnes ikke | subtree-diff `HEAD:shared`↔`commons/main` (docs-only); `ls ~/repos/okf-toolkit` + `git ls-remote` → not found | | R-2 (Infinity) | orkestrator kjørte mot live `ir`+`validator`: `unit_cost:Infinity` → ValidatedProposal(True, inf, p90=inf); `assumptions:[1.0,Infinity]` → True, p90=inf; NaN → ValidationError | | R-3 (tom CSV) | orkestrator: `ingest._render_table([])` → `ValueError: not enough values to unpack`; korrekthetsagent: delvis bundle bekreftet i kopi | | R-4 (BOM) | orkestrator: `ingest.py` har 5× `encoding="utf-8"`, 0× `utf-8-sig`; korrekthetsagent kjørte korrupt header i kopi | | R-6 (id-traversal) | orkestrator: `VerdictDocument(id="../../escaped")` aksepteres verbatim; sikkerhetsagent skrev fil utenfor inbox i kopi | | 4/4 detach røde | testkvalitetsagent, throwaway-kopi, import-isolasjon verifisert, revert→265 passed, arbeidstre urørt | | SDK-premisser + modell-id | SDK-agent mot `.venv/.../types.py` (0.2.110) + PyPI/GitHub/platform.claude.com (2026-07-16) | | C-F7 / R-9 fold-injeksjon | sikkerhetsagent, kjørt i kopi: inbox verbatim; bundle-seed på run-stien (`run_s10.py:73`) verbatim | | Guard-plan §2-tabell rad 1–6 | sikkerhetsagent, kjørt: path-escape avvist, sql read-only, http fail-fast, 0 egress-call-sites | | S10-artefakter urørt | kun lest; ingen re-kjøring | --- ## 8. Oppsummering for sesjonsplanen Nye ugatede hardening-sesjoner (byggbare nå, injisert i [sesjonsplanen](2026-07-16-sesjonsplan-paritet.md)): - **C2.6** — validator/IR finitthets- og gyldighets-hardening (**R-2** primær; + negative bånd, tom-streng modell-id, død turn-safety-net-vokter). Høyest verdi: lukker en faktisk validator-omgåelse på kjørestien. Golden uendret (finite tall). - **K2.9** — ingest-robusthet (**R-3** tom CSV fail-fast før disk-mutasjon; **R-4** `utf-8-sig`; **R-5** malformert-lenket-fil-semantikk med eksplisitt beslutning; **R-7** valgfri max_rows- strømming). Golden uendret. - **R-1/K4** — closed-loop to-kjørings-simulering (§11-raden), allerede i planen. - **R-6** → foldet inn i **C2.5** (id-grammatikk på `VerdictDocument`). - **R-8** → foldet inn i **C3.2** (count-ærlighet ved seeding). - **R-10** → bindinger foldet inn i **C2.0** (budsjett-stopp på ny sti) + **C2.5** (sdk_client syntetisk-strøm-test).