# Bilder i OKF-bundles, trinn 1: de bæres (0.10.0) Ordre `20260916T050910Z-1628427832-from-.claude`, trinn 1 av 2. Trinn 2 (`okf describe`, Claude vision) er ikke i denne leveransen og ikke i denne rapporten. Utgangspunktet er operatørens premiss, ordrett: «det som ender opp i en bundle etter en prosess med å konvertere X antall kilder MÅ være 100 % riktig». --- ## § 0 Premissene målt først Ordren oppgir hva PM målte i dette repoet og ber om at det gjentas (Verifiseringsloven, ansikt 3). Målt på `332961a`, 2026-09-16: | Påstand | Målt her | Status | | --- | --- | --- | | Ingen leser henter, navngir eller kopierer et bilde | `page.images` og `extract_table`: **0 treff** i `src/`. `handle_starttag` leser aldri `attrs` (`extract.py`). `` forekommer ikke i XML-leseren. `page.to_image` finnes kun inne i OCR-grenen | **Bekreftet** | | Eneste skriver er `write_bytes(..., content: str)` | Ja, UTF-8, ingen binær skrivesti | **Bekreftet** | | 108 grep-treff over 23 filer | Målt her: **127 treff over 12 filer** med `grep -rIEn` over `src/*.py` | **Avviker** — PMs kommando er ikke oppgitt, så tallene er ikke sammenliknbare. Substansen (ingen treff er en bildeleser) er bekreftet ved gjennomlesing av alle 127 | | SPEC er taus om binære filer | `_okf-canonical` `ad30107`: § 3 «a directory tree of markdown files», § 11 punkt 1 scoper til `.md`, § 6.3 er en konvensjon | **Bekreftet** | To premisser i ordren er **ikke** reprodusert og er merket som det: «84 filer i kildezip-ens `graphics/`» — katalogen jeg har lesetilgang til (`~/repos/vegnormal-okf/build/860019-html/graphics`) holder **109 filer**, og XML-en refererer **50** av dem. Det er en annen artefakt enn zip-en ordren siterer, ikke en motsigelse. R761-målingen som utløste ordren er ikke etterprøvd her i sin helhet; det jeg målte selv er at side 496 i PDF-en bærer **2 DCTDecode-bilder** rett under teksten «Tabell 84-2:», og at hele dokumentet bærer **50 bildeobjekter på 38 av 701 sider**, fordelt **29 DCTDecode / 21 FlateDecode** — samme antall som NISO-STS-leveransens 50 ``. --- ## § 1 Gaten, skrevet rød først `tests/test_asset_gate.py`, skrevet før én linje kapabilitetskode. Nevneren leses ut av **kilden** (`page.images`, `word/media/`, `ppt/media/`, ``, lokal sti eller `data:`-URI | `alt` | | `.xml` | ``, href-en og så `graphics/` | ingen — STS har intet captionsfelt her | `.xlsx` er **bevisst ikke** en rad: konverteren skriver én pipe-tabell per ark, og en toradersblokk inne i en slik tabell ville brutt rad-lokatoren `source_rows` leses tilbake ut av. Målt 2026-09-16: **0 av 4** K2-arbeidsbøker bærer media i det hele tatt, så raden er en uttalt grense og ikke et tap. **Etiketten gjettes ikke.** To av de fire formatene har intet captionselement — verken et PDF-bildeobjekt eller en STS-`` bærer ett, og «Figur 11.1 …»-linja et menneske leser er en søsken-`

` leseren allerede emitterer på egen linje. Å utlede en etikett fra nærmeste linje ville vært en umerket heuristikk. **Layouten.** `assets/` i bundle-rota, `-`. I konseptet, der bildet sto: ```markdown ![Tabell 84-2 Toleranseklasser](/assets/e54e5f5da0e8-tabell-84-2.png) Image: graphics/tabell-84-2.png (120x90 px) -- Tabell 84-2 Toleranseklasser ``` Basenavnet og ikke stien: målt på fixture-innboksen ble ett bilde skrevet **to ganger under to navn i én kjøring**, fordi HTML-dokumentet peker på `graphics/figur-84-1.png` og STS-dokumentet på `figur-84-1.png` — med digesten i begge navnene som annonserte at bytene var like. Stien er en egenskap ved pekeren, ikke ved bildet, og hele originalen overlever på pekerens egen linje. **Typen snuses, aldri påstås.** En `.jpg` som i virkeligheten er en PNG bæres som PNG under et `.png`-navn; alternativet er en bundle hvis filnavn er uenige med sitt eget innhold. --- ## § 3 PDF: to ruter, og hvorfor rasterisering ble felt `get_data()` kjører hver filter pdfminer kjenner og stopper ved bildekodekene, så en `DCTDecode`-strøm kommer tilbake som en ferdig JPEG og en `FlateDecode`-strøm som rå sampler. **Ruten velges av BYTENE, ikke av filternavnet:** snus resultatet som et bildeformat, bæres det ordrett; ellers kodes samplene til PNG med stdlib-`zlib`. Måling som begrunner det: R761 har **29 av 50** DCTDecode og **21** FlateDecode. Over det 33-dokumenters K2-korpuset er populasjonen **4 828 objekter**, og filtrene er blandet nok (`FlateDecode`, `DCTDecode`, `JPXDecode`, `ASCII85Decode`-kjeder, `CCITTFaxDecode`) til at en gjetning fra filternavnet ville vært gal på flere hundre. **Alternativet ordren nevnte — rendret bbox ved 200 dpi — ble felt på determinisme.** Et rasterisert utsnitt ville vært én kodesti og håndtert hver filter, men bytene, og dermed assetens innholdsadresserte navn og hele bundlens digest, ville vært avhengige av hvilken versjon av rasteriseren som var installert. Det er nøyaktig egenskapen `OCR_DPI` sin egen docstring allerede innrømmer at OCR-tekst ikke kan ha. En innebygd strøm har ingen slik avhengighet. **Det koderen ikke kan uttrykke EKSAKT, nekter den for:** stencilmaske, `Decode`-array, CMYK, alt annet enn 8-bits sampler, en `SMask` som ikke lar seg bære. Koden er `asset_pdf_unsupported`, den telles, og den skriver en linje i konseptet. Et bilde som er plausibelt feil farge er feil på en måte ingen konsument kan oppdage. **Ingen størrelsesgulv, og det er også en måling.** Det opplagte filteret er «ignorer alt under N piksler», og fordelingen tilbyr ingen N. Over de 4 828 objektene: **149** uten oppgitt størrelse, **162** under 32x32, **92** under 64x64, **406** under 128x128, **498** under 256x256, **590** under 512x512, **2 931** større. Et bredt spenn uten gap — motsatt av `OCR_CID_SHARE`, som er bimodal med ingenting mellom modene. En terskel lest av ingen gap er et tall dette repoet valgte, og det ville stille droppet noens lille tabell. --- ## § 4 Kontrollen på bytene To hele bygg av det 43-dokumenters referansekorpuset (`K2/trinn1`), og `diff -r` mellom dem. En eksponeringstelling er ikke en kontroll. **Kontroll 1 — flytter opt-outen noe?** `332961a` bygget fra `git archive` mot HEAD med `--no-assets`: ``` 865 filer på begge sider. diff -rq: ÉN forskjell. 14a15 > * **Images**: NOT CARRIED — this run did not look for images, ... ``` Hvert eneste konsept er byte-identisk. Den ene forskjellen er den nye `log.md`-linja, og den er med vilje: en bundle ingen lette etter figurer i må ikke kunne forveksles med en bundle av dokumenter som ikke hadde noen. **Kontroll 2 — hva koster defaulten?** Samme commit, `--no-assets` mot default: | | `--no-assets` | default | | --- | --- | --- | | konsepter | 453 | **454** | | markdown-filer | 865 | **867** | | assets | 0 | **2 964** | | bundle-størrelse | 4,7 MB | **115 MB** | | veggtid | 2 414 s | **3 088 s** | | topp-RSS | 6,26 GB | **8,74 GB** | | md-filer som skiller seg | — | **422 av 865** | `log.md`: **2 964 båret av 3 145 funnet** (181 nektet, 5,8 %). **4 622 pekere** mot 2 964 filer — innholds-dedupen folder 1 658 gjentakelser inn i filene de allerede er. **Det ene nye konseptet har en MÅLT årsak.** Kandidaten `- 20 …torv ødometerapparat …` i `Del II Bilag 3.2.1 - RIG-R01 Datarapport.pdf` er en `rule:outline`-kandidat som `--outline-gate` slipper inn når ett gjenfunnet overskriftsspenn dekker `OUTLINE_SHARE = 0.20` av teksten. Målt: | | tekst | spennet | andel | gaten | | --- | --- | --- | --- | --- | | `--no-assets` | 71 255 | 13 566 | **0,190** | droppet | | default | 90 854 | 27 757 | **0,306** | sluppet inn | Seksjonen holder **146** av dokumentets bilder. Pekerne er kroppstekst, så spennet vokste og krysset terskelen. Det er ikke en segmenteringsregel som endret seg — det er den samme regelen som leser en lengre tekst. --- ## § 5 Konsumentflatene på en bundle MED `assets/` § 11 punkt 1 scoper konformans til `.md`-filer, så en `.png` i `assets/` deltar ikke. Målt, ikke antatt, på en bundle bygget fra fixture-innboksen (6 assets, 6 konsepter): | Flate | Resultat | | --- | --- | | `okf check --skill … --payload …` | `conformant: 17 rules over 4 excerpts and 2 withheld entries, 0 findings`, rc **0** | | `okf skill` | rc **0**, SKILL.md skrevet | | `okf consume` | rc **0**, 4 utdrag; pekerne reiser med utdragsteksten | | `okf quality` | rc **3** (ingenting kunne dømmes — hver filtype har 1 dokument, under gulvet på 5). Ingen falsk `PASS`, ingen krasj | | guard 1.4.0 `okf.import_bundle` (Dør C) | **6 av 6** konsepter slått sammen; pekerblokkene passerer gaten som kroppstekst | **Guarden avviser ikke binære filer** — den ser dem ikke, fordi importøren går over `.md`. Ingen `coord-send` til `llm-ingestion-pipeline-security` er derfor nødvendig for trinn 1. **Men Dør C bar dem ikke.** Målt 2026-09-17, før reparasjonen: importen slo sammen **6 av 6** konsepter og skrev **ingen `assets/`-katalog i det hele tatt**, så hver `![…](/assets/…)` i den importerte bundlen pekte på en fil som ikke var der — samme «komplett og ikke»-defekt én dør bortenfor. Dør C bærer nå de assetene et SAMMENSLÅTT konsept peker på, etter samme innholdsidentitetsregel den allerede eier. Aldri hele avsenderens `assets/`: et bilde som hører til et konsept gaten nektet, skal ikke sitte på ryggen av ett den slapp gjennom. --- ## § 6 Hva dette IKKE dekker - **Trinn 2 er ikke bygget.** `okf describe`, transkripsjon med vision, verifisering mot bildet — ingenting av det finnes. Invarianten «no model calls anywhere in the run path» er uberørt: `assets.py` ser aldri på et bilde. - **`.png`/`.jpg` som EGNE innboksfiler er fortsatt utenfor scope** (`extractor_unknown`), som ordren sier. Fixture-innboksens to PNG-er rapporteres som `extractor_unknown: 2/7` på begge commits. - **R761 er ikke bygget her.** Tallene over er K2 og fixture-innboksen. En R761-bygging hører hjemme i `vegnormal-okf` og er deres ordre, ikke denne. - **`--no-assets`-kontrollen er kjørt på ETT korpus.** N = 1 korpus, 43 dokumenter. Den sier ingenting om et korpus med andre filtyper. - **181 av 3 145 bilder ble nektet** og kodene er talt, men ingen har sett på hva de 181 var. «5,8 % nektet» er et tall, ikke en diagnose. - **Kostnaden er publisert, ikke forsvart.** 4,7 MB -> 115 MB på 43 dokumenter er en 24x bundle. Om defaulten skal stå er operatørens, og tallene over er hva den avgjørelsen skal tas på. --- ## § 7 Reproduksjon ```bash # gaten uv run pytest tests/test_asset_gate.py -q # baselinen, fra git archive og aldri fra arbeidstreet git archive 332961a | tar -x -C /tmp/base332961a PYTHONPATH=/tmp/base332961a/src python3 -m llm_ingestion_okf.cli build \ ~/corpora/okf-telling-20260829/K2/trinn1 --bundle /tmp/k2-base \ --bundle-id k2-trinn1-20260903 --okf-version 0.2 # de to byggene okf build ~/corpora/okf-telling-20260829/K2/trinn1 --bundle /tmp/k2-off \ --bundle-id k2-trinn1-20260903 --okf-version 0.2 --no-assets okf build ~/corpora/okf-telling-20260829/K2/trinn1 --bundle /tmp/k2-on \ --bundle-id k2-trinn1-20260903 --okf-version 0.2 diff -rq /tmp/k2-base /tmp/k2-off # ett avvik: log.md diff -rq /tmp/k2-off /tmp/k2-on # 422 md-filer + 2 964 assets ```