portfolio-optimiser-claude/docs/2026-07-16-sesjonsplan-paritet.md
Kjell Tore Guttormsen 8d5554ba3f docs(portfolio): spec-gap §7 i operatør-køen + paritetsrad 24/25 rettet
Coord-runde 2026-07-25, ingen kodeendring (gate urørt: 603 passed, ruff+mypy rene).

- brief §7: projeksjons-kontrakten over hovedboken som spec-gap, meldt uavhengig
  fra BEGGE stacker. MAF-søskenets ordlyd bevart verbatim; våre tre skjønnsvalg
  målt mot kode (runding av kronebeløp: ingen hos oss, én hos dem — reell
  divergens på identiske inndata). Anbefalt inn i D-A-bolken.
- paritetsplan rad 24/25: STALE «Gjenstår i MAF» rettet etter at MAF eksplisitt
  frigav vårt planverk. Rad 25 merket «hash ikke oppgitt — spurt» framfor antatt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MQu2xxwedckjU56byu1aUG
2026-07-25 07:41:52 +02:00

60 KiB
Raw Blame History

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 (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; R-serien til review-paritet 2026-07-16; 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:sharedcommons/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) LANDET (S5.3-serien 905b2f9/1b990f0/d44305c/c0d3ba4, bekreftet av MAF 2026-07-25; MERK: deres c0d3ba4 dokumentsync er IKKE test-håndhevet — de tok imot formen vår, køet ikke bygget) 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) LANDET iflg. MAF 2026-07-25 (de beskrev egne skjønnsvalg i S5.4; commit-hash IKKE oppgitt — spurt. Divergens bekreftet: de runder et kronebeløp, vi runder ingen) 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) DEFEKTInfinity 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. 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 » (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_feasibleRejection; (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.pylibrary-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 plannedintegrated 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

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)
  5. K5 → C3.5 → K6 → K7 → K8 (outbox/kost/preflight/drill)
  6. K9 → K10 → K11 → K12 (HITL/varsling/verdirapport/CLI-sluttsync)
  7. 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.
  8. 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