321 lines
24 KiB
Markdown
321 lines
24 KiB
Markdown
# 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 |
|