Replace the unverified/absolute novelty statement with the defensible four-part-contract form, verified against a focused adversarial PyPI+GitHub survey (2026-07-15): - BRIEF §11: 'assumed, not verified' -> verified survey with sources. Names aig-guardian (real, query-time; blurs only the minimal-dep-library differentiator), GuardLLM (nearest neighbour, runtime hardening, no scan-before-persist / capability isolation / fail-secure), and ipi-scanner (orphaned placeholder repo, recorded for honesty not as prior art). - README: differentiator moved from 'library vs hosted/model' to the full four-part write-time contract. - PLAN §27-31: drop the unverifiable 'the first' superlative. Also promotes the v1.0 session plan (PLAN-v1.md) and the cross-model review (review-2026-07.md) into docs/ on the open/ mirror, referenced by PLAN.md's re-sequencing addendum.
21 KiB
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.
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_outputogokf.import_bundleskal surface EchoLeak-klassen (markdown-bilde/lenke/refdef/autolink/aktiv-HTML) som findings som materdisposition— uten å bryte report/mutasjon-separasjonen. - Scope-grense: rører IKKE lexicon/entropy/secret-logikk, contract, fence,
sanitize. Ingen ny runtime-dep (stdlib-only).
neutralizes muterende API beholdes uendret (bakoverkompatibelt). - Avhengigheter: ingen (kan starte først).
- Filer: nytt
src/llm_ingestion_guard/active_content.py(report-onlyscan_active_content(text, source) -> Report, OWASP LLM05); refaktorneutralize.pytil å dele regex-tabellen; editoutput.py(scan_outputsteg 6: kallscan_active_content); edit__init__.py(eksporterscan_active_content); edittests/test_showcase.py+tests/test_okf_showcase.py(plant EchoLeak-vektor); nytests/test_active_content.py. - TDD-plan (failing FØRST):
test_active_content.py::test_markdown_image_is_reported—scan_active_content("")inneholder labelactive:markdown-image, severity HIGH. (Rødt: modulen finnes ikke.)test_screen_output_reports_echoleak—screen_output("", PRESET_USER_UPLOAD).dispositioner QUARANTINE_REVIEW+ (ikke WARN).test_okf_import_flags_body_echoleak— bundle med markdown-bilde i body → aggregat ≠ WARN.- Minimal impl: del regexene, report-only pass, wire i
scan_output. - 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.pypasserer 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.
- «neutralize og den nye detektoren kan dele samme regex-tabell uten
atferdsendring i neutralize.» Test: eksisterende
- 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('', 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_bundleskal 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 skanneindex.md-bodyen (lest først, høyest-prioritert injeksjonsflate). Behold shadow-rejektet i upload/front-end-konteksten. - Scope-grense: rører IKKE
validate_concept_paths 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_graphuendret); edittests/test_okf.py+tests/test_okf_showcase.py(nytt: legitimt bundle medindex.md/log.mdADMITer; injeksjon iindex.mdFANGES; shadow-upload i front-end REJECTer fortsatt). - TDD-plan (failing FØRST):
test_okf.py::test_legit_index_and_log_admit— bundle {index.md, log.md, tables/users.md} (rene) → aggregat WARN, ingenerrorpå index/log. (Rødt i dag: FAIL_SECURE, verifisert i review-proben.)test_okf.py::test_injection_in_index_body_is_caught— injeksjon iindex.md-body → concept-report haroverride:ignore-previous. (Rødt i dag: findings=[].)test_okf_inbox_uploads.py::test_reserved_name_upload_is_rejected— MÅ fortsatt REJECTe (front-end shadow-reject bevart).- 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-rootindex.md(spec). Hvis body-skann kjørerparse_frontmatterpå en index.md kan strict-gaten tripp. Test:test_index_with_okf_version_frontmatter_admits— skann index.md-body, ikke reject på lovligokf_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_egressover dekodet base64-plaintext, så en base64-innpakket credential fanges av LLM02-gaten. - Scope-grense: kun
output.pydecode-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 tilscan_secret_egress(blob.decoded)meddecoded:egress:*-relabel); edittests/test_output.py; edit README honest-limits (restgap: hex-innpakket). - TDD-plan:
test_output.py::test_base64_wrapped_secret_is_caught— output med base64(AWS-nøkkel, fragment-bygget gitleaks-safe) → finding-labeldecoded:egress:aws-access-key-id. (Rødt i dag — Probe 3 bekreftet [].)- Minimal impl: i decode-rescan-løkken, kjør også
scan_secret_egresspåblob.decoded, relabeldecoded:<label>, bær blob-offset. - Restgap-test: hex-innpakket secret er FORTSATT ikke fanget → dokumenter som honest-limit (bevisst avgrensning, ikke stille miss).
- Nøkkelantakelser (+ test):
- «evidence bærer aldri secret-verdien, også for den dekodede varianten.» Test:
assert nøkkel-fragmentet ikke i
finding.evidence.
- «evidence bærer aldri secret-verdien, også for den dekodede varianten.» Test:
assert nøkkel-fragmentet ikke i
- Verifisering:
PYTHONPATH=src .venv/bin/pytest tests/test_output.py→ N grønne (N = før +2).PYTHONPATH=src .venv/bin/python /path/to/probe.py(Probe 3 fra reviewen) → base64-linjen viser nådecoded:egress:aws-access-key-id.
Session C — Novelty-survey + README/BRIEF-reframe (injisert av review MAJOR #2)
- Mål: erstatt det uverifiserte/absolutte novelty-claimet med den forsvarbare kompositt-kontrakt-formen; oppdater BRIEF §11 fra «assumed» til verifisert-med- avgrensning.
- Scope-grense: docs only (
BRIEF.md,README.md). Ingen kodeendring. Ingen ny absolutt novelty-setning. - Avhengigheter: ingen.
- Filer: edit
docs/BRIEF.md§11; editREADME.mdposisjonering; ev. editdocs/PLAN.md-posisjonering (§19-45) — men PLAN er live-plan, la Opus avgjøre om den røres eller kun refereres. - TDD-plan (docs — verifiserbar via review, ikke pytest): ingen failing test;
i stedet en verifiseringslogg i BRIEF §11 som lister
ipi-scanner+aig-guardianmed URL og hvorfor de ikke motbeviser kompositt-kontraktet. - Nøkkelantakelser (+ test):
- «ingen bibliotek pakker det fulle firdelte kontraktet som minimal-dep kode.» Test: gjenta PyPI/GitHub-surveyen (søk «RAG ingestion security», «write-time prompt injection», «ipi-scanner», «ingest guard»); bekreft at ingen ny kandidat dekker karantene+isolasjon+scan-før-persist+fail-secure samlet. Merk dato.
- Verifisering:
grep -n "assumed, not verified" docs/BRIEF.md→ tom (claimet ikke lenger uverifisert);grep -niE "query-time.*or hosted|the only" README.md→ ingen absolutt formulering igjen.
Session D — Kalibrerings-konsolidering (injisert av review Akse 4; Node-prereq)
- Mål: samle alle kalibrerings-konstanter (entropy-gulv, MAX_SCAN_CHARS,
rot13-min, cognitive-load-lengder, disposition-rangeringer) i én dokumentert flate
calibration.py, så Node-porten kan speile nøyaktig samme tall. - Scope-grense: ren refaktor — null atferdsendring. Ingen terskeljustering (det er en separat, senere kalibrerings-oppgave). Kun flytting + navngiving.
- Avhengigheter: bør komme ETTER A (så aktivt-innhold-severities også bor der).
- Filer: nytt
src/llm_ingestion_guard/calibration.py; editentropy.py,lexicon.py,disposition.py,active_content.pytil å importere derfra. - TDD-plan:
- Snapshot-test FØRST: kjør hele suiten, lagre at 321(+delta) er grønne.
- Flytt konstanter; importer.
- Regresjon: identisk testresultat (ingen ny/endret assertion) beviser null atferdsendring.
- Nøkkelantakelser (+ test): «flyttingen endrer ingen verdi.» Test: hele
suiten grønn uendret; en eksplisitt
test_calibration.pyasserter de konkrete tallene (5.4/128, 5.1/64, 4.7/40, 1_000_000, 40, 2000/2500) som en frossen kontrakt Node-porten deler. - Verifisering:
PYTHONPATH=src .venv/bin/pytest→ samme antall grønne som før sesjonen (ingen delta i test-count utovertest_calibration.py).
Session E — Docs/versjons-sync + SECURITY/CONTRIBUTING + honest-limits (injisert av review MINORs)
- Mål: fjern versjons-drift; legg til manglende åpen-kildekode-artefakter; oppdater honest-limits med de residualene reviewen avdekket.
- Scope-grense: docs/metadata only. Ingen kodeendring.
- Avhengigheter: etter A/B/C (så honest-limits reflekterer faktisk tilstand).
- Filer:
README.md(badgetests-275→faktisk N; statusv0.1→v1.0; honest-limits: HIGH-i-trusted-prosa-residual, base64/hex-secret-restgap, quarantine-floor-note);docs/BRIEF.md:6-7(fjern «No code yet»); nyttSECURITY.md(disclosure-policy, Forgejo-kontakt); nyttCONTRIBUTING.md. - TDD-plan: ingen pytest; verifiser via grep-sjekker under.
- Nøkkelantakelser (+ test): «badge-tallet matcher faktisk suite.» Test:
badge-N ==
pytest-output. - Verifisering:
PYTHONPATH=src .venv/bin/pytest -q | tail -1→ «N passed»;grep -n "tests-${N}_passing" README.mdtreffer.grep -niE "v0\.1|275_passing|No code yet" README.md docs/BRIEF.md→ tom.test -f SECURITY.md && test -f CONTRIBUTING.md→ exit 0.
Session F — .pdf-beslutning (operatør-gate; to gjensidig utelukkende spor)
- Mål: avklar det siste format-gapet. Anbefaling: konsesjon (F1).
- Rasjonale for konsesjon: front-end er en dev-scoped showcase, ikke shippet
kode; README honest-limits sier allerede pdf-ekstraksjon er upålitelig; å legge
til
reportlabKUN for å lage white-on-white-test-fixtures er uforholdsmessig (to nye dev-deps for et demo-format). Binærlag-carriers (OCR/font-stego) er uansett eksplisitt out-of-scope. Konsesjon svekker ikke v1.0. - Spor F1 (anbefalt) — Konseder
.pdfpermanent:- Filer:
README.mdhonest-limits (.pdf= bevisst honest-limit, ikke TODO);docs/PLAN.md§247-tabell (marker.pdf-raden «conceded»). - Verifisering:
grep -n "pdf" README.mdviser konsesjon, ikke «known gap».
- Filer:
- Spor F2 (kun hvis operatør vil ha .pdf) — Bygg
.pdf-slice:- Operatør-gate FØRST: bekreft
pypdf(lesing) +reportlab(skrive white-on-white fixtures) som nye[dev]-deps — aldri core. Kjerne-invariantdependencies=[]MÅ holde. - Filer:
pyproject.toml([dev]+=pypdf,reportlab);tests/inbox_frontend.py(_extract_pdf+ dispatch.pdf);tests/test_okf_inbox_uploads.py(slice 2i:_make_pdfmed white-on-white + normal-tekst injeksjon, detach-proof). - TDD:
test_pdf_whiteonwhite_injection_is_caught(rødt) →_extract_pdf→ grønt;test_pdf_detach_proof. - Verifisering:
PYTHONPATH=src .venv/bin/pytest tests/test_okf_inbox_uploads.py→ +N grønne;python -c "import tomllib,pathlib; d=tomllib.loads(pathlib.Path('pyproject.toml').read_text()); assert d['project']['dependencies']==[] and 'pypdf' in ' '.join(d['project']['optional-dependencies']['dev'])"→ exit 0.
- Operatør-gate FØRST: bekreft
- Avhengigheter: uavhengig; kan gjøres når som helst før G.
Session G — v1.0 freeze + release (FRYSER Python-surfacen — Node-prereq)
- Mål: shippe v1.0.0; fryse den offentlige surfacen som porten oversetter.
- Scope-grense: ingen ny feature. Kun versjons-bump, CHANGELOG, tag, push.
- Avhengigheter: A, B, C, D, E, F ferdig (alle review-funn lukket/konsedert).
- Filer:
pyproject.toml(version = "1.0.0",Development Status :: 5 - Production/Stable);README.mdbadge;__init__.py__version__;CHANGELOG.md([1.0.0]-entry som lister A-F). - TDD-plan: ingen ny test; hele suiten grønn er release-gaten.
- Nøkkelantakelser (+ test): «alle versjonsreferanser er synkrone.» Test:
grep alle fire filer for versjonsstreng, bekreft
1.0.0overalt. - Verifisering:
PYTHONPATH=src .venv/bin/pytest→ alle grønne.grep -rn "1\.0\.0" pyproject.toml README.md src/llm_ingestion_guard/__init__.py CHANGELOG.md→ treffer i alle fire;grep -rn "0\.2\.0" …→ ingen dangling ref.git tag v1.0.0+ push tilopen/(durabelt autorisert). STATE.md røres ikke av tag (local-only).
TRACK 2 — Node/TS-port (stream 3). Starter etter Session G.
Ryggrad: en delt parity-fixture (fixtures/parity/*.json: input → forventede labels/severities) som BÅDE Python og TS må tilfredsstille. Uten den porter du et
bevegelig mål. Den delte injection_lexicon.json splittes aldri (PLAN §13.3).
Session P0 — Parity-fixture-ryggrad + TS-scaffold
- Mål: etabler golden-fixtures + TS-prosjektskjelett; Python-impl asserter mot fixtures.
- Scope-grense: ingen TS-detektor-logikk ennå; kun scaffold + fixtures + Python- parity-test.
- Avhengigheter: Session G (frossen surface) + D (kalibrering konsolidert).
- Filer:
fixtures/parity/{sanitize,lexicon,entropy,output,okf}.json; nytests/test_parity_fixtures.py(Python-siden);node/package.json,node/tsconfig.json,node/vitest.config.ts. - TDD-plan:
test_parity_fixtures.pykjører hvert fixture-input gjennom Python- impl og asserter forventede labels (rødt til fixtures skrives, så grønt). - Nøkkelantakelse (+ test): «fixtures fanger den faktiske Python-atferden.» Test: Python-parity-test grønn.
- Verifisering:
PYTHONPATH=src .venv/bin/pytest tests/test_parity_fixtures.py→ grønt;cd node && npm i && npx vitest run→ tomt/skjelett kjører.
Session P1 — report + severity + lexicon-loader (TS)
- Mål: TS-typene + loader som leser SAMME
injection_lexicon.json. - Avhengigheter: P0.
- Filer:
node/src/report.ts,node/src/lexicon-loader.ts,node/test/*.test.ts. - TDD: vitest: loader kompilerer alle mønstre; antall == Python
load_lexicon(). - Nøkkelantakelse (+ test): «JS-regex-motoren aksepterer alle mønstrene uten flag-oversettelsestap.» Test: hver pattern kompilerer; parity på pattern-count.
- Verifisering:
cd node && npx vitest run test/lexicon-loader.test.ts→ grønt; count ==PYTHONPATH=src .venv/bin/python -c "from llm_ingestion_guard.lexicon import load_lexicon; print(len(load_lexicon()))".
Session P2 — sanitize + entropy + normalize (TS)
- Avhengigheter: P1.
- Filer:
node/src/sanitize.ts,node/src/entropy.ts,node/src/normalize.ts. - TDD: vitest kjører
fixtures/parity/{sanitize,entropy}.json→ samme labels. - Nøkkelantakelse (+ test): «base64/rot13/homoglyph-primitiver gir bit-lik output i JS og Python.» Test: parity-fixtures grønne begge sider.
- Verifisering:
cd node && npx vitest run(sanitize+entropy) grønt mot fixtures.
Session P3 — lexicon.scan + variant-set (TS)
- Avhengigheter: P2.
- Filer:
node/src/lexicon.ts. - TDD:
fixtures/parity/lexicon.json(raw/normalized/folded/rot13-varianter) → samme dedupede labels. - Nøkkelantakelse (+ test): «dedup-by-id og variant-rekkefølge matcher.» Test: parity-fixture med multi-variant-treff.
- Verifisering:
cd node && npx vitest run test/lexicon.test.tsgrønt.
Session P4 — output + active_content + neutralize + disposition (TS)
- Avhengigheter: P3. (Inkluderer aktivt-innhold fra Session A.)
- Filer:
node/src/output.ts,node/src/active_content.ts,node/src/neutralize.ts,node/src/disposition.ts. - TDD:
fixtures/parity/output.json+ disposition-tabell-fixtures. - Nøkkelantakelse (+ test): «fail-closed + carrier/CRITICAL any-tier + compound matcher Python.» Test: disposition-parity-fixtures inkl. transform_failed-caset.
- Verifisering:
cd node && npx vitest run(output+disposition) grønt mot fixtures.
Session P5 — contract-asserters + top-level bookends (TS)
- Avhengigheter: P4.
- Filer:
node/src/contract.ts,node/src/index.ts(prepareInput/screenOutput). - TDD: tool-carrying request raiser; credential-leak raiser; happy path passerer.
- Verifisering:
cd node && npx vitest run test/contract.test.tsgrønt.
Session P6 — OKF-adapter (TS)
- Avhengigheter: P5.
- Filer:
node/src/okf.ts. - TDD: port
_poisoned_bundle/_clean_bundlefratest_okf_showcase.pysom fixture; samme aggregat-disposition + link-graf. - Nøkkelantakelse (+ test): «strict frontmatter-parser gir samme reject-set.» Test: OKF-parity-fixture (T2/T3/T4/T5a/dangling).
- Verifisering:
cd node && npx vitest run test/okf.test.tsgrønt.
Session P7 — Parity-CI + Node-README + versjons-sync + tag
- Avhengigheter: P6.
- Filer:
node/README.md,node/package.json(versionsynk med Python-linjen), CI-hook som kjører begge suiter mot samme fixtures. - Verifisering: både
PYTHONPATH=src .venv/bin/pytestogcd node && npx vitest rungrønne mot sammefixtures/parity/; versjonsstreng synk; tag + push tilopen/.
Avhengighetsgraf + anbefalt sekvens
A ─┐
A2 ─┤ (A, A2, B, C uavhengige; kjør i den rekkefølgen som passer)
B ─┤
C ─┤
├─► D ─► E ─┐
F ─┘ ├─► G (v1.0 FRYS) ─► P0 ─► P1 ─► P2 ─► P3 ─► P4 ─► P5 ─► P6 ─► P7
│
(F operatør-gated, uavhengig, må være lukket/konsedert før G)
- A, A2, B, C kan tas i valgfri rekkefølge (uavhengige). Start med A eller A2 (begge review-MAJOR; A = unsafe admit, A2 = over-block + uskannet index.md).
- D etter A (så aktivt-innhold-severities bor i
calibration.py). - E etter A/B/C (honest-limits skal reflektere faktisk tilstand).
- F når som helst før G; anbefalt spor F1 (konsesjon). Dep-tillegg (F2) er operatør-gate uansett.
- G er frysepunktet. Node-porten (P0-P7) starter FØRST etter G, ellers porter du et bevegelig mål. P0 avhenger også av D (kalibrering konsolidert).
- P0-P7 er sekvensielle (hver bygger på forrige), med parity-fixture som felles kontrakt.
.pdf-beslutningen sitter i F (før G). Python-frysen sitter i G (før P0).