portfolio-optimiser/docs/review-2026-07.md

321 lines
24 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.

# 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 611 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 13
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 AE-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: 12 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: 12 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 |