docs(review): kryssmodell-review 2026-07 (14 funn, 11 detach-bevis) + revidert roadmap + sesjonsplan Fase 2-6
This commit is contained in:
parent
847ed90135
commit
e8cc86a84b
3 changed files with 903 additions and 0 deletions
151
docs/plan/2026-07-06-reell-kjoring-analyse-plan.md
Normal file
151
docs/plan/2026-07-06-reell-kjoring-analyse-plan.md
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
# Analyse & plan — første reelle virksomhetskjøring
|
||||||
|
|
||||||
|
> **Status:** UNDER UTFØRELSE — revidert 2026-07-10. **Fase 1 (klynge A) EKSEKVERT**
|
||||||
|
> (`a44256a`→`847ed90`, 10/10 steg, 279/4 grønn). Kryssmodell-review 2026-07-09
|
||||||
|
> ([funn](../review-2026-07.md)) injiserte endringer i Fase 2–6; den detaljerte, gjeldende
|
||||||
|
> per-sesjons-planen er [sesjonsplanen 2026-07-10](2026-07-10-sesjonsplan-fase2-6.md)
|
||||||
|
> (S2.0–S5.3 + beslutnings-kø D-A–D-E + operatør-milepæler M1–M3). Denne fila forblir
|
||||||
|
> task-definisjonen/gap-analysen; sesjonsplanen er utførelses-nivået.
|
||||||
|
>
|
||||||
|
> **Kilde:** operatør-scenario 2026-07-06 + [gap-analyse](#) forankret i målbilde §2–§4,
|
||||||
|
> ingest-målbilde §11/§12, research §15. Ingen modell-kostnad påløpt.
|
||||||
|
|
||||||
|
## 1. Scenariet (operatørens ord, presisert)
|
||||||
|
|
||||||
|
Et team i en virksomhet starter en seriøs kjøring over en portefølje av flere uavhengige
|
||||||
|
prosjekter. Hvert prosjekt skal kostnadsreduseres innenfor **flere dimensjoner** (anta ~10 per
|
||||||
|
prosjekt), og det kjøres typisk **én dimensjon per prosjekt per kjøring**. Inn i systemet legges
|
||||||
|
i tillegg **all relevant kontekst**:
|
||||||
|
|
||||||
|
- faglig kunnskap + faglige vurderingsmetoder,
|
||||||
|
- tidligere vurderinger (både **godkjente og forkastede**),
|
||||||
|
- hvor mye som er **spart så langt**,
|
||||||
|
- **målsetning** per prosjekt og totalt for porteføljen,
|
||||||
|
- + øvrig relevant kontekst.
|
||||||
|
|
||||||
|
Data er en blanding av **eksterne og interne** kilder, lagt inn enten **manuelt** eller **hentet**
|
||||||
|
fra databaser, rapporter, **MCP-servere** og annet.
|
||||||
|
|
||||||
|
## 2. Hva som gjenbrukes (bygget + load-bearing-testet)
|
||||||
|
|
||||||
|
Ikke bygg på nytt. Dette står og er dekket av detach-bevisende tester:
|
||||||
|
8-stegs-loopen (kontekst→hypotese→debatt→validér→forbedre→foreslå→feedback→promotér), den
|
||||||
|
**blokkerende deterministiske validatoren** (solver + Monte Carlo), budsjett-/stopp-tak,
|
||||||
|
provenance-stempling, ingest-kjernen (CSV + SQL på begge stacker; HTTP MAF-demo), gated
|
||||||
|
wiki-promotering (fail-closed), async verdict-inbox, ExpeL-folden, og den delte framework-nøytrale
|
||||||
|
kjernen (`shared/` = OKF + golden-suite + ekspert-persona-skill).
|
||||||
|
|
||||||
|
## 3. Gap-analyse: scenario → dagens tilstand → gap
|
||||||
|
|
||||||
|
Fem gap-klynger. Kostnad er konsentrert i C og D.
|
||||||
|
|
||||||
|
### A. Domenemodellering (offline, gratis, load-bearing-testbar)
|
||||||
|
- **Dimensjon som begrep.** I dag: kandidat-tiltak per prosjekt. Trengs: «dimensjon» som
|
||||||
|
førsteklasses scoping av hypoteserommet (hvilken kostnadsakse en kjøring adresserer) + IR/config
|
||||||
|
for det + orkestrering av N prosjekter × 10 dimensjoner (hvilken dimensjon når, dedup på tvers).
|
||||||
|
- **Mål + besparelses-hovedbok.** I dag: ingen. Trengs: persistent, provenance-stemplet hovedbok
|
||||||
|
over *realiserte* besparelser per prosjekt + portefølje, mål-tilstand per prosjekt + totalt, og
|
||||||
|
«stopp når mål nådd»-logikk (distinkt fra token-budsjett-taket).
|
||||||
|
- **Faglige vurderingsmetoder.** I dag: deterministisk validator + persona. Trengs: encoding av
|
||||||
|
domenets faktiske metoder — dels som kunnskap i OKF-bundelen, dels evt. som validator-regler.
|
||||||
|
|
||||||
|
### B. Datainntak for virkeligheten
|
||||||
|
- **MCP wiret i kjørestien.** I dag: extension-point-demo (`build_mcp_server`), ikke wiret.
|
||||||
|
Trengs: MCP som en ekte ingest-kilde (bygg extension-punktet du eksplisitt vil bruke).
|
||||||
|
- **Dokument/rapport-connector.** I dag: ingen (kun CSV/SQL/HTTP). Trengs: PDF/DOCX/rapport →
|
||||||
|
OKF-bundle med provenance.
|
||||||
|
- **Live-kilde-herding.** I dag: alt mot committede fixtures / lokal mock — ingen bundle er
|
||||||
|
materialisert fra en ekte kilde, noensinne. Trengs: credentials-håndtering, feilhåndtering,
|
||||||
|
live-provenance, inkrementell re-ingest.
|
||||||
|
|
||||||
|
### C. Skala + henting (kostnad begynner her)
|
||||||
|
- **Semantisk henting.** I dag: feature-key-match. Ved «all faglig kunnskap + alle tidligere
|
||||||
|
dommer» blir dette utilstrekkelig — embeddings/vektor-henting (B2/R4/U10) blir load-bearing.
|
||||||
|
- **Concurrency + kostnadsstyring.** I dag: sekvensiell `run_portfolio` + per-kjøring token-tak.
|
||||||
|
N×10 kjøringer krever concurrent fan-out (U1, bevisst kuttet) + kostnadsstyring på tvers av mange
|
||||||
|
kjøringer.
|
||||||
|
|
||||||
|
### D. Kjøretid mot ekte modell (kostnad + tenant)
|
||||||
|
- **Azure/Foundry-profil eksersert.** I dag: kun lokal/offline; azure-blokk i `model_map.json` er
|
||||||
|
placeholders. Trengs: ekte deployments i virksomhetens tenant.
|
||||||
|
- **Live-modell-kjøring.** I dag: alt ende-til-ende er skriptet offline-sim (bevis for plumbing,
|
||||||
|
ikke for at en levende LLM produserer forslaget/dommen). Trengs: første genuine kjøring, validert
|
||||||
|
i liten skala før full portefølje.
|
||||||
|
|
||||||
|
### E. HITL-operasjoner i skala
|
||||||
|
- **Verdict-ruting + -sporing.** I dag: én mappe-inbox. Ved mange kandidater trengs ruting til rett
|
||||||
|
fagekspert, sporing av utestående dommer, oversikt for teamet.
|
||||||
|
- **Varsling.** I dag: `notify=`-stub. Trengs: ekte leveranse (e-post/Teams/webhook — B11).
|
||||||
|
|
||||||
|
## 4. Åpne designbeslutninger (må avklares i Fase 0, ikke av meg nå)
|
||||||
|
|
||||||
|
> **Status 2026-07-10 (post Fase 1 + review):** (1) dimensjonsmodell **AVKLART** av Fase 1
|
||||||
|
> (både kontekst-subsett OG kandidat-constraint; dobbelttelling løst av ledgerens dimensjonsfrie
|
||||||
|
> sum-nøkkel). (2) hovedbok-sannhet **I HOVEDSAK AVKLART** (typet `SavingsLedger` + fail-closed
|
||||||
|
> `realize`-ekspertgate; driftskonvensjon gjenstår). (3) mål-semantikk **DELVIS** (absolutt +
|
||||||
|
> prosent + hard/soft bygget; prosent-BASELINE uavklart → beslutning **D-E**). (4) første
|
||||||
|
> live-kilde **ÅPEN** → **D-B** (NB: krever samtidig bevisst amendment av frossen
|
||||||
|
> ingest-målbilde §8/§11 — review P1). (5) vektor-store **ÅPEN** → **D-C** (research-grunnlag
|
||||||
|
> klart: numpy brute-force > sqlite-vec; LanceDB utelukket på Intel-Mac). (6) stack-paritet
|
||||||
|
> **ÅPEN** → **D-E**. D-referanser: [sesjonsplanen §2](2026-07-10-sesjonsplan-fase2-6.md).
|
||||||
|
|
||||||
|
1. **Dimensjon-modell:** er en dimensjon en delmengde av bundle-konteksten, en constraint på
|
||||||
|
kandidat-typer, eller begge? Hvordan unngås dobbelttelling av besparelser på tvers av dimensjoner?
|
||||||
|
2. **Besparelses-hovedbok:** hvor bor sannheten om «realisert» besparelse — en ny RAW-fil-lag à la
|
||||||
|
verdicts, eller en egen typet store? Hvem stempler «realisert» (ekspert-gate, som promotering)?
|
||||||
|
3. **Mål-semantikk:** absolutt beløp, prosent, eller per-dimensjon-vekting? Stopp-adferd ved nådd mål.
|
||||||
|
4. **Første live-kilde:** hvilken (DB / rapport / MCP) er tryggest å herde først?
|
||||||
|
5. **Vektor-store-valg:** hvilken embeddings-backend passer begge stacker + kostnadsdisiplinen?
|
||||||
|
6. **Rettferdig stack-paritet:** kjører den ekte kjøringen på MAF, Claude-SDK, eller begge for
|
||||||
|
sammenligning? (påvirker hvor Azure/Foundry vs. Claude-API-kostnaden lander)
|
||||||
|
|
||||||
|
## 5. Faset plan (roadmap)
|
||||||
|
|
||||||
|
Hver fase er uavhengig verifiserbar; kostnad konsentreres i Fase 4+. Utføres via Voyage-pipeline
|
||||||
|
(`/trekbrief → /trekresearch → /trekplan → /trekexecute → /trekreview`) per fase — én fase per
|
||||||
|
større arbeidsbolk, aldri one-shot.
|
||||||
|
|
||||||
|
> **Revisjon 2026-07-10 (etter kryssmodell-review):** Fase 1 EKSEKVERT (`847ed90`). Fase 2–6 er
|
||||||
|
> detaljert og REVIDERT i [sesjonsplanen](2026-07-10-sesjonsplan-fase2-6.md) — viktigste
|
||||||
|
> endringer: «MCP wiret i kjørestien» presisert til **MCP-ingest-konnektor** (method-spec §3
|
||||||
|
> forbyr query-time retrieval); live-kilde-herding **gated på bevisst ingest-målbilde-amendment**
|
||||||
|
> (D-B — konflikt med frossen §8/§11, review P1); concurrent fan-out krever beslutning **D-D**
|
||||||
|
> først (determinisme-modell, review F6); nye funn-injiserte sesjoner **S2.0**
|
||||||
|
> (portefølje-læringssløyfe, F1), **S2.1** (outbox/output-laget, P4), **S2.5** (inbox-herding,
|
||||||
|
> F7/F11), **S2.7** (validator-stramming, F2), **S4.0** (kostbaseline-forankring, F3); Fase 4/6
|
||||||
|
> splittet i offline-sesjoner (S4.0–S4.2) + operatør-milepæler **M1–M3**.
|
||||||
|
|
||||||
|
- **Fase 0 — Dyp planlegging + research.** Voyage trekbrief/trekplan per etterfølgende fase; ekstern
|
||||||
|
research på vektor-stores, MCP-connector-mønstre, Foundry-deployment. Avklar §4-beslutningene.
|
||||||
|
*Output:* per-fase-briefer. *Kostnad:* ingen (planlegging).
|
||||||
|
- **Fase 1 — Domenemodellering (klynge A).** Dimensjon-begrep, mål + besparelses-hovedbok,
|
||||||
|
vurderingsmetode-encoding. Offline, fixtures, load-bearing. *Kostnad:* ingen.
|
||||||
|
- **Fase 2 — Datainntak for virkeligheten (klynge B).** MCP-wiring, dokument-connector,
|
||||||
|
live-kilde-herding (mot én kontrollert kilde). *Kostnad:* minimal.
|
||||||
|
- **Fase 3 — Skala + henting (klynge C).** Embeddings/semantisk henting, concurrent fan-out,
|
||||||
|
kostnadsstyring på tvers av kjøringer. *Kostnad:* lav (embeddings).
|
||||||
|
- **Fase 4 — Kjøretid mot ekte modell (klynge D).** Eksersér Azure/Foundry-profil i tenant; første
|
||||||
|
live-modell-kjøring i liten skala. *Kostnad:* reell API — hardt tak, minimal verifisering.
|
||||||
|
- **Fase 5 — HITL-operasjoner i skala (klynge E).** Verdict-ruting/-sporing + varsling (B11).
|
||||||
|
- **Fase 6 — Pilot.** Én ekte kjøring: **én dimensjon, ett prosjekt**, live kilde + live modell +
|
||||||
|
ekte ekspert. Verifiser hele sløyfa mot virkeligheten FØR skalering til full N×10-portefølje.
|
||||||
|
- **Deretter:** skaler til full portefølje.
|
||||||
|
|
||||||
|
## 6. Verifisering (suksesskriterier, testbare)
|
||||||
|
|
||||||
|
- **Fase 1:** en dimensjon-scopet kjøring produserer kun kandidater innen dimensjonen (test);
|
||||||
|
hovedboken akkumulerer realisert besparelse med provenance og respekterer et mål-stopp (test).
|
||||||
|
- **Fase 2:** en bundle materialiseres fra en live kilde via MCP + via dokument-connector, med
|
||||||
|
korrekt provenance (golden mot kontrollert kilde).
|
||||||
|
- **Fase 3:** semantisk henting returnerer relevant tidligere dom over en syntetisk stor base;
|
||||||
|
concurrent portefølje-kjøring gir identisk resultat som sekvensiell (determinisme bevart).
|
||||||
|
- **Fase 4:** én dokumentert live-modell-kjøring innen token-tak; Azure/Foundry-profil grønn.
|
||||||
|
- **Fase 6:** pilot-kjøringen lukker sløyfa (forslag → validér → ekspert-dom → promotering →
|
||||||
|
neste kjørings kontekst bærer dommen) mot ekte modell + ekte kilde.
|
||||||
|
|
||||||
|
## 7. Grenser (uendret, gjelder også her)
|
||||||
|
|
||||||
|
Rent teknisk rammeverk (deployer eier DPIA/ROS/formål). 90%-prinsipp: bygg generisk kjerne +
|
||||||
|
extension points, ikke de siste 10 %. Deterministisk validator forblir obligatorisk + blokkerende.
|
||||||
|
Kostnadsdisiplin: offline/lokal primært; ekte modell kun målrettet minimal verifisering, harde tak.
|
||||||
|
`shared/` forblir framework-nøytralt; commons PULL-ONLY.
|
||||||
431
docs/plan/2026-07-10-sesjonsplan-fase2-6.md
Normal file
431
docs/plan/2026-07-10-sesjonsplan-fase2-6.md
Normal file
|
|
@ -0,0 +1,431 @@
|
||||||
|
# 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.
|
||||||
|
> 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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
### 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).
|
||||||
|
- **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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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"]
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
**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**
|
||||||
|
5. **S4.1** (når som helst) → **M1** (operatør) · **S4.0 → S4.2** → **M2** (operatør)
|
||||||
|
6. **S5.1 → S5.2 → S5.3** → **M3** (operatør; krever også D-B-amendment + ekte ekspert)
|
||||||
|
|
||||||
|
**Offline-fullførings-grensen** går etter S5.3: alt over den linjen er gratis, autonomt og
|
||||||
|
load-bearing-testbart; M1–M3 er de eneste stegene som koster penger, krever tenant eller krever
|
||||||
|
et menneske.
|
||||||
321
docs/review-2026-07.md
Normal file
321
docs/review-2026-07.md
Normal file
|
|
@ -0,0 +1,321 @@
|
||||||
|
# Uavhengig kryssmodell-review — portfolio-optimiser (2026-07-09)
|
||||||
|
|
||||||
|
> **Reviewer:** Fable 5 (xhigh), uavhengig av Opus 4.8 som skrev koden. **Mandat:** adversarisk
|
||||||
|
> fire-akse-review + re-plan — se [sesjonsplanen](plan/2026-07-10-sesjonsplan-fase2-6.md)
|
||||||
|
> (opprinnelig levert som `replan-proposed.md`; promotert 2026-07-10 på operatør-instruks,
|
||||||
|
> samtidig som roadmap + STATE.md ble oppdatert). Under selve reviewen ble ingen live-filer endret.
|
||||||
|
>
|
||||||
|
> **Metode:** all kode lest (21 moduler, 3 754 linjer), alle nøkkel-tester lest, full lokal suite
|
||||||
|
> + ruff + mypy kjørt, OKF-spec hentet (WebFetch), og **5 detach-eksperimenter utført i en
|
||||||
|
> throwaway-kopi** (scratchpad — live-repoet urørt). Hver påstand under har `fil:linje` eller
|
||||||
|
> kommando-belegg; verifiseringslogg nederst.
|
||||||
|
|
||||||
|
## 0. Baseline (verifisert, ikke sitert fra STATE)
|
||||||
|
|
||||||
|
| Påstand | Resultat | Belegg |
|
||||||
|
|---|---|---|
|
||||||
|
| Testsuite | **279 passed / 4 skipped** (25.5s) — STATEs tall stemmer | `uv run pytest -q` |
|
||||||
|
| Lint / typer | ruff clean; mypy clean (21 filer) | `uv run ruff check .`; `uv run mypy src` |
|
||||||
|
| Push-status | HEAD `847ed90` == `origin/main` — STATEs push-påstand stemmer | `git rev-parse` begge |
|
||||||
|
| Testomfang | 244 test-funksjoner / 46 filer (283 collected = 279+4; orienteringens «~278/~54» var upresis) | `grep -c "def test"` |
|
||||||
|
| De 4 skip | env-gatede live-tester (Foundry/Ollama) — ingen ble trigget | `test_foundry_profile_live.py:19-22` |
|
||||||
|
| STATEs 3 plan-avvik | Alle tre bekreftet reelle og ærlig dokumentert; ingen skjuler en defekt | progress.json steg 3/6-noter; `run.py:517-537` (main uten ledger/goals) |
|
||||||
|
|
||||||
|
**Detach-bevis (load-bearing-stikkprøver, ALLE 11 RØDE i kopien):** (1) Steg-1-fold detached →
|
||||||
|
`test_step1` rød; (2) checker-override detached → `test_checker_gate` rød; (3) `link_in_index`
|
||||||
|
detached fra `promote_verdict` → `test_step8` ×2 + `test_simulation_loadbearing` røde; (4)
|
||||||
|
`import agent_framework` injisert i `okf.py` → `test_okf_is_maf_free` rød; (5) verdict-eksklusjon
|
||||||
|
fjernet fra `context_files` → `test_okf` ×2 røde; (6) Steg-5 reason-injeksjon detached →
|
||||||
|
`test_step5` rød; (7) dimension `admits`-gate detached → `test_dimension_loadbearing` rød;
|
||||||
|
(8) ledgerens dimensjonsfrie dedup detached → `test_ledger` rød (sum dobles); (9) fail-closed
|
||||||
|
`realize`-gate detached → rød; (10) Steg-7 inbox-ingest detached → `test_step7` rød; (11)
|
||||||
|
portefølje-goal-stop detached → `test_goal_stop_is_load_bearing` rød. Detach 6–11 bekrefter
|
||||||
|
uavhengig STATEs Fase-1-detach-påstander (som ellers kun sto i progress.json).
|
||||||
|
**Metoden holder på alle stikkprøvde sømmer.** I tillegg: simuleringskonsollen
|
||||||
|
(`uv run python -m portfolio_optimiser.simulation`) kjørt ende-til-ende i kopien — EXIT 0,
|
||||||
|
sløyfe lukket, ærlig merking i output.
|
||||||
|
Én nyanse: under detach 5 forble Steg-1s empty-store-kontroll grønn — verdict-BODYen bruker
|
||||||
|
desimalkomma («0,82»), dot-formen bor kun i frontmatter (som ikke rendres i `bundle_context`),
|
||||||
|
så dekningen for den detachen bæres av okf-nivå-testene, ikke run-nivå-kontrollen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Funn (rangert, mest alvorlig først)
|
||||||
|
|
||||||
|
Ingen BLOCKER i dagens *påstands-omfang* (single-run-claims er sanne og detach-beviste). Funn 1–3
|
||||||
|
er MAJOR fordi de gjør **neste fasers premisser** falske eller farlige hvis de ikke fikses først.
|
||||||
|
|
||||||
|
### F1 [MAJOR · Akse 1 + ærlighet] Læringssløyfa er DØD på portefølje-stien — docstring påstår den lever
|
||||||
|
|
||||||
|
- **Belegg:** `run_portfolio` (`run.py:429-514`) har ingen `bundle_dir`/`verdict_dir`-parametre og
|
||||||
|
kaller `run_project` uten dem (`run.py:496-508`). Steg-1-folden er gated på
|
||||||
|
`bundle_dir is not None` (`run.py:312`). Dermed når **ingen tidligere dom noensinne noen
|
||||||
|
hypotese-prompt** i en portefølje-kjøring. Docstringen påstår likevel «a verdict on project k
|
||||||
|
informs the ExpeL retrieval of project k+1 (**the cross-project learning loop**)»
|
||||||
|
(`run.py:100-104`, `run.py:445-448`). Det som faktisk skjer er kun post-generering-`retrieved`
|
||||||
|
(`run.py:367-377`) — som koden selv innrømmer «is NOT what reaches the prompt».
|
||||||
|
`test_portfolio.py:131-151` asserter kun `retrieved`, aldri prompten — grønn-men-død på
|
||||||
|
akkurat den sløyfa målbildets §5-diagnose handlet om.
|
||||||
|
- **Feilscenario:** operatør kjører `run_portfolio` med seeded store og forventer (per docstring)
|
||||||
|
at prosjekt 2 lærer av prosjekt 1s dom. Ingen prompt endres; alle hypoteser er uinformerte;
|
||||||
|
rapporter bygget på «cross-project learning» er usanne (målbilde §1 kardinalregel).
|
||||||
|
- **Konsekvens for planen:** Fase 3 (concurrent fan-out) skalerer i dag en portefølje-sti uten
|
||||||
|
læring — premissfeil som blir BLOCKER hvis den ikke fikses FØR Fase 3.
|
||||||
|
- **Fiks:** per-prosjekt `bundle_dir` + `verdict_dir` i konfig, tred gjennom `run_portfolio`;
|
||||||
|
load-bearing test: dom på prosjekt k MÅ nå prosjekt k+1s **prompt** (ikke bare `retrieved`).
|
||||||
|
→ Re-plan sesjon **S2.0**.
|
||||||
|
|
||||||
|
### F2 [MAJOR · Akse 1] Validator: CBC-nominalen gater aldri — selv-forfattede assumptions blåser P90 over «maximum feasible»
|
||||||
|
|
||||||
|
- **Belegg:** eneste numeriske blokker er P90 + metode-cap (`validator.py:133-152`);
|
||||||
|
`nominal_feasible` beregnes (`validator.py:129`) men brukes aldri som gate. `assumptions`
|
||||||
|
forfattes av modellen selv (`generate.py:71-78`; `ir.py:35` har ingen invariant om at bandet
|
||||||
|
omslutter `unit_cost`). **Kjørt bevis (i kopien):** golden-bundelens egne tall — total 300 000,
|
||||||
|
nominal 90 000, band [0.70, 1.40] — validerer claim **100 000 > 90 000** (p90=121 057). Med band
|
||||||
|
helt over unit_cost ([1.8, 2.2]) validerer claim **55 000 mot nominal 30 000** (p90=65 058).
|
||||||
|
- **Feilscenario (live, Fase 4/6):** proposer setter et optimistisk band → claim over den
|
||||||
|
deterministiske feasibility-grensen stemples `validated` og når eksperten som «validator-godkjent».
|
||||||
|
Modul-docstringens «a real CBC solve **bounds** the maximum feasible saving» (`validator.py:7-8`)
|
||||||
|
er usann som gate-påstand; method-spec §3 Steg 4 pkt. 2 («capped at a policy fraction») håndheves
|
||||||
|
ikke.
|
||||||
|
- **Fiks (golden-kompatibel — golden claim 30 000 ≤ 90 000 påvirkes ikke):** (a) strukturell blokk
|
||||||
|
også på `claimed > nominal_feasible`; (b) IR-invariant `low ≤ unit_cost ≤ high` per assumption-band
|
||||||
|
(golden-bandet [0.70,1.40] omslutter 1.0 → OK). **Krever spec-amendment i commons FØRST**
|
||||||
|
(method-spec §3/§7 er frossen; PULL-ONLY) + speiling i D7-søskenet (utenfor dette repoets scope,
|
||||||
|
flagges). → Beslutnings-sesjon **D-A**, bygg-sesjon **S2.7**.
|
||||||
|
|
||||||
|
### F3 [MAJOR · Akse 1/4] Ingen forankring av `affected_items` mot prosjektets faktiske kostbaseline
|
||||||
|
|
||||||
|
- **Belegg:** `_parse_ir` aksepterer vilkårlige modell-forfattede kostlinjer (`generate.py:71-78`).
|
||||||
|
Road-stien har `project.cost_items` (`reference_domain.py:39-52`) men avstemmer aldri; bundle-stien
|
||||||
|
bygger `Project` med `cost_items=()` (`run.py:168-195`). Både IR-invarianten (claim ≤ items-total,
|
||||||
|
`ir.py:37-44`) og hele validatoren regner på selv-deklarerte tall.
|
||||||
|
- **Feilscenario:** proposer dikter `{code:"XX", quantity:1e6, unit_cost:10}` → total 10 MNOK →
|
||||||
|
P90 3 MNOK → claim 2,9 MNOK stemples `validated`. Mot en ekte modell (Fase 4/6) er «validatoren
|
||||||
|
avgjør tallene» tom mot hallusinerte kostlinjer; kun checker-LLM-en (fail-open) står imellom.
|
||||||
|
- **Fiks:** ny fail-closed avstemmings-stage: hver `affected_items.code` MÅ finnes i prosjektets
|
||||||
|
kostbaseline, `quantity`/`unit_cost` innenfor toleranse mot baseline. Bundle-stien trenger en
|
||||||
|
kostbaseline-projeksjon (naturlig produsert av ingest-laget). Spec-amendment i commons.
|
||||||
|
→ **D-A** + sesjon **S4.0** (må være grønn FØR første live-kjøring M2).
|
||||||
|
|
||||||
|
### F4 [MAJOR · Akse 2 · UTFORDRING MOT FROSSEN RAMME] OKF-navigasjonen avviser spec-ens ANBEFALTE lenkeform
|
||||||
|
|
||||||
|
- **Belegg:** `navigate_bundle` hopper over ethvert mål som inneholder `/` (`okf.py:124-125`) — og
|
||||||
|
method-spec §3 Steg 1 **pinner** dette («Targets containing a path separator are out-of-bundle and
|
||||||
|
MUST be skipped»). Men OKF-spec-en (hentet 2026-07-09) sier absolutt/bundle-relativ form («Begin
|
||||||
|
with `/`, interpreted relative to the bundle root») er den **anbefalte** formen. Målbildet §4
|
||||||
|
beskriver selv cross-links som «bundle-relative `/…`».
|
||||||
|
- **Feilscenario:** en bundle forfattet etter OKF-anbefalingen (`](/tiltak.md)`) navigerer til KUN
|
||||||
|
index → `bundle_citations` tom → `run_project` raiser «no citable content» (`run.py:278-279`).
|
||||||
|
Enhver ekstern OKF-bundle (f.eks. generert av Googles referanse-tooling) kan være ukonsumerbar.
|
||||||
|
- **Vurdering:** dette er en feil i den frosne method-spec-en, ikke i `okf.py` (som implementerer
|
||||||
|
spec-en trofast). Kun operatøren kan endre den. **Anbefalt amendment:** ledende `/` mappes til
|
||||||
|
bundle-rot (fortsatt fail-closed boundary-sjekk; subkataloger kan fortsatt avvises om ønsket).
|
||||||
|
→ **D-A**. (Relatert MINOR: `log.md` er reservert i OKF-spec, men en index-lenke til den ville
|
||||||
|
rendre den som konseptfil i `bundle_context` — `okf.py:88-92` filtrerer kun `index.md` + verdict.)
|
||||||
|
|
||||||
|
### F5 [MAJOR · Akse 2/3] ExpeL-substratet er én-kandidat-per-bundle — skalerer ikke til klynge C
|
||||||
|
|
||||||
|
- **Belegg:** `seed_store_from_bundle` nøkler **alle** `type: verdict`-filer på bundelens ENE
|
||||||
|
kandidat fra `validator-input.json` (`verdicts.py:415-430`; `okf.py:26` — én projeksjon per
|
||||||
|
bundle). To dommer om samme kandidat re-mintes til samme id; en dom om en ANNEN kandidat
|
||||||
|
feil-nøkles til denne kandidatens features.
|
||||||
|
- **Feilscenario:** et reelt prosjekt med ~10 dimensjoner × flere tiltak: promotert dom om tiltak B
|
||||||
|
foldes inn i hypoteser om tiltak A (feil læringssignal), eller kolliderer på id og droppes.
|
||||||
|
Roadmap C («all faglig kunnskap + alle tidligere dommer») forutsetter multi-kandidat.
|
||||||
|
- **Fiks:** per-verdict features (les fra verdict-filas egen frontmatter/IR-referanse) + bundle-
|
||||||
|
layout for flere kandidater. Spec-amendment (seeding-regelen §3 Steg 1 er normativ). → **D-A** +
|
||||||
|
sesjon **S3.2**.
|
||||||
|
|
||||||
|
### F6 [MAJOR · Akse 4] Concurrency-forutsetningene finnes ikke — og determinisme-kriteriet er selvmotsigende i dag
|
||||||
|
|
||||||
|
- **Belegg:** `link_in_index`/index-RMW er ikke-atomisk (`okf.py:183-190`, dokumentert MVP-grense;
|
||||||
|
også `ingest.py:587-598`); ledgeren er hel-fil skriv/les uten låsing (`ledger.py:141-162`);
|
||||||
|
VerdictStore er in-memory first-wins (`verdicts.py:225-236`). Videre: Fase 3-kriteriet
|
||||||
|
«concurrent == sekvensiell» kolliderer med dagens semantikk der én delt store tres gjennom
|
||||||
|
kjøringene i rekkefølge — k+1 ser k (`run.py:443-448`); concurrent gjør ankomstrekkefølgen
|
||||||
|
udeterministisk.
|
||||||
|
- **Feilscenario:** to samtidige kjøringer promoterer mot samme bundle → tapt index-lenke
|
||||||
|
(last-write-wins på RMW) → dommen blir unavigerbar og læringen stille borte.
|
||||||
|
- **Fiks:** eksplisitt concurrency-modell FØR bygging (per-prosjekt-isolasjon under kjøring +
|
||||||
|
deterministisk merge-barriere mellom bølger; én-skriver-regel eller fil-lås for index/ledger).
|
||||||
|
→ Beslutnings-sesjon **D-D**, bygg **S3.4/S3.5**.
|
||||||
|
|
||||||
|
### F7 [MINOR nå → MAJOR ved Fase 2/6 · Akse 4] Verdict-inboxen er en uvalidert prompt-injeksjonsflate
|
||||||
|
|
||||||
|
- **Belegg:** tolerant last validerer kun nøkkel-tilstedeværelse (`verdicts.py:174-196`);
|
||||||
|
`decision` valideres ALDRI mot vokabularet (§4.2-kravet) — `{"decision": "hva-som-helst"}`
|
||||||
|
entrer storen; `rationale` flyter verbatim inn i hypotese-prompten
|
||||||
|
(`verdicts.py:249-252` → `run.py:312-315`). Ingen størrelses-tak per fil.
|
||||||
|
- **Feilscenario:** en fremmed/fiendtlig JSON i inbox-mappa («Ignore all previous instructions…»
|
||||||
|
som rationale) injiseres i prompten på neste kjøring. Med flere brukere/eksperter (klynge E)
|
||||||
|
er mappa en delt angrepsflate.
|
||||||
|
- **Fiks (fortsatt tolerant — skip, aldri raise):** vokabular-sjekk på `decision`, tak på
|
||||||
|
fil-/rationale-størrelse, antall-tak per merge. → sesjon **S2.5**.
|
||||||
|
|
||||||
|
### F8 [MINOR · Akse 1] Metode-cap er fail-open på fri-tekst `measure` — asymmetrisk med dimension-gaten
|
||||||
|
|
||||||
|
- **Belegg:** energiregelen keyer på eksakt streng `energy_efficiency` (`validator.py:40-47,141-152`)
|
||||||
|
— modell-forfattet fri-tekst; «energy efficiency retrofit» omgår den strengere capen.
|
||||||
|
`dimension.admits` er motsatt fail-closed allowlist (`dimension.py:38-44`) — i en dimensjon-scopet
|
||||||
|
kjøring med `allowed_measure_types={"energy_efficiency"}` tvinges eksakt streng, så gapet gjelder
|
||||||
|
primært u-scopede kjøringer.
|
||||||
|
- **Fiks:** metode-regler bør keyes via dimensjons-/metode-registeret (konfig), ikke strenglikhet i
|
||||||
|
validatoren. Kan tas sammen med S4.0.
|
||||||
|
|
||||||
|
### F9 [MINOR · Akse 1] Prosent-mål: float-trunkering + subset-avhengig baseline + latent null-baseline
|
||||||
|
|
||||||
|
- **Belegg:** `int(goal.percent / 100 * baseline_ore)` trunkerer med float-matte (`run.py:423`) —
|
||||||
|
bryter Decimal/ROUND_HALF_UP-disiplinen (`run.py:411-413`, `ledger.py:198-201`). Baseline =
|
||||||
|
sum av kun de prosjektene som er MED i passet (`run.py:465`) — samme mål-% gir ulik terskel
|
||||||
|
avhengig av subsettet. Bundle-deriverte prosjekter har `total_cost=0` (`run.py:192` cost_items=())
|
||||||
|
→ terskel 0 → øyeblikkelig stopp (latent; unåelig i dag siden run_portfolio kun tar
|
||||||
|
referanse-prosjekter — blir reell ved S2.0).
|
||||||
|
- **Fiks:** Decimal-konvertering; baseline-semantikk avklares i **D-E** (del av §4.3-resten);
|
||||||
|
guard i S2.0.
|
||||||
|
|
||||||
|
### F10 [MINOR · Akse 1 + ærlighet] BudgetMiddleware «short-circuits» først ETTER at kallet er fullført
|
||||||
|
|
||||||
|
- **Belegg:** `await call_next()` kjøres før charge (`budget.py:89-100`) — kallet som krysser taket
|
||||||
|
fullføres (og koster) før raise. For Fase 3-kostnadsstyring på tvers trengs pre-call-sjekk
|
||||||
|
(nekter å starte kall når resttaket er brukt). Docstring-ordet «short-circuits the moment the cap
|
||||||
|
is crossed» lover hakket mer enn mekanikken.
|
||||||
|
- **Fiks:** pre-call guard i tillegg; tas i **S3.5**.
|
||||||
|
|
||||||
|
### F11 [MINOR · Akse 1] Offline-bevisstrategien hviler på MAF-private API-er
|
||||||
|
|
||||||
|
- **Belegg:** alle syntetiske klienter overrider `_inner_get_response`/`_build_response_stream`
|
||||||
|
(`simulation.py:86-114`, `conftest.py:59-80,118-141,175-181` — fire duplikater av mønsteret);
|
||||||
|
suiten emitter allerede `ExperimentalWarning` fra `agent_framework` (skills/harness). En
|
||||||
|
MAF-minor-oppgradering kan stille knekke HELE det offline ende-til-ende-beviset.
|
||||||
|
- **Fiks:** pin `agent-framework-core` strammere (`>=1.9,<2` e.l.) + én delt scripted-klient
|
||||||
|
(redusér 4→1 implementasjoner) + en vakt-test som feiler ved MAF-versjonsskifte med beskjed om
|
||||||
|
bevisst re-verifisering. Kan tas i S2.5 eller egen liten sesjon.
|
||||||
|
|
||||||
|
### F12 [MINOR · ærlighet] CHANGELOG er utdatert etter Fase 1
|
||||||
|
|
||||||
|
- **Belegg:** `CHANGELOG.md:22` sier «237 passing tests» (nå 279); ingen Fase-1-oppføring
|
||||||
|
(dimension/ledger/goals/metode-regel mangler). Versjonssync-regelen krever konsistens.
|
||||||
|
- **Fiks:** oppdatér i **S2.0** (samme sesjon som docstring-fiksen for F1).
|
||||||
|
|
||||||
|
### F13 [SUGGESTION · Akse 1] Småting
|
||||||
|
|
||||||
|
- `_authored_texts`: `isinstance(out, str)`-grenen er død kode — en str har ingen `messages`, så
|
||||||
|
den filtreres bort på linja over (`run.py:127-131`).
|
||||||
|
- `_checker_verdict` matcher `VERDICT: REJECT` hvor som helst i teksten (`run.py:153`); en checker
|
||||||
|
som SITERER formatet («enten 'VERDICT: APPROVE' eller 'VERDICT: REJECT'») feiltolkes som reject
|
||||||
|
(spec-en pinner reject-presedens, så dette er spec-konformt — men en siste-linje-parse ville vært
|
||||||
|
robustere; evt. spec-nyansering i D-A).
|
||||||
|
- `retrieval._score` bruker substring-match per token (`retrieval.py:97`) — «as» matcher «asphalt»;
|
||||||
|
greit for MVP, erstattes uansett i S3.3.
|
||||||
|
- Road-stiens retrieval-query er hardkodet «cost saving measure» (`run.py:274`).
|
||||||
|
|
||||||
|
### F14 [MINOR · ærlighet] «Magentic er eksperimentell» er utdatert som begrunnelse (Python)
|
||||||
|
|
||||||
|
- **Belegg (delegert research, kildeført):** orkestreringene (`GroupChatBuilder`/`MagenticBuilder`)
|
||||||
|
leveres i `agent-framework-orchestrations` **1.0.0, «Production/Stable»** (PyPI, 18. juni 2026);
|
||||||
|
MAF-Python-doc-sidene bærer ingen experimental-banner (banneret gjelder Semantic Kernel og .NET).
|
||||||
|
CLAUDE.md/målbildets «IKKE Magentic, som er eksperimentell» er dermed utdatert for Python.
|
||||||
|
- **Vurdering:** Group Chat-VALGET står seg — Magentic-doc-en sier selv «untested … outside of the
|
||||||
|
original Magentic-One design» og anbefaler Group Chat for enklere koordinering. Kun
|
||||||
|
BEGRUNNELSEN bør omformuleres (operatør-beslutning; CLAUDE.md/målbildet er frosne).
|
||||||
|
- **Motverifisering utført:** research-agentens andre utfordring — at middleware skal virke på bar
|
||||||
|
`BaseChatClient` per docs — ble **empirisk tilbakevist** mot installert 1.9.0: agent-nivå
|
||||||
|
`ChatMiddleware` på en bar `BaseChatClient`-subklasse fyrer ALDRI (calls=0, kjørt i kopien).
|
||||||
|
Repoets «(verified)»-premiss (`simulation.py:74-76`, `conftest.py:31-35`) er korrekt; docs
|
||||||
|
divergerer fra 1.9.0-implementasjonen — enda en grunn til versjonsvakten i F11.
|
||||||
|
|
||||||
|
## 2. Akse 3 — plan-fullstendighet (funn som endrer re-planen)
|
||||||
|
|
||||||
|
- **P1 [MAJOR]:** Roadmap Fase 2 («live-kilde-herding», «inkrementell re-ingest») **motsier det
|
||||||
|
frosne ingest-målbildet**: §8 «Inkrementell re-ingest er et extension point, ikke MVP», §11
|
||||||
|
«Ingen live-kilde forekommer noensinne i programmet», §12 «Ingen scheduler». Frossen-dokument-
|
||||||
|
regimet krever en *bevisst* amendment før Fase 2 kan bygge dette. → **D-B** (operatør).
|
||||||
|
- **P2 [MINOR]:** Roadmap B-overskriften «MCP wiret i kjørestien» motsier sin egen brødtekst
|
||||||
|
(«MCP som en ekte ingest-kilde») og CLAUDE.md-invarianten (FunctionTool er kjøresti-sømmen;
|
||||||
|
method-spec §3 forbyr query-time retrieval). Re-planen løser det som **MCP-ingest-konnektor**
|
||||||
|
(ny kildefamilie i manifestet), aldri kjøresti-wiring.
|
||||||
|
- **P3 — status på de seks §4-beslutningene:** §4.1 dimensjonsmodell: **avklart av Fase 1** (både
|
||||||
|
kontekst-subsett OG kandidat-constraint; dobbelttelling løst av ledgerens dimensjonsfrie
|
||||||
|
sum-nøkkel). §4.2 hovedbok-sannhet: **i hovedsak avklart** (typet store + fail-closed
|
||||||
|
ekspert-gate `realize`); driftskonvensjon (hvor fila bor, hvem kjører realize) gjenstår. §4.3
|
||||||
|
mål-semantikk: **delvis** (absolutt+prosent+hard/soft bygget; prosent-BASELINE uavklart — F9).
|
||||||
|
§4.4 første live-kilde: **åpen** (blokkert av P1). §4.5 vektor-store: **åpen**. §4.6
|
||||||
|
stack-paritet: **åpen** (operatør). → beslutnings-sesjoner D-B/D-C/D-E i re-planen.
|
||||||
|
- **P4 [MAJOR]:** **Output-laget (målbilde §3) er ikke bygget:** ingenting persisterer forslag/
|
||||||
|
avvisninger/provenance til disk — `run_project` returnerer bare objekter; output-laget består i
|
||||||
|
dag KUN av verdict-inboxen. Klynge E («sporing av utestående dommer») og
|
||||||
|
sammenligningsprotokollens §4.2 («artefakter er fasit») forutsetter en outbox. → sesjon **S2.1**.
|
||||||
|
- **P5:** `run_portfolio` mangler også `verdict_dir` — klynge E-ruting forutsetter den (dekkes av
|
||||||
|
S2.0).
|
||||||
|
- **P6:** Stubs/gap med sesjons-hjem: `notify=`-stub (`run.py:229,382-383`) → S5.2;
|
||||||
|
`build_mcp_server` uwiret demo (`datasource.py:79-92`) → S2.2 gjenbruker kjernen; Azure-blokk =
|
||||||
|
placeholders (`data/model_map.json`) → S4.1 + M1; `main()` kan ikke drive Fase-1-featurene
|
||||||
|
(ingen dimension/ledger/goals/bundle-args, `run.py:517-537`) → S5.3.
|
||||||
|
|
||||||
|
## 3. Akse 4 — skala/drift/HITL (oppsummert; detaljer i re-planen)
|
||||||
|
|
||||||
|
- **Klynge C:** concurrent==sekvensiell-kriteriet krever bølge-barriere-design (F6/D-D);
|
||||||
|
kostnadstak finnes kun per kjøring (`run.py:284-287`) — på tvers-styring mangler (S3.5).
|
||||||
|
- **Klynge D:** Azure-profilen er reell kode (`FoundryChatClient`, `backends.py:68-78`) men
|
||||||
|
u-eksersert: placeholders i model_map, ingen preflight-validering av env/deployment før
|
||||||
|
klient-bygging, ingen dokumentert auth-oppskrift. Offline-byggbart: preflight + env-kontrakt +
|
||||||
|
artefakt-fangst (S4.1/S4.2); selve kjøringen er operatør-milepæl (M1/M2).
|
||||||
|
- **Klynge E:** ruting/sporing forutsetter outbox (P4) + per-ekspert-konfig; varsling må følge
|
||||||
|
ingen-stille-egress (opt-in-flagg, injiserbar transport — samme mønster som `ingest.py:297-314`).
|
||||||
|
- **Delvis feil midt i portefølje:** en exception i prosjekt k (f.eks. `BudgetExceeded` i
|
||||||
|
`run_project`) propagerer ut av `run_portfolio` (`run.py:496-509` — ingen try) og KASTER hele
|
||||||
|
passets resultater (runs 1..k-1 tapes). Akseptabelt for MVP, men må avklares før N×10 (S3.4).
|
||||||
|
|
||||||
|
## 4. Ambisiøse utvidelser (FORSLAG — utenfor godkjent A–E-scope, krever operatør-godkjenning)
|
||||||
|
|
||||||
|
1. **Lærings-dashboard (statisk HTML):** generator over ledger + verdicts + runs — realisert vs
|
||||||
|
modellert per prosjekt/dimensjon, mål-progresjon, ExpeL-treff. Ren offline-artefakt av
|
||||||
|
eksisterende data. *Kostnad: 1–2 sesjoner.*
|
||||||
|
2. **Wiki-query-CLI:** `python -m portfolio_optimiser.ask "…"`— naviger bundle + store og svar
|
||||||
|
«hva vet wikien om kandidat X» (strukturert, ikke LLM). Gir ekspertene innsyn i
|
||||||
|
læringsgrunnlaget. *Kostnad: 1 sesjon.*
|
||||||
|
3. **Verdict-provenance-graf:** eksportér kjeden dom → promotering → fold → forslag som
|
||||||
|
mermaid/DOT fra provenance-stemplene — «hvorfor foreslo systemet dette»-sporbarhet.
|
||||||
|
*Kostnad: 1 sesjon.*
|
||||||
|
4. **Konsept-kvalitetsscore:** per bundle-fil: staleness (timestamp), lenketetthet,
|
||||||
|
siterings-dekning fra kjøringer — flagger råtnende wiki-innhold for kurator. *Kostnad: 1–2 sesjoner.*
|
||||||
|
5. **Kjørings-diff:** sammenlign to output-lag-artefakter (prompter/utfall/percentiler) —
|
||||||
|
regresjonsverktøy på metodenivå når spec/valider endres. *Kostnad: 1 sesjon.*
|
||||||
|
|
||||||
|
## 5. Utfordringer mot frosne beslutninger (KLART MERKET — kun operatøren kan endre)
|
||||||
|
|
||||||
|
| # | Frossen ramme | Utfordring | Belegg |
|
||||||
|
|---|---|---|---|
|
||||||
|
| U-1 | method-spec §3 Steg 1 («path separator MUST be skipped») | Avviser OKF-spec-ens anbefalte `/`-lenkeform → eksterne bundles ukonsumerbare (F4) | `okf.py:124-125`; OKF-spec (WebFetch 2026-07-09) |
|
||||||
|
| U-2 | method-spec §3 Steg 4 / §7.2 (validator-semantikk frossen) | Stage-2-«cap» håndheves ikke; selv-forfattede bands omgår den (F2); kostbaseline-forankring mangler (F3) | Kjørte moteksempler, `validator.py:125-153` |
|
||||||
|
| U-3 | Ingest-målbilde §8/§11/§12 (frossen) | Roadmap Fase 2 planlegger eksplisitt det målbildet forbyr (P1) — én av dem må vike, bevisst | Begge dokumenter sitert over |
|
||||||
|
| U-4 | method-spec §3 Steg 1 seeding-regel | Én-kandidat-nøkling skalerer ikke til klynge C (F5) | `verdicts.py:415-430` |
|
||||||
|
|
||||||
|
Ingen utfordring rettes mot: deterministisk validator som obligatorisk/blokkerende (bekreftet
|
||||||
|
riktig og reelt blokkerende på sitt input-domene), maker-checker-gaten (reelt gatende, detach-bevist),
|
||||||
|
fail-closed promotering/realisering (detach-bevist), load-bearing-regelen (den fanget reelt), eller
|
||||||
|
90 %-prinsippet.
|
||||||
|
|
||||||
|
## 6. Verifiseringslogg
|
||||||
|
|
||||||
|
| Påstand | Kilde/kommando |
|
||||||
|
|---|---|
|
||||||
|
| 279 passed / 4 skipped | `uv run pytest -q` (25.5s, 2026-07-09) |
|
||||||
|
| ruff clean / mypy clean | `uv run ruff check .`; `uv run mypy src` (21 filer) |
|
||||||
|
| HEAD pushet | `git rev-parse HEAD` == `git rev-parse origin/main` == 847ed901 |
|
||||||
|
| 11 detach-eksperimenter røde | scratchpad-kopi: kirurgisk detach + `uv run pytest <fil>` per søm (se §0) |
|
||||||
|
| Simuleringskonsollen virker | `uv run python -m portfolio_optimiser.simulation` i kopien → EXIT 0, «LEARNING LOOP CLOSED» |
|
||||||
|
| S2.7-antakelsen (bands omslutter unit_cost i alle fixtures) | skann av alle JSON-fixtures + inline-band-grep → NULL brudd (eneste inline-treff laster den sjekkede JSON-en) |
|
||||||
|
| Re-planens avhengighetsgraf er gyldig mermaid | validert + rendret via Mermaid-verktøyet (kun sesjonsnavn sendt) |
|
||||||
|
| OKF-krav (type/reserverte/lenker/toleranse) | WebFetch `raw.githubusercontent.com/GoogleCloudPlatform/knowledge-catalog/main/okf/SPEC.md` |
|
||||||
|
| F2-moteksempler validerer | `uv run python` i kopien: claim 100k>nominal 90k → ValidatedProposal p90=121057; claim 55k>nominal 30k → ValidatedProposal p90=65058 |
|
||||||
|
| F1: fold gated på bundle_dir; run_portfolio uten bundle_dir | `run.py:312`, `run.py:429-514` (lest) |
|
||||||
|
| P1-konflikten | `docs/plan/2026-07-06-reell-kjoring-analyse-plan.md:50-57` vs `docs/plan/2026-07-03-maalbilde-ingest-lag.md:161-166,199-204,207-215` |
|
||||||
|
| CHANGELOG stale | `CHANGELOG.md:22` («237 passing tests») |
|
||||||
|
| Live-tester ikke trigget | 4 skips = env-gatede (`test_foundry_profile_live.py:19`, `test_local_profile_live.py`, `test_portfolio_live.py`); ingen endpoints satt |
|
||||||
|
| STATEs 3 avvik | `progress.json` steg-noter + `run.py:517-537` + `ledger.py:165-192` |
|
||||||
|
| Magentic GA i Python | Delegert research: PyPI `agent-framework-orchestrations` 1.0.0 «Production/Stable»; MS Learn orchestration-sider uten experimental-banner |
|
||||||
|
| Middleware fyrer IKKE på bar BaseChatClient (1.9.0) | Empirisk: minimal `BaseChatClient`-subklasse + `Agent(middleware=[mw])` → calls=0 (kjørt i kopien) |
|
||||||
|
| Vektor-store-fakta (numpy/sqlite-vec/faiss/LanceDB-wheels) | Delegert research m/ PyPI-files-kilder; brukes i D-C (replan); ikke-kryssjekkede punkter der er merket «Ikke verifisert» |
|
||||||
|
| Foundry-config (FoundryChatClient: project_endpoint+model+credential; Entra) | Delegert research mot MS Learn provider-sider; brukes i S4.1 |
|
||||||
Loading…
Add table
Add a link
Reference in a new issue