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

24 KiB
Raw Blame History

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 (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_verdicttest_step8 ×2 + test_simulation_loadbearing røde; (4) import agent_framework injisert i okf.pytest_okf_is_maf_free rød; (5) verdict-eksklusjon fjernet fra context_filestest_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_contextokf.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-252run.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