portfolio-optimiser/shared/docs/plan/2026-07-20-f1-freetext-connector-direction.md

7.1 KiB
Raw Blame History

F1 — fritekst-kilder i ingest-spec: retningsvurdering

Status: vurdering, ikke vedtak. Spec-en er IKKE endret. Beslutning tas av operatøren. Foranledning: llm-ingestion-okf v0.3.1 har ingen fritekst-connector; file er en streng CSV-leser og §5-body rendres som markdown-tabell med newline-kollaps. To uavhengige repo (claude-playlist-corpus, claude-code-llm-wiki) blokkeres. Spørsmålet stilt til commons: løses dette i ingest-spec.md (dør A) eller i bibliotekets dør B (innboks, ikke spec-styrt)?


1. Retning: løses i spec-en, i dør A

Fire grunner, i synkende vekt:

  1. Arkitekturregelen krever det. §1: «data reaches the model ONLY via OKF bundles», og bundlen materialiseres deterministisk FØR loopen. Fritekst er data. Ingenting i §1§3 skiller tabulært fra ustrukturert — utelukkelsen er et artefakt av at v1s file-connector ble en CSV-leser, ikke en prinsipiell grense. Skyves den vanligste kunnskapsbase-kilden ut til dør B, faller den samtidig utenfor provenance-stemplingen (§7), verdict-reservasjonen (§3), golden-regimet (§11) og determinismen (§10) — spec-ens dekning uthules der den betyr mest.
  2. Sitatkravet peker samme vei. Method-spec §9 krever «file + exact text span + snippet». En span inn i en tabell-rendret, newline-kollapset body er i praksis ubrukelig; en verbatim dokument-body er det naturlige span-målet. Fritekst gjennom dør A gjør sitatkjeden bedre, ikke svakere.
  3. Determinismen er lettere, ikke vanskeligere. Verbatim bytes har ingen typekoercion, ingen tallformatering, ingen NULL-semantikk — hele §5s tabell-fallgruvefelt forsvinner. Det finnes ikke noe determinisme-argument for å holde fritekst ute.
  4. Spec-en har allerede render-modusen — feilplassert. §5 sier for http: «the response body verbatim inside a fenced code block». Verbatim-render finnes altså, men er bundet til transport (http) i stedet for til innholdsform. Det er den faktiske defekten: §5 konflaterer kildetype og body-form. F1 er symptomet.

Grensen mot dør B holder likevel — men den går ikke ved innholdsform. Den går ved adresserbarhet + re-eksekverbarhet: en kilde med manifest-deklarert, repeterbar query mot en adresserbar kilde hører i dør A (en transkripsjonsfil under root kvalifiserer). Materiale uten re-eksekverbar kilde-query — limt inn tekst, en e-post, et engangsdropp — hører i dør B. Bruker vi innholdsform som grense i stedet, havner halve kunnskapsbasen utenfor spec-en av en grunn som ikke er prinsipiell.

Foreslått form (retning, ikke spec-tekst)

Ikke en ny kildetype document — den ville duplisere files root- og grensesjekk-logikk. I stedet: skill body-form fra kildetype. Et nytt påkrevd extraction-felt (arbeidsnavn render) med table | verbatim. file+table = dagens CSV-leser; file+verbatim = fritekst-dokumentet som mangler; http beskrives retroaktivt som verbatim og blir dermed konsistent i stedet for et unntak. Ett felt, én rad i §12s kryssjekk-tabell, ingen ny kildetype.

2. Hva §5 må si om verbatim body-render

Seks punkter. De fire første er ikke valgfrie — dagens http-render mangler dem alle, så dette lukker et latent hull samtidig som det løser F1.

  • Bytes og dekoding. Innholdet leses som bytes og dekodes som UTF-8, strengt. Dekodefeil er en ERROR — aldri erstatningstegn, aldri lossy koercion. Samme disiplin som §5s «any other value type MUST fail (never silent coercion)».
  • Linjeskift — her er «verbatim» nødt til å vike. §5 krever LF-only og nøyaktig én avsluttende newline. CRLF/CR MÅ derfor normaliseres til LF, og etterfølgende blank plass ved EOF kollapses til nøyaktig én LF. Uten denne regelen motsier «verbatim» og «LF-only» hverandre, og golden-ekstraksjoner blir plattformavhengige. Spec-en MÅ si rett ut at renderen dermed er lossy mot kildens eksakte bytes — og at en eventuell kildehash regnes over kildens bytes, ikke de rendrede.
  • Fencing — obligatorisk, med deterministisk bredde. Fence-lengde = max(3, lengste backtick-run i innholdet + 1) (CommonMark-standard, deterministisk).
  • Fencing er sikkerhetsbærende, ikke kosmetikk. Ufenset markdown-passthrough er IKKE v1. Grunnen er konkret: method-spec §3 Step 1 navigerer ved å følge body-lenker (](target.md)), så et ufenset ingested dokument kan injisere navigasjonskanter utenom index.md og forsøke bundle-escape-stier. I tillegg rendres read-context som ## {type}: {title}-seksjoner — et dokument med egne ##-overskrifter visker ut seksjonsgrensene, og en ledende --- kan leses som et nytt frontmatter-blokk. Fenset innhold har ingen av delene. (Ufenset passthrough = utvidelsespunkt.)
  • max_rows for dokumenter. Feltet er radorientert og meningsløst for fritekst. Enkleste konsistente lesning: for verbatim teller max_rows LF-delimiterte linjer, og overskridelse er ERROR (§8, aldri stille trunkering). Åpen underbeslutning: en 40 MB transkripsjon passerer en linjegrense lenge før den blir håndterbar — trengs et valgfritt max_bytes i tillegg? Anbefaling: ja, valgfritt, men dette er verdt et eksplisitt operatørvalg.
  • source_query og provenance ellers: uendret — relativ sti, whitespace-kollapset (§5/§7).

ingested_at-determinismen bevares — med én felle å lukke eksplisitt

Verbatim-render leser ingen klokke. ingested_at forblir et eksplisitt påkrevd argument, stemplet ordrett; §5s regel er urørt. Fellen er en fristende «forbedring»: å stemple kildefilens mtime eller størrelse som ekstra provenance. Det MÅ forbys — git bevarer ikke mtime, så et golden-fixture ville gi ulikt resultat ved frisk utsjekking, og §11s byte-for-byte-krav brytes. Verdt en eksplisitt MUST NOT.

§11 — nye seams

  • Ny golden-case, konvensjon examples/ingest-golden-verbatim/. Fixturet MÅ inneholde et dokument med CRLF, en backtick-run ≥ 3, en ledende ---, og ikke-ASCII — så escape-reglene fryses av golden i stedet for å leve i prosa.
  • Ny load-bearing rad: testen MÅ feile når fence-bredden slutter å vokse med innholdets lengste backtick-run (dvs. når ingested innhold kan bryte ut av fencen).

3. Sikkerhetsobservasjon (til operatøren, ikke en spec-endring)

Dette er første gang dør A materialiserer innhold som er untrusted av opphav selv om transporten er lokal og first-party: root eies av operatøren, men en transkripsjon eller artikkel under root er tredjeparts tekst. Guard-adopsjonsplanen (2026-07-16 §4) definerte Trigger A som «når http/MCP-kildetypen bygges». En fritekst-connector treffer samme untrusted-grense uten å gå via http — så Trigger A bør trolig omformuleres fra transport til opphav, og kan dermed inntreffe tidligere enn planen antar. Flagget her, ikke besluttet.

4. Hva dette IKKE avgjør

  • Ingen spec-tekst er skrevet. §4, §5, §11 og §12 er urørt.
  • render-feltnavnet er et arbeidsnavn.
  • max_bytes-spørsmålet står åpent (§2).
  • Dør B (innboks) berøres ikke: den beholder materiale uten re-eksekverbar kilde-query.