# Beslutningsgrunnlag — Session G, v1.0-frysen **Målt:** 2026-08-13, mot `HEAD` = `2466d26` (v0.7.0 + tre test-commits). **Hva dette er:** underlaget for én operatørbeslutning — skal Python-surfacen fryses som `1.0.0`. **Hva dette ikke er:** beslutningen. Ingen tag, ingen versjonsbump, ingen frosset surface er utført i økten som skrev dette. Ingen kode er endret. Alle tall her er produsert av en kommando i samme økt; verifiseringsloggen står nederst. `docs/PLAN-v1.md:231` er gate-teksten dette måles mot. --- ## 1. Rammen som gjør gaten tellbar `docs/PLAN-v1.md:24` sier det selv: **v1.0 er primært et governance-løfte under semver.** Det er ikke en påstand om at biblioteket er ferdig, og ikke en påstand om at de 35 begrensningene er borte. Uten den rammen leses `docs/LIMITATIONS.md` som 35 blokkere, og dokumentet argumenterer mot sin egen konklusjon. Med rammen blir spørsmålet tellbart: **hvor mange av de 35 kan bare lukkes ved å endre betydningen, formen eller medlemskapet til noe i `__all__`?** Bare de blokkerer, fordi bare de tvinger `2.0.0`. Tre bøtter, én per begrensning: - **(a)** lukkes med kalibrering, lexicon-data eller et predikat → ikke-brytende - **(b)** lukkes additivt (ny funksjon, nytt felt, ny implementasjon i en eksisterende søm) → minor - **(c)** lukkes bare ved å endre et eksportert symbols betydning eller form → **major** - **(–)** kan ikke lukkes i det hele tatt (permanent konsesjon, scope-grense, ren måling) En fjerde akse er **uavhengig av semver og må ikke blandes med den**: hvilke begrensninger som, når de lukkes, fyrer konsument-varslingsplikten i `docs/PLAN-v1.md:419`. Et løfte kan brytes av en endring som er helt lovlig under semver. Den aksen er merket separat under. --- ## 2. Gatens literalkrav — hva som faktisk er sant ### 2.1 Session A–F ferdig — **OPPFYLT** F var den siste operatør-gaten, og den landet som anbefalt spor F1 (konsesjon), ikke som TODO: `docs/PLAN.md:309` fører `.pdf`-raden som **conceded**, `docs/PLAN.md:323` og `README.md:226` beskriver den som «refused as an unsupported format», ikke som et kjent hull. A, A2, B, C, D, E er ute i tagger (`v0.3.0`–`v0.7.0`, tolv tagger totalt). ### 2.2 Suite, dekningsmatrise og dokumenttall — **OPPFYLT, re-målt** | påstand | kilde | målt i dag | |---|---|---| | 792 tester | STATE | `792 passed in 13.11s` | | 129/129 klasser | STATE/README | `Caught classes: 129/129 demonstrated (recall 100%)` | | 6/6 dokumenterte gap | STATE | `Documented gaps: 6/6 still hold as documented` | | 35 begrensninger | README | `grep -c '^- \*\*'` → `35` | Ingen avvik. `CHANGELOG.md` `[Unreleased]` er tom («Nothing yet»), så det finnes ingen ushippet atferd som frysen ville binde uten å ha beskrevet. ### 2.3 Ni versjonsflater synkrone — **OPPFYLT på 0.7.0** Alle ni current-state-flatene `docs/PLAN-v1.md:277` navngir står på 0.7.0: `pyproject.toml:7`, `__init__.py:66`, README badge/status/install-pin (`:5`, `:36`, `:46`), `SECURITY.md:9` («pre-1.0 (`0.7.x`, alpha)»), `docs/BRIEF.md:6`, `CLAUDE.md:14`, `docs/ADOPTION-BRIEF.md:7`/`:143`. Sorteringen current-state vs. proveniens er ikke gjort her — den hører til selve release-utførelsen, og `docs/PLAN-v1.md:290` sier den kommer før første redigering. ### 2.4 «Første ekte integrasjon grønn» — **IKKE OPPFYLT ETTER BOKSTAVEN, dekket etter hensikten** Dette er gatens tyngste krav og det eneste som ikke lar seg avgjøre med en kommando i dette repoet. Måling av hva som faktisk foreligger, lest fra coord-arkivet: | dato | hva okf faktisk gjorde | guard-versjon | |---|---|---| | 2026-07-26 | 0.3.1-målingen **utsatt** (kvote) | — | | 2026-07-31 | «signalet er mottatt og rutet, kjøringen er ikke gjort» | — | | 2026-08-02 | scratch-venv, resolvet versjon bekreftet via `importlib.metadata`, ingen nye avvik | **0.3.4** | | 2026-08-12 | differensial på egen dør, begge dører, Python 3.11 **og** 3.14, tagger resolvet | **0.3.4 → 0.6.1** | **Kjøringen gaten ber om ordrett — fixture-settet grønt mot `v0.3.1` — ble aldri gjort.** Den ble utsatt to ganger og deretter overhalt av virkeligheten: okf gikk rett på 0.3.4 og senere på en 0.3.4→0.6.1-differensial. Målt mot gatens **hensikt** (`docs/PLAN-v1.md:19-25`: bevisbyrden skal komme utenfra, og release-hygienen skal ha overlevd én ekte syklus) er det som foreligger sterkere enn det som ble bestilt: en differensial på konsumentens egen dør, på to Python-versjoner, med taggene resolvet — som dessuten **korrigerte en påstand vi hadde publisert** («raw-html loosening is 1 of 3 forms»). En fixture-kjøring mot en tagg vi valgte ville ikke ha gjort det. **Restgapet er presist og lite: ingen har kjørt 0.7.0-runtimen.** Nyeste guard-versjon noen integrator har eksekvert er **0.6.1**. 0.7.0-deltaet er bærersplitten og no-URL-narrowingen. Det deltaet har én ekstern kryss-sjekk fra en annen vinkel: `llm-security-commons` bygde klassifikatoren opp igjen fra sin egen JSON, uten import fra pakken vår, og differensialtestet mot `active_tag_class` over 42 probe-tagger med **0 uenigheter**. Det validerer klassifikatoren som *data*, ikke runtime-atferden. ### 2.5 Surface-deltaet siden sist eksternt pinnede versjon — **MÅLT** `git diff v0.3.4..HEAD -- src/llm_ingestion_guard/__init__.py`, per symbol: | symbol | endring siden 0.3.4 | semver-klasse | målt eksternt? | |---|---|---|---| | `Risk` | **lagt til** (0.5.0, aksesplittelsen) | additiv | nei | | `DEFAULT_ACTION_MAP` | **lagt til** (0.5.0) | additiv | nei | | `assert_within_input_cap` | **lagt til** (0.4.0) | additiv | nei | | `OversizeInputError` | **lagt til** (0.4.0) | additiv | nei | | alle øvrige 42 | uendret navn og signatur | — | 0.3.4 / 0.6.1 | **Ingen symboler er fjernet eller omdøpt siden 0.3.4.** Verifisert med `git diff v0.3.4..HEAD -- __init__.py | grep '^-'`: de eneste slettede linjene er versjonsstrengen, en kommentar, og en `__all__`-linje som ble skrevet om for å *legge til* navn. Hele surface-veksten er additiv. `__all__` er ikke hele den frosne flaten — `okf` eksporteres som navnerom, så signaturene der fryses også. Målt separat (`git diff v0.3.4..HEAD -- okf.py | grep -E '^[-+](def |class )'`): én endring, `link_graph(bundle)` → `link_graph(bundle, max_scan_chars=MAX_SCAN_CHARS)`. En keyword-parameter med default, bakoverkompatibel for enhver eksisterende kaller — men den gjør `okf.link_graph`s trunkeringsgrense til del av kontrakten fra 1.0.0. Det som *har* flyttet seg er atferd inne i allerede eksporterte funksjoner — elleve commits over `src/`, hvorav de som endrer utfall er: input-cap-refusjonen (0.4.0), aksesplittelsen (0.5.0), rå-HTML-narrowingen (0.6.0), ZWJ-kontekstfiksen (0.6.1) og bærersplitten (0.7.0). Det er den ærlige formuleringen av risikoen ved å fryse nå: **formen er stabil, atferden har beveget seg i fem strekk, og fire eksporterte symboler har aldri vært gjennom en ekstern kjøring.** --- ## 3. De 35 begrensningene, bøttet Hver rad navngir det eksporterte symbolet lukkingen ville røre, eller «ingen». Linjenummer er `docs/LIMITATIONS.md`. **⚠️ = lukking fyrer konsument-løfte 1.** | # | linje | begrensning (kort) | rører | bøtte | |---|---|---|---|---| | 1 | :7 | strukturell uløselighet i tekstlaget | ingen | – | | 2 | :11 | lone HIGH i trusted prosa → WARN | `PRESET_TRUSTED_SOURCE`, `Policy` | a | | 3 | :17 | karantenegulvet er no-op under upload-preset | `Policy.quarantine_default` | a | | 4 | :26 | semantisk/faktisk poisoning usynlig | `SourceGroundingCheck` (søm finnes) | b | | 5 | :30 | adversarial-ML-evasion, tokenizer-mismatch | ingen | – | | 6 | :33 | dormant / broken-link-injeksjon | `okf.link_graph` | b | | 7 | :38 | OKF reserverte filer (`index.md`/`log.md`) | `okf.import_bundle` | – (avgjort i A2) | | 8 | :43 | én-nøkkels blokksekvens misparses stille | `okf.import_bundle` | b ⚑ | | 9 | :63 | T2 begrenser import, ikke emisjon | `okf` | b | | 10 | :68 | OKF v0.2-konsept kan ikke traversere import | `okf` | b | | 11 | :75 | persist-gate dekker ikke kjøringsrisiko | ingen | – | | 12 | :83 | dokument som *beskriver* angrep er FP | ingen | – | | 13 | :88 | tospråklig tekst tripper homoglyf-regelen | lexicon-data | a ⚑ *(«fix is pending»)* | | 14 | :93 | insider-redigeringer utenfor trusselmodell | ingen | – | | 15 | :95 | text-only, parser ingen filer | ingen | – | | 16 | :99 | kun ekstrahert tekst skannes; `.pdf` konsedert | dev-showcase | – (F1) | | 17 | :112 | lexicon-funn dedupliseres per id (`count=1`) | `Finding.count` | a | | 18 | :115 | ren beaconing er bare LOW | `calibration.py` | a ⚠️ | | 19 | :123 | korte opake URL-segmenter slipper gjennom | `scan_entropy`-terskler | a | | 20 | :130 | percent-escapes teller som databærende | kalibrering | a ⚠️ | | 21 | :166 | percent-escape slår ut tokeniseringen | kalibrering | a | | 22 | :190 | legitime CDN-hex-id-er tripper permanent | kalibrering | a | | 23 | :197 | ikke-tom query graderes som databærende | kalibrering | a ⚠️ | | 24 | :219 | rå-HTML-residualet er ekte HTML, ikke over-reach | `is_active_tag` | a | | 25 | :267 | bærersplitten strammer trusted tier | shippet 0.7.0 | – | | 26 | :292 | `count` teller ikke lenger endetagger | `Finding.count` | – (allerede flyttet) | | 27 | :300 | stor minoritet av benigne dokumenter persisterer ikke | måling | – | | 28 | :391 | URL-fragmenter graderes ikke | kalibrering | a ⚠️ | | 29 | :396 | hex-innpakket secret-egress fanges ikke | `scan_entropy` | b | | 30 | :402 | prosa som nevner `