# 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 ` 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 |