portfolio-optimiser/docs/plan/2026-07-10-sesjonsplan-fase2-6.md
Kjell Tore Guttormsen a3b238307c docs(repo): meet the org repo-standard gate — 0 ERROR
Ran `repo-standard` (v0.1.1, class `standalone`) and fixed everything it
flagged as ERROR, plus the WARN links that were genuinely dead.

README first screen:
- opening line is now byte-identical to the forge description, so
  description == catalog == README is machine-checkable (badges moved below).
- `## Install` (required for class `standalone`): clone + `uv sync`, stated as
  clone-only because the shared spec, persona skill and example bundles under
  `shared/` are read from the working tree at run time. `uv run pytest` named as
  the verification, with the fact that no CI runner exists said out loud rather
  than implied by a badge.
- `## Non-goals` (required): the five limits already binding in CLAUDE.md —
  not a compliance product, not a portfolio-level reallocator, not autonomous
  decision-making, not turnkey, not a model benchmark.

Dead relative links (measured, not guessed):
- `docs/plan/2026-07-10-sesjonsplan-fase2-6.md` pointed at
  `../2026-07-14-revisjonspakke-DF-DI.md` six times; the file sits in
  `docs/plan/`, not `docs/`. (The sibling `../review-2026-07.md` links are
  correct and untouched.)
- the Fase-1 spike brief linked repo-root-relative from
  `.claude/projects/…/`; re-anchored with `../../../`.

The one remaining README ERROR was a gate false positive: `checkInternalLinks`
resolves targets against `git ls-files`, which lists files only, so a link to a
directory can never resolve. `[shared/](shared/)` now points at
`shared/README.md` — a better target anyway, since that file carries the
pull-only subtree rule. Not fixed here: the classifier lives in another repo.

Remaining WARNs are all inside `shared/`, deliberately untouched: it is a
pull-only commons subtree, and the nav-golden files are byte-level fixtures
that gate `test_nav_golden_*` — four of them are OKF bundle-internal links,
and the `/etc/passwd` ones are the negative escape fixture doing its job.

Suite green: 630 passed, 4 skipped (markdown-only diff; no test touched).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ri3aVJPfynCZtHRhesCzUH
2026-08-03 21:56:19 +02:00

594 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Sesjonsplan — Fase 2 → offline-fullførings-grense (S2.0S5.3 · D-AD-E · M1M3)
> **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-FD-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-FD-I](2026-07-14-revisjonspakke-DF-DI.md).
> Funn-referanser (F1F14, P1P6, U-1U-4) peker til [reviewen](../review-2026-07.md).
> **Beslutnings-sesjonene D-AD-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 (M1M3)**, 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.0S4.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-AD-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 1005 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-FD-I — BESLUTTET 2026-07-14** (operatør-samtale, intensjonsanalyse). Til forskjell fra D-AD-E
over er disse fire allerede avgjort; fasit-protokoll + innplassering i
[revisjonspakke D-FD-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. 16):** (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 <modell>» —
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 12 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). M1M3 er fortsatt
de eneste stegene som koster penger, krever tenant eller krever et menneske.