portfolio-optimiser-claude/CLAUDE.md
Kjell Tore Guttormsen 90a41774fc test(sdk): the pin was a permission, so give the premises a proof
The guard checked whether the installed SDK satisfied the pin. Nobody had
ever checked whether anyone had READ it. Those are different questions, and
the gap between them was a whole version range: pinned >=0.2.111,<0.3,
premises source-verified through 0.2.110, installed 0.2.120. Every build in
between was admissible and unexamined — `uv sync --upgrade` would have kept
806 tests green on an SDK no one had opened. Written red first: a guard
handed 0.2.140 returned it without complaint.

_VERIFIED_THROUGH is the ratchet. It records the newest build actually read
at source, and a newer one fails naming the five premises to re-check. The
pin is untouched and was never the defect — measurement dissolved the
premise that it needed lifting. It was not too narrow but too wide, and a
wider permission is not repaired by widening it further.

The premises themselves were prose the failure message recited. Nothing
tested them, so one that stopped being true would have surfaced on the one
live paid run (S10, D6). They are now a table introspected against the
installed package, with the printed prose derived from that same table so a
checked attribute cannot go unreported or a reported one unchecked. The
premise introspection structurally cannot see — that query() yields an
AssistantMessage then a closing ResultMessage — is named apart, and is the
honest reason the human reading still has to happen.

Value-proved, not merely named: disabling the ratchet reds 1 test, stubbing
the inventory to "no gaps" reds 3, re-hardcoding the prose reds 1, and
lowering _VERIFIED_THROUGH below the installed build reds the real
installed-version test rather than only a monkeypatched one.

0.2.139 read at source (0.2.120 -> 0.2.139, latest on PyPI today; STATE said
0.2.134, measured 08-09 and stale). The public query.py is byte-identical,
every premise field keeps its type and default, and the parser changes are
additive. One needed a look: 0.2.139 added a skills path defaulting
setting_sources to ["user", "project"], which would have undone the S10
isolation fix — it fires only on None, so the explicit [] is out of reach.
Prose carrying stale version claims moved with the reading, never ahead of
it: each was re-verified at 0.2.139 before being restated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014dKDjVG7qrBh9NkAAxutqN
2026-08-18 16:57:57 +02:00

77 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# portfolio-optimiser-claude
## Kontekst
Søsken-implementasjon (beslutning D7) av portfolio-optimiser-metoden på **Claude Agent SDK**:
generisk, åpent Python-rammeverk som finner kostnadsbesparelser INNI hvert prosjekt i en
portefølje. Bygges på **metode-spec-en** (`shared/method-spec.md`) som normativ kilde.
**Åpen konkurranse med MAF-søskenet** (`~/repos/portfolio-optimiser`, operatørbeslutning
2026-08-09): alle tilgjengelige midler er lov for å nå den beste løsningen, inkludert å lese
søskenets kode, og utvekslingen kan gå begge veier. **Grensen er hensikten, ikke kilden:**
kopiér kun der det faktisk gir mening for løsningen — **aldri som snarvei**. De reelle
begrensningene er hva MAF og Claude Agent SDK hver for seg tilbyr, og det er nettopp den
forskjellen sammenligningen skal måle. Fram til 2026-08-09 ble repoet bygget fra speccen
alene; det er datert i README og skal ikke påstås om arbeid etter den datoen. Program-planer
(sesjonsplan S5S12, sammenligningsprotokoll, frossent målbilde) bor i hovedrepoets
`docs/plan/`. Løpende state: `STATE.md` (local-only).
## Stack
Python ≥3.10. **Claude Agent SDK** (`claude-agent-sdk` ≥0.2, kildeverifisert 0.2.139 per
2026-08-18 — pinnen `>=0.2.111,<0.3` er en TILLATELSE, `_VERIFIED_THROUGH` i
`tests/test_sdk_version_guard.py` er beviset og går rødt på en ulest nyere build. Per
2026-07-03: MIT-lisens, bundler Claude Code CLI — ingen separat installasjon; import og
konstruksjon fungerer uten API-nøkkel, nøkkel trengs kun ved faktisk `query()`).
Pakkehåndtering: `uv`. Pydantic for kontrakts-validering.
## Konvensjoner
- Type hints overalt; `mypy --strict`. `ruff` for lint+format. `pytest` for test.
- **Testsuiten kjører uten API-nøkkel og uten nettverk** (invariant fra S5; håndhevet ved at
all kontrakts-/kontekst-logikk er ren config/fil-lag).
- Modell-valg som konfig: `src/portfolio_optimiser_claude/data/model_map.json`
(rolle→Claude-modell-id per backend-profil, hver profil krever `default`; validert av
`contracts.py`). Modell-id-er verifiseres mot offisiell doc før endring.
- SDK-primitiver (verifisert 2026-07-03): `query()`/`ClaudeSDKClient`, `ClaudeAgentOptions`
(`max_turns`, `max_budget_usd` — førsteklasses USD-tak), `AgentDefinition` (subagenter;
sub-felt er camelCase), `@tool` + `create_sdk_mcp_server` (in-process MCP), hooks
(`PreToolUse` med `permissionDecision: "deny"` for deterministisk gating). MERK:
`SessionStart`/`SessionEnd` finnes IKKE som Python-callback-hooks.
- `shared/` er en **git subtree** av
[`portfolio-optimiser-commons`](https://git.fromaitochitta.com/open/portfolio-optimiser-commons)
(source of truth; remote `commons`). Synk er **PULL-ONLY**: endringer committes i commons og
hentes med `git subtree pull --prefix=shared commons main --squash`. ALDRI `git subtree push`
fra konsument — re-split lekker hele konsument-historikken inn i commons (observert + opprydd
i MAF-repoet 2026-07-03). Se `shared/README.md`.
## 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 — kun tekniske
forutsetninger (lokal-only, provenance, ingen stille egress) + disclaimer.
- **Metode-spec-en er normativ** (RFC 2119). Ærlighets-regelen (§1) er ufravikelig: ingen
artefakt påstår mer enn implementasjonen gjør; skriptede stand-ins merkes som det.
- **Deterministisk validator obligatorisk + blokkerende** (§3 steg 4); golden-suiten
(`shared/examples/bygg-energi-mikro/golden.json`) er ENESTE fasit (§7).
- **Load-bearing tester** (§11): hver søm bevises med en test som blir RØD når sømmen
detaches — grønn-men-død er feilmoden regelen finnes for.
- **Stoppkriterier + budsjett-tak påkrevd ved oppstart** (§8; `contracts.py` fail-fast).
På kjørestien kommer USD-taket i tillegg via `ClaudeAgentOptions.max_budget_usd`.
- **Kostnadsdisiplin (D6):** alt utvikles offline/deterministisk; den ENE ekte API-kjøringen
i programmet er S10 (mikro-bundle, billigste egnede modell — per model_map Haiku 4.5 —
harde tak, kost loggføres).
- **STATE.md er local-only** (gitignored — repoet skal publiseres; STATE når aldri et
offentlig speil).
## Communication patterns
### Linking to local files
When pointing to local files in responses, always use markdown link syntax with a descriptive name:
- Use `[Human-friendly name](file:///absolute/path)` — never bare `file:///...` URLs or autolinks `<file://...>`.
- Always use absolute paths. Never `~/` or relative paths.
- For multiple files, render as a bullet list of named markdown links.
Why: bare `file://` URLs only render the first as clickable across multiple lines. Named markdown links make each entry independently clickable and look cleaner.