feat(ingest): I3 — D7-speil av ingest (filkatalog/CSV), bygget fra commons-spec alene

Speiler MAF I2 fra shared/ingest-spec.md alene: manifest → CSV-konnektor →
materialisert OKF-bundle, byte-identisk med den delte golden-fasiten.

- ingest.py: ManifestContract (pydantic, fail-fast, file-kilde, verdict-reservasjon
  §3, id-grammatikk, max_rows), CSV-konnektor (boundary-checked fail-closed),
  materialisering (§5-frontmatter eksakt rekkefølge, markdown-tabell m/ escaping,
  LF-only, SHA-256 manifest-stamp), index-generering (§6), replacement §3/§5.
- okf.py: _parse_index_entry — tolererer frontmatterløs index (method-spec §3:
  index rendres via body = summary, ikke som typet concept-fil). Golden var
  spec-konform; D7-okf var strengere enn standarden. Scoped: non-index concept-
  filer krever fortsatt type (honesty-test).
- examples/ingest-golden-file/: repo-lokal golden (byte-frossen kopi av I2s fasit).
- Speiltester (I2s load-bearing-sett, alle detach-bevist røde): golden byte-fasit
  + mutasjonskontroller · provenance/navigability/verdict-reservasjon/re-ingest-safety
  · kontrakt fail-fast/max_rows/boundary/kollisjon · spec-integritet §11.
- docs/2026-07-04-I3-brief.md: brief + de to operatør-avgjorte beslutningene.

Suite 239 passed uten nøkkel/nettverk (189 + 50 nye) · ruff + mypy --strict rene.

[skip-docs] README + docs/extending.md er bevisst utsatt til I7 per sesjonsplan
(programmet batcher ingest-doc der, avgrenset til det D7 faktisk har — CSV nå,
SQL/HTTP senere). Endringen er dokumentert i docs/2026-07-04-I3-brief.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MM6BWb1hWmJZuXFZ7rjxT
This commit is contained in:
Kjell Tore Guttormsen 2026-07-04 06:12:43 +02:00
commit e03bd79876
15 changed files with 958 additions and 2 deletions

View file

@ -0,0 +1,69 @@
# I3 — D7-speil av ingest (filkatalog/CSV) — brief
> Forankret i hovedrepoets sesjonsplan
> `~/repos/portfolio-optimiser/docs/plan/2026-07-03-sesjonsplan-ingest.md` §I3 og dette
> repoets STATE. Bygget **fra `shared/ingest-spec.md` alene** (subtree-pull fra commons) —
> aldri ved reverse-engineering av MAF-koden. Golden-**bytene** (den delte fasiten) er lest,
> siden spec §11 gjør dem til konformanskravet begge stacker reproduserer.
## Mål (§I3)
Samme ingest-kontrakt konsumert **uendret** fra commons i dette søskenrepoet — beviser at
ingest-spec-en faktisk er delt: D7s uavhengige impl reproduserer MAF I2s golden **byte-identisk**
fra spec alene. Scope = `file`-kildetypen (lokal CSV-katalog). `sql` = senere (I5), `http` =
ikke i D7 (målbildet: HTTP/MCP demonstreres MAF-only, I6).
## To beslutninger (operatør-avgjort denne økta)
1. **Golden-fasit: repo-lokal kopi.** I2 la ingest-golden repo-lokalt (`examples/ingest-golden-file`,
ulikt method-golden i `shared/examples/`). D7 speiler mønsteret: kopi i dette repoets
`examples/ingest-golden-file/`, speiltest peker dit. Holder I3 som én lukket økt, ingen
commons-runde. (Alternativ — løfte golden til commons — avvist: egen GATET runde + avvik fra
I2-mønsteret.)
2. **okf relaksert for frontmatterløs index (følg standarden).** Funn: den delte golden-ens
`index.md` har **ingen** frontmatter; D7s `okf.navigate_bundle` krevde det → kunne ikke både
reprodusere golden byte-identisk OG navigere den med uendret okf. **method-spec §3 Step 1 er
avgjørende:** «the rendered read-context is *the index body (the summary)* followed by each
*non-index concept file* as a `## {type}: {title}` section» — standarden behandler index via
sin *body*, ikke som en typet concept-fil. Golden-en er altså spec-konform; D7s okf var
**strengere enn standarden**. Fix: `okf` tolererer nå frontmatterløs index (hele fila = body,
`type` defaulter til `index`); frontmatter-ful kuratert index parses uendret; non-index
concept-filer krever fortsatt `type` (relaksasjonen er scoped, bevist av egen honesty-test).
(Alternativ — «typet index overalt» via commons-RFC + MAF golden-regen — avvist: omskriver
standarden for å romme en D7-quirk, endrer frossen fasit, flerrepo GATET.)
## Leveranse
- `examples/ingest-golden-file/` — repo-lokal golden (kopi av I2s, byte-frossen).
- `src/portfolio_optimiser_claude/ingest.py``ManifestContract` (pydantic, fail-fast,
`file`-kilde, verdict-reservasjon §3, id-grammatikk, `max_rows`>0), CSV-konnektor
(boundary-checked fail-closed), materialisering (§5-frontmatter i eksakt rekkefølge,
markdown-tabell m/ §5-escaping, LF-only, SHA-256 manifest-stamp), index-generering (§6),
replacement-semantikk (§3/§5).
- `src/…/okf.py``_parse_index_entry` (frontmatterløs index-toleranse, method-spec §3).
- Speiltester (I2s load-bearing-sett): `test_ingest_golden.py` (byte-fasit + 3 mutasjonskontroller),
`test_ingest_loadbearing.py` (provenance / navigability / verdict-reservasjon / re-ingest-safety),
`test_ingest.py` (kontrakt fail-fast, max_rows, boundary, kollisjon, escaping),
`test_ingest_spec_loadbearing.py` (spec-integritet §11: fil finnes · navngir ingen toolkit ·
dokumenterer hvert kontrakt-felt) — D7-analogen til MAFs I1-guard.
## SDK-kroker (STATE steg 4 — søk-først)
I3 (filkatalog/CSV) introduserer **ingen SDK-flate**: ingest er deterministisk, null modellkall.
Søk-først SDK-krok-reverifisering (API-drift på `create_sdk_mcp_server`) er relevant for I6
(HTTP/MCP), ikke I3 — utsatt dit kroken faktisk brukes. Eksisterende SDK-klient (`sdk_client.py`,
`test_sdk_isolation.py`) uendret og grønn i suiten.
## Verifisering (kjørt, grønn)
- Golden **byte-identisk** konsumert (5/5, inkl. idempotens + mutasjonskontroller).
- Speiltester **røde ved detach** — alle fire sømmer bevist load-bearing (detach-bevis-skript).
- Søskensuite **grønn uten nøkkel/nettverk**: 239 passed (189 + 50 nye).
- `ruff check` + `ruff format` rene · `mypy --strict src`: no issues (16 filer).
## Åpent (forslag, ikke utført — utenfor I3-scope)
- **Standard-evolusjon:** vil operatøren senere ha «alle bundle-filer er typede concept-filer»
(typet index overalt) som bevisst OKF-endring, er det en egen koordinert commons-RFC (begge
specs, begge golden, begge okf). Dagens permissive okf er forover-kompatibel med det.