docs(review): full-paritet review + sesjonsplanverk + beslutningsbrief (Fable 5 + 6 Opus-agenter)

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>
This commit is contained in:
Kjell Tore Guttormsen 2026-07-16 08:36:07 +02:00
commit d746891f06
4 changed files with 1254 additions and 1 deletions

View file

@ -1,6 +1,12 @@
# Sesjonsplan D7 — speilings-sesjoner + egne funn (C1.1C4.0 · delt kø D-AD-E)
> **Status:** GJELDENDE sesjonsplan for dette repoet. Forfattet av kryssmodell-reviewen
> **⚠️ SUPERSEDED 2026-07-16:** erstattet av
> [`2026-07-16-sesjonsplan-paritet.md`](2026-07-16-sesjonsplan-paritet.md) (operatør-mandat
> full MAF-paritet; §4-listen «Overføres IKKE hit» er OPPHEVET og re-klassifisert der).
> C-sesjonene C1.1/C2.0/C2.5/C2.7/C2.8/C3.2/C3.5/C4.0 er videreført i den nye planen.
> Dokumentet under beholdes uendret som historikk.
>
> **Status:** ~~GJELDENDE~~ sesjonsplan for dette repoet. Forfattet av kryssmodell-reviewen
> 2026-07-10 (Fable 5) — funn-referanser (C-F2/C-F3/C-F4/C-F5/C-F7, C-N1C-N4, C-P1) peker
> til [reviewen](review-2026-07.md); MAF-referanser (F1F14, D-AD-E, S2.x/S3.x/S4.x) peker
> til hovedrepoets `docs/review-2026-07.md` og `docs/plan/2026-07-10-sesjonsplan-fase2-6.md`.

View file

@ -0,0 +1,122 @@
# Beslutningsbrief til operatøren — hva som venter på deg, og hva det låser opp her (2026-07-16)
> Skrevet i klarspråk, uten funn-ID-er i selve spørsmålene (ID-er kun i fotnoter).
> Beslutningene tas i HOVEDREPOETS delte kø — ingenting under besluttes i dette repoet.
> Kontekst: operatør-mandatet 2026-07-16 er full paritet med MAF-repoet; den nye
> [sesjonsplanen](2026-07-16-sesjonsplan-paritet.md) planlegger rundt gatene under, aldri
> forbi dem. Alt ugatet arbeid (16 sesjoner) kan kjøres uavhengig av alle punktene her.
## 1. Spec-rettelsene i regelverket (den delte «D-A»-bolken)
**Hva det gjelder:** Fire kjente svakheter i det delte regelverket begge søsken bygger fra:
(1) validatoren har en beregnet «maksimalt gjennomførbar besparelse»-grense som aldri
håndheves, og modellen forfatter selv usikkerhetsbåndene som kan blåse den; (2) det finnes
ingen forankring mot prosjektets faktiske kostlinjer, så oppdiktede kostlinjer kan passere
validering; (3) lenkeformen med ledende skråstrek, som standardformatet anbefaler, hoppes over
i stedet for å forstås; (4) læringslageret nøkler alle dommer i en bundle på ett og samme
kandidat-tiltak, så dommer om ulike tiltak blandes.
**Hva det låser opp her:** fire ferdig-spesifiserte byggesesjoner med kjørte moteksempler som
test-fixtures.¹ Rettelse (2) er i tillegg et absolutt krav FØR en eventuell ny live-kjøring.
**Anbefaling:** godkjenn alle fire som én bolk. Kompatibiliteten er ferdig verifisert på denne
siden: regel-strammingen (1) bryter verken fasit-eksemplet eller noen eksisterende testdata.
Etter godkjenning: rettelsene skrives i fellesrepoet, BEGGE søsken henter dem, og BEGGE
testsuiter kjøres — aldri én side alene.
**Ta samtidig stilling til en kollisjon vi har flagget:** planen for semantisk henting (egen
beslutning, se pkt. 5) står i dag i direkte konflikt med regelverkets frosne krav om at
likhets-rangering aldri skal bygge på overflatetekst. Enten må regelverket bevisst amenderes
på det punktet (som del av denne bolken), eller så må semantisk henting re-scopes til å bygge
på strukturelle kjennetegn alene.² **Anbefaling:** re-scoping — det bevarer det frosne
prinsippet og er implementerbart uten regelendring.
## 2. Innholdsmodell-amendmenten (kunnskapsbibliotek + dimensjonskatalog)
**Hva det gjelder:** Utkastet fra 14. juli (ligger klart i hovedrepoet³) som gir regelverket
et kildebelagt kunnskapsbibliotek (tiltaksmønstre/erfaringsnotater/faglige råd), en
dimensjonskatalog som konfig, og «sammendrag først»-lesing så biblioteket kan vokse uten å
drukne prompten. Venter kun på din godkjenning av teksten.
**Hva det låser opp her:** hele bibliotek-/katalog-sesjonen i dette repoet (K13 i den nye
planen) — som også er dette repoets eneste planlagte kilde til dimensjons-begrepet, som igjen
gir ruting og scoping full verdi. Tilsvarende sesjon på MAF-siden (S3.5) venter på det samme.
**Anbefaling:** godkjenn utkastet. Det er konstruert for å holde begge repoenes
spec-integritetsvakter grønne, og fasit-eksemplene endres ikke. Merk at dette repoets egen
spec-vakt bygges i første ugatede sesjon (C1.1) med samme robusthet, så amendmenten kan landes
før eller etter den — rekkefølgen er likegyldig.
## 3. Verktøykasse-repoet (okf-toolkit)
**Hva det gjelder:** Besluttet 14. juli: felles formatverktøykasse i eget repo. Repoet finnes
ennå ikke (verifisert i dag), og briefens fire åpne spørsmål venter på deg: endelig navn,
lisens (MIT-presedens), publiseringsrekkefølge (kjerne først?), og bekreftelse på eget
Forgejo-repo.⁴
**Hva det låser opp her:** skrive-halvdelen av formatlaget, som dette repoet mangler og
MAF-repoet har — selve hovedgevinsten i «minst like godt»-mandatet ditt. Adopsjonssesjonen
(K20) er planlagt og venter kun på at verktøykassa finnes og at du godkjenner koordinert
migrering i begge søsken samtidig.
**Anbefaling:** svar på de fire spørsmålene og kjør repo-init-prompten i briefen. Inntil da
er skrive-halvdelen udekket her, og skråstrek-lenkerettelsen (pkt. 1.3) bygges lokalt med en
eksplisitt merknad om senere flytting inn i verktøykassa.
## 4. Omfanget for ekte datakilder (den delte «D-B»-beslutningen)
**Hva det gjelder:** Om (og hvordan) ingest-laget skal utvides mot virkeligheten:
MCP-kildefamilie, PDF-dokumentkilde, og http/live-kilde med herding — som i dag er bevisst
avvist i begge søsken og krever en bevisst amendment av det frosne ingest-målbildet.
**Hva det låser opp her:** tre konnektor-sesjoner (K14K16). Merk endringen fra forrige plan:
paritetsmandatet ditt betyr at disse nå SKAL speiles her når beslutningen lander — den gamle
planens «ingen HTTP/MCP-økt i dette repoet» er opphevet. Sikkerhetsvakten
(llm-ingestion-guard) wires inn i samme økt som http-kilden — det er dens hovedplass.⁵
**Anbefaling:** som MAF-planens anbefaling (nedskop til mock-herding nå; SQL som første
live-kilde senere) — men med eksplisitt tillegg om at amendmenten skrives felles så begge
søsken bygger samme kildefamilier.
## 5. De øvrige delte beslutningene (vektor-lager, samtidighet, mål/paritet)
**Hva det gjelder / låser opp her:**
- **Vektor-lager** (semantisk henting): låser opp K17 her — MEN se kollisjonen i pkt. 1.
- **Samtidighet/feilmodell:** låser opp K18/K19 her (parallell porteføljekjøring +
kostnadsstyring på tvers). Blir først aktuelt etter at porteføljefundamentet (K1K3, ugatet)
er bygget — ingen hast fra dette repoets side.
- **Mål-semantikk + stack-paritet for ekte kjøring:** låser opp prosentmål-delen av
målkontrakten her, og avgjør om dette repoet i det hele tatt skal kjøre live igjen
(M2-analogen). Uten den beslutningen bygges alt live-forberedende (estimat, preflight,
drill) ferdig offline — og stopper der.
**Anbefaling:** ingen hast på noen av disse tre fra dette repoets side; ta dem når MAF-løpet
trenger dem. Kun koblingen i pkt. 1 (semantisk henting vs. frossen rangeringsregel) bør tas
NÅ, fordi den hører hjemme i samme bolk som spec-rettelsene.
## 6. NYTT beslutningsbehov avdekket av paritetsplanen (foreslås inn i den delte køen)
**Hva det gjelder:** Paritetsplanen gir dette repoet en besparelses-hovedbok, målkontrakt og
outbox (K1/K5) som MAF allerede har — men formatene er i dag stack-lokale på begge sider.
Skal de to søsknenes hovedbok-/outbox-artefakter være SAMMENLIGNBARE på filnivå (delt format i
fellesrepoet), eller holder det at kapabiliteten er lik mens formatene divergerer?
**Hvorfor det er ditt valg:** delt format krever en commons-amendment (mer koordinering, men
direkte sammenlignbare kjøringsartefakter til sammenligningsprotokollen og verdirapporten);
stack-lokalt er raskere og kan konvergeres senere via verktøykasse-repoet.
**Anbefaling:** stack-lokalt NÅ (K1/K5 bygger uten å vente), med eksplisitt
konvergens-vurdering når verktøykassa (pkt. 3) og verdirapportene på begge sider finnes.
Foreslås protokollert som nytt punkt i den delte køen i hovedrepoet — ikke besluttet her.
---
**Fotnoter (pekere for etterprøving):**
¹ D-A pkt. 14 ↔ funn C-F2/C-F3/C-F4/C-F5 (`review-2026-07.md` §1) → sesjonene
C2.7/C4.0/C2.8/C3.2 (`2026-07-16-sesjonsplan-paritet.md` §4).
² C-P1 (`review-2026-07.md` §1) — method-spec §3 Steg 1 «structural, never textual» vs
MAF-planens S3.1/D-C.
³ `portfolio-optimiser/docs/plan/2026-07-14-commons-amendment-utkast.md` (§5.2 flagger
D7-speilingen eksplisitt).
`portfolio-optimiser/docs/plan/2026-07-14-toolkit-repo-brief.md` §8.
`docs/2026-07-16-llm-ingestion-guard-plan.md` §4 G1; K16 i den nye sesjonsplanen.

View file

@ -0,0 +1,374 @@
# 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 [LOWMEDIUM · 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 [LOWMEDIUM · 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 [LOWMODERAT · 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 16 | 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).

View file

@ -0,0 +1,751 @@
# Sesjonsplan — full MAF-paritet (K-serien + videreførte C-sesjoner · delt kø D-AD-E)
> **Status:** GJELDENDE sesjonsplan for dette repoet. ERSTATTER
> [`2026-07-10-sesjonsplan-d7.md`](2026-07-10-sesjonsplan-d7.md) (markert superseded med peker
> hit). Forfattet 2026-07-16 (Fable 5, review-/plan-økt) på operatør-mandatet **FULL
> MAF-PARITET** (D-G pkt. 2: «Claude-repoet skal være minst like godt som MAF-repoet»,
> operatør-besluttet 2026-07-16). Den gamle planens §4-liste «Overføres IKKE hit
> (MAF-spesifikt)» er **OPPHEVET som blanko-unntak** — hvert punkt er re-klassifisert i
> paritetsmatrisen (§1). Ingen stille kutt.
>
> Funn-referanser: C-serien peker til [review 2026-07-10](review-2026-07.md); R-serien til
> [review-paritet 2026-07-16](2026-07-16-review-paritet.md); MAF-referansene (S2.0S5.4,
> D-AD-I, M1M3, T0) til hovedrepoets `docs/plan/2026-07-10-sesjonsplan-fase2-6.md`,
> `2026-07-14-revisjonspakke-DF-DI.md`, `2026-07-14-commons-amendment-utkast.md` og
> `2026-07-14-toolkit-repo-brief.md` — lest på KAPABILITETS-/PLAN-nivå; MAF-kildekode leses
> aldri (D7-regelen står for metode-laget; D-G endrer den bevisst KUN for OKF-formatlaget,
> og kun via okf-toolkit når det finnes).
>
> Prinsipp (uendret): hver bygg-sesjon er én-sesjons-stor, offline, gratis (ScriptedClient),
> TDD (Iron Law: failing test først), load-bearing (§11: detach-punkt per søm), og
> **Opus 4.8 xhigh-eksekverbar** med «Les STATE.md og følg instruksjonene». Ingen
> live-kjøringer: D6 står — S10 var programmets ene; ny spend krever eksplisitt
> operatør-godkjenning (M2-analogen §6 er operatør-milepæl, aldri autonom sesjon).
> «Suiten kjører uten nøkkel/nett» er invariant i ALLE sesjoner under.
---
## 0. Faktisk gate-status ved planleggingstidspunktet (verifisert 2026-07-16)
| Gate | Status | Belegg |
|---|---|---|
| D-AD-E (delt kø) | ÅPNE | MAF-plan §2 + MAF-STATE 2026-07-16 («Operatør-kø uendret») |
| D-FD-I | BESLUTTET 2026-07-14 | revisjonspakka (fasit) |
| Commons-amendment (D-F/D-G-utkastet) | **IKKE landet** — utkast venter operatør | subtree-diff `HEAD:shared``commons/main`: kun `.gitignore` + guard-adopsjonsdoc, ingen spec-endring |
| okf-toolkit-repo (T0) | **Finnes IKKE** | `ls ~/repos/okf-toolkit` + `git ls-remote` mot Forgejo → not found |
| MAF-fremdrift | Fase 1 + Fase 2-bolk A (S2.0/S2.1/S2.5) + S3.6 + S4.1 + S4.2 + S5.1 komplett; S5.2 EXECUTE neste; suite 389 | MAF `STATE.md` 2026-07-16 07:40 |
| Dette repoet | 265 passed offline · ruff/mypy rene · HEAD `b46f32c` == origin/main; open @ `42f320b` | kjørt i Steg 0 (review-paritet §0) |
| `shared/`-subtree | 2 commons-commits bak (docs-only) | samme diff; hentes ved neste koordinerte pull — ingen spec-endring, ufarlig |
Planen bygger på DENNE statusen. Endres gate-status (amendment lander, toolkit-repo opprettes),
gjelder §4-sesjonenes utløser-tekst, ikke antakelsene her.
## 1. Paritetsmatrise MAF ↔ D7 (kapabilitetsnivå)
Kilde: MAF-planene/STATE (aldri MAF-src). Klassifisering: **FINNES HER** (kapabiliteten er
bygget og testet i dette repoet) / **BYGGBAR NÅ** (ingen beslutnings-gate) / **GATED**
(navngitt gate) / **SPEILES IKKE** (etterprøvbar begrunnelse). Alle den gamle planens
«Overføres IKKE»-punkter er re-klassifisert her; ingen rad er stille droppet.
| # | Kapabilitet (MAF-referanse) | MAF-status | D7-status i dag (belegg) | Klassifisering | D7-sesjon |
|---|---|---|---|---|---|
| 1 | Dimensjons-begrep + katalog (Fase 1 / D-F pkt. 5 / amendment §13.3) | Bygget (Fase 1); katalog spec-es av amendmenten | MANGLER — ingen dimensjons-modell i src (filkatalog) | **GATED: commons-amendment (D-F)** — katalog-kontrakten blir spec (§13.3); bygges fra spec-en når den lander, ikke fra utkast | K13 |
| 2 | Mål-kontrakt (absolutt/prosent, hard/soft, stopp ved mål) (Fase 1) | Bygget; prosent-baseline åpen (D-E) | MANGLER | **BYGGBAR NÅ** (absolutt + hard/soft); prosent-baseline-semantikken alene er **GATED: D-E** | K1 |
| 3 | Besparelses-hovedbok (typet ledger, fail-closed `realize`-ekspertgate, provenance) (Fase 1) | Bygget | MANGLER | **BYGGBAR NÅ** (stack-lokal — MAF bygde den også uten delt spec) | K1 |
| 4 | Portefølje-kjøring (flere prosjekter, referanse-konfig, sekvensiell) (Fase 1) | Bygget | MANGLER — eneste kjøresti er `run_s10.py` (én bundle, fasit-frossen) | **BYGGBAR NÅ** | K2 |
| 5 | Portefølje-læringssløyfe — dom på prosjekt k når k+1s prompt (S2.0) | **FERDIG** (bolk A) | MANGLER (ingen portefølje-sti; primitivene finnes: store/fold/inbox) | **BYGGBAR NÅ** (etter K2) | K3 |
| 6 | Offline closed-loop-bevis — to-kjørings-simulering m/ markør (method-spec §11-raden «Closed loop»; MAF-original) | Bygget (originalbygget, `test_simulation_loadbearing`-raden) | **MANGLER** — step7/step8-testene beviser hver sin halvdel; ingen test kjører markøren gjennom to komponerte kjøringer (funn **R1**) | **BYGGBAR NÅ** — også et konformitetsgap, ikke bare paritet | K4 |
| 7 | Outbox-persistens (S2.1) | **FERDIG** | DELVIS — `artifacts.py` persisterer proposal/result/provenance/usage deterministisk (også stop-artefakter), men uten run_id-navngitt forslag/utfall-par → ingen pending-sporing mulig | **BYGGBAR NÅ** (utvidelse av artifacts-laget) | K5 |
| 8 | MCP-ingest-konnektor (S2.2, D-B-gated) | GATED D-B | MANGLER; `extending.md` ærlig «ubygd» | **GATED: D-B + commons-ingest-amendment** — SDK-nativ analog: in-process MCP-fixture via `create_sdk_mcp_server` (SDK-primitiv, verifisert i CLAUDE.md) i stedet for MAFs `mcp`-pakke-klient | K14 |
| 9 | Dokument-konnektor PDF (S2.3, D-B-gated) | GATED D-B | MANGLER | **GATED: D-B + commons-ingest-amendment** | K15 |
| 10 | Konnektor-herding http (S2.4, D-B-gated) | GATED D-B (MAF HAR http-demo å herde) | MANGLER MER — D7 har ingen http-kilde i det hele tatt (avvist fail-fast, `ingest.py:129`; valgfri per ingest-spec §1) | **GATED: D-B** — sesjonen bygger http-familien + herding + guard-G1-wiring i ett | K16 |
| 11 | Inbox-/robusthetsherding (S2.5 ↔ C2.5) | **FERDIG** (bolk A) | MANGLER — vokabular-sjekk/tak finnes ikke (C-F7 kjørt-bevist); SDK-pin løs uten versjonsvakt (C-N3) | **BYGGBAR NÅ** | C2.5 |
| 12 | Validator-stramming nominal-gate + band-invariant (S2.7 ↔ C2.7) | GATED D-A | Samme spec-defekt bekreftet her (C-F2, kjørt bevis) | **GATED: D-A pkt. 1 + commons-pull** | C2.7 |
| 13 | Semantisk henting (S3.1, D-C) | GATED D-C | MANGLER; OBS delt kollisjon C-P1: D-C som planlagt (cosine over tekst) bryter frossen §3 Steg 1 «structural, never textual» | **GATED: D-C OG C-P1-avklaring i D-A-bolken** (begge må lande) | K17 |
| 14 | Multi-kandidat ExpeL-seeding (S3.2 ↔ C3.2) | GATED D-A | Feature-halvdelen bekreftet defekt her (C-F5); id-halvdelen alt løst (verbatim) | **GATED: D-A pkt. 4 + commons-pull** | C3.2 |
| 15 | Concurrency/feilmodell (S3.3, D-D) | GATED D-D | Ikke anvendbar i dag (ingen portefølje-sti) — blir reell når K2 lander | **GATED: D-D** (+ K2 som forutsetning) | K18 |
| 16 | Kostnadsstyring på tvers av kjøringer (S3.4) | GATED D-D | MANGLER; pre-call-guard-delen er ugatet (C-N4) | **GATED: D-D** for portefølje-delen; pre-call-guard + run-total USD-tak er **BYGGBAR NÅ** | K19 (gated) + C3.5 (nå) |
| 17 | Innholdsmodell/bibliotek-materialisering + summary-tier-render (S3.5, D-F) | GATED amendment | MANGLER | **GATED: commons-amendment (D-F/D-G-utkastet)** — inkluderer §13-typene, `library`-ingest-familien (4a/5a), summary-tier (§3 Steg 1-endringen) og dimensjonskatalogen (rad 1) | K13 |
| 18 | Kostnadssimulering FØR kjøring (S3.6, D-I pkt. 3 — MÅ-krav) | **FERDIG** | MANGLER | **BYGGBAR NÅ** — D7-analog: what-if over `model_map.json` (Claude-modeller × effort), prisdata som skjema-validert konfig m/ kilde+dato | K6 |
| 19 | Kostbaseline-forankring (S4.0 ↔ C4.0) | GATED D-A | Samme spec-defekt bekreftet her (C-F3, 2.9 MNOK-beviset) | **GATED: D-A pkt. 2 + commons-pull** | C4.0 |
| 20 | Preflight (S4.1 — Foundry-spesifikk i MAF) | **FERDIG** | MANGLER | **BYGGBAR NÅ** — SDK-nativ analog: Anthropic-API/SDK-preflight, offline (env-kontrakt, model-map-konsistens, placeholder-nekt) — Foundry-auth-delen SPEILES IKKE (sant MAF-spesifikk på kapabilitetsnivå: D7s backend er Anthropic-API) | K7 |
| 21 | Live-kjørings-drill + artefakt-fangst (S4.2) | **FERDIG** | MANGLER | **BYGGBAR NÅ** (etter K5+K7) — `--live-dry-run`: bygg alt, STOPP før første modellkall (kall-tellende mock beviser 0 kall) | K8 |
| 22 | HITL-verdict-ruting + pending-sporing (S5.1) | **FERDIG** | MANGLER | **BYGGBAR NÅ** (etter K5 — id-join outbox↔inbox) | K9 |
| 23 | Varsling/Notifier (S5.2) | Plan klar, EXECUTE neste | MANGLER (D7 har ingen notify-stub engang — bygges rent) | **BYGGBAR NÅ** — kapabilitets-koordinering med MAF S5.2 (protokoll-form, webhook-opt-in, socket-fri suite), aldri kode-titting | K10 |
| 24 | CLI-paritet + dokumentsync + D-H-oppskrift (S5.3) | Gjenstår i MAF | MANGLER — ingen leverbar CLI utover fasit-stien | **BYGGBAR NÅ** (sist i ugatet rekke; C2.0 er første skritt) | C2.0 (inngang) + K12 (full paritet) |
| 25 | Verdirapport per kjøring (S5.4, D-I pkt. 2) | Gjenstår i MAF | MANGLER | **BYGGBAR NÅ** (etter K5 + K1 + K6) | K11 |
| 26 | okf-toolkit-adopsjon inkl. skrive-halvdelen A2 (D-G/T0) | T0 blokkerer også MAF | MANGLER — `okf.py` er kun-les (134 l); skrive-primitivene (promotion har egne) deles ikke | **GATED: T0** (repoet finnes ikke, verifisert 2026-07-16) + operatør (§8-spørsmålene i toolkit-briefen) | K20 |
| 27 | llm-ingestion-guard-wiring | MAF: planned (S2.4/S2.5-utvidelse) | Planned her (guard-plan 2026-07-16): G1 http-persist-gate / G2 inbox-depth / G3 import_bundle | G1 **GATED: D-B** (inn i K16) · G2 valgfri depth **vurderes i C2.5** · G3 **GATED: D-E**-retning | K16 / C2.5 / (D-E) |
| 28 | Steg-7 kjøresti-inngang (C2.0 — D7-egen) | (MAF har den via run-laget) | MANGLER (C-N2) | **BYGGBAR NÅ** | C2.0 |
| 29 | Method-spec-integritetsvakt (C1.1 — §11-raden «Spec integrity») | MAF HAR (`test_method_spec_loadbearing.py`, referert i amendment-utkastet §5.1) | MANGLER (C-N1) | **BYGGBAR NÅ** | C1.1 |
| 30 | OKF `/`-lenke-mapping (C2.8 / D-A pkt. 3 / D-G pkt. 1) | GATED D-A (MAF mangler også bygg-sesjon — flagget) | Spec-tro skip i dag (`okf.py:112`) | **GATED: D-A pkt. 3 + commons-pull** | C2.8 |
| 31 | Budsjett-guard pre-call + run-total USD (C3.5) | Del av S3.4 i MAF | MANGLER (C-N4; per-kall-USD-tak finnes alt — bedre enn MAF) | **BYGGBAR NÅ** | C3.5 |
| 32 | Operatør-milepæler M1M3 | Definert i MAF-planen | Ingen definert her | **SPEILES som milepæl-definisjoner** (§6) — aldri autonome sesjoner. M1-analogen (tenant+smoke) er Foundry-spesifikk; D7-ekvivalenten (nøkkel + preflight grønn) foldes inn i M2-analogens forutsetninger | §6 |
| 33 | Validator robust mot ikke-finite tall (review-paritet R-2 — D7-EGEN korrekthet) | (egen implementasjons-defekt) | **DEFEKT**`Infinity` gir `validates=True` (kjørt-bevist) | **BYGGBAR NÅ** | C2.6 |
| 34 | Ingest robust mot tom/BOM/malformert kilde (R-3/R-4/R-5/R-7 — D7-EGEN) | (egen implementasjons-defekt) | **DEFEKT** — tom CSV krasjer midt i materialisering → delvis bundle (kjørt-bevist) | **BYGGBAR NÅ** | K2.9 |
| 35 | Closed-loop to-kjørings-vakt (R-1 — §11-raden «Closed loop») | MAF HAR (`test_simulation_loadbearing`) | MANGLER — §11 rad 8 ubundet | **BYGGBAR NÅ** | K4 |
**Re-klassifisering av den opphevede «Overføres IKKE»-listen** (gammel plan §4): S2.0-portefølje
→ rad 45 (BYGGBAR NÅ); S2.1-outbox → rad 7 (BYGGBAR NÅ — «output-laget finnes her» var bare
delvis sant); S2.2 MCP → rad 8 (GATED D-B, SDK-nativ analog); S2.3 doc → rad 9 (GATED D-B);
S2.4 http-herding → rad 10 (GATED D-B); S3.1 → rad 13 (GATED D-C+C-P1); S3.3 → rad 15 (GATED
D-D); S3.4 → rad 16 (delt: C3.5 nå, K19 gated); S4.1/S4.2-Foundry → rad 2021 (SDK-nativ
analog, BYGGBAR NÅ; kun Foundry-*auth* speiles ikke); S5.x → rad 2225 (BYGGBAR NÅ-kjede);
M1M3 → rad 32. Setningen «`extending.md` står: ingen D7-HTTP/MCP-økt planlagt» er ikke
lenger dekkende — K14K16 planlegges nå, gated D-B; `extending.md` oppdateres i K16
(docs-ærlighet, §1).
## 2. Den delte beslutnings-køen (uendret — bor i HOVEDREPOET)
D-AD-E ligger i MAF-planens §2 og besluttes der; dette repoet planlegger ingen egen variant —
kun sesjoner gated på den. Det som gater arbeid HER, med anbefaling i klarspråk, står i
[beslutningsbriefen](2026-07-16-beslutningsbrief.md). Koblings-flagget **C-P1** (D-C ↔ frossen
§3 Steg 1) står uendret og MÅ inn i D-A-bolken. Ett NYTT beslutnings-kandidat-punkt foreslås
inn i den delte køen via briefen (delbarhet av ledger-/outbox-format) — det besluttes ALDRI
ensidig her.
**Commons-regel (uendret):** amendments skrives i commons, pulles med
`git subtree pull --prefix=shared commons main --squash`. ALDRI subtree push. Etter hver
amendment-bølge re-pinner/puller BEGGE repo og BEGGE suiter kjøres (sammenligningsprotokollen);
golden re-fryses kun koordinert i commons.
## 3. Byggbare NÅ (ingen beslutnings-gate) — anbefalt rekkefølge
> Felles verifisering for ALLE sesjoner (gjentas ikke per sesjon): full gate
> (`uv run pytest` + `uv run ruff check .` + `uv run ruff format --check .` +
> `uv run mypy src`) grønn; golden-regresjonen (`test_bygg_energi_mikro.py` +
> ingest-goldens) uendret grønn; suiten kjører uten nøkkel/nett; `runs/s10/` urørt;
> `run_s10.py` byte-urørt (fasit-sti). Nye moduler holdes SDK-frie (ren stdlib+pydantic)
> med mindre sesjonen eksplisitt sier noe annet, og SDK-frihet bindes med import-vakt etter
> `test_okf`-purity-mønsteret.
### C1.1 Method-spec-integritetsvakt (C-N1) — UENDRET fra gammel plan, ÅPNING
- **Mål:** §11-raden «Spec integrity» håndheves lokalt — spec-drift i subtree-synken fanges her.
- **Scope (filliste):** ny `tests/test_method_spec_loadbearing.py` (KUN testkode).
- **IKKE-scope:** ingen src-endring, ingen spec-endring, ingen endring i ingest-spec-vakten.
- **Gate:** ingen.
- **TDD-plan:** testen er selv vakten; RØD-bevis via mutert spec-KOPI i `tmp_path`
(parametrisert mot kopien, aldri mot `shared/`): (a) slett fila → rød; (b) injiser
rammeverksnavn (MAF/Claude/Anthropic/Azure/…, samme navneliste som ingest-vakten) → rød;
(c) fjern et konsumert §12-felt → rød. Innhold: (1) fil-eksistens + struktur-markører
(`## 1.``## 12.`, `### Step 1``### Step 8`, «MUST»); (2) framework-nøytralitet;
(3) §12-dekning for feltene DENNE koden konsumerer (`decision`, `rationale`, `id`,
`proposal_features`, `affected_codes`, `measure_type`, `claimed_saving_nok`, `description`,
IR-feltene `project_id`/`measure`/`affected_items`/`code`/`quantity`/`unit_cost`/`assumptions`,
golden-feltene `outcome`/`validates`/`nominal_feasible`/`p10`/`p50`/`p90`,
learning-surface-feltene, `type`, vokabular-verdiene `approved`/`rejected`/
`approved_with_adjustment`). Feltlisten valideres mot review-paritet §-vedlegget
(spec-konformitetsagentens konsumliste) i sesjonen. Presisjons-lærdom fra
detach-stikkprøven av ingest-vakten (review-paritet §3): substring-hvor-som-helst-assertion
gjør at et felt som også nevnes i løpeteksten ikke detacher ved ren tabell-fjerning —
dokumentér samme begrensning i method-spec-vakten, eller scope assertion til
§12-tabellblokken der det er praktisk.
- **Nøkkelantakelse (+test):** markørene i dagens method-spec matcher listen — testes FØRST
ved å kjøre vakten grønn mot uendret `shared/method-spec.md` før mutasjons-parametrene.
- **Verifisering:** `uv run pytest tests/test_method_spec_loadbearing.py` grønn; mutasjons-
parametrene røde mot kopi; full gate. **VIKTIG koordinering:** amendment-utkastet (§5.1) er
konstruert mot MAFs vakt-design (presence-asserts, ikke forbud mot nye seksjoner) — bygg
vakten HER med samme presence-semantikk, så en senere amendment-pull ikke gjør den rød.
### C2.5 Inbox-herding + SDK-versjonsvakt (C-F7, C-N3) — UENDRET + guard-G2-vurdering
- **Mål:** inboxen slutter å være en uvalidert injeksjonsflate; SDK-attributt-premissene får
en vakt som tvinger bevisst re-verifisering ved oppgradering.
- **Scope (filliste):** `src/portfolio_optimiser_claude/inbox.py`, `pyproject.toml`,
`tests/test_step7_async_loop_loadbearing.py` (utvidelse), ny `tests/test_sdk_version_guard.py`.
- **IKKE-scope:** guard-*wiring* (kun vurderingsnotat, se under); endring i tolerant-lasting-
semantikken for øvrige felter; `experience.py`.
- **Gate:** ingen.
- **TDD-plan:** RØD: inbox-fil med `decision: "hva-som-helst"` når ALDRI storen — reviewens
kjørte C-F7-bevis blir fixturen (detach-punkt: fjern vokabular-sjekken → den entrer → rød).
Vokabular på fil-laget = §4.2-settet {approved, rejected, approved_with_adjustment}; ukjent
→ SKIP, aldri raise (tolerant-regelen består). RØD: rationale > konfigurerbart tak →
fail-fast med presis feil (aldri stille kutt); RØD: antall filer > tak → fail-fast. RØD
(R-6): `VerdictDocument.id` med path-traversal (`../../escaped`) → avvist/sanert FØR
`write_verdict` skriver (detach id-grammatikken → fil skrives utenfor inbox → rød;
sikkerhetsagentens kjørte bevis blir fixturen; speil `_ID_RE` fra `ingest.py` eller
`promotion._filename_token`-saneringen). RØD:
versjonsvakt mot fake-versjon (monkeypatch `importlib.metadata.version`) — melding skal
navngi `sdk_client.py`-premissene som må re-verifiseres. Pin: **`claude-agent-sdk>=0.2.111,<0.3`**
(sdk-review 2026-07-16: alle attributt-premisser verifisert mot installert 0.2.110 OG mot
releases t.o.m. nyeste 0.2.120 — ingen brudd; 0.2.111 bærer NDJSON-/content-parsing-fikser
som treffer nettopp denne klientens leseløkke, se review-paritet §2). Sesjonen re-kjører
attributt-verifiseringen mot versjonen `uv sync` faktisk resolver.
I tillegg (fra R4/R5, test-detach + sdk-review): (5) bind `sdk_client._complete_async`-
meldingshåndteringen offline med en syntetisk strøm (fake_query som yield-er
AssistantMessage/ResultMessage-verdier: tekst-aggregering, error-fail-paths,
usage/kost-ekstraksjon, `_total_tokens`-fail-closed, `max_budget_usd_per_call<=0`-guarden
— i dag kjøres ingenting av dette i suiten); (6) rett `test_sdk_isolation`-docstringen som
hevder `--system-prompt ""`-serialisering testkroppen ikke beviser (grønn-men-død for
akkurat den claimen — ærligst å omformulere docstringen; å binde transport-serialisering
ville lese SDK-privat API og gjeninnføre F11-skjørhet).
- **Guard-G2-vurdering (leveranse i sesjonen, ikke wiring):** kort avsnitt i commit-meldingen/
STATE om `scan_output`-depth over `rationale` er verdt en senere additiv økt —
C2.5-allowlisten er den nødvendige delen (guard-planen §4 G2). Markørlinjen i STATE
oppdateres hvis vurderingen endrer trigger-bildet.
- **Nøkkelantakelse (+test):** step7-suitens tolerant-lasting består uendret — kontrolltest:
eksisterende `test_non_json_broken_and_incomplete_files_are_skipped` forblir grønn.
- **Verifisering:** nye tester grønne + detach → rød; step7-suiten uendret grønn; full gate.
### C2.0 Steg-7 kjøresti-inngang (C-N2) — UENDRET
- **Mål:** en skipbar inngang komponerer §5-sekvensen (merge inbox → seed → fold → run), så
README-ens inbox-avsnitt er sant om en leverbar sti.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/run.py` (offline-testbar
komposisjonsfunksjon `compose_run_context(bundle_dir, inbox_dir=None, k=...)` + tynn CLI
`python -m portfolio_optimiser_claude.run --bundle/--inbox/--out` som gjenbruker
contracts/meter/artifacts), ny `tests/test_run_entrance_loadbearing.py`, README-avsnittet
om inboxen (peker på ny inngang).
- **IKKE-scope:** `run_s10.py` (byte-urørt fasit); ny live-kjøring (CLI testes kun med
ScriptedClient; SDK-klienten wires men eksekveres ikke i suiten); portefølje (K2).
- **Gate:** ingen.
- **TDD-plan:** RØD: dom droppet i inbox-mappe → inngangens komponerte kontekst bærer dommens
markør (detach-punkt: fjern merge-kallet i komposisjonen → rød); kontroll: tom/manglende
inbox → base-kontekst uendret; RØD: inngangen skriver ALDRI til inboxen (byte-sammenligning
før/etter, §5-rolledelingen); RØD (fra R3, test-detach-coverage): budsjett-stopp under
kjøring via den NYE inngangen → stopp-artefaktene persisteres + strukturert exit
(ScriptedClient + lavt tak — binder orkestrerings-grenen `run_s10.py:99-114` aldri fikk
bundet; fasit-stien forblir byte-urørt, bindingen skjer på den nye stien).
- **Nøkkelantakelse (+test):** komposisjonen gjenbruker `fold_experience`/
`merge_inbox_into_store`/`seed_store_from_bundle` uendret — ingen ny søm, kun wiring;
testes ved at ingen eksisterende testfil endres.
- **Verifisering:** ny testfil grønn + detach → rød; `grep -n "inbox" README.md` viser claim
som matcher en skipbar sti; full gate.
### C2.6 Validator/IR finitthets- og gyldighets-hardening (R-2 primær) — NY, BYGGBAR NÅ, HØY VERDI
- **Mål:** den obligatoriske validatoren kan ikke lenger settes ut av spill av ikke-finite
eller ugyldige tall — `Infinity` klarerer ikke porten vakuøst lenger.
- **Scope (filliste):** `ir.py` (`allow_inf_nan=False` på de numeriske feltene, eller eksplisitt
finitthetssjekk av `quantity`/`unit_cost`/`claimed_saving_nok` + bånd-endepunkter; forby
negative bånd-verdier), `contracts.py` (`min_length` på modell-id-verdier — tom-streng-
bug), `loop.py` (fjern/dokumentér den døde `check_turn_safety_net`-vokteren `:111` — eller
gjør §8s turn-safety-net faktisk nåbar), `tests/test_ir.py`, `tests/test_validator.py`,
`tests/test_contracts.py`.
- **IKKE-scope:** kostbaseline (C4.0); nominal-gate (C2.7, D-A-gated) — dette er ren
finitthets-/gyldighets-hardening, ikke spec-strammingen.
- **Gate:** ingen. **Golden uendret** (golden-tallene er finite).
- **TDD-plan:** RØD: `unit_cost: Infinity` → IR-konstruksjon eller validering avvises (i dag
`ValidatedProposal(validates=True, p90=inf)` — reviewens kjørte R-2-bevis blir fixturen;
detach-punkt: gjeninnfør `allow_inf_nan=True` → inf validerer → rød); RØD: `assumptions`-bånd
med `Infinity`/negativ endepunkt → avvist; kontroll: NaN avvises fortsatt (regresjon);
kontroll: golden-bundelens finite tall validerer uendret; RØD: tom-streng modell-id →
ModelMapContract avviser.
- **Nøkkelantakelse (+test):** ingen golden-/fixture-tall er ikke-finite — VERIFISERT
(golden.json + alle fixtures er finite); bindes ved at `test_bygg_energi_mikro` forblir grønn.
- **Verifisering:** nye tester grønne + detach → rød; golden uendret; full gate.
### K2.9 Ingest-robusthet (R-3/R-4/R-5/R-7) — NY, BYGGBAR NÅ
- **Mål:** ingest krasjer ikke midt i materialisering på tomme/BOM-/malformerte kilder, og
etterlater aldri en delvis bundle.
- **Scope (filliste):** `ingest.py` (R-3: vokt tom `rows` med fail-fast FØR disk-mutasjon —
aldri delvis bundle; R-4: les CSV med `utf-8-sig`; R-7 valgfritt: `LIMIT max_rows+1`/
`fetchmany` for SQL + streaming-tell for CSV), `okf.py` (R-5: eksplisitt beslutning for
malformert-men-eksisterende lenket concept-fil — tolerér-og-hopp ELLER dokumentert fail-fast,
ikke stille asymmetri), nye tester + evt. golden-fixture-tillegg (repo-lokalt, ikke `shared/`).
- **IKKE-scope:** ny kildefamilie (K14K16, D-B-gated); endring i eksisterende ingest-goldens
(byte-fasit).
- **Gate:** ingen.
- **TDD-plan:** RØD: tom CSV-ekstraksjon → fail-fast MED bundle uendret (detach fail-fast-
vakten → delvis bundle + krasj → rød; kontroll: byte-sjekk at ingen fil ble skrevet ved feil);
RØD: BOM-CSV → header `code` ikke `code` (detach `utf-8-sig` → korrupt header → rød); RØD:
malformert lenket fil → valgt semantikk bevist (hopp-og-fortsett: resten av bundelen
navigeres; ELLER fail-fast: presis feil) — detach → motsatt oppførsel → rød.
- **Nøkkelantakelse (+test):** eksisterende ingest-goldens er upåvirket (ingen tom/BOM/
malformert fixture i dem) — bindes ved at begge ingest-golden-suiter forblir grønne.
- **Verifisering:** nye tester grønne + detach → rød; ingest-goldens uendret; full gate.
### K4 Closed-loop to-kjørings-simulering (R-1 — §11-raden «Closed loop»; MAF-paritet rad 6)
- **Mål:** §11s siste udekkede rad (utover C1.1) lukkes: en skriptet to-kjørings-simulering
beviser at persona-markøren krysser kjøringer VIA promotering — og IKKE krysser uten.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/simulation.py` (deterministisk
to-run-driver over `run.py`-komposisjonen + persona-artefakten + promotion; ScriptedClient;
null modellkall), ny `tests/test_simulation_loadbearing.py`.
- **IKKE-scope:** endring i loop/promotion/persona-primitivene; live-kjøring; portefølje.
- **Gate:** ingen (forutsetter C2.0 — komposisjonsinngangen er kjøretøyet).
- **TDD-plan:** RØD: kjøring A → persona-dom (markør fra `shared/skills/expert-reviewer/`-
artefakten) → promotering → kjøring B med FERSK store: markøren MÅ nå Bs hypotese-prompt
(detach-punkt 1: fjern promoterings-steget → markøren uteblir → rød). RØD (symmetrisk
kontroll): UTEN promotering krysser markøren ALDRI (detach-punkt 2: la konteksten rendre
verdict-laget → markøren lekker → rød — gjenbruker verdict-eksklusjonens invariant på
simulerings-nivå). Honesty-krav: modulen docstring-merkes som skriptet stand-in (§1).
- **Nøkkelantakelse (+test):** persona-artefaktens `marker` er substring av `rationale`
(spec §4.3) — allerede bundet av `test_persona_skill_loadbearing.py`; simuleringen
gjenbruker den, ingen ny persona-kilde.
- **Verifisering:** ny testfil grønn + begge detach-punkter røde; full gate.
### K1 Besparelses-hovedbok + mål-kontrakt (paritetsrad 23; Fase-1-analog)
- **Mål:** D7 får en typet hovedbok over realiserte besparelser (fail-closed ekspert-gate,
provenance-stemplet) og en mål-kontrakt (absolutt beløp, hard/soft) med «stopp når mål
nådd»-logikk — distinkt fra token-budsjettet.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/ledger.py` (typet `SavingsLedger`;
`realize` KUN med eksplisitt ekspert-argument + eksplisitt timestamp — samme
determinisme-regel som promotion §6; deterministisk JSON-persistens sort_keys/indent 2/LF),
ny `goals.py` (`GoalContract`: absolutt mål, hard/soft; fail-fast konstruksjon §10-stil),
nye `tests/test_ledger_loadbearing.py` + `tests/test_goals.py`.
- **IKKE-scope:** prosent-mål-baseline (D-E-gated — kontrakten reserverer feltet med
eksplisitt `NotImplementedError`-nekt + TODO-peker til D-E, aldri stille semantikk);
portefølje-wiring (K2); verdirapport (K11).
- **Gate:** ingen for absolutt-delen. **Prosent-delen: D-E.**
- **TDD-plan:** RØD: `realize` uten ekspert-stempel → nekt (detach-punkt: fjern gate-sjekken
→ uekspederte «realiserte» tall entrer boken → rød); RØD: hovedboken akkumulerer og
persisterer deterministisk (to identiske sekvenser → byte-identisk fil); RØD: hardt mål
nådd → strukturert stopp-signal (typet event, aldri stille); soft mål → flagg uten stopp;
RØD: prosent-mål forsøkt konstruert → eksplisitt nekt med D-E-melding.
- **Nøkkelantakelse (+test):** ledger-semantikk kan speiles på kapabilitetsnivå fra
MAF-planens beskrivelse (typet store, fail-closed realize, dimensjonsfri sum-nøkkel mot
dobbelttelling) uten MAF-kode — RISIKO: detaljsemantikk kan divergere fra MAF; markeres i
docstring som stack-lokal kontrakt (delbarhet er foreslått beslutningspunkt i briefen).
Test på antakelsen: dimensjonsfri sum-nøkkel bindes med en to-dimensjoner-samme-prosjekt-
fixture der summen IKKE dobbelttelles.
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K2 Portefølje-kjøring (paritetsrad 4; Fase-1-analog)
- **Mål:** `run_portfolio` kjører N prosjekter sekvensielt fra en skjema-validert
referanse-konfig (per-prosjekt bundle-sti), med collect-per-prosjekt-resultater — den
kjørestien MAF fikk i Fase 1 og D7 aldri har hatt.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/portfolio.py`
(`run_portfolio(projects, client, meter, ...)` + `PortfolioResult`), ny
`data/reference_projects.json` (eksempel-konfig; skjema-validert fail-fast i
`contracts.py`-stil), `contracts.py` (konfig-modell), mini-bundle-fixture repo-lokalt under
`tests/data/` (ALDRI i `shared/`), ny `tests/test_portfolio.py`.
- **IKKE-scope:** læringstreing på tvers (K3); concurrency (K18/D-D); goals/ledger-wiring
utover å ta imot dem som valgfrie argumenter; endring i `loop.py`-kjernen.
- **Gate:** ingen.
- **TDD-plan:** RØD: to-prosjekt-konfig → begge kjøres, resultatliste i konfig-rekkefølge
(deterministisk); RØD: malformet konfig (manglende bundle-sti) → fail-fast FØR første
kjøring (§10-disiplin; detach-punkt: fjern skjema-valideringen → kjøring starter på
ugyldig konfig → rød); RØD: feil i prosjekt 1 propagerer som typet feil — feilpolicyen
(collect-and-continue vs raise) dokumenteres eksplisitt som stack-lokalt valg inntil D-D
(default: raise, som «i dag kastes alt» — endres bevisst av K18 når D-D lander).
- **Nøkkelantakelse (+test):** loop-kjernen er re-entrant per prosjekt (fresh debate-state
per run — spec §3 Steg 3) — bindes med test: to sekvensielle kjøringer deler ingen
muterbar tilstand utover eksplisitt delt store.
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K3 Portefølje-læringssløyfe (S2.0-analog; paritetsrad 5)
- **Mål:** en dom på prosjekt k når prosjekt k+1s hypotese-prompt i samme porteføljepass —
delt store + verdict_dir tres gjennom `run_portfolio`.
- **Scope (filliste):** `portfolio.py` (delt store + `verdict_dir`-parameter tres gjennom;
inbox-merge FØR hver kjørings fold — gjenbruker C2.0-komposisjonen),
`tests/test_portfolio_learning_loadbearing.py`.
- **IKKE-scope:** semantisk henting (K17); multi-kandidat-nøkling (C3.2 — dagens
feature-nøkling brukes som den er, med kjent C-F5-begrensning notert).
- **Gate:** ingen (forutsetter K2 + C2.0).
- **TDD-plan:** RØD: dom om prosjekt k (droppet i verdict_dir mellom kjøringene / promotert
i ks bundle) → k+1s genererings-prompt bærer dommens id+markør (detach-punkt: fjern
store-/verdict_dir-trådingen gjennom porteføljen → rød); kontroll: tom store/inbox → k+1
uendret base-prompt.
- **Nøkkelantakelse (+test):** first-write-wins-merge (§4.2) gjør gjentatte merges over
porteføljepasset idempotente — bindes med dobbel-merge-test på porteføljenivå.
- **Verifisering:** ny testfil grønn + detach → rød; full gate.
### K5 Outbox-persistens (S2.1-analog; paritetsrad 7)
- **Mål:** hver kjørings forslag/utfall persisteres run_id-navngitt i en outbox-mappe, så
utestående dommer kan spores (K9) og live-artefakter fanges (K8) — sammenligningsprotokoll
§4.2-formen.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/outbox.py` (gjenbruker
`artifacts.py`-serialisering; skriver `{run_id}-proposal.json` + `{run_id}-outcome.json`
med outcome-type, reason/percentiler, checker_verdict, provenance-referanse, verdict-id;
`run_id` PÅKREVD når outbox er satt — ingen wall-clock-default), `run.py` (valgfritt
`outbox_dir`/`run_id`), ny `tests/test_outbox_loadbearing.py`.
- **IKKE-scope:** ruting/pending (K9); varsling (K10); endring i `artifacts.py`-formatene
(S10-fasiten leses av dem).
- **Gate:** ingen.
- **TDD-plan:** RØD: kjøring med outbox_dir → filparet finnes, byte-deterministisk
(sort_keys/indent 2/LF; to kjøringer samme input+run_id → `diff` tom) (detach-punkt: fjern
outbox-skrivingen → rød); kontroll: uten outbox_dir skrives ingenting; RØD: outbox satt
uten run_id → fail-fast. Docstring siterer method-spec §2: systemet EIER output-laget
(rolledelingen §5 gjelder inbox/wiki, ikke systemets eget output-lag).
- **Nøkkelantakelse (+test):** `ValidatedProposal`/`Rejection` er JSON-serialiserbare via
eksplisitt mapping — testes FØRST med begge utfallstyper (artifacts.py gjør alt dette i
dag for run-artefaktene; antakelsen er at samme mapping bærer outcome-formen).
- **Verifisering:** ny testfil grønn + detach → rød; full gate.
### C3.5 Budsjett-guard (C-N4) — VIDEREFØRT, løftet fra «valgfri» til paritetskrav (rad 16/31)
- **Mål:** pre-call-guard (nekt kall når rest-tak < 0) + eksplisitt run-total USD-grense over
SDK-klientens akkumulerte kost — på plass FØR enhver fremtidig live-kjøring.
- **Scope (filliste):** `budget.py` (pre-call-sjekk-primitiv + run-total USD-tak i meteren),
`loop.py` (guard-kall FØR `client.complete`), `sdk_client.py` (eksponer akkumulert
`total_cost_usd` — leses allerede fra ResultMessage), `tests/test_budget.py` (+utvidelse).
- **IKKE-scope:** portefølje-nivå-tak (K19, D-D-gated); endring i post-charge-semantikken
(består som belte-og-seler).
- **Gate:** ingen.
- **TDD-plan:** RØD: mock-klient som teller kall — når rest < neste kall er kallet ALDRI
gjort (detach-punkt: fjern pre-call-guarden → kallet skjer → rød); RØD: run-total USD-tak
krysset → strukturert stopp (samme event-form som token-stoppen, §8); kontroll:
usage-fail-closed-testen (`test_budget`) uendret grønn.
- **Nøkkelantakelse (+test):** `ResultMessage.total_cost_usd` akkumuleres pålitelig per kall
på 0.2.110 (verifisert i sdk-review 2026-07-16, review-paritet §2) — versjonsvakten fra
C2.5 dekker drift.
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K6 Kostnadssimulering før kjøring (S3.6-analog; D-I pkt. 3 MÅ-krav; paritetsrad 18)
- **Mål:** operatøren ser estimert kost for en (portefølje-)kjøring FØR start — what-if over
model_map (Claude-modeller × effortnivåer), prisdata som skjema-validert konfig.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/costsim.py` (funksjons-API +
`python -m portfolio_optimiser_claude.costsim`; deterministisk øvre-grense-estimat fra
portefølje-form × tak — merket ESTIMAT i output), ny `data/pricing.example.json` (skjema:
kr-eller-USD/Mtok per modell-id, PÅKREVD kilde+dato-felt; fail-fast ved manglende/ugyldig
pris — aldri hardkodet), `contracts.py` (pris-skjema), nye tester.
- **IKKE-scope:** live prisoppslag (nett); faktisk modellkall; kvalitets-benchmarking
(kvalitetsutsagn kun som veiledning-med-kilde, D-I-regelen).
- **Gate:** ingen.
- **TDD-plan:** RØD: konfig uten pris for en modell i model_map → fail-fast «missing price
for <modell-id>» (detach-punkt: fjern pris-valideringen → estimat gjettes → rød); RØD:
deterministisk og skalerer med modell × effort (to modeller, samme portefølje →
forskjellig, reproduserbart estimat; detach effort-faktoren → identisk → rød); RØD:
grep-guard: ingen pris-literal i `costsim.py`-kildekode (pris MÅ komme fra konfig).
- **Nøkkelantakelse (+test):** eksempel-prisene kan kildeføres mot offisiell Anthropic-
prisside med dato (sdk-modelmap-reviewens kilder gjenbrukes); IKKE VERIFISERT utover
review-datoen — derfor kilde+dato-felt i skjemaet, så stale priser er synlige, aldri stille.
- **Verifisering:** nye tester grønne + detach → rød; `uv run python -m
portfolio_optimiser_claude.costsim` gir estimat-tabell uten nettverk; full gate.
### K7 SDK-/API-preflight (S4.1-analog, SDK-nativ; paritetsrad 20)
- **Mål:** alt som KAN valideres uten et modellkall valideres før operatøren betaler for ett.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/preflight.py`
(`python -m portfolio_optimiser_claude.preflight --profile anthropic`): env-kontrakt
(`ANTHROPIC_API_KEY` satt + ikke-placeholder-form — nøkkelVERDIEN valideres aldri online),
model_map-konsistens (profil finnes, `default` finnes, modell-id-form), placeholder-nekt
også i `resolve_model`-stien, SDK-importerbarhet + bundlet CLI til stede
(claude-agent-sdk-premisset), budsjett-/stoppkontrakt satt. Nekt-liste med aksjonérbar
melding per mangel. Tester.
- **IKKE-scope:** faktisk API-kall (aldri — preflighten er selve grensen); Foundry/Azure
(SPEILES IKKE, rad 20-begrunnelsen).
- **Gate:** ingen.
- **TDD-plan:** RØD: manglende env-nøkkel → strukturert nekt med aksjonérbar melding
(detach-punkt: fjern env-sjekken → preflight «grønn» uten nøkkel → rød); RØD:
placeholder-modell-id i map → nekt; RØD: grep-guard ingen socket-/httpx-sti i modulen.
- **Nøkkelantakelse (+test):** «bundlet CLI til stede» kan sjekkes offline via SDK-pakkens
egne filer (verifiseres i sesjonen mot installert 0.2.110; hvis ikke mulig offline —
droppes sjekken eksplisitt med begrunnelse i docstring, aldri en nett-sjekk).
- **Verifisering:** nye tester grønne + detach → rød; `uv run python -m
portfolio_optimiser_claude.preflight` korrekt nekt/OK uten nettverk; full gate.
### K8 Live-kjørings-drill + artefakt-fangst (S4.2-analog; paritetsrad 21)
- **Mål:** en eventuell fremtidig live-kjøring (M2-analogen, operatør-gated) er ferdig-rigget:
alt bygges, artefakter fanges, drillen er øvd offline — uten ett modellkall.
- **Scope (filliste):** `run.py` (`--live-dry-run`: last kontrakter, kjør preflight, bygg
klient-konfig, valider budsjett, skriv kjøringskonfig-logg (modell-id, parametre, tak —
sammenligningsprotokoll §4.3-form) til outbox, STOPP før første modellkall), tester med
kall-tellende mock.
- **IKKE-scope:** selve live-kjøringen (M2-analog, operatør); endring i preflight/outbox.
- **Gate:** ingen (forutsetter K5 + K7).
- **TDD-plan:** RØD: dry-run mot kall-tellende mock-klient → NULL kall skjedde (detach-punkt:
fjern stopp-punktet → kall telles → rød); RØD: artefakt-settet (konfig-logg + preflight-
resultat) komplett og deterministisk i outbox.
- **Nøkkelantakelse (+test):** klient-konstruksjon uten kall er gratis og nøkkel-fri
(CLAUDE.md-verifisert SDK-premiss: import og konstruksjon fungerer uten API-nøkkel) —
bindes med test som konstruerer klienten i suiten (uten nøkkel).
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K9 HITL verdict-ruting + pending-sporing (S5.1-analog; paritetsrad 22)
- **Mål:** operatøren ser hvilke forslag som venter på dom og hvem som skal dømme — ren
fil-basert id-join outbox↔inbox.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/hitl.py` (pending-registry =
outbox-forslag uten matchende inbox-/promotert dom, id-join på verdict-id; ruting-konfig
`nøkkel → ekspert` skjema-validert fail-fast — nøkkelen er en konfig-streng nå,
dimensjonskatalog-integrasjon kommer med K13), CLI `python -m portfolio_optimiser_claude.hitl
pending|route`, tester.
- **IKKE-scope:** varsling (K10); web-UI; skriving til inbox (rolledelingen §5 — hitl LESER
begge lag).
- **Gate:** ingen (forutsetter K5).
- **TDD-plan:** RØD: forslag i outbox uten dom → listes utestående; dom lander i inbox →
forsvinner fra pending (detach-punkt: fjern id-joinen → dømt forslag listes fortsatt →
rød); RØD: malformet ruting-konfig → fail-fast; RØD: hitl skriver aldri (byte-sammenligning
av outbox+inbox før/etter).
- **Nøkkelantakelse (+test):** outbox-outcome bærer verdict-id-en å joine på (K5-formen) —
bindes i K5s testfil og gjenbrukes her.
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K10 Varsling/Notifier (S5.2-analog; paritetsrad 23)
- **Mål:** leverbare varslings-implementasjoner uten å bryte ingen-stille-egress.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/notify.py` (`Notifier`-protokoll +
`console`/`file`/`webhook`; webhook krever eksplisitt per-kjøring opt-in-flagg — mirror av
ingest-spec §8-mønsteret («flagget er et run-argument, aldri konfig-felt») — + injiserbar
transport, canned i tester, null socket i suiten), `run.py`/`hitl.py` (opt-in seam), tester.
- **IKKE-scope:** e-post/Teams-adaptere; retry-politikk; kø.
- **Gate:** ingen. **Koordineringspunkt (kapabilitetsnivå):** MAF S5.2 eksekveres nå — les
MAF-STATE/plan-oppsummeringen (aldri koden) før sesjonen og speil kapabilitetsvalgene
(protokoll-form, webhook-opt-in, socket-fri suite) der de er beskrevet på plan-nivå.
- **TDD-plan:** RØD: webhook uten opt-in → nekt fail-fast (detach-punkt: fjern opt-in-sjekken
→ transporten kalles → rød); RØD: canned transport mottar korrekt payload (struktur, ikke
prosa); RØD: grep-guard ingen socket-sti utenfor den injiserbare seamen.
- **Nøkkelantakelse (+test):** payload-formen kan holdes stack-lokal (ingen delt spec for
varsling) — divergens mot MAF er akseptert og dokumentert i docstring.
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K11 Verdirapport per kjøring (S5.4-analog; D-I pkt. 2; paritetsrad 25)
- **Mål:** hver kjøring/porteføljepass produserer en deterministisk verdirapport — modellert →
ekspert-korrigert → realisert, målprogresjon, tallfestet læringseffekt, kost-mot-verdi —
uten ett modellkall.
- **Scope (filliste):** ny `src/portfolio_optimiser_claude/valuereport.py` (ren projeksjon
over ledger (K1) + outbox (K5) + costsim-estimat (K6); JSON + markdown-render, byte-stabil
sort_keys/indent/LF), `run.py`/`portfolio.py` (opt-in `--value-report`), tester + committede
fixtures.
- **IKKE-scope:** dashboard; live-oppdatering; modellkall; nivå-2-README-påstander (kommer
FØRST når beviset finnes — D-I pkt. 1).
- **Gate:** ingen (forutsetter K1 + K5 + K6).
- **TDD-plan:** RØD: to kjøringer med økende godkjenningsandel → læringseffekt-metrikk stiger
og gap-krymping rapporteres (detach-punkt: fjern gap-beregningen → flat/feil → rød); RØD:
prosjekt uten dom → «realisert» tomt/umerket, ALDRI lik modellert (detach ærlighets-grensen
→ realisert=modellert → rød); RØD: byte-determinisme (samme ledger+outbox → identisk
rapport, `diff`).
- **Nøkkelantakelse (+test):** ledger+outbox bærer alt rapporten trenger — rapporten er ren
projeksjon, ingen ny tilstand; testes ved å bygge rapport fra committede fixtures uten
kjøring.
- **Verifisering:** nye tester grønne + detach → rød; full gate.
### K12 CLI-paritet + dokumentsync + D-H-oppskrift (S5.3-analog; paritetsrad 24) — SISTE ugatede
- **Mål:** operatøren driver ALT bygget (kjøring, portefølje, inbox, outbox, ledger, goals,
pending, costsim, preflight, verdirapport) fra kommandolinja; README/extending.md/CHANGELOG
påstår nøyaktig det koden gjør (§1); D-H-oppskriften leveres som dokument.
- **Scope (filliste):** `run.py` (samlende CLI-flater: `--bundle/--inbox/--outbox/--verdict-dir/
--goals/--ledger/--portfolio/--value-report`), `README.md`, `docs/extending.md`,
`CHANGELOG.md`, nytt `docs/oppskrift-kunnskapsbase.md` (D-H pkt. 1: dokumentert team-prosess,
ærlig 12-ukers-forventning; IKKE veiviser — B9 er FORKASTET i D-H), CLI-tester.
- **IKKE-scope:** nye kapabiliteter; fabrikk-/demo-sti (T0-gated — oppskriften peker på den
som fremtidig, merket ubygd).
- **Gate:** ingen (forutsetter at K1K11-kjeden er landet — ellers krymper scope ærlig).
- **TDD-plan:** RØD: CLI-kjøring med goals+ledger på fixtures → mål-stopp observerbar i
output (detach-punkt: koble goals fra CLI-stien → stopp uteblir → rød); RØD:
ærlighets-grep i testform: README-claims-listen (inbox, portefølje, CLI) matcher skipbare
stier (testen leser README og asserter at hver dokumentert kommando finnes i CLI-hjelpen).
- **Nøkkelantakelse (+test):** ingen — ren wiring + docs.
- **Verifisering:** CLI-tester grønne; manuell `uv run python -m portfolio_optimiser_claude.run
--help`-røyk dokumentert i STATE; full gate.
## 4. Gatede sesjoner (startes IKKE før gaten er utløst — eksakt utløser per sesjon)
> Felles mal for D-A-bølgen (uendret fra gammel plan): amendment lander i commons →
> koordinert subtree-pull i BEGGE repo → BEGGE suiter kjøres → failing test først
> (review-moteksemplene ER fixturene) → impl → detach-bevis → full gate → golden uendret
> (eller koordinert re-frosset i commons FØRST).
### C2.7 Validator-stramming (C-F2) — **Gate: D-A pkt. 1 godkjent + commons-pull utført**
Uendret fra gammel plan §3: (a) strukturell blokk `claimed > nominal_feasible``Rejection`;
(b) IR-invariant `low ≤ unit_cost ≤ high` (Pydantic `model_validator`). Premiss VERIFISERT
2026-07-10 (golden- og fixture-kompatibel: alle ikke-tomme bands omslutter unit_cost;
golden-claim 30 000 ≤ 90 000). TDD: moteksempel #1 (claim 100k/nominal 90k/band [0.70,1.40])
→ MÅ bli Rejection; band [1.8,2.2] om unit_cost 1.0 → ValidationError; detach nominal-blokken
→ rød. Filer: `validator.py`, `ir.py`, `tests/test_validator.py`, `tests/test_ir.py`;
`test_bygg_energi_mikro.py` uendret grønn.
### C2.8 OKF `/`-lenke-mapping (C-F4) — **Gate: D-A pkt. 3 godkjent + commons-pull utført**
Uendret fra gammel plan §3: ledende `/` mappes til bundle-rot i `navigate_bundle`;
boundary-sjekken består fail-closed. TDD: `](/tiltak.md)` → navigeres (detach → rød);
`](/../escape.md)` → fortsatt skippet. Filer: `okf.py`, `tests/test_okf.py`. Merk: MAF-siden
mangler fortsatt egen F4-byggsesjon — flagget står (operatøren legger den til der).
**Toolkit-kobling:** lander T0/K20 FØR D-A pkt. 3, flyttes `/`-mappingen inn i okf-core og
C2.8 blir en konsum-oppgradering — sesjonene merges da eksplisitt.
### C3.2 Per-verdict seeding (C-F5) — **Gate: D-A pkt. 4 godkjent + commons-pull utført**
Uendret fra gammel plan §3: seed leser features fra verdict-filas egen frontmatter (nye
valgfrie felt `affected_codes`/`measure_type`/`claimed_saving_nok`; fallback = dagens
nøkling); `promote` skriver feltene; multi-kandidat-fixture repo-lokal. TDD: to kandidater i
én bundle → dom om B når ALDRI As prompt via retrieval på As features (detach → rød);
eksisterende step1/step7/step8 uendret grønne (fallback). **R-8 (count-ærlighet, foldet inn):**
`seed_store_from_bundle` skal returnere FAKTISK lagret antall (eller logge drops), ikke
fil-antallet — RØD: to seed-filer uten `verdict_id`, ulik decision → i dag `seeded=2` men
`len(store)==1`; med per-verdict-nøkling blir de distinkte, og count matcher (detach nøklingen
→ count/antall divergerer igjen → rød). Filer: `experience.py`, `promotion.py`,
loadbearing-testene, ny fixture.
### C4.0 Kostbaseline-forankring (C-F3) — **Gate: D-A pkt. 2 godkjent + commons-pull utført**
Uendret fra gammel plan §3: baseline-projeksjon i bundle (`cost-baseline.json`,
commons-format) + fail-closed avstemmings-stage i `validate_proposal` (ukjent kode →
Rejection; utenfor toleranse → Rejection; toleranse konfig; baseline-argument VALGFRITT =
dagens oppførsel, men kjøresti-inngangen (C2.0/`run.py`) SETTER den når fila finnes). TDD:
F3-moteksemplet (diktet kode, 10 MNOK) → Rejection «unknown cost code» (detach → validated →
rød); golden-bundelen (uten baseline-fil) validerer uendret. Filer: `validator.py`,
`okf.py`/`ir.py` (loader), commons-fixture, nye tester. **MÅ være grønn før M2-analogen.**
### K13 Innholdsmodell + bibliotek + dimensjonskatalog + summary-tier (S3.5-analog) —
**Gate: commons-amendmenten (D-F/D-G-utkastet 2026-07-14) operatør-godkjent, committet i
commons OG pullet hit (koordinert med MAF; begge suiter grønne etter pull)**
- **Scope:** bygg fra den nye spec-teksten (aldri fra utkastet før det er landet):
(1) `dimension_catalog.py` — §13.3-katalogen, skjema-validert fail-fast (id, label,
allowed_measure_types, allowed_code_prefixes) + scoping-gate; eksempel-katalog under
`data/`; (2) `ingest.py``library`-kildefamilien (4a/5a: `library-{id}.md`, kuratert
frontmatter + additivt provenance-lag, kildebelegg-håndheving fail-fast ved tom/manglende
`sources`); (3) `okf.py` — summary-tier-render for bibliotek-typene (description-linje
default; full body kun for deterministisk pre-run-utvalg — kandidatens dimensjon); (4) ny
golden-case `examples/ingest-golden-library/` KUN hvis commons shipper den (fasit eies der).
- **TDD:** RØD per amendmentens fire detach-punkter (S3.5-malen): ugyldig katalog → fail-fast;
materialisert bibliotek-fil når prompten via `bundle_context` (detach → markør borte → rød);
summary-tier gir én linje, ikke fulltekst (detach → fulltekst-stuffing → rød);
separasjons-kontroll: bibliotek-fil når ALDRI prompten via dom-kanalen (folden).
- **Nøkkelantakelse (+test):** default uten bibliotek-filer er byte-identisk med dagens
render (amendment §3.2s garanti) — bindes med regresjonstest på eksisterende bundle FØR ny
funksjonalitet skrives. C1.1-vakten skal forbli GRØNN gjennom pullen (amendment §5.1 er
konstruert for det — verifiseres som pull-ens første sjekk).
- **Verifisering:** nye tester grønne + detach → rød; `test_bygg_energi_mikro` + begge
ingest-goldens uendret; full gate; sammenligningsprotokoll-kjøring i BEGGE repo.
### K14 MCP-ingest-konnektor (S2.2-analog) — **Gate: D-B besluttet + ingest-spec-amendment
(mcp-familien) landet i commons + pullet hit**
SDK-nativ analog: kildefamilie `mcp` i manifestet; test-fixture = in-process MCP-server via
SDK-primitivet `create_sdk_mcp_server`/`@tool` (av-demoer extension-pointet uten socket).
Per-run-grant: eksplisitt flagg (ingest-spec §8-mønsteret — run-argument, aldri manifest-felt).
TDD: manifest med `type: mcp` uten grant → `IngestError` (detach → rød); golden-materialisering
mot fixture-server byte-deterministisk; verdict-reservasjonen gjelder også mcp-familien.
Nøkkelantakelse (testes FØRST i sesjonen): SDK-MCP-serveren kan drives i-prosess uten socket —
hvis ikke, subprosess-fixture med eksplisitt merking. OBS: `ingest.py` er i dag SDK-fri —
mcp-konnektoren legges i EGEN modul (f.eks. `ingest_mcp.py`) så ingest-kjernens renhet består
(detach-vaktene i `test_ingest_loadbearing` urørte).
### K15 Dokument-konnektor PDF (S2.3-analog) — **Gate: D-B besluttet (inkl. pypdf-dep-valget)
+ ingest-spec-amendment (doc-familien) landet + pullet**
Tekst-ekstraksjon per side, fenced-verbatim-render (gjenbruk http-regelen fra spec §5),
`max_rows`-cap, fail-fast på ikke-tekst-PDF; DOCX utsatt (D-B). TDD: golden byte-determinisme;
cap-overskridelse → feil, aldri trunkering; pin pypdf-versjon (golden fanger drift).
### K16 http-kilde + konnektor-herding + guard-G1 (S2.4-analog) — **Gate: D-B besluttet MED
http/live-kilde i scope (i dag eksplisitt utenfor D7-roadmap)**
Bygger http-familien (i dag avvist fail-fast — det er selve utløseren som endrer det):
`base_url`/`credential_ref` per ingest-spec §4, nettverks-opt-in per §8 (fail-fast uten flagg
— load-bearing §11-raden «Network gate»), timeout + feilkategorisering mot injisert transport
(aldri live), OG guard-planens G1-wiring i samme økt: `prepare_input` → deterministisk render
`screen_output` FØR skriving av konseptfila; FAIL_SECURE → ikke skriv, alert med gate-kode
uten innhold. Guard-dep pinnes + versjonsvakt (samme disiplin som C2.5). TDD: per feilmodus
canned transport → presis IngestError; uten opt-in → nekt (detach → rød); injeksjons-fixture →
fila skrives ALDRI (detach guard-gaten → payload lander i bundelen → rød — guard-planens §7).
STATE-markørlinjen oppdateres `planned``integrated` med detach-bevis.
### K17 Semantisk henting (S3.1-analog) — **Gate: D-C besluttet OG C-P1 avklart i D-A-bolken
(amendment av ranking-regelen ELLER re-scoping til strukturelle features) + evt. commons-pull**
Scope avhenger av C-P1-utfallet: (a) amendment-varianten: `Retriever`-seam +
`HybridRanker` (strukturell + cosine over lagrede vektorer, delt format .npy+jsonl per D-C),
deterministisk fake-embedder i tester; (b) re-scoping-varianten: embeddings over strukturelle
features alene. Uansett: ny `semretrieval.py` SDK-fri, opt-in seam i `experience.py`, ingen
endring i `shared/`. TDD: syntetisk base der KUN den nye komponenten kan rangere riktig dom
øverst (detach → rød); determinisme ×2 kjøringer; fake-embedder bit-stabil.
`test_surface_text_never_contributes` består i variant (b); i variant (a) endres den KUN slik
amendmenten krever.
### K18 Concurrency/feilmodell (S3.3-analog) — **Gate: D-D besluttet (+ K2 landet)**
Bølge-modellen fra D-D-fasiten når den kommer: `run_portfolio(concurrency=k)`, snapshot per
bølge, deterministisk merge-barriere, collect-and-continue-feilpolicy (erstatter K2s
raise-default bevisst). TDD: concurrency=3 byte-identisk med concurrency=1 på 3-prosjekt-
fixturen (detach barrieren → rød over repetisjoner); injisert feil i prosjekt 2 → 1+3 bevart.
Nøkkelantakelse å teste FØRST: asyncio-samtidighet i én event-loop med ScriptedClient
(SDK-klienten er async — anyio-premisset verifiseres i sesjonen).
### K19 Kostnadsstyring på tvers (S3.4-analog) — **Gate: D-D besluttet (deler feilpolicy)**
`PortfolioBudget` (globalt tak + per-run-tak) over C3.5-primitivene; spend-persistens
(deterministisk JSON, eksplisitt stamp); oppstartsnekt når rest < minimum per kjøring. TDD:
globalt tak krysses midt i passet → strukturert stopp med fullførte runs bevart (detach → rød);
pre-call-guard nekter kallet (kall-tellende mock).
### K20 okf-toolkit-adopsjon inkl. skrive-halvdelen (D-G/T0) — **Gate: okf-toolkit-repoet
finnes, T1 (okf-core + formatprøve) er publisert/installerbar, OG operatøren har godkjent
koordinert A1+A2-migrering (toolkit-briefens §7: migreringene er koblet, ikke uavhengige)**
- **Scope:** importér `okf-core`; `okf.py`-lesekjernen delegerer/erstattes; skrive-halvdelen
(render_frontmatter/write_concept_file/link_in_index-ekvivalentene) blir tilgjengelig her —
D-Gs «minst like godt»-hovedgevinst; kjør formatprøven (conformance-testsettet) mot egne
bundler i suiten; `pyproject.toml` (+dep, pin + versjonsvakt samme disiplin som C2.5).
- **TDD:** RØD: formatprøven kjører i suiten og en bevisst malformert fixture-bundle feiler
minst én conformance-test; RØD: skrive-primitivene importérbare og brukt av promotion-stien
(detach → promotion faller tilbake/feiler → rød); eksisterende okf-purity-vakt beholdes
(okf-core er stdlib-only per toolkit-briefen §2 — verifiseres, ikke antas).
- **Bevisst avvik:** dette endrer «from spec alone» for formatlaget — operatør-besluttet
(D-G pkt. 2, revisjonspakke §0.2); dokumenteres i README/CLAUDE.md i samme sesjon.
- **Verifisering:** full gate; golden uendret; sammenligningsprotokoll-kjøring i BEGGE repo
(A1-migreringen skjer koordinert på MAF-siden).
### Guard G3 (`okf.import_bundle`) — **Gate: delt D-E-retning åpner bundle-utveksling**
Ingen egen sesjon planlegges før gaten: wiring-formen står i guard-planen §4 G3.
## 5. Avhengighetsgraf + anbefalt sekvens
```mermaid
flowchart TB
subgraph OPER["Operatør-gater (delt kø i HOVEDREPOET + amendment/toolkit)"]
DA["D-A (+C-P1)"]; DB["D-B"]; DC["D-C"]; DD["D-D"]; DE["D-E"]
AMD["commons-amendment D-F/D-G"]; T0[["T0 okf-toolkit (finnes ikke)"]]
end
C11["C1.1 spec-vakt"] --> C25["C2.5 inbox+SDK-vakt+R6"] --> C20["C2.0 kjøresti-inngang"]
C26["C2.6 validator-finitthet (R2)"]; K29["K2.9 ingest-robusthet (R3-R7)"]
C20 --> K4["K4 closed-loop-sim (R1)"]
C20 --> K2["K2 portefølje-kjøring"]
K1["K1 ledger+goals"] --> K2 --> K3["K3 portefølje-læring"]
K5["K5 outbox"] --> K8["K8 live-drill"]; K7["K7 preflight"] --> K8
K5 --> K9["K9 HITL-ruting"] --> K10["K10 notifier"]
K1 --> K11["K11 verdirapport"]; K5 --> K11; K6["K6 costsim"] --> K11
C35["C3.5 budsjett-guard"] --> K8
K3 --> K12["K12 CLI-paritet+oppskrift"]; K9 --> K12; K11 --> K12
DA --> C27["C2.7 validator"]; DA --> C28["C2.8 /-lenker"]; DA --> C32["C3.2 seeding"]; DA --> C40["C4.0 kostbaseline"]
AMD --> K13["K13 bibliotek+katalog+summary-tier"]
DB --> K14["K14 MCP-ingest"]; DB --> K15["K15 doc-PDF"]; DB --> K16["K16 http+guard-G1"]
DC --> K17["K17 semantisk henting"]; DA -. C-P1-avklaring .-> K17
DD --> K18["K18 concurrency"]; DD --> K19["K19 kost på tvers"]; K2 --> K18; C35 --> K19
T0 --> K20["K20 okf-core-adopsjon"]
C40 --> M2A{{"M2-analog: evt. ny live-kjøring (operatør + D-E + D6-ny-godkjenning)"}}
K8 --> M2A; DE --> M2A
```
**Anbefalt sekvens (ugatet først — åpningen C1.1 → C2.5 → C2.0 står, med review-begrunnelse
uendret):**
1. **C1.1 → C2.5 → C2.0** (vakt → herding → inngang)
2. **C2.6 → K2.9** (korrekthets-hardening — R-2 validator-omgåelsen har høyest verdi av alt
ugatet; kan tas rett etter C1.1 hvis ønskelig, uavhengig av C2.5/C2.0)
3. **K4** (closed-loop-sim — lukker §11-gapet R-1 tidlig)
4. **K1 → K2 → K3** (portefølje-fundamentet)
4. **K5 → C3.5 → K6 → K7 → K8** (outbox/kost/preflight/drill)
5. **K9 → K10 → K11 → K12** (HITL/varsling/verdirapport/CLI-sluttsync)
6. Ved D-A (når som helst, uavhengig av 15): **C2.7 → C2.8 → C3.2 → C4.0** etter koordinert
pull; ved amendment: **K13**; ved D-B: **K14 → K15 → K16**; ved D-C+C-P1: **K17**; ved
D-D: **K18 → K19**; ved T0: **K20**.
7. Vedlikehold når som helst: koordinert subtree-pull av de to docs-only commons-commitene
(ingen spec-endring — men kjør begge suiter etter pull likevel, protokoll-regelen).
**Koordineringspunkter med MAF (absolutte):** (a) etter HVER amendment-bølge: begge repo
puller subtree og BEGGE suiter kjøres — aldri én side alene; (b) golden re-fryses kun
koordinert i commons; (c) K10 speiler MAF S5.2 på kapabilitetsnivå (plan-lesing, aldri kode);
(d) K20/A1-migreringen er koblet på tvers av repoene (toolkit-brief §7); (e) C2.8-flagget om
manglende MAF-F4-byggsesjon står til operatøren har lagt den til der.
## 6. Operatør-milepæler (aldri autonome sesjoner)
- **M2-analog — eventuell ny live-kjøring:** krever ALT av: eksplisitt operatør-godkjenning
(D6: ny spend), D-E (stack-paritet for ekte kjøring — avgjør om D7 i det hele tatt skal
kjøre live igjen), C4.0 grønn (kostbaseline FØR ekte modell), C3.5 + K6 + K7 + K8 grønne
(guard, estimat, preflight, drill), harde tak satt. Forutsetningen «nøkkel satt + preflight
grønn» er D7-ekvivalenten av MAFs M1. Utfall re-rulles ikke (protokoll §4.5).
- **M3-analog (pilot):** følger MAF-planens M3 — ikke planlagt her før D-B/D-E/toolkit-
fabrikken foreligger; D7-deltakelse avgjøres av D-E.
## 7. Leveringsklar-definisjon (binær sjekkliste)
«Leveringsklart» er nådd når ALLE punkter under er sanne (hver enkelt er kommando-verifiserbar):
- [ ] Alle paritetsmatrise-rader (§1) er enten **FINNES HER** (bygget + load-bearing-testet)
eller **SPEILES IKKE** med begrunnelse operatøren har sett — ingen rad står i BYGGBAR
NÅ; GATED-rader er enten utløst+bygget eller eksplisitt operatør-akseptert som
utestående (beslutnings-køens ansvar, dokumentert i STATE).
- [ ] Full gate grønn: `uv run pytest` (alle tester, offline uten nøkkel/nett) +
`uv run ruff check .` + `uv run ruff format --check .` + `uv run mypy src`.
- [ ] §11-tabellen komplett: hver rad (inkl. «Spec integrity» C1.1 og «Closed loop» K4) har
en navngitt test som er detach-bevist rød (bevis i testfil eller sesjonslogg).
- [ ] README/docs ærlige (§1): hver kapabilitets-påstand matcher en skipbar sti (K12s
ærlighets-test grønn); skriptede stand-ins merket; ingen nivå-2-verdipåstand uten
D-I-belegget.
- [ ] Guard-markørlinjen i STATE.md er korrekt (planned/integrated per faktisk wiring-status,
med detach-bevis hvis integrated).
- [ ] Golden-bytene uendret mot commons (eller re-frosset KOORDINERT i commons med begge
suiter grønne).
- [ ] `runs/s10/` byte-urørt; `run_s10.py` byte-urørt.
- [ ] Subtree i synk med commons (ingen utestående spec-commits; verifisert med subtree-diff).
- [ ] STATE.md oppdatert etter konvensjonen; CHANGELOG dekker alle landede sesjoner.
- [ ] **`open`-speiling som EGEN operatør-gated sluttpost:** push til `open` skjer ALDRI
automatisk — operatøren godkjenner eksplisitt hvilke commits som speiles, som siste
handling etter alt over.
## 8. Endringer mot 2026-07-10-planen
| Gammel plan | Endring |
|---|---|
| §4 «Overføres IKKE hit (MAF-spesifikt)» | **OPPHEVET** — re-klassifisert rad for rad i §1 (operatør-mandat 2026-07-16) |
| C1.1/C2.0/C2.5/C2.7/C2.8/C3.2/C3.5/C4.0 | Videreført uendret i substans (C3.5 løftet fra «valgfri» til paritetskrav; C2.5 utvidet med guard-G2-vurdering) |
| «ingen D7-HTTP/MCP-økt planlagt» | Erstattet av K14K16, gated D-B |
| (fantes ikke) | K-serien: K1K20 (paritetssesjoner) + M2-/M3-analoger + leveringsklar-definisjon (§7) |
| (fantes ikke) | Review-paritet R-serien (2026-07-16): C2.6 (R-2 validator-finitthet), K2.9 (R-3/R-4/R-5/R-7 ingest-robusthet), K4 (R-1 closed-loop); R-6→C2.5, R-8→C3.2, R-10→C2.0/C2.5 |