# Sesjonsplan — Fase 2 → offline-fullførings-grense (S2.0–S5.3 · D-A–D-E · M1–M3) > **Status:** GJELDENDE sesjonsplan. Forfattet som re-plan-forslag av kryssmodell-reviewen > 2026-07-09 (Fable 5); **promotert til gjeldende plan 2026-07-10 på operatør-instruks.** > Roadmapen (`2026-07-06-reell-kjoring-analyse-plan.md`) er revidert med kryssreferanser hit. > **Revidert 2026-07-14:** beslutningene D-F–D-I (kunnskapsinnholdsmodell, OKF-modul/fabrikk/evaluator, > oppsett/brukervennlighet, verdibevis/kostnadsstyring) ble truffet i operatør-samtale og er flettet inn > her (§2-oppføringer + nye sesjoner S3.5/S3.6/S5.4 + S5.3-utvidelse); fasit i > [revisjonspakke D-F–D-I](2026-07-14-revisjonspakke-DF-DI.md). > Funn-referanser (F1–F14, P1–P6, U-1–U-4) peker til [reviewen](../review-2026-07.md). > **Beslutnings-sesjonene D-A–D-E er fortsatt ÅPNE (operatør-kø):** bygg-sesjoner gated på dem > starter ikke før beslutning foreligger; **S2.0/S2.1/S2.5 er byggbare NÅ** (ingen > beslutnings-avhengighet). Endringer mot roadmapen er merket `[ENDRET]` / `[NY]`. > Frosne dokumenter (målbilde, ingest-målbilde, method-spec) er IKKE endret — amendments > skjer kun via D-A/D-B + commons (PULL-ONLY). > > Prinsipp: hver bygg-sesjon er én-sesjons-stor, offline, gratis (skriptede klienter), TDD > (Iron Law), og Opus-eksekverbar med kun «Les STATE.md og følg instruksjonene». Kost/tenant/ > ekspert-gatede steg er **operatør-milepæler (M1–M3)**, aldri autonome Opus-sesjoner. --- ## 1. Endringer mot roadmapen (oppsummert) | Roadmap | Endring | Begrunnelse | |---|---|---| | Fase 2 «MCP wiret i kjørestien» | `[ENDRET]` → **MCP-ingest-konnektor** (ny kildefamilie i manifestet), aldri kjøresti-wiring | P2: method-spec §3 forbyr query-time retrieval; CLAUDE.md-invariant | | Fase 2 «live-kilde-herding, inkrementell re-ingest» | `[ENDRET]` → gated på beslutning **D-B**; default-scope er herding av http-konnektoren mot mock (timeouts/retry/feilkategorier), IKKE live kilde/inkrementell | P1/U-3: frossen ingest-målbilde §8/§11/§12 forbyr dette; bevisst amendment kreves | | Fase 3 «concurrent fan-out» | `[ENDRET]` → krever beslutning **D-D** først (determinisme-modell) + fikse F1/F6 FØR concurrency | Concurrent==sekvensiell er i dag selvmotsigende (delt store, RMW-index) | | Fase 4 | `[ENDRET]` → splittes i offline-byggbar del (S4.0–S4.2, autonome sesjoner) + operatør-milepæler (M1/M2) | Mandat-skille | | Fase 5 | `[ENDRET]` → forutsetter outbox (S2.1) som roadmapen ikke nevner | P4: output-laget (målbilde §3) er ubygget | | — | `[NY]` S2.0 (portefølje-læringssløyfe), S2.1 (outbox), S2.5 (inbox-herding), S2.7 (validator-stramming), S4.0 (kostbaseline-forankring) | Injisert av funn F1, P4, F7/F11, F2, F3 | | — | `[NY]` Beslutnings-sesjoner D-A–D-E FØRST | §4-restene + spec-amendments må avgjøres før bygg | **Funn → sesjon-mapping:** F1→S2.0 · F2→D-A+S2.7 · F3→D-A+S4.0 · F4→D-A (spec) · F5→D-A+S3.2 · F6→D-D+S3.3 · F7→S2.5 · F8→S4.0 · F9→D-E+S2.0(guard) · F10→S3.4 · F11→S2.5 · F12→S2.0 · F14→D-A (tekst) · P1→D-B · P4→S2.1 · P6→S4.1/S5.2/S5.3. **Commons-regel (gjelder alle spec-amendments):** `shared/` er PULL-ONLY subtree. Endringer i method-spec/ingest-spec/bundle-layout skrives i `portfolio-optimiser-commons` og pulles hit (`git subtree pull --prefix=shared commons main --squash`). D7-søskenet må speile senere — UTENFOR dette repoets sesjoner; flagges i hver berørt sesjon. --- ## 2. Beslutnings-sesjoner (FØRST — operatør + kort AI-brief, ingen kode) ### D-A — Spec-amendments i commons (method-spec) - **Avgjør:** (1) validator nominal-gate + IR-invariant `low ≤ unit_cost ≤ high` (F2 — golden- kompatibel, verifisert); (2) kostbaseline-avstemmings-stage (F3); (3) `/`-prefiksede cross-links mappes til bundle-rot (F4/U-1); (4) per-verdict seeding-nøkkel (F5/U-4); (5) tekst-justering «Magentic eksperimentell» → «planleggingstung/uprøvd utenfor Magentic-One» (F14; kun begrunnelse, valget står). - **Output:** godkjent/avvist per punkt + utkast til commons-endringer. Blokkerer: S2.7, S3.2, S4.0. ### D-B — Fase 2-scope vs frossen ingest-målbilde - **Avgjør:** (1) amendes ingest-målbildet bevisst (live-kilde/inkrementell/§11), eller nedskopes roadmap-B til mock-herding (P1/U-3 — anbefalt: nedskop nå, live utsettes til M3-forberedelse); (2) §4.4 første live-kilde (anbefalt: **SQL/database** — `read_sql` er allerede den mest herdede konnektoren, read-only by construction); (3) dokument-konnektor-avgrensning: PDF-tekst via `pypdf` (ny dep) nå, DOCX utsatt; (4) MCP-kildefamiliens nettverks-/subprosess-grant-form. - **Output:** Fase 2-scope-erklæring + evt. målbilde-amendment. Blokkerer: S2.2, S2.3, S2.4. ### D-C — Vektor-store (§4.5) - **Grunnlag (delegert research, kildeført i review §6):** ved 100–5 000 dokumenter er ANN unødvendig; eksakt søk er deterministisk og offline-testbart. Rangering: **1) numpy brute-force** (BSD, universelle wheels, delt format .npy+jsonl er enklest for begge stacker), **2) sqlite-vec 0.1.9** (eksakt KNN, MIT/Apache, x86_64-wheel OK — men pre-v1.0, pin), 3) faiss-cpu (x86_64-wheel krever macOS 15+ — sjekk `sw_vers` først). **LanceDB: dealbreaker** (ingen Intel-Mac-wheel); ChromaDB/DuckDB-VSS: frarådet (tunge deps / eksperimentell VSS). - **Avgjør:** valg (anbefalt: numpy) + delt lagringsformat med SDK-søskenet + embeddings-kilde for ekte kjøring (Azure OpenAI embeddings vs lokal — kun konfig; tester bruker alltid fake-embedder). - **Output:** beslutning D8. Blokkerer: S3.1. ### D-D — Concurrency- og feil-modell (Fase 3) - **Avgjør:** (1) determinisme-modell for concurrent fan-out — anbefalt: **bølge-modell** (kjøringer i en bølge ser samme store-snapshot; deterministisk merge-barriere mellom bølger; sekvensiell == bølgestørrelse 1); (2) én-skriver-regel eller fil-lås for index/ledger (F6); (3) feilpolicy ved delvis feil midt i portefølje (anbefalt: collect-and-continue, per-run-feil i `PortfolioResult`, aldri tap av fullførte runs — i dag kastes alt, review §3). - **Output:** concurrency-kontrakt. Blokkerer: S3.3, S3.4. ### D-E — Mål-semantikk-rest + stack-paritet - **Avgjør:** (1) prosent-måls baseline-kilde (F9: subset-avhengig i dag; bundle-prosjekter har baseline 0) — anbefalt: eksplisitt `baseline_ore` i GoalContract/GoalConfig, aldri implisitt sum; (2) §4.6 stack-paritet for den ekte kjøringen (MAF, SDK eller begge — operatørens kost-avveining; liveness-asymmetri-erklæringen i sammenligningsprotokollen §3 består uansett). - **Output:** semantikk-beslutning. Blokkerer: del av S2.0 (guard-form), M2-konfig. --- **D-F–D-I — BESLUTTET 2026-07-14** (operatør-samtale, intensjonsanalyse). Til forskjell fra D-A–D-E over er disse fire allerede avgjort; fasit-protokoll + innplassering i [revisjonspakke D-F–D-I](2026-07-14-revisjonspakke-DF-DI.md). Frosne dokumenter er IKKE endret ennå — amendments skjer via commons (PULL-ONLY) i egne økter. Oppføringene under oppsummerer beslutningen + hva den blokkerer/åpner (ikke hele fasiten). ### D-F — Kunnskapsinnholdsmodell `[BESLUTTET 2026-07-14]` - **Status:** BESLUTTET 2026-07-14. Fasit: [revisjonspakke §1](2026-07-14-revisjonspakke-DF-DI.md). - **Kjerne:** ny delt innholdsmodell i commons (kunnskapstyper tiltaksmønstre/erfaringsnotater/faglige råd, alle med påkrevd kildebelegg) — KUN lesestoff for forslagsstilleren, validatorens regler urørt. Streng separasjon fra dommene (bibliotek via `bundle_context`; korreksjoner KUN via ExpeL-folden). Delt dimensjonsbibliotek materialiseres inn i hver bundle via ingest-mønsteret (`generated: true`, flat navigasjon — ikke kryssbundle-lenker); dimensjonskatalog som skjema-validert konfig; trinnvis lesing (sammendrag-først) er nytt krav. - **Blokkerer/åpner:** gated på commons-amendment FØRST (PULL-ONLY; D7-speiling flagges) → åpner **S3.5** (koordineres med S3.2). Realistisk energi-innhold (§1 pkt. 7) = egen senere innholds- produksjonsjobb, ikke del av S3.5. Fabrikk-avhengige deler venter på toolkit-repoet (D-G). ### D-G — OKF: felles modul, fabrikk, evaluator `[BESLUTTET 2026-07-14]` - **Status:** BESLUTTET 2026-07-14. Fasit: [revisjonspakke §2](2026-07-14-revisjonspakke-DF-DI.md) (+ kryssrepo-inventar §0.2). - **Kjerne:** standard-kompatibel OKF (F4/`/`-lenkeformen rettes — forsterker **D-A pkt. 3**), egne utvidelser dokumenteres eksplisitt som utvidelser. **Felles OKF-kode JA** (endrer D7s «from spec alone» bevisst; gir Claude-repoet skrive-halvdelen A2 mangler i dag). Nytt tredje signatur-repo (arbeidstittel `okf-toolkit`): delt les/naviger/skriv-kjerne + conformance-testsett + bundle-evaluator + bundle-fabrikk. Rekkefølge: modul + formatprøve først (fjerner 6-steders-duplikasjonen), fabrikk + evaluator deretter mot energi-caset. - **Blokkerer/åpner:** toolkit lever i EGET repo utenfor denne sesjonskøen → **ekstern avhengighet (T0)** som blokkerer fabrikk-avhengige deler av D-F/D-H. Guard forblir eget sikkerhets-repo (avhengighet). ### D-H — Oppsett og brukervennlighet `[BESLUTTET 2026-07-14]` - **Status:** BESLUTTET 2026-07-14. Fasit: [revisjonspakke §3](2026-07-14-revisjonspakke-DF-DI.md). - **Kjerne:** oppsett gjøres ALLTID av et lite team (teknisk + fag); leveransen er en **oppskrift** (dokumentert prosess), ikke en veiviser (B9-onboarding + guidet dom-kommando FORKASTET). Fagpersonen leverer filer i egne formater — aldri skjema/JSON; **fabrikken** (D-G) AI-oversetter fri-format-dommer til strengt format, godkjenning i praksis = stikkprøver med provenance-peker til ekspertens originalfil. Demo-sti: klon → unzip energi-eksempel i bundle-innboks → fabrikk bygger → hele sløyfa kjører. Obsidian/VS Code som lese-løsning først. - **Blokkerer/åpner:** oppskrift-dokumentet (§3 pkt. 1) legges til **S5.3** som dokumentleveranse; dom-oversettelse + demo-sti er fabrikk-avhengige (D-G/T0), blokkert til toolkit finnes. ### D-I — Verdibevis + kostnadsstyring `[BESLUTTET 2026-07-14]` - **Status:** BESLUTTET 2026-07-14. Fasit: [revisjonspakke §4](2026-07-14-revisjonspakke-DF-DI.md). - **Kjerne:** publiserings-påstand = **nivå 2** (realistisk case, modellerte tall, aldri salgsspråk over beleggsnivået); nivå 3 (ekte pilot) er åpen invitasjon. **Verdirapport per kjøring** (kjerne): hovedbok-basert modellert→korrigert→realisert, målprogresjon, tallfestet læringseffekt, kost-mot-verdi. **Kostnadssimulering FØR kjøring** (MÅ-krav): what-if over modell-mappet (modeller × effortnivåer), prisdata som skjema-validert konfig (aldri hardkodet), kvalitetsutsagn merket som veiledning med kilde. - **Blokkerer/åpner:** åpner **S5.4** (verdirapport, etter S2.1-outbox + ledger) og **S3.6** (kostnadssimulering, fri/offline). README oppdateres FØRST når nivå-2-beviset finnes (pkt. 1). --- ## 3. Opus-sesjons-specs (offline, autonome) > Felles for alle: Iron Law (failing test først); `uv run pytest` / `uv run ruff check .` / > `uv run mypy src` grønne ved slutt; skriptede klienter (null modell-kost); commit per sesjon i > repoets Conventional-Commit-stil; STATE.md overskrives ved sesjonsslutt. ### S2.0 `[NY]` Portefølje-læringssløyfe + ærlighets-fiks (F1, F12, P5, F9-guard) - **Mål:** en dom på prosjekt k når prosjekt k+1s hypotese-PROMPT i `run_portfolio` — og ingen docstring påstår mer enn koden gjør. - **Scope:** per-prosjekt `bundle_dir` (valgfritt felt i `reference_projects.json`) + `verdict_dir`-parameter tres gjennom `run_portfolio` → `run_project`. Percent-goal-guard: eksplisitt feil (ValueError) når percent-mål settes og baseline er 0. CHANGELOG oppdateres (Fase 1 + denne). IKKE bygges: concurrency, semantisk henting, outbox. - **Avhengigheter:** ingen (første bygg-sesjon). - **Filer:** `run.py`, `reference_domain.py`, `data/reference_projects.json` (+ evt. mini-bundle fixture under `data/`), `tests/test_portfolio.py`, ny `tests/test_portfolio_learning_loadbearing.py`, `CHANGELOG.md`. - **TDD:** (1) RØD: test der prosjekt k får dom (via delt store, bundle-sti) og k+1s genererings-prompt MÅ inneholde dommens id+markør — detach-punkt: fjern bundle_dir-trådingen i run_portfolio → rød (i dag ER den rød — det er poenget). (2) RØD: empty-store-kontroll. (3) RØD: percent-mål + baseline 0 → ValueError. - **Nøkkelantakelser:** bundle-fixture kan gjenbruke `bygg-energi-mikro`-form uten commons-endring (test: legg fixture under `src/portfolio_optimiser/data/`, aldri i `shared/`). - **Verifisering:** `uv run pytest tests/test_portfolio_learning_loadbearing.py` → grønn; detach (revert tråding) → rød; full suite + ruff + mypy grønne; `grep -n "cross-project learning" src/` viser docstring som nå stemmer med wiring. ### S2.1 `[NY]` Outbox-persistens — output-laget materialiseres (P4) - **Mål:** hver kjørings forslag/avvisning/provenance persisteres som deterministisk JSON i en outbox-mappe (målbilde §3s output-lag), så utestående dommer kan spores (Fase 5) og live-artefakter fanges (sammenligningsprotokoll §4.2). - **Scope:** `run_project(outbox_dir=..., run_id=...)` (begge valgfrie; `run_id` er påkrevd når outbox er satt — ingen wall-clock-default). Skriver `{run_id}-proposal.json` / `{run_id}-outcome.json` (outcome-type, reason/percentiler, checker_verdict, provenance, verdict-id). Systemet SKRIVER outbox (lovlig: rolledelingen §3 gjelder inbox/wiki, ikke systemets eget output-lag — sitér method-spec §2 i docstring). IKKE bygges: ruting, varsling. - **Avhengigheter:** ingen (parallell med S2.0). - **Filer:** `run.py`, ny `outbox.py` (ren stdlib+pydantic — MAF-fri? Nei: importerer validator-typer som er rene; hold `agent_framework`-fri og legg til i `_MAF_FREE_MODULES`), `tests/test_outbox_loadbearing.py`. - **TDD:** RØD: kjøring med outbox_dir → forventet filpar finnes, byte-deterministisk (sort_keys, indent 2, LF); detach-punkt: fjern outbox-skrivingen → rød. Kontroll: uten outbox_dir skrives ingenting. - **Nøkkelantakelser:** `ValidatedProposal`/`Rejection` er JSON-serialiserbare via eksplisitt mapping (test først med begge utfallstyper). - **Verifisering:** ny testfil grønn; detach → rød; full gate grønn; to kjøringer med samme input+run_id → byte-identiske filer (`diff`). ### S2.2 MCP-ingest-konnektor (gated: D-B) `[ENDRET fra «MCP i kjørestien»]` - **Mål:** en fjerde kildefamilie `mcp` i ingest-manifestet materialiserer OKF-filer fra en MCP-server — datainntak, aldri kjøresti. - **Scope:** ingest-spec-amendment i commons FØRST (skjema for `{type:"mcp", id, server_ref, tool}`; `server_ref` = navn på env-var med kommando, mirror `connection_ref`) → subtree pull → impl i `ingest.py`. Transport: stdio-klient via `mcp`-pakken (allerede dep). Per-run-grant: gjenbruk `allow_network`-mønsteret med nytt eksplisitt flagg (per D-B). Test: in-process fixture-server (gjenbruk `datasource.build_mcp_server`-kjernen mot committed docs — det AV-DEMOER extension-pointet). IKKE: fjern-servere, auth utover env-ref. - **Avhengigheter:** D-B (+ commons-pull). - **Filer:** `ingest.py`, `tests/test_ingest_mcp*.py`, golden-fixture `examples/ingest-golden-mcp/` (i commons — flagg), `docs/extending.md`-avsnitt. - **TDD:** RØD: manifest med `type: mcp` uten grant → `IngestError` (grant-detach → rød); RØD: golden-materialisering mot fixture-server → byte-deterministisk bundle; RØD: verdict-reservasjonen gjelder også mcp-familien. - **Nøkkelantakelser:** `mcp`-klient kan drives i-prosess mot FastMCP uten socket (test som første steg — hvis ikke, subprosess mot `python -m`-fixture med eksplisitt merking). - **Verifisering:** `uv run pytest tests/test_ingest_mcp*.py` grønn; detach grant-sjekken → rød; full gate grønn. ### S2.3 Dokument-konnektor (`doc`-familie; gated: D-B) - **Mål:** PDF-rapport → OKF-konseptfil med provenance, deterministisk mot committed fixtures. - **Scope:** ingest-spec-amendment (commons) for `{type:"doc"}`; `pypdf` som ny dep (D-B); tekst-ekstraksjon per side, fenced-verbatim-rendering (gjenbruk `_render_fenced_block`-regler), `max_rows`-cap, fail-fast på ikke-tekst-PDF. DOCX: IKKE (utsatt per D-B). - **Avhengigheter:** D-B; uavhengig av S2.2. - **Filer:** `ingest.py`, `pyproject.toml` (+pypdf), `tests/test_ingest_doc*.py`, golden-fixture med liten committed PDF. - **TDD:** RØD: golden byte-determinisme; RØD: kodefence-linje i ekstrakt → `IngestError` (gjenbruk http-regelen); RØD: cap-overskridelse → feil, aldri trunkering. - **Nøkkelantakelser:** `pypdf`-ekstraksjon er bit-stabil på tvers av versjoner for fixture-PDF-en (test: pin versjon; golden fanger drift). - **Verifisering:** nye tester grønne + detach → rød; full gate grønn. ### S2.4 Konnektor-herding mot mock (gated: D-B) `[ENDRET fra «live-kilde-herding»]` - **Mål:** http-konnektoren tåler virkelighetens feilmoduser — bevist mot injisert transport, aldri live. - **Scope:** timeout-parameter på `_urllib_get`; feilkategorisering (transport/auth/decode) i `IngestError`-melding; ingest-logg beriket (§8: kilde, tidspunkt, radantall — aldri innhold); retry KUN hvis D-B sier ja (default nei — determinisme). Inkrementell re-ingest: IKKE (frossen §8) med mindre D-B amender. - **Avhengigheter:** D-B. - **Filer:** `ingest.py`, `tests/test_ingest_http*.py`. - **TDD:** RØD per feilmodus (canned transport som raiser URLError/timeout/feil-encoding) → presis IngestError; detach timeout → rød. - **Verifisering:** nye tester grønne; full gate grønn; `grep` bekrefter ingen nye socket-stier utenfor `_urllib_get`. ### S2.5 `[NY]` Inbox- og robusthets-herding (F7, F11) - **Mål:** verdict-inboxen slutter å være en uvalidert injeksjonsflate, og offline-bevisets MAF-avhengighet får en versjonsvakt. - **Scope:** (1) `load_verdicts_from_dir`: decision-vokabular-sjekk (§4.2 — ukjent decision → SKIP, fortsatt tolerant, aldri raise), tak på rationale-lengde + antall filer per merge (konfigurerbart, fail-fast ved overskridelse — aldri stille kutt); (2) konsolider de fire `_inner_get_response`-duplikatene (conftest ×3 + simulation) til én delt scripted-klient (plassering: `tests/`-hjelper + simulation beholder sin — eller flytt til `simulation.py` og importér i conftest; velg det som ikke gir src→tests-import); (3) MAF-versjonsvakt: test som feiler med handlingsrettet melding hvis `agent_framework.__version__` ikke matcher pinnet versjon (tvinger bevisst re-verifisering av privat-API-premissene ved oppgradering); (4) pin `agent-framework-core>=1.9.0,<2` i pyproject. - **Avhengigheter:** ingen. - **Filer:** `verdicts.py`, `conftest.py`, `simulation.py`, `pyproject.toml`, `tests/test_verdicts.py`, ny `tests/test_maf_version_guard.py`. - **TDD:** RØD: inbox-fil med `decision: "banana"` → hoppes over (detach vokabular-sjekken → den entrer storen → rød); RØD: rationale > tak → skip + logg; versjonsvakt-testen rød mot en fake-versjon. - **Verifisering:** full gate grønn; `grep -c "_inner_get_response" tests/ src/` viser redusert duplikat-antall. ### S2.7 `[NY]` Validator-stramming (F2; gated: D-A + commons-pull) - **Mål:** den deterministiske validatoren håndhever sin egen stage-2-grense, og assumptions kan ikke forfattes forbi den. - **Scope:** (1) strukturell blokk `claimed > nominal_feasible` → Rejection (i tillegg til P90); (2) IR-invariant: hvert assumption-band MÅ omslutte itemets `unit_cost` (`low ≤ unit_cost ≤ high`) — Pydantic `model_validator`. Golden-kompatibilitet er verifisert i review (claim 30k ≤ 90k; band [0.70,1.40] ∋ 1.0). IKKE: kostbaseline-avstemming (S4.0), metode-registry (S4.0). - **Avhengigheter:** D-A pkt. 1 + commons-spec pullet. - **Filer:** `validator.py`, `ir.py`, `tests/test_validator.py`, `tests/test_ir*.py` (eller tilsvarende), `tests/test_bygg_energi_mikro.py` (uendret grønn — regresjonsbevis). - **TDD:** RØD: reviewens moteksempel #1 (claim 100k, nominal 90k, band [0.70,1.40]) MÅ bli Rejection; RØD: band [1.8,2.2] rundt unit_cost 1.0 → ValidationError ved konstruksjon; detach-punkt: fjern nominal-blokken → moteksempel-testen rød. - **Nøkkelantakelser:** ingen eksisterende test/fixture bruker band som ikke omslutter unit_cost — **VERIFISERT 2026-07-09 av reviewer** (skann av alle JSON-fixtures + inline-band-grep: null brudd; se review §6). Antakelsen er dermed et bekreftet premiss, ikke en risiko. - **Verifisering:** `uv run pytest tests/test_validator.py tests/test_bygg_energi_mikro.py` grønn; golden uendret; full gate grønn. **Flagg:** D7-søsken må speile (egen sesjon i søsken-repoet). ### S3.1 Semantisk henting bak seam (gated: D-C) `[roadmap C]` - **Mål:** hentingen skalerer forbi feature-key-match uten å ofre determinisme/offline-testbarhet. - **Scope (per D-C-anbefaling numpy):** `Retriever`-protokoll; `HybridRanker` = dagens strukturelle score + cosine over lagrede vektorer (vekting konfig); vektor-lager = `.npy` + id-jsonl ved siden av verdict-storen; `Embedder`-protokoll med deterministisk fake i tester (hash-basert); ekte embeddings-klient (Azure/lokal) KUN som konfig-punkt, aldri kalt i tester. IKKE: ANN, ekstern DB, endring i `shared/`. - **Avhengigheter:** D-C; uavhengig av S2.x. - **Filer:** ny `semretrieval.py` (MAF-fri, i `_MAF_FREE_MODULES`), `verdicts.py` (retrieve-seam), `run.py` (opt-in), `pyproject.toml` (numpy), nye tester. - **TDD:** RØD: syntetisk stor base (500+ verdicts) der KUN cosine-komponenten kan rangere riktig dom øverst (strukturell score uavgjort ved konstruksjon) — detach cosine → rød; RØD: determinisme (to kjøringer → identisk rangering); RØD: fake-embedder-bit-stabilitet. - **Nøkkelantakelser:** numpy-dep er akseptabel i MAF-repoet (IKKE i `shared/`) — bekreftet av D7-grensen (semantikk-laget er stack-lokalt). - **Verifisering:** nye tester grønne + detach → rød; full gate grønn; `test_okf_is_maf_free` utvidet til `semretrieval.py` grønn. ### S3.2 Multi-kandidat ExpeL-substrat (F5; gated: D-A pkt. 4 + commons-pull) - **Mål:** dommer nøkles på SIN kandidat, ikke bundelens ene IR-projeksjon. - **Scope:** seed leser features fra verdict-filas egen frontmatter (nye valgfrie felt: `affected_codes`/`measure_type`/`claimed_saving_nok`; fallback = dagens kandidat-nøkling for bakoverkompatibilitet); `promote_verdict` skriver disse feltene; multi-kandidat-bundle-fixture. - **Avhengigheter:** D-A + commons (spec §3 Steg 1 seeding-regel + bundle-eksempel). - **Filer:** `verdicts.py`, `tests/test_step8_promotion_loadbearing.py`, ny multi-kandidat-fixture (repo-lokal under `data/`), nye tester. - **TDD:** RØD: to kandidater i én bundle → dom om kandidat B når ALDRI kandidat As hypotese-prompt (detach per-verdict-nøklingen → rød fordi B-dommen feil-nøkles til A). - **Verifisering:** nye tester grønne; eksisterende step1/step7/step8-suite uendret grønn (fallback-kompatibilitet); full gate grønn. **Flagg:** D7-speiling. ### S3.3 Concurrent fan-out (F6; gated: D-D) `[roadmap C]` - **Mål:** N kjøringer parallelt med bevart determinisme: concurrent == sekvensiell. - **Scope (per D-D bølge-modell):** `run_portfolio(concurrency=k)`; bølge = inntil k prosjekter med samme store-SNAPSHOT; deterministisk merge-barriere (sortert på project_id) mellom bølger; feilpolicy collect-and-continue (`RunResult`-slot med feil-felt, fullførte runs bevares); én-skriver-regel for index/ledger håndheves (ingen promote/realize i kjørestien — allerede sant; assert + dok). - **Avhengigheter:** D-D, S2.0 (bundle-portefølje), helst S3.1 (men ikke hard). - **Filer:** `run.py`, `tests/test_portfolio_concurrent_loadbearing.py`. - **TDD:** RØD: `concurrency=3` gir BYTE-identisk aggregat + store-innhold som `concurrency=1` på 3-prosjekt-fixturen (detach barrieren → udeterministisk → rød over repetisjoner — kjør testen med fast rekkefølge-probe, ikke tidsavhengig); RØD: injisert feil i prosjekt 2 → prosjekt 1+3s resultater bevart. - **Nøkkelantakelser:** MAF-klienter er asyncio-samtidige i én loop (dokumentert modell er asyncio; OS-tråd-garanti er IKKE dokumentert — hold alt i én loop). - **Verifisering:** ny test grønn ×20 repetisjoner (`pytest --count` el. loop); full gate grønn. ### S3.4 Kostnadsstyring på tvers (F10) `[roadmap C]` - **Mål:** et globalt token-tak over HELE porteføljepasset (og på tvers av pass), håndhevet FØR kall — ikke bare per kjøring etterpå. - **Scope:** `PortfolioBudget` (globalt tak + per-run-tak); pre-call-guard i `BudgetMiddleware` (nekt kall når rest < 0 — i tillegg til dagens post-charge); spend-persistens (deterministisk JSON, eksplisitt stamp-argument); oppstartsnekt når resttak < konfigurert minimum per kjøring. - **Avhengigheter:** D-D (deler feilpolicy), S3.3 (bølge-integrasjon) — kan bygges før S3.3 mot sekvensiell sti. - **Filer:** `budget.py`, `run.py`, `tests/test_budget.py`, ny loadbearing-test. - **TDD:** RØD: globalt tak krysses midt i passet → strukturert stopp med fullførte runs bevart (detach global-sjekken → passet løper forbi taket → rød); RØD: pre-call-guard nekter kallet (klient-mock som teller kall — kallet skal ALDRI skje). - **Verifisering:** nye tester grønne + detach → rød; full gate grønn. ### S3.5 `[NY]` Innholdsmodell + fagbibliotek-materialisering (D-F; gated: commons-amendment) - **Mål:** delt kunnskapsinnholdsmodell (tiltaksmønstre/erfaringsnotater/faglige råd med kildebelegg) materialiseres inn i prosjekt-bundles som rent lesestoff for forslagsstilleren — uten å røre validatoren og uten å blande seg med dommene. - **Scope (revisjonspakke §1 pkt. 1–6):** (1) nye kunnskapstyper i delt spec (commons-amendment FØRST — PULL-ONLY, D7-speiling flagges); (2) dimensjonskatalog som skjema-validert konfig (id, label, allowed_measure_types, allowed_code_prefixes) + fail-fast loader — rammeverket shipper eksempel-katalog, deployer eier sin; (3) bibliotek-materialisering via ny ingest-kildefamilie (`generated: true`, manifest-ref, re-materialisering eier egne filer — flat/same-dir navigasjon, IKKE kryssbundle-lenker, IKKE manuell duplisering); (4) trinnvis lesing i `bundle_context`: sammendrag-først (én linje per kunnskapsfil), fulltekst hentes ved behov. Streng separasjon håndheves: bibliotek via `bundle_context`, organisasjonens korreksjoner KUN via ExpeL-folden. **IKKE i scope:** energi-innholdet i realistisk skala (§1 pkt. 7 = egen senere innholds-produksjonsjobb, avhengig leveranse); validator↔bibliotek-kobling (framtidig beslutning); toolkit-fabrikken (D-G, eget repo). - **Avhengigheter:** commons-amendment for innholdsmodellen (D-F-utkast operatør-godkjent + `git subtree pull`) FØRST; koordineres med **S3.2** (multi-kandidat — deler seed/frontmatter-stien). Etter S2.0/S2.1/S2.5. - **Filer:** `okf.py` (trinnvis lesing / sammendrag-seam — MAF-fri, håndhevet av `test_okf_is_maf_free`), ny `dimension_catalog.py` (loader, fail-fast), `ingest.py` (bibliotek-kildefamilie), commons-spec + eksempel-katalog (i commons — flagg), repo-lokal bibliotek-fixture under `data/`, nye tester. - **TDD:** (1) RØD: dimensjonskatalog med ugyldig felt (ukjent `allowed_measure_type`-referanse) → fail-fast ved lasting — detach-punkt: fjern skjema-valideringen → passerer → rød; (2) RØD: materialisert bibliotek-fil når forslags-prompten via `bundle_context` (markør fraværende fra bundelen ellers) — detach materialiseringen → markøren forsvinner → rød; (3) RØD: trinnvis lesing gir sammendraget i navigert kontekst men IKKE fulltekst før eksplisitt henting — detach sammendrag-seamen → hele fullteksten stuffes inn → rød; (4) RØD: en bibliotek-fil MÅ ALDRI nå hypotese-prompten via dom-kanalen (separasjons-kontroll — biblioteket lekker aldri inn i ExpeL-folden). - **Nøkkelantakelser:** trinnvis-lesing-kravet (D-F pkt. 6) kan realiseres uten å bryte method-spec §3 Steg 1s render-kontrakt — HVIS ikke, er sammendrag-seamen del av commons-amendmenten (flagges eksplisitt, jf. revisjonspakke §6-nøkkelantakelsen). Bibliotek-fixture ligger repo-lokalt under `data/` (aldri i `shared/`) til energi-innholdet materialiseres. - **Verifisering:** nye tester grønne + hvert detach-punkt → rød; `test_okf_is_maf_free` utvidet grønn; full suite + ruff + mypy grønne. **Flagg:** commons-amendment + D7-speiling (egen sesjon i søsken-repoet). ### S3.6 `[NY]` Kostnadssimulering før kjøring (D-I pkt. 3; offline, ikke gated) - **Mål:** operatøren ser estimert kost for en portefølje-kjøring FØR den startes — what-if over modell-mappet (modeller × effortnivåer) — så kjøringskost blir en informert beslutning, ikke en overraskelse etterpå. - **Scope (revisjonspakke §4 pkt. 3):** `python -m portfolio_optimiser.costsim` (+ funksjons-API): estimér tokens/kost for en gitt portefølje under valgt modell + effortnivå; **prisdata som skjema- validert konfig** (kr/1k tokens per modell, med kilde- + dato-felt — ALDRI hardkodet; fail-fast ved manglende/ugyldig pris); kvalitets-avveininger per modellvalg merket som **veiledning med kilde**, aldri målt fakta uten belegg; output = estimat-tabell (per rolle/modell/effort) + `kost-mot-verdi`-felt klart for verdirapporten (S5.4). Deler tak-begreper med budsjettmaskineriet (`PortfolioBudget` / `BudgetMiddleware` fra S3.4), men estimerer FØR kall. Adopsjonssti dokumenteres (start liten → eskaler med tilliten). IKKE: live prisoppslag (nettverk), faktisk modellkall (rent offline), kvalitets-benchmarking. - **Avhengigheter:** ingen commons-avhengighet (offline, stack-lokalt); drar nytte av **S3.4** (budsjett-typer) og mater **S5.4** (verdirapport). Kan bygges før S3.5. - **Filer:** ny `costsim.py` (ren stdlib + pydantic — MAF-fri, legg i `_MAF_FREE_MODULES`), `data/pricing.example.json` (skjema + eksempelpriser med kilde/dato), `budget.py` (deling av tak-begreper), nye tester. - **TDD:** (1) RØD: konfig uten pris for en modell i mappet → fail-fast «missing price for » — detach-punkt: fjern pris-valideringen → estimatet gjettes → rød; (2) RØD: estimatet er deterministisk og skalerer med modell × effort (to modeller, samme portefølje → forskjellig, reproduserbart estimat) — detach effort-faktoren → identisk estimat → rød; (3) RØD: hardkodet pris i kildekode fanges av grep-guard (pris MÅ komme fra konfig). - **Nøkkelantakelser:** token-estimatet kan bygges fra portefølje-formen (antall prosjekter × runder × tak) uten et modellkall — deterministisk øvre-grense-estimat, ikke prediksjon; merkes eksplisitt som estimat i output. - **Verifisering:** nye tester grønne + detach → rød; `uv run python -m portfolio_optimiser.costsim` gir estimat-tabell uten nettverk (grep-guard: ingen socket-sti i modulen); full gate grønn. ### S4.0 `[NY]` Validator-forankring mot kostbaseline (F3, F8; gated: D-A pkt. 2) - **Mål:** `affected_items` avstemmes fail-closed mot prosjektets faktiske kostbaseline — den deterministiske gaten kan ikke lenger mates med hallusinerte kostlinjer. - **Scope:** baseline-projeksjon i bundle (`cost-baseline.json`: code→{quantity, unit_cost} — commons-amendment) + fra `reference_domain.cost_items` på road-stien; ny avstemmings-stage i `validate_proposal` (kode finnes ikke i baseline → Rejection; quantity/unit_cost utenfor toleranse → Rejection; toleranse konfig); metode-registry: metode-caps keyes via dimensjon/konfig, ikke strengen `energy_efficiency` (F8). Baseline-argument er VALGFRITT i første omgang (None = dagens oppførsel) så eksisterende suite står — men run-stiene SETTER den. - **Avhengigheter:** D-A + commons; S2.x-ingest kan generere baseline-projeksjonen (soft). - **Filer:** `validator.py`, `run.py`, `okf.py` (baseline-loader, fail-fast), commons-fixture, nye tester. - **TDD:** RØD: reviewens F3-scenario (dikta kode `XX`, 10 MNOK) → Rejection «unknown cost code» (detach avstemmingen → validated → rød); kontroll: ekte baseline-linje validerer som før. - **Verifisering:** nye tester grønne + detach → rød; golden uendret; full gate grønn. **Flagg:** D7-speiling. MÅ være grønn før M2. ### S4.1 Azure/Foundry-preflight (offline) `[roadmap D, offline-delen]` - **Mål:** alt som KAN valideres uten et modellkall, valideres før operatøren betaler for ett. - **Scope:** `python -m portfolio_optimiser.preflight --profile azure`: env-kontrakt (`PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` satt + URL-form; deployment-navn ≠ `REPLACE-WITH-*`; modell-map-konsistens), credential-sjekk KUN som dokumentert manuelt steg (aldri auto-login). Nekter live-profil med placeholders (fail-fast også i `resolve_model`-stien). Dokumentér auth-oppskrift (Entra: `az login` / DefaultAzureCredential; FoundryChatClient tar project_endpoint+model+credential — verifisert mot MS Learn, se review §6). Env-override for model_map (`PORTFOLIO_MODEL_MAP` sti) så tenant-navn aldri committes. - **Avhengigheter:** ingen (uavhengig av Fase 2/3). - **Filer:** ny `preflight.py`, `backends.py`, `contracts.py` (placeholder-regel), tester. - **TDD:** RØD: placeholder-deployment + azure-profil → strukturert nekt (detach placeholder-sjekken → rød); RØD: manglende endpoint-env → aksjonérbar feilmelding. - **Verifisering:** `uv run python -m portfolio_optimiser.preflight --profile azure` gir korrekt nekt/OK uten nettverk (bevis: kjør med nettverk utilgjengelig antatt — ingen socket-kode i modulen, grep-guard); full gate grønn. ### S4.2 Live-kjørings-drill + artefakt-fangst (offline) `[roadmap D, offline-delen]` - **Mål:** den ENE fremtidige live-kjøringen (M2) er ferdig-rigget: alt bygges, artefakter fanges, og drillen er øvd offline. - **Scope:** `--live-dry-run`-modus i main(): last kontrakter, bygg klienter, valider budsjett, STOPP før første modellkall (bevist med kall-tellende mock); kjøringskonfig-logg (modell-id, parametre, tak — sammenligningsprotokoll §4.3); outbox-fangst (S2.1) av proposal/checker/ validator/tokens per §4.2. - **Avhengigheter:** S2.1, S4.1. - **Filer:** `run.py`, `preflight.py`/`main`, tester. - **TDD:** RØD: dry-run mot mock-faktory → null chat-kall skjedde (detach stopp-punktet → kall telles → rød); RØD: artefakt-settet komplett i outbox. - **Verifisering:** nye tester grønne; full gate grønn. ### S5.1 Verdict-ruting + sporing `[roadmap E]` - **Mål:** teamet ser hvilke forslag som venter på dom, og hvem som skal dømme. - **Scope:** pending-registry = outbox-forslag uten matchende inbox-dom (id-join); ruting-konfig `dimension → ekspert` (fail-fast skjema); CLI `python -m portfolio_optimiser.hitl pending|route`; ren fil-basert (ingen server). IKKE: varsling (S5.2), web-UI. - **Avhengigheter:** S2.1 (outbox); S2.0 (verdict_dir i portefølje). - **Filer:** ny `hitl.py`, tester. - **TDD:** RØD: forslag i outbox uten dom → listes utestående; dom lander i inbox → forsvinner (detach id-joinen → rød). - **Verifisering:** nye tester grønne; full gate grønn. ### S5.2 Varsling — B11 fra stub til ekte `[roadmap E]` - **Mål:** `notify=` får leverbare implementasjoner uten å bryte ingen-stille-egress. - **Scope:** `Notifier`-protokoll + `console`/`file`/`webhook`-impl; webhook krever eksplisitt per-kjøring opt-in-flagg (mirror `allow_network`, ingest §8-mønsteret) + injiserbar transport (canned i tester — null socket i suiten); konfig fail-fast. - **Avhengigheter:** S5.1 (naturlig kobling), ellers uavhengig. - **Filer:** ny `notify.py`, `run.py` (seam består), tester. - **TDD:** RØD: webhook uten opt-in → nekt fail-fast (detach → rød); RØD: canned transport mottar korrekt payload; RØD: ingen socket-sti utenfor den injiserbare seamen (grep-guard à la `_urllib_get`). - **Verifisering:** nye tester grønne; full gate grønn. ### S5.3 CLI-paritet + dokumentsync (P6, F12-rest) - **Mål:** operatøren kan drive ALT bygget (dimensjon, mål, ledger, bundle-portefølje, inbox, outbox) fra kommandolinja, og dokumentene påstår nøyaktig det koden gjør. - **Scope:** `main()` (+ evt. `portfolio`-subkommando): `--dimension-config`, `--ledger`, `--goals`, `--bundle-dir`, `--verdict-dir`, `--outbox-dir`; README/extending.md/CHANGELOG-sync; ærlighets-grep (ingen «learning loop»-claims på stier uten fold). - **Scope (D-H pkt. 1, tillegg):** oppsett-**oppskrift** som dokumentleveranse — dokumentert team-prosess (teknisk + fag) for å bygge en kunnskapsbase, med ærlig README-forventning (en god base tar 1–2 uker dedikert arbeid; kvaliteten på investeringen avgjør resultatet); IKKE en veiviser / B9-onboarding- intervju (FORKASTET). - **Avhengigheter:** S2.0, S2.1 (og drar nytte av alt over). - **Filer:** `run.py`, `README.md`, `docs/extending.md`, `CHANGELOG.md`, CLI-tester. - **TDD:** RØD: CLI-kjøring med goals+ledger på fixtures → GoalReached-stopp observerbar i output. - **Verifisering:** CLI-tester grønne; full gate grønn; manuell `uv run python -m portfolio_optimiser.run …`-røyk på fixtures dokumentert i STATE. ### S5.4 `[NY]` Verdirapport per kjøring (D-I pkt. 2) - **Mål:** hver kjøring (og porteføljepass) produserer en deterministisk verdirapport — modellert → ekspert-korrigert → realisert per prosjekt/dimensjon, målprogresjon, tallfestet læringseffekt og kost-mot-verdi — som artefakt, uten et eneste modellkall. - **Scope (revisjonspakke §4 pkt. 2):** hovedbok-basert rapportgenerator over ledgeren + outbox (S2.1): (a) besparelses-kolonner modellert/korrigert/realisert per prosjekt + dimensjon; (b) målprogresjon mot GoalContract; (c) **læringseffekt tallfestet** — godkjenningsandel per kjøring + om modellert-vs- forventet-gapet krymper over kjøringer; (d) **kost-mot-verdi** («kjøringen kostet X, identifiserte kvalitetssikret modellert besparelse Y» — X fra S3.6-estimat / faktisk token-forbruk). Rapport = deterministisk artefakt (JSON + markdown-render; sort_keys / indent / LF / byte-stabil). Ærlighet: realiserte tall KUN der dom finnes; modellerte merkes som modellert (aldri salgsspråk over beleggsnivået, D-I pkt. 1). IKKE: web-dashboard, live-oppdatering, modellkall. - **Avhengigheter:** **S2.1** (outbox — forslag/utfall-artefakter) + ledger (S2.0-læringssløyfe); konsumerer **S3.6**-kostestimat for kost-mot-verdi-feltet. - **Filer:** ny `valuereport.py` (ren stdlib + pydantic — MAF-fri, `_MAF_FREE_MODULES`), `run.py` (opt-in `--value-report`), nye tester. - **TDD:** (1) RØD: to kjøringer med økende godkjenningsandel → læringseffekt-metrikken stiger og gap-krympingen rapporteres — detach-punkt: fjern gap-beregningen → flat/feil → rød; (2) RØD: prosjekt uten dom → «realisert» er tomt/umerket, ALDRI lik modellert — detach ærlighets-grensen → realisert = modellert → rød; (3) RØD: byte-determinisme (samme ledger + outbox → identisk rapport, `diff`). - **Nøkkelantakelser:** ledgeren + outbox bærer allerede alt rapporten trenger (modellert fra proposal, korrigert/realisert fra dommene, kost fra token-forbruk / S3.6) — rapporten er ren projeksjon, ingen ny tilstand (test: bygg rapport fra committede fixtures uten kjøring). - **Verifisering:** nye tester grønne + detach → rød; `run.py --value-report` på fixtures gir byte-stabil rapport uten nettverk; full gate grønn. --- ## 4. Operatør-gatede milepæler (IKKE autonome Opus-sesjoner) ### M1 — Foundry-tenant-oppsett + smoke (etter S4.1) - **Forutsetninger:** operatør oppretter i tenant: én Foundry-prosjekt-endpoint + ÉN Global-Standard-deployment av billig modell (gpt-4o-mini-/gpt-5-mini-klassen — én deployment dekker begge roller; research-verifisert). `az login`. Sett `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` + `PORTFOLIO_FOUNDRY_DEPLOYMENT` + model_map-override. - **Manuelle steg:** kjør preflight (S4.1) → grønn; kjør `uv run pytest tests/test_foundry_profile_live.py`. - **Kost:** ett trivielt kall (øre-nivå; pris ikke verifisert — sjekk Azure-priser først). ### M2 — Første live-modell-kjøring, liten skala (etter S4.0 + S4.2 + M1) - **Forutsetninger:** S4.0 grønn (validator forankret — F3 lukket FØR ekte modell), harde tak satt lavt (f.eks. max_tokens 20k), outbox-fangst på. - **Manuelle steg:** én `run_project` mot energi-bundelen, azure-profil; artefakter arkiveres; utfall re-rulles IKKE (sammenligningsprotokoll §4.5). - **Kost:** én kjøring innenfor taket; dokumenteres i rapport med konfig-logg. ### M3 — Fase 6-pilot (etter Fase 5 + M2 + D-B-amendment for live kilde) - **Forutsetninger:** én ekte kilde (per §4.4-beslutningen) herdet og materialisert med ekte provenance; én ekte fagekspert onboardet på inbox/outbox-kontrakten (samme mappe-grensesnitt som personaen); ruting+varsling (S5.1/S5.2) i drift; ingest-målbilde-amendment på plass (P1). - **Manuelle steg:** én dimensjon × ett prosjekt ende-til-ende; verifiser målbilde §7-sløyfa mot virkeligheten; FØRST deretter skalering mot N×10. --- ## 5. Avhengighetsgraf + anbefalt sekvens ```mermaid flowchart TB subgraph DEC["Beslutnings-sesjoner (operatør)"] DA["D-A spec-amendments"]; DB["D-B Fase2-scope/ingest-målbilde"] DC["D-C vektor-store"]; DD["D-D concurrency"]; DE["D-E mål/paritet"] end S20["S2.0 portefølje-læring (F1)"]; S21["S2.1 outbox (P4)"]; S25["S2.5 inbox-herding (F7/F11)"] S27["S2.7 validator-stramming (F2)"]; S22["S2.2 MCP-ingest"]; S23["S2.3 doc-konnektor"]; S24["S2.4 http-herding"] S31["S3.1 semantisk henting"]; S32["S3.2 multi-kandidat (F5)"]; S33["S3.3 concurrent (F6)"]; S34["S3.4 kost på tvers (F10)"] S40["S4.0 kostbaseline-forankring (F3)"]; S41["S4.1 preflight"]; S42["S4.2 live-drill"] S51["S5.1 ruting/sporing"]; S52["S5.2 varsling"]; S53["S5.3 CLI-paritet"] S35["S3.5 innholdsmodell/bibliotek (D-F)"]; S36["S3.6 kostnadssimulering (D-I)"]; S54["S5.4 verdirapport (D-I)"] DFC["D-F commons-amendment (PULL-ONLY)"]; T0[["T0 toolkit-repo (eget repo — okf-toolkit)"]] M1{{"M1 tenant+smoke"}}; M2{{"M2 første live-kjøring"}}; M3{{"M3 pilot"}} DA --> S27; DA --> S32; DA --> S40 DB --> S22; DB --> S23; DB --> S24; DB -. amendment .-> M3 DC --> S31; DD --> S33; DD --> S34; DE --> M2 S20 --> S33; S20 --> S51; S21 --> S42; S21 --> S51 S41 --> M1; S40 --> M2; S42 --> M2; M1 --> M2 S51 --> S52; S20 --> S53; S21 --> S53 S33 --> M3; S34 --> M3; S52 --> M3; M2 --> M3; S31 --> M3; S32 --> M3 DFC --> S35; S32 -. koordineres .-> S35 S21 --> S54; S34 -. kost .-> S54; S36 -. kost-mot-verdi .-> S54 T0 -. fabrikk .-> M3 ``` **Anbefalt sesjonssekvens** (parallellitet mulig der grafen tillater; én fase = Voyage-syklus): 1. **D-A, D-B** (kan tas i samme operatør-bolk; D-C/D-D/D-E kan vente) 2. **S2.0 → S2.1 → S2.5** (ingen beslutnings-avhengighet — kan starte FØR/parallelt med D-runden) 3. **S2.7** (etter D-A + commons-pull) · **S2.2 → S2.3 → S2.4** (etter D-B) 4. **D-C, D-D, D-E** → **S3.1 → S3.2 → S3.4 → S3.3**; **S3.5** etter D-F commons-amendment + koordinert med S3.2; **S3.6** fritt (offline, når som helst — mater S5.4) 5. **S4.1** (når som helst) → **M1** (operatør) · **S4.0 → S4.2** → **M2** (operatør) 6. **S5.1 → S5.2 → S5.3** · **S5.4** etter S2.1 + ledger → **M3** (operatør; krever også D-B-amendment + ekte ekspert + toolkit-fabrikken T0) **Ekstern avhengighet (T0 toolkit-repo, eget repo — `okf-toolkit`, D-G):** delt OKF-modul + conformance + bundle-evaluator + bundle-fabrikk lever i sitt eget signatur-repo, utenfor denne sesjonskøen. Fabrikk-avhengige deler av D-F/D-H (realistisk energi-innhold via fabrikken, ekspert-dom-oversettelse, demo-sti, M3-pilot) er blokkert til T0 finnes; S3.5/S3.6/S5.4 er valgt slik at de IKKE er fabrikk-gatede. **Offline-fullførings-grensen** går etter S5.3/S5.4: alt over den linjen er gratis, autonomt og load-bearing-testbart (også de nye S3.5/S3.6/S5.4). To ikke-autonome forbehold blant det nye: S3.5s commons-amendment (operatør-godkjenning + PULL-ONLY) og T0 toolkit-repoet (eget repo). M1–M3 er fortsatt de eneste stegene som koster penger, krever tenant eller krever et menneske.