portfolio-optimiser/CLAUDE.md
Kjell Tore Guttormsen 4ec778c855 feat(fase3): make the maker-checker checker actually gate the reasoning
Closes gap #3 (maalbilde §5): the GroupChat checker critiqued into the void —
output_from=[proposer] surfaced only the proposer, so an explicit checker
rejection was ignored and the deterministic validator was the sole gate. Two
falsifiers now act on the same candidate: the validator gates the NUMBERS
(blocking, unchanged), the checker gates the REASONING (maalbilde §2/§6).

- workflow.py: output_from=agents surfaces both participants; the checker
  instruction ends with a VERDICT: APPROVE / VERDICT: REJECT - <reason> line.
- run.py: _authored_texts() reads author_name through out.messages (MAF 1.9.0
  puts it there, not on the AgentResponse); _debate_text() now selects the
  PROPOSER-authored output (fixes a latent texts[-1] regression that would feed
  the checker's verdict to generation at even round counts); _checker_verdict()
  parses the gate decision. An explicit REJECT overrides an otherwise-validated
  outcome to a checker-sourced Rejection. Opt-in-reject (fail-open on a missing
  marker). RunResult gains checker_verdict; provenance.validator_decision is
  stamped from the validator outcome BEFORE the override, so it never conflates
  the two falsifiers (provenance honesty).

Load-bearing (maalbilde §7): tests/test_checker_gate_loadbearing.py is a PAIR —
an explicit checker REJECT on a VALIDATOR-VALID proposal yields a Rejection whose
reason carries the checker's reason while validator_decision stays "validated";
the causality control (checker APPROVE, same proposer) validates normally. Proven
RED on BOTH detach points (revert output_from, or drop the override).

Suite 134->136 passed, 4 skipped; mypy + ruff check clean. Pre-existing
ruff-format drift (backends/budget/verdicts/test_contracts) left untouched for a
surgical diff.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHR8iKxJRxDiDfNw8HZmWE
2026-06-30 07:24:30 +02:00

55 lines
4.5 KiB
Markdown

# portfolio-optimiser
## Kontekst
Generisk, åpent Python-rammeverk på **Microsoft Agent Framework (MAF)** som finner kostnadsbesparelser INNI hvert prosjekt i en portefølje av uavhengige prosjekter. Multi-agent samarbeid genererer kandidat-tiltak; en obligatorisk deterministisk validator avgjør verdiene; fageksperter vurderer via HITL, og systemet lærer av dommene. Publiseres på Forgejo for alle som har MAF.
Bakgrunn og beslutninger: [research](docs/research/2026-06-23-prior-art-platform.md) (§15 implementeringsregister), [plan](docs/plan/2026-06-23-incremental-plan.md). Løpende state: `STATE.md` (local-only).
## Stack
Python ≥3.10. MAF (`agent-framework-core` 1.9.0). Pakkehåndtering: `uv`. To backend-profiler: Azure/Foundry (full) + lokal (fallback).
## Konvensjoner
- Type hints overalt (`mypy` der mulig). Pydantic for validering/IR.
- `ruff` for lint+format. `pytest` for test.
- Modell-valg som konfig (modell-map rolle→Foundry-deployment), ikke spredt i kode.
- Metode kodifiseres som **Agent Skill** (`agentskills.io`: `SKILL.md` + `scripts/` + `references/`).
- Datatilgang via **MCP**-servere; data-source-konfig JSON-Schema-validert, fail-fast.
## Kommandoer
- Sync: `uv sync`
- Test: `uv run pytest`
- Lint: `uv run ruff check .` + `uv run ruff format .`
- Type: `uv run mypy src`
## Arbeidsflyt (invarianter)
- **Rent teknisk rammeverk:** deployer eier DPIA/ROS/behandlingsformål. Bygg IKKE compliance-funksjoner — kun tekniske forutsetninger (lokal-only, provenance, ingen stille egress) + disclaimer.
- **90%-prinsipp:** bygg den generiske kjernen + tydelige extension points; jakt IKKE de siste 10 %.
- **Deterministisk validator er obligatorisk og blokkerende** — aldri valgfri plugin.
- **Framework-nøytral kontekst-søm:** OKF-bundle-navigasjon (`okf.py`) og den delte
`shared/`-kjernen er ren stdlib — null `agent_framework`/`mcp`-import, så samme bundles
konsumeres uendret av begge stacker (D7-portabel). Håndhevet av
`tests/test_okf.py::test_okf_is_maf_free`; importér aldri MAF inn i kontekst-laget.
- **OKF-navigert bundle-kontekst (ikke stuffing):** på bundle-stien bygges agent-lese-konteksten
ved å NAVIGERE bundelen (`okf.bundle_context`: index + frontmatter + cross-links, progressiv
disclosure) — aldri keyword-chunk-stuffing (målbilde §2/§4). `type: verdict`-laget ekskluderes
fra denne konteksten: tidligere dommer når hypotese-prompten KUN via den gatede ExpeL-folden.
Load-bearing: `test_bundle_context_excludes_verdict_layer` + empty-store-kontrollen i
`test_step1_expel_loadbearing.py` (realiseringssignalet lekker aldri inn via kontekst).
- **Stoppkriterier + budsjett-tak påkrevd ved oppstart** (fail-fast, aldri ubegrenset loop).
- **Group Chat maker-checker** som debatt-default (IKKE Magentic, som er eksperimentell).
- **To falsifiserere, samme kandidat (Steg 3/4, målbilde §2/§6):** den deterministiske validatoren
gater *tallene* (blokkerende), checkeren gater *resonnementet*. Checkeren avslutter turen med en
`VERDICT: APPROVE` / `VERDICT: REJECT — <grunn>`-linje; et eksplisitt avslag blokkerer et ellers
validert forslag (`run_project` overflater begge debatt-deltakere via `output_from=agents` og
overstyrer utfallet til en checker-kilde-`Rejection`). Gaten er opt-in-reject (fail-open ved
manglende markør), og `provenance.validator_decision` forblir ærlig — den speiler KUN validatoren,
aldri checkeren (de to falsifisererne blandes aldri). Load-bearing:
`tests/test_checker_gate_loadbearing.py` blir rød ved BEGGE detach-punkt (revert `output_from`,
eller fjern override). Checkeren «må faktisk gate, ELLER vi slutter å kalle det maker-checker».
- **Kostnadsdisiplin:** utvikle primært på lokal profil (gratis); Foundry/Azure (privat tenant finnes) kun til målrettet, minimal verifisering; billigste modeller + små syntetiske data + harde token-tak. Ingen tunge test-kjøringer.
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.
## Communication patterns
When linking to local files in responses, use named markdown links — `[Human-friendly name](file:///absolute/path)`, never bare `file://` URLs or autolinks `<file://...>`, always absolute paths (never `~/` or relative), one bullet per file when there are several. (Bare `file://` URLs render only the first as clickable across multiple lines; named links stay independently clickable.)