docs(plan): the content gate and the honesty sentence, on a track that does not touch the freeze

The demo shows "download and run". Implying you can point this at your own sources and build a
knowledge base claims three things the code does not carry -- and A5 (the code may not claim more
than it does) binds the presenter too, not just the source.

Measured first, and one measurement changed the plan: the guard is NOT v0.2 alpha. That figure came
from our own 2026-07-16 inclusion plan, which is a premise rather than a fact. It is v0.3.4, seven
published tags, `dependencies = []` -- stdlib only. Our okf pin (v0.3.2) declares no dependencies
either, so the guard is not coupled to it, and the 0.3.5-vs-0.4.0 release argument concerns the
release AFTER v0.3.4. Adoption moved from risky to tractable on that one reading.

The three claims, made precise: the demo bundle was hand-curated (honesty), the ingest path writes
unscanned (buildable), and the generic bundle factory does not exist (deferred at O1, not buildable
in four days). Two close with code, one with a sentence.

The two tracks are separated on a measured fact: `simulation.py` does not import `ingest`, so Door A
work cannot disturb what Wednesday freezes. Criterion 5 is the one that proves it -- the walkthrough
must stay byte-identical.

Four decisions are named as decisions rather than settled silently: which policy preset, fail-closed
versus flag-and-write, where the guard's report lands in provenance, and keeping `--strict`
meaningful across a seam that ships no py.typed. The honesty paragraph is written in BOTH variants
up front, so Wednesday is an observation and not a judgement call on stage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XoHJCKBTjFKcjsfEQyGbzh
This commit is contained in:
Kjell Tore Guttormsen 2026-08-06 19:49:13 +02:00
commit 295e9665fe

View file

@ -0,0 +1,141 @@
# Innholdsgate og ærlighet — plan fram til demoen 13. august
> **Utløser.** Demoen viser «last ned → kjør». Sier vi, eller antyder vi, at man kan peke systemet
> mot sine egne kilder og bygge en kunnskapsbase, påstår vi noe koden ikke bærer. Det er A5-brudd i
> muntlig form — koden får ikke påstå mer enn den gjør, og det gjelder også presentatøren.
>
> **Kjører parallelt med demo-uke-planen** (`2026-08-06-demo-uke-plan.md`), ikke i stedet for den.
> Den planen eier kjørestien og fryser den onsdag 12. Denne eier Door A, som ikke er på kjørestien.
## 0. Målte premisser (verifisert 2026-08-09, ikke gjenbrukt fra eldre dokument)
| Påstand | Målt | Kommando |
|---|---|---|
| Guarden er «v0.2 alpha» | **FEIL**`v0.3.4`, syv publiserte tagger | `git -C ~/repos/llm-ingestion-pipeline-security tag` |
| Guarden er tung å adoptere | **FEIL**`dependencies = []`, stdlib-only | `pyproject.toml` i guard-repoet |
| Vår okf-pin drar inn guarden | **NEI**`v0.3.2` har `dependencies = []` | `git show v0.3.2:pyproject.toml` |
| Vi må vente på release-nummer-striden | **NEI** — den gjelder utgivelsen *etter* `v0.3.4` | guard-repoets STATE |
| Door A er på demoens kjøresti | **NEI**`simulation.py` importerer ikke `ingest` | `grep "^from portfolio_optimiser" simulation.py` |
| Ingesten skanner innhold i dag | **NEI**`ingest.py:20-25` sier det selv | modul-docstring |
Den første raden er grunnen til at denne planen finnes: inkluderingsplanen fra 16. juli
(`2026-07-16-llm-ingestion-guard-inclusion.md`) sier «v0.2 (alpha)», og det premisset gjorde
adopsjonen urimelig risikabel. Målt er den tre mindre-versjoner nyere og uten avhengigheter.
**Den planen skal merkes som utdatert på dette punktet, ikke slettes.**
## 1. De tre påstandene, presist — og hvilke som kan lukkes
Formuleringen «tre ting som ikke holder» var upresis. Målt er de:
| # | Påstand | Status | Til torsdag? |
|---|---|---|---|
| P1 | Bundelen i demoen ble laget av pipelinen | **Nei** — den er håndkuratert i commons | **Ærlighet.** Kan ikke bygges bort |
| P2 | Ingesten skanner innhold før skriving | **Nei**`materialize` skriver det den får | **JA — bygges** |
| P3 | Det finnes en generisk bundle-fabrikk (`okf-toolkit`, T0) | **Nei** — eget repo, finnes ikke, utsatt ved O1 | **Ærlighet.** Nytt repo på fire dager er utelukket |
**To av tre lukkes med kode, én med en setning.** Å forsøke P3 før torsdag ville brutt scope-vaktet
(O1 er en tatt beslutning) og satt demoen i spill for en gevinst ingen tilhører etterspør.
## 2. To spor, med vilje adskilt
**Spor A — demoen.** Eies av demo-uke-planen. Fryses onsdag 12. Rører ikke `ingest.py`.
**Spor B — innholdsgaten.** Denne planen. Rører KUN `ingest.py` + `pyproject.toml` + ny test.
Adskillelsen er ikke kosmetisk: `simulation.py` importerer ikke `ingest` (målt), så Spor B kan
lande etter frysen uten å røre det som fryses. Faller Spor B, står demoen uendret — den mister kun
retten til å si at gaten finnes, som er nøyaktig statusen i dag.
## 3. Dagsplan
**Søndag 9. / mandag 10. — Spor B, byggingen.**
Wire `llm_ingestion_guard` inn i `ingest.materialize` (`ingest.py:167`), som er repoets ENE
skrivepunkt på Door A. Fire beslutninger må tas, ikke antas — de er listet i §4.
**Tirsdag 11. — GO/NO-GO på commons (Spor A, uendret).**
Spor B rører ikke commons; de to konkurrerer ikke om samme fil.
**Onsdag 12. — generalprøve + frys (Spor A).**
Spor B skal være committet FØR frysen hvis den skal nevnes i demoen, men den er ikke en
frys-betingelse. Er den ikke ferdig: si status som den er, ikke som den var planlagt.
**Torsdag 13. — demo.** Ærlighets-avsnittet leses opp som skrevet i §5.
## 4. Byggingen — fire beslutninger som må måles, ikke antas
1. **Hvilken policy-preset?** Guarden eksporterer `PRESET_TRUSTED_SOURCE` og `PRESET_USER_UPLOAD`.
Door A henter fra eksterne kilder oppgitt i et manifest — det er ikke det samme som en fil en
bruker laster opp, og ikke det samme som en kilde vi eier. **Les begge presetene i installerte
bytes før du velger.** Feil preset gir enten en gate som slipper alt gjennom eller en som gjør
ingest ubrukelig.
2. **Fail-closed eller flagg-og-skriv?** Repoets presedens er entydig — `promote_verdict` er
fail-closed, `write_concept_file` validerer og reparerer aldri. **Forslag: avvist dokument
skrives IKKE**, og `materialize` raiser en typet feil på linje med `IngestStampError`. Motargument
som må avvises eksplisitt før valget: en bundle der ett av femti dokumenter avvises blir en halv
bundle. Avgjør om avvisning er per dokument eller per bundle.
3. **Hvor havner guardens `Report`?** Provenance er en invariant her. Funnene skal ikke kastes —
de hører i `ingest_manifest`, ved siden av eierskaps-stempelet. Avgjør felt-navn og form.
4. **`mypy --strict` over sømmen.** Guarden shipper ingen `py.typed`, så symbolene ankommer som
`Any`. okf løser det med en `[[tool.mypy.overrides]]`-blokk (`ignore_missing_imports = true`) OG
en adapter som koerserer hver verdi til konkret type. **Kopier begge deler** — kun overriden ville
gjort `--strict` meningsløs på vår side av sømmen.
**Avhengigheten pinnes som okf gjør det**, git-kilde + tagg:
`llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", rev = "v0.3.4" }`.
Aldri en flytende ref på en sikkerhetskomponent.
**Load-bearing-krav (ufravikelig).** En test som bare er grønn beviser ingenting. Testen skal:
- mate `materialize` et fikstur-dokument med kjent injeksjons-innhold, og kreve at det IKKE skrives;
- ha en kontroll: et rent dokument som skrives uendret (ellers kan ikke testen skille «gaten virker»
fra «ingenting skrives»);
- måles RØD ved minst tre mutasjoner: detach gate-kallet · gjør avvisningen til en advarsel ·
bytt til en preset som slipper alt gjennom.
- Måles mot HELE suiten, med restaurering fra scratchpad + `shasum -c`.
## 5. Ærlighets-avsnittet — teksten som faktisk sies på scenen
Erstatter ingenting i demo-uke-planens §1; **kommer i tillegg**, og leses opp:
> «To ting om innholdet dere ser. For det første: denne kunnskapsbasen er laget for hånd, ikke
> produsert av systemet. Det finnes en ingest-vei som henter eksterne kilder inn i formatet, men
> eksempelet her gikk ikke gjennom den, og den generiske fabrikken som skulle laget slike baser er
> ikke bygget — den er bevisst utsatt.
> For det andre, om sikkerhet: [HVIS SPOR B ER LANDET] ingest-veien skanner nå innholdet før det
> skrives, med en egen sikkerhetskomponent, slik at forgiftet kildeinnhold ikke havner i basen.
> [HVIS IKKE] ingest-veien skriver i dag det den får, uten innholds-skanning. Gaten er planlagt og
> komponenten finnes, men den er ikke koblet inn ennå. Peker dere dette mot upålitelige kilder i
> dag, er det deres ansvar, ikke systemets.»
De to variantene er skrevet på forhånd med vilje: valget mellom dem er en **observasjon** onsdag, ikke
en vurdering torsdag morgen.
## 6. Verifisering — konkrete kriterier
1. `uv sync` grønn med guarden som deklarert dep; `uv run python -c "import llm_ingestion_guard as g; print(g.__version__)"``0.3.4`.
2. Ny load-bearing-test grønn; **tre mutasjoner målt røde** mot hele suiten, med kontroll.
3. `uv run pytest -q` grønn — og **antallet skal ha økt**, ikke bare være grønt.
4. `uv run ruff check .` + `uv run mypy src` rene, med `--strict` fortsatt meningsfull over sømmen
(adapter, ikke bare override).
5. **Demoens kjøresti byte-uendret:** `uv run python -m portfolio_optimiser.simulation` gir fortsatt
åtte merkede steg og identisk stdout over to kjøringer. Dette er Spor B's viktigste kriterium —
det beviser at gaten ikke lekket inn i det som fryses.
6. `git diff --stat``shared/examples/` → tomt. Goldenene er load-bearing.
## 7. Hva denne planen IKKE gjør
- **Bygger ikke `okf-toolkit` (T0/D-G).** O1 står. Håndteres av §5.
- **Adopterer ikke okf `v0.4.0+`.** Vi står på `v0.3.2` gjennom demoen; v0.4.0+ gjør guarden til
hard runtime-dep, og den koblingen tas etter demoen, ikke før.
- **Rører ikke commons.** Den har sin egen frist 11. august og sin egen økt.
- **Rører ikke `docs/presentasjon-portfolio-optimiser.html`** — eies av en annen sesjon.
- **Wirer ikke guarden på bundle-LESE-stien.** Gaten sitter ved skriving (Door A). En bundle som
allerede ligger i repoet er kuratert av et menneske; å skanne den ved lesing er en annen
beslutning med en annen begrunnelse, og den er ikke tatt.
## 8. Risiko
| Risiko | Utslag | Tiltak |
|---|---|---|
| Preset-valget viser seg feil under bygging | Gate som slipper alt, eller blokkerer alt | §4.1: les begge i installerte bytes FØR valg; testen har både positiv og kontroll |
| Guarden avviser demo-bundelen ved en feil | Panikk onsdag | Gaten sitter kun på Door A; demo-bundelen leses, ikke ingestes. Kriterium 5 fanger lekkasje |
| Spor B tar lengre tid enn ventet | Ett løfte mindre i demoen | §5 har ferdigskrevet NEI-variant; ingen improvisasjon på scenen |
| `mypy --strict` blir meningsløs over sømmen | Stille typetap | §4.4: adapter OG override, som okf gjorde |