portfolio-optimiser-claude/docs/review-2026-07.md
Kjell Tore Guttormsen 3f106fcfd1 docs(review): kryssmodell-review + sesjonsplan-synk mot delt beslutnings-kø D-A–D-E
Fable 5-review av D7-søskenet, speil av MAF-reviewen 2026-07-09: baseline
re-verifisert (265/ruff/mypy), F2/F3/F5/F7-analoger kjørt-bevist, F1/F11
avkreftet i MAF-form, egne funn (method-spec-vakt mangler, Steg-7-inngang,
SDK-versjonsvakt, D-C↔spec-kollisjon), 11/11 detach-eksperimenter røde i
throwaway-kopi. Sesjonsplan: byggbare-nå C1.1/C2.5/C2.0 + D-A-gatede speil
C2.7/C2.8/C3.2/C4.0.
2026-07-10 07:29:35 +02:00

21 KiB
Raw Blame History

Uavhengig kryssmodell-review — portfolio-optimiser-claude (2026-07-10)

Reviewer: Fable 5 (xhigh), uavhengig av Opus 4.8 som skrev koden. Mandat: speil av MAF-søskenets kryssmodell-review (2026-07-09) — verifiser hvert MAF-funn mot DENNE kodebasen (premisser, ikke fakta) + uavhengig sveip etter D7-egne blindsoner. Ingen live-filer ble endret under selve reviewen; fletting (dette dokumentet + sesjonsplan + STATE) er operatør-forhåndsgodkjent.

Metode: all kildekode lest (16 moduler), alle nøkkel-tester lest, full lokal suite + ruff + mypy kjørt, F2/F3/F5/F7-moteksempler KJØRT mot live-modulene (read-only), og 11 detach-eksperimenter utført i en throwaway-kopi (scratchpad — live-repoet urørt). Hver påstand har fil:linje- eller kommando-belegg; verifiseringslogg nederst.

0. Baseline (verifisert, ikke sitert fra STATE)

Påstand Resultat Belegg
Testsuite 265 passed (1.6s) — STATE/CHANGELOG-tallet stemmer uv run pytest -q
Lint / typer ruff clean (40 filer formatert); mypy clean (16 filer, strict) uv run ruff check .; uv run mypy src
Push-status HEAD 42f320b == origin/main == open/main — STATEs påstand stemmer git rev-parse alle tre
Offline-invariant Suiten kjører uten nøkkel/nettverk; ingen egress-stier i src/ utenfor SDK-klienten suite-kjøring + grep urllib|socket|requests
S10-artefakter runs/s10/ urørt (fasit, protokoll §4.2/§4.5) — IKKE re-kjørt kun lest

Detach-bevis (load-bearing-stikkprøver, ALLE 11 RØDE i kopien — MAF-paritet 11/11): (1) Steg-1-fold detached → test_step1 + test_step7 røde (4 failed); (2) checker-override detached → test_checker_gate rød (3); (3) inbox-merge detached → test_step7 rød (2); (4) promotion-index-lenke detached → test_step8 rød (5); (5) verdict-eksklusjon fjernet fra bundle_contexttest_okf + test_step8 røde (2); (6) setting_sources=[]Nonetest_sdk_isolation rød (2); (7) informert refinement detached → test_step5 rød (2); (8) verbatim-id-seeding re-mintet → test_step8 rød (2); (9) re-ingest-lagsikkerhet detached (alle .md «stamped») → test_ingest_loadbearing rød (1); (10) import claude_agent_sdk i okf.py → purity-testene røde (2); (11) usage-fail-closed detached → test_budget rød (1). Kopien verifisert isolert (pakken importerte fra kopiens src/) og revertert ren etterpå. Metoden holder på alle stikkprøvde sømmer.


1. Funn (rangert, mest alvorlig først)

Ingen BLOCKER i dagens påstands-omfang: single-run-claims er sanne, detach-beviste, og README/extending.md er gjennomgående ærlige (§1). Funn 12 og 45 er spec-nivå-defekter som finnes her by construction (samme frosne spec) og deler beslutning D-A med MAF-siden.

C-F2 [MAJOR · spec-nivå] Validator: nominal-grensen gater aldri — selv-forfattede bands blåser P90 forbi «maximum feasible» (F2-analog: BEKREFTET)

  • Belegg: eneste numeriske blokk er P90 (validator.py:68); nominal_feasible beregnes (validator.py:53) men brukes aldri som gate. assumptions forfattes av modellen selv (loop.py:66-73 parser fritt; ir.py:30-49 har ingen invariant om at bandet omslutter unit_cost). Kjørt bevis (live-modul, read-only): golden-bundelens egne tall — total 300 000, nominal 90 000, band [0.70, 1.40] — validerer claim 100 000 > 90 000 (p90=121 057.09). Band helt over unit_cost ([1.8, 2.2]) validerer claim 55 000 mot nominal 30 000 (p90=65 058.49). Nøyaktig samme tall som MAF-moteksemplene — bekrefter at defekten er spec-båren, ikke implementasjonsbåren.
  • Feilscenario: en ekte proposer setter et optimistisk band → claim over den deterministiske feasibility-grensen stemples validated og persisteres som «validator-godkjent» i run-artefaktene.
  • Fiks: speiling av MAF S2.7 — (a) strukturell blokk claimed > nominal_feasible; (b) IR-invariant low ≤ unit_cost ≤ high. Golden- OG fixture-kompatibel her, verifisert: golden-claim 30 000 ≤ 90 000; alle ikke-tomme bands i dette repoets fixtures omslutter unit_cost ([0.8,1.2]∋1.0 test_ir.py:26; [0.70,1.40]∋1.0 golden; (0.70,1.50)∋1.0 test_bygg_energi_mikro.py:83; resten tomme). Gated på D-A pkt. 1 + commons-pull → sesjon C2.7.

C-F3 [MAJOR · spec-nivå] affected_items forankres ikke mot noen kostbaseline (F3-analog: BEKREFTET)

  • Belegg: _parse_candidate aksepterer vilkårlige modell-forfattede kostlinjer (loop.py:66-73); ingenting i bundle-formatet bærer en kostbaseline å avstemme mot. Kjørt bevis: diktet linje {code:"XX-DIKTET", quantity:1e6, unit_cost:10} → total 10 MNOK → claim 2 900 000 stemples validated (degenererte percentiler 3.0 MNOK uten band). IR-invarianten (claim ≤ items-total, ir.py:39-49) regner kun på selv-deklarerte tall.
  • Feilscenario: mot en ekte modell er «validatoren avgjør tallene» tom mot hallusinerte kostlinjer; kun checker-LLM-en (fail-open, loop.py:200-212) står imellom.
  • Fiks: speiling av MAF S4.0 — fail-closed avstemmings-stage mot en baseline-projeksjon i bundelen (commons-amendment for cost-baseline.json). Gated på D-A pkt. 2 → sesjon C4.0. MÅ være grønn før enhver eventuell fremtidig live-kjøring i dette repoet (D6: ny spend krever uansett ny operatør-godkjenning).

C-N1 [MAJOR · konformitet · D7-EGEN] Method-spec-integritetsvakten mangler — §11-raden «Spec integrity» er uimplementert

  • Belegg: ingen test i repoet refererer method-spec.md (grep -rn "method-spec\|method_spec" tests/ → null treff i testkode). Spec §11 krever: «this spec goes missing, names a framework, or stops documenting a consumed contract field» → rød (referansetest test_method_spec_loadbearing.py); §1 pkt. 3 gjør §11-tabellen til konformitetskrav. Ingest-spec-en HAR sin analog (test_ingest_spec_loadbearing.py — forbidden-toolkits + kontraktsfelt-dekning), så mønsteret finnes i repoet; method-spec-vakten ble aldri speilet. Spec-intro-ens «a guard test keeps it that way» er dermed sann kun via MAF-søskenets test — dette repoet håndhever den ikke selv.
  • Feilscenario: en commons-pull som (utilsiktet) endrer method-spec-en — fjerner et kontraktsfelt fra §12-tabellen eller introduserer rammeverksnavn — passerer denne suiten grønt; D7 mister sin eneste lokale detektor for spec-drift i subtree-synken.
  • Fiks: ny tests/test_method_spec_loadbearing.py etter ingest-spec-mønsteret (fil-eksistens, forbidden-toolkits, §12-kontraktsfelt-dekning for feltene DENNE implementasjonen konsumerer). Ingen beslutnings-avhengighet → sesjon C1.1 (byggbar NÅ).

C-F5 [MAJOR · spec-nivå] ExpeL-seeding feilnøkler features på bundelens ENE kandidat (F5-analog: BEKREFTET, med nyanse — id-halvdelen er allerede løst her)

  • Belegg: seed_store_from_bundle leser features fra load_validator_input(bundle_dir) — bundelens ENE IR-projeksjon — og gir ALLE type: verdict-filer de samme features (experience.py:132, experience.py:145-152). Nyansen: verdict_id leses VERBATIM fra frontmatter (experience.py:147), så id-kollisjonen MAF-funnet beskrev er avverget for promoterte filer — load-bearing-bevist (test_step8:test_two_promoted_candidates_both_seed_verbatim_ids). Kjørt bevis for rest-defekten: to promoterte dommer om ulike kandidater (E01/led-retrofit vs E99/ventilation) seedes begge med bundle-features (ENERGI-TOTAL-EL/LED) — retrieval kan ikke skille dem; en spørring med kandidat Bs egne features rangerer på feil grunnlag.
  • Feilscenario: flerkandidat-bundle: dom om tiltak B foldes inn i hypoteser om tiltak A — feil læringssignal, presist det klynge C-skalering forutsetter at ikke skjer.
  • Fiks: speiling av MAF S3.2 — seed leser features fra verdict-filas egen frontmatter (promote skriver dem), fallback dagens nøkling. Gated på D-A pkt. 4 → sesjon C3.2.

C-F4 [MAJOR · frossen-ramme-utfordring] /-prefiksede OKF-lenker hoppes over — spec-tro implementasjon av en spec-feil (F4-analog: BEKREFTET)

  • Belegg: navigate_bundle skipper ethvert mål med / eller \ (okf.py:112) — method-spec §3 Steg 1 pinner regelen. OKF-spec-en anbefaler bundle-rot-relativ /-form (MAF-review U-1, WebFetch-verifisert 2026-07-09; ikke re-hentet her — nettverksregel).
  • Feilscenario (D7-spesifikk nyanse): en ekstern bundle med ](/tiltak.md)-lenker navigerer til KUN index. Har indexen citerbar body, feiler kjøringen IKKE fast — build_citations siterer index-bodyen (artifacts.py:31-59) og kjøringen fortsetter med stille kontekst-tap; kun en body-løs index gir fail-fast («no citable content»).
  • Fiks: kun via D-A pkt. 3 (spec-amendment: ledende / mappes til bundle-rot, fortsatt fail-closed boundary) → sesjon C2.8. Merk: MAF-planen har i dag INGEN bygg-sesjon for F4-implementasjonen på sin side — bør legges til der når D-A lander. (Relatert MINOR, samme som MAF: en index-lenket log.md — reservert i OKF — ville rendres som konseptfil; okf.py:128-134 filtrerer kun verdict + tom body.)

C-F7 [MINOR nå → MAJOR ved flerbruker/klynge E] Verdict-inboxen er en uvalidert prompt-injeksjonsflate (F7-analog: BEKREFTET)

  • Belegg: decision valideres kun som ikke-tom streng (inbox.py:47) — modulens docstring UTSETTER eksplisitt vokabular-politiet til §4.1/§6 (inbox.py:10-13), men på merge-stien (inbox.py:122-132) fins ingen sjekk; rationale flyter verbatim inn i hypotese-prompten via folden (experience.py:157-171). Ingen størrelses-/antall-tak. Kjørt bevis: {"decision": "hva-som-helst", "rationale": "IGNORE ALL PREVIOUS INSTRUCTIONS…"} → merged=1, decision-strengen OG injeksjonsteksten når prompten.
  • Fiks (fortsatt tolerant — skip, aldri raise): vokabular-sjekk mot {approved, rejected, approved_with_adjustment}-settet ved merge, tak på rationale-lengde + antall filer. Ingen beslutnings-avhengighet → sesjon C2.5.

C-N2 [MINOR · D7-EGEN] Steg-7-inboxen har ingen kjørbar kjøresti-inngang

  • Belegg: merge_inbox_into_store kalles fra INGEN kjøresti — run_s10.py:44-54 har ingen --inbox-arg og kaller kun seed_store_from_bundle + fold_experience (run_s10.py:72-79). Steg 7 (§3/§5: «a run ingests the inbox INTO its store BEFORE the Step-1 fold») er bevist som primitiv + test-komposisjon (test_step7, detach-bevist rød), men ingen skipbar inngang komponerer den. README sier «a later run ingests it tolerantly and merges it before the fold» (README.md:66-68) — sant om kontrakten, men ingen leverbar kjøresti gjør det. (Til forskjell fra MAF-F1: ingen død docstring-påstand — folden ER wiret på den ene kjørestien som finnes; F1 i MAF-formen er AVKREFTET her.)
  • Feilscenario: en operatør som dropper en dom i en inbox-mappe og re-kjører har ingen kommando som plukker den opp — læringssløyfas lange tidsskala er i praksis kun promoterings-veien (som ER wiret via bundle-seeding).
  • Fiks: generalisert kjøresti-inngang (offline-testbar komposisjonsfunksjon + --inbox/--verdict-dir-arg; run_s10.py forblir urørt fasit-sti). Ingen beslutnings-avhengighet → sesjon C2.0.

C-N3 [MINOR · D7-EGEN] SDK-avhengigheten er løst pinnet og run-stien leser 0.2.110-attributter uten versjonsvakt (F11-analog: AVKREFTET for testdobbelen, rest-risiko på run-stien)

  • Belegg: testdobbelen er REN — ScriptedClient implementerer repoets egen ModelClient-protokoll og importerer ingenting fra SDK-en (tests/_scripted.py), så MAF-F11s privat-API-skjørhet i suiten er AVKREFTET her. Men run-stien leser AssistantMessage.error/.model, ResultMessage.usage/.total_cost_usd/.is_error/.subtype/.errors («verifisert mot 0.2.110», sdk_client.py:12-17,108-121) med pin claude-agent-sdk>=0.2 (pyproject.toml:8) — enhver 0.x-oppgradering kan endre attributt-semantikken stille, og _total_tokens summerer da et KRYMPET felt-subsett uten feil (sdk_client.py:66-71).
  • Fiks: pin >=0.2,<0.3 + versjonsvakt-test med handlingsrettet melding (tvinger bevisst re-verifisering av attributt-premissene ved oppgradering) → inn i sesjon C2.5.

C-N4 [MINOR] Token-taket lades ETTER fullført kall; USD-taket er kun per-kall (F10-analog: DELVIS BEKREFTET)

  • Belegg: meter.charge_tokens kalles etter client.complete returnerer (loop.py:91, budget.py:43-51) — kallet som krysser taket fullføres (og koster) før raise, som i MAF. USD: per-kall-tak finnes førsteklasses (sdk_client.py:87, ClaudeAgentOptions.max_budget_usd — bedre enn MAF), men ingen run-total USD-grense; totalen er kun indirekte bundet (antall kall × per-kall-tak, antall kall bundet av meteren). Ærlighets-nyansen fra MAF-F10 er AVKREFTET her: ingen docstring lover «short-circuits» — budget.py:1-9 beskriver mekanikken presist.
  • Fiks (lav prioritet): pre-call-guard + eksplisitt run-total USD-tak → valgfri sesjon C3.5 (uavhengig; mest verd før en eventuell ny live-kjøring).

C-S1 [SUGGESTION] Checker-parse: VERDICT: REJECT-substring-presedens hvor som helst (F13-analog, spec-pinnet)

  • _REJECT_PATTERN.search over hele teksten (loop.py:42,186-194): en checker som SITERER formatet feiltolkes som reject. Spec-konformt (§3 Steg 4 pinner presedensen) — flagges som robusthet; evt. siste-linje-parse er en D-A-nyansering (allerede i MAF-planens D-A pkt. 5-nabolag).

C-S2 [SUGGESTION] max_rows-cap håndheves etter full innlesing

  • _read_csv leser hele fila før cap-sjekk (ingest.py:190-197); _read_sql gjør fetchall() før sjekk (ingest.py:244-247). Fail-fast i UTFALL (aldri stille trunkering, §8-konformt), men en enorm lokal kilde leses helt inn i minnet først. Akseptabel MVP-grense for lokale kilder; noteres for en evt. streaming-variant.

C-S3 [SUGGESTION] Døde/små ting

  • promotion._link_from_index er ikke-atomisk RMW — dokumentert MVP-grense (promotion.py:92-95), samme som MAF-F6s index-del; blir først reell ved concurrency (D-D), som D7 ikke har noen sti for i dag.
  • model_map.json har kun anthropic-profil (Bedrock/Vertex/Foundry er env-switch-konfig per design) — ingen placeholder-problem à la MAF-P6 her.

C-P1 [MAJOR · plan-nivå · DELT — flagges til hovedrepoet] MAF-planens S3.1 (semantisk henting) kolliderer med frossen method-spec §3 Steg 1

  • Belegg: method-spec §3 Steg 1 (frossen): «Ranking is structural, never textual — surface text MUST NOT contribute to similarity.» MAF-planens S3.1 bygger «HybridRanker = dagens strukturelle score + cosine over lagrede vektorer» gated KUN på D-C — men cosine over embeddings av rationale-/overflatetekst ER tekstlig similarity og bryter spec-en uten amendment. D7 håndhever regelen i dag (experience.py:83-95 + load-bearing test_surface_text_never_contributes).
  • Konsekvens: D-C-beslutningen må enten (a) kobles til et D-A-punkt som amender ranking-regelen bevisst, eller (b) scope S3.1 til embeddings over strukturelle features. Gjelder BEGGE repo — dette er en delt beslutnings-forutsetning MAF-reviewen ikke fanget.

2. MAF-funnene — status i dette repoet

MAF-funn Status her Belegg / hvor
F1 (portefølje-læringssløyfe død) Avkreftet i MAF-formen — ingen portefølje-sti finnes; folden ER wiret på eneste kjøresti (run_s10.py:72-79); ingen død docstring. Rest-analog: C-N2 (ingen kjørbar Steg-7-inngang) §1 C-N2
F2 (nominal gater aldri) Bekreftet (kjørt bevis, identiske moteksempel-tall) §1 C-F2
F3 (ingen kostbaseline) Bekreftet (kjørt bevis, 2.9 MNOK validert) §1 C-F3
F4 (/-lenker skippes) Bekreftet (okf.py:112; spec-tro; stille-tap-nyanse) §1 C-F4
F5 (én-kandidat-seeding) Bekreftet med nyanse — id-halvdelen ALLEREDE løst (verbatim verdict_id + load-bearing test); feature-halvdelen kjørt-bevist §1 C-F5
F6 (concurrency/RMW) Ikke anvendbar — ingen portefølje-/concurrent-sti; RMW-grensen dokumentert (promotion.py:95). Relevant først ved D-D-speiling §1 C-S3
F7 (inbox-injeksjon) Bekreftet (kjørt bevis) §1 C-F7
F8 (metode-cap fail-open) Ikke anvendbar — D7-validatoren har ingen metoderegler (kun §3-Steg-4-kjernen); golden krever dem ikke validator.py (hele)
F9 (prosent-mål/baseline) Ikke anvendbar — ingen goals/ledger-lag i D7 filkatalog
F10 (budsjett post-charge) Delvis bekreftet — post-charge ja; docstring ærlig; per-kall-USD-tak finnes (bedre enn MAF); run-total USD-tak mangler §1 C-N4
F11 (privat-API i doubles) Avkreftet for testdobbelen (egen protokoll); rest-risiko på run-stien (løs pin, ingen versjonsvakt) §1 C-N3
F12 (CHANGELOG stale) AvkreftetCHANGELOG.md:16 sier 265 == suiten baseline
F13 (småting) Delvis — checker-parse-presedensen gjelder (C-S1); _authored_texts-dødkoden og retrieval-substring er MAF-spesifikke (D7-similarity er ren strukturell) §1 C-S1
F14 (Magentic-begrunnelse) Ikke anvendbar — ingen MAF/Magentic-referanser i D7 grep
P1/P2 (roadmap vs ingest-målbilde / MCP-wiring) Ikke anvendbar — D7 har ingen Fase-2-roadmap; extending.md er ærlig om HTTP/MCP ubygd (peker-only) docs/extending.md:128-160
P4 (output-lag ubygget) I hovedsak avkreftet herartifacts.py persisterer proposal/result/provenance/usage + stop-artefakter deterministisk (runs/s10/). Rest: run_id-basert outbox + pending-sporing KUN hvis D7 skal speile Fase 5 (ikke planlagt) artifacts.py:83-152
P5 (verdict_dir i portefølje) Ikke anvendbar (ingen run_portfolio) filkatalog
P6 (stubs: notify/MCP-demo/Azure-placeholders/main-paritet) Ikke anvendbar i MAF-formen — ingen slike stubs her; CLI-paritets-resten dekkes av C-N2/C2.0 §1 C-N2
U-1U-4 (frossen-ramme-utfordringer) Samme status som F4/F2+F3/P1/F5 over — D7 slutter seg til U-1/U-2/U-4; U-3 ikke anvendbar §1

3. Utfordringer mot frosne beslutninger (KLART MERKET — kun operatøren kan endre)

D7-siden SLUTTER SEG TIL MAF-reviewens U-1 (/-lenker), U-2 (validator-semantikk) og U-4 (seeding-regelen) med egne kjørte bevis (§1), og legger til:

# Frossen ramme Utfordring Belegg
U-5 (ny) method-spec §3 Steg 1 («structural, never textual») Delt D-C-plan (semantisk henting) er uimplementerbar uten amendment eller re-scoping — koblingen D-C↔D-A mangler i den delte køen §1 C-P1

Ingen utfordring rettes mot: golden-suiten (reprodusert eksakt), maker-checker-gaten (detach-bevist), fail-closed promotering (detach-bevist), verdict-eksklusjonen (detach-bevist), SDK-isolasjonsvalget setting_sources=[] (detach-bevist; S10-post-mortem- begrunnelsen står), eller load-bearing-regelen (11/11 stikkprøver røde).

4. Verifiseringslogg

Påstand Kilde/kommando
265 passed / ruff clean / mypy clean (16 filer) uv run pytest -q (1.60s); uv run ruff check .; uv run ruff format --check .; uv run mypy src (2026-07-10)
HEAD == origin/main == open/main git rev-parse HEAD origin/main open/main → alle 42f320b
11 detach-eksperimenter røde throwaway-kopi i scratchpad; driver-script med kirurgisk patch + uv run pytest <målfil> per søm + git-revert; kopi-isolasjon verifisert (portfolio_optimiser_claude.__file__ peker i kopien); 11/11 RED, 0 anomalier
F2-moteksempler validerer uv run python mot live-modul (read-only): claim 100k>nominal 90k → ValidatedProposal p90=121 057.09; claim 55k>nominal 30k (band [1.8,2.2]) → ValidatedProposal p90=65 058.49
F3-moteksempel validerer samme script: diktet linje 1e6×10 → claim 2.9 MNOK → ValidatedProposal (percentiler 3.0 MNOK)
F5-feilnøkling kjørt i kopien: to promoterte dommer (E01 vs E99) → begge store-records bærer bundle-features (ENERGI-TOTAL-EL/LED); distinkte ids (verbatim)
F7-injeksjon kjørt i kopien: decision:"hva-som-helst" + injeksjons-rationale → merged=1, begge når prompten via folden
Method-spec-vakt mangler grep -rn "method-spec|method_spec" tests/ → 0 treff; test_ingest_spec_loadbearing.py finnes (mønsteret)
C2.7-antakelsen (alle bands omslutter unit_cost) grep over tests/ + examples/ + shared/examples/: eneste ikke-tomme bands [0.8,1.2]∋1.0, [0.70,1.40]∋1.0, (0.70,1.50)∋1.0 — null brudd
Ingen egress i src utenfor SDK-klient grep -rn "urllib|socket|requests\.|http" src/…/*.py → kun docstring-treff i ingest.py
S10-artefakter urørt kun lest; ingen re-kjøring (protokoll §4.2/§4.5)
OKF-spec-ens /-anbefaling IKKE re-verifisert her (nettverksregel denne sesjonen) — arvet fra MAF-review §6 (WebFetch 2026-07-09), merket som arvet premiss
SDK-attributt-premisser («verifisert mot 0.2.110») arvet fra kodens egne verifikasjonsnotater (sdk_client.py:12-17, CLAUDE.md) — ikke re-verifisert mot nyere SDK; nettopp derfor C-N3 krever versjonsvakt