Operatør-mandat 2026-07-16: full MAF-paritet (D-G pkt. 2). Review-økt, ingen kildekode. - review-paritet.md: C-serien re-verifisert (alle åpne), 10 nye R-funn. Kjørt-bevist av orkestrator: R-2 (Infinity → validates=True, NaN avvist), R-3 (tom CSV krasjer ingest → delvis bundle). R-1 = §11-raden «Closed loop» ubundet. 4/4 detach røde i throwaway-kopi; SDK-premisser holder mot 0.2.110→0.2.120; model-id gyldig+billigst; docs §1-ærlige. - sesjonsplan-paritet.md: ERSTATTER 2026-07-10-planen. 35-raders paritetsmatrise (opphevet «Overføres IKKE»-lista, re-klassifisert rad for rad). 16 ugatede sesjoner + gatede med eksakte utløsere + avhengighetsgraf + binær leveringsklar-sjekkliste. - beslutningsbrief.md: operatør-køen i klarspråk (D-A+C-P1, amendment, toolkit-§8, D-B, + nytt foreslått delt punkt). - 2026-07-10-sesjonsplan-d7.md: superseded-merket med peker. Steg 0 verifisert: commons-amendment IKKE landet, okf-toolkit-repo finnes IKKE. STATE.md er local-only (gitignored) — ikke committet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
374 lines
27 KiB
Markdown
374 lines
27 KiB
Markdown
# 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).
|