feat(assets): a bundle carries the images its sources declare (0.10.0)
Until now no reader in this package fetched, named, described or copied a single image. `<img>`'s attributes were never read, a NISO-STS `<graphic>` was walked past, a PDF was opened for its text alone, the converter's markdown writer dropped every picture, and the only writer into a bundle took `content: str`. The two lossiness warnings said so on every run, which made the loss honest and did not make it smaller. Measured on R761 Prosesskoden:2025, published as a 701-page PDF and as a NISO-STS delivery: the process text is carried in full while 12 `Tabell N-N` and 9 `Figur N-N` captions stand over nothing, because that publisher ships those tables as raster pictures in both. Process 84's "toleranseklasse ... er gitt i tabell 84-2" points at empty space. THE GATE WAS WRITTEN FIRST AND RED. `tests/test_asset_gate.py` reads its denominator out of the source (`page.images`, `word/media/`, `ppt/media/`, `<img`, `<graphic`), never from a constant here. Measured at332961a, built from `git archive` and not from the editable tree: carried 0 of 8 local images across 5 documents (9 declared), and no `assets/` at all. After: 8 of 8, with the ninth a remote source carried as a pointer without a file. FIVE READERS PLACE, ONE MODULE DECIDES. `assets.py` owns what an image is (sniffed from the bytes, never from the claimed extension), what it is called (`<sha256[:12]>-<the source's own basename>`) and how it is pointed at (one two-line block, one regex). `.xlsx` is deliberately not a row: a block inside its pipe tables would break the `source_rows` locator, and 0 of 4 K2 workbooks hold media. A PDF stream that is already a file is carried VERBATIM (29 of R761's 50 objects are DCTDecode); raw samples are encoded to PNG with stdlib zlib, so no new dependency. Rendering the page region was the alternative and was felled on determinism: a rasterised crop's bytes, and therefore the asset's content-addressed name and the bundle's digest, would depend on the installed rasteriser. What the encoder cannot express exactly is refused with a code and counted, never approximated. NO SIZE FLOOR, and that is a measurement: over the 4 828 image objects of the K2 corpus the size distribution is a broad spread with no gap, unlike OCR_CID_SHARE's bimodal one, so a threshold would be a number we chose. ON BY DEFAULT, AND THE CONTROL IS TWO WHOLE BUILDS. The 43-document reference corpus at332961aversus rebuilt at HEAD with `--no-assets`: 865 files on both sides, `diff -rq` reports ONE difference, the added `Images: NOT CARRIED` line in log.md. Every concept byte-identical. Against the default: 453 -> 454 concepts, 865 -> 867 md, 0 -> 2 964 assets (2 964 carried of 3 145 found, 4 622 pointers), 4.7 MB -> 115 MB, 2 414 s -> 3 088 s, peak RSS 6.26 -> 8.74 GB, 422 of 865 md files differ. The one new concept has a measured cause: the pointers are body text, so a section holding 146 of that document's images grew from 19.0 % to 30.6 % of the extracted text and crossed `--outline-gate`'s 0.20 share clause. THE IMAGE BYTES ARE NOT SCREENED. The guard is text-only, the pointer block passes the gate as body text, the picture beside it passes nothing, and log.md says so on every run. Also fixed, both found by measuring rather than by reading: - a markdown image is no longer read as a cross-reference. `structure._LINK` never looked at the character in front of the bracket, so every pointer would have arrived in the index as an edge to a concept that cannot exist. - Door C carries the assets its merged concepts point at. Before this, importing a bundle built with `--assets` merged 6 of 6 concepts and wrote no `assets/` at all, so every pointer named a missing file. Report: docs/2026-09-17-bilder-i-bundlen-trinn1.md Spec proposal: docs/plan/okf-assets-section-6-4.md Suite 1 955 passed / 1 skipped (from 1 896), ruff and mypy --strict clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
332961a19c
commit
bc39e8091f
33 changed files with 3638 additions and 64 deletions
270
docs/2026-09-17-bilder-i-bundlen-trinn1.md
Normal file
270
docs/2026-09-17-bilder-i-bundlen-trinn1.md
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
# 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`). `<graphic>` 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 `<graphic>`.
|
||||
|
||||
---
|
||||
|
||||
## § 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/`, `<img`,
|
||||
`<graphic`), aldri fra en konstant i dette repoet — en konstant er repoet som
|
||||
påstår sin egen forventning, og den blir gal i det en fixture regenereres.
|
||||
|
||||
Målt på `332961a`, bygget fra `git archive` og ikke fra arbeidstreet (et
|
||||
editable install leser `src/` live, så en «før»-kjøring i dette treet ville målt
|
||||
endringen den skulle gå forut for):
|
||||
|
||||
```
|
||||
carried 0 of 2 local (2 declared) prosess-84-tabell.pdf
|
||||
carried 0 of 1 local (1 declared) prosess-84-notat.docx
|
||||
carried 0 of 1 local (1 declared) prosess-84-presentasjon.pptx
|
||||
carried 0 of 2 local (3 declared) prosess-84-web.html
|
||||
carried 0 of 2 local (2 declared) prosess-84-sts.xml
|
||||
---------------------------------------------------------------
|
||||
carried 0 of 8 local images across 5 documents (9 declared),
|
||||
and the bundle held no assets/ directory at all.
|
||||
```
|
||||
|
||||
Etter trinn 1: **8 av 8**, og det niende (en `https://`-kilde) er en peker uten
|
||||
fil, talt som funnet-og-ikke-båret.
|
||||
|
||||
**En fixture-defekt gaten fant selv:** de fem dokumentene het først
|
||||
`prosess-84.{pdf,docx,pptx,html,xml}`. Dørens egen § 3-kollisjonsregel refuserte
|
||||
to av dem (`inbox_slug_collision: 2/7`), så to lesere ble aldri kjørt og gaten
|
||||
rapporterte en bæredefekt som i virkeligheten var en fixturedefekt. Fem
|
||||
forskjellige stammer nå.
|
||||
|
||||
---
|
||||
|
||||
## § 2 Hva som ble bygget
|
||||
|
||||
**Fem lesere PLASSERER, én modul BESTEMMER.** `llm_ingestion_okf.assets` eier
|
||||
hva et bilde er, hva det heter og hvordan det pekes på; leserne vet bare hvor i
|
||||
sitt eget dokument bildet står og hva kilden kaller det.
|
||||
|
||||
| Rad | Hvor bildet hentes | Etikett |
|
||||
| --- | --- | --- |
|
||||
| `.pdf` | bilde-XObjects på siden (`page.images`) | ingen — PDF har intet captionsfelt |
|
||||
| `.docx` `.pptx` `.odt` `.rtf` | konverterens `--extract-media` | `descr`/alt fra containeren |
|
||||
| `.html` `.htm` | `<img src alt>`, lokal sti eller `data:`-URI | `alt` |
|
||||
| `.xml` | `<graphic xlink:href>`, href-en og så `graphics/<navn>` | 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-`<graphic>` bærer ett, og «Figur 11.1
|
||||
…»-linja et menneske leser er en søsken-`<p>` leseren allerede emitterer på egen
|
||||
linje. Å utlede en etikett fra nærmeste linje ville vært en umerket heuristikk.
|
||||
|
||||
**Layouten.** `assets/` i bundle-rota,
|
||||
`<sha256[:12]>-<kildens eget BASENAVN><snuset suffiks>`. I konseptet, der bildet
|
||||
sto:
|
||||
|
||||
```markdown
|
||||

|
||||
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 `` 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
|
||||
```
|
||||
96
docs/plan/okf-assets-section-6-4.md
Normal file
96
docs/plan/okf-assets-section-6-4.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Proposed SPEC § 6.4: `assets/`, the bytes a concept points at
|
||||
|
||||
Status: **a proposal, raised from a consumer**. Written in this repository
|
||||
because this repository implements the shape; the wording belongs upstream and
|
||||
`_okf-canonical` is not edited from here. Pinned commit read while writing:
|
||||
`ad30107` (OKF v0.2).
|
||||
|
||||
## Why it is needed
|
||||
|
||||
OKF v0.2 is silent about non-markdown files. § 3 says "A bundle is a directory
|
||||
tree of markdown files"; § 11's conformance list scopes every clause to `.md`
|
||||
files; § 6.3 makes `references/` a convention for external material carried as
|
||||
concepts. So a picture is neither permitted nor forbidden — it is unaddressed,
|
||||
and a producer that carries one is guessing about where it goes and what a
|
||||
consumer may assume.
|
||||
|
||||
The need is not hypothetical. Measured on R761 Prosesskoden:2025, a Norwegian
|
||||
road-construction process code published both as a 701-page PDF and as a
|
||||
NISO-STS XML delivery: the process text is carried in full, and 12 `Tabell N-N`
|
||||
and 9 `Figur N-N` captions stand over nothing, because the publisher ships
|
||||
those tables as raster images in **both** deliveries. Process 84 says
|
||||
"toleranseklasse ... er gitt i tabell 84-2" and table 84-2 is a JPEG. A bundle
|
||||
built from that document reads as complete and is not.
|
||||
|
||||
## The proposed wording
|
||||
|
||||
> ### 6.4 The `assets/` convention
|
||||
>
|
||||
> A bundle MAY carry non-markdown files that its concepts point at — images
|
||||
> extracted from a source document, and anything else a concept embeds rather
|
||||
> than describes. An `assets/` directory at the bundle root conventionally
|
||||
> holds them.
|
||||
>
|
||||
> A concept points at an asset with a standard markdown image or link whose
|
||||
> target is a path-valued reference under § 6.2 — the bundle-relative form
|
||||
> (`/assets/<name>`) is recommended, for the same reason § 6.1 recommends it
|
||||
> for links between concepts: it is stable when a concept moves within its
|
||||
> subdirectory.
|
||||
>
|
||||
> Asset file names are the producer's. A content-addressed name (for example a
|
||||
> prefix of the file's SHA-256 followed by a readable remnant of the source's
|
||||
> own name) is RECOMMENDED, because it makes the same bytes dropped twice one
|
||||
> file and makes a rebuild of one corpus produce one bundle.
|
||||
>
|
||||
> An asset is not a concept. It carries no frontmatter, it is not enumerated by
|
||||
> § 8's index files, and § 11's conformance clauses do not apply to it — they
|
||||
> are scoped to `.md` files, and this section does not widen them.
|
||||
>
|
||||
> Consumers MUST NOT reject a bundle because it carries files they do not
|
||||
> recognise, and MUST tolerate an asset pointer whose target is absent, for the
|
||||
> same reason § 6.1 requires them to tolerate a broken link: the pointer may
|
||||
> record that the source had a figure this bundle does not hold.
|
||||
|
||||
## What it does NOT propose
|
||||
|
||||
- **No screening claim.** Whether the bytes of an asset were examined is
|
||||
outside this section and outside the format. This library states it per run
|
||||
in `log.md` because its own gate is text-only; a picture is not text and did
|
||||
not pass it.
|
||||
- **No required directory.** `assets/` is a convention, exactly as
|
||||
`references/` is. A producer that puts its images elsewhere and points at
|
||||
them correctly is conformant.
|
||||
- **No new frontmatter family.** This library writes a count (`images: N`) on
|
||||
its own profiles, and that is a local key, not a proposal. § 11 already tells
|
||||
consumers not to reject a concept over an unknown key.
|
||||
|
||||
## Conformance measured, not assumed
|
||||
|
||||
The claim "existing consumers do not break" is § 11 item 1 scoping to `.md`
|
||||
files, plus the consumer-side MUST NOTs. Measured on a bundle WITH `assets/`,
|
||||
built by `okf build` from the fixture inbox:
|
||||
|
||||
| Surface | Result |
|
||||
| --- | --- |
|
||||
| `okf check` (17 rules) | `conformant: 17 rules over 4 excerpts and 2 withheld entries, 0 findings`, rc 0 |
|
||||
| `okf skill` | rc 0 |
|
||||
| `okf consume` | rc 0, 4 excerpts; the pointers travel with the excerpt text |
|
||||
| `okf quality` | rc 3 — "nothing could be judged", because each file type has one document and the floor is five. No false `PASS`, no crash |
|
||||
| guard 1.4.0 `okf.import_bundle` | 6 of 6 concepts merged; the pointer blocks pass the gate as body text |
|
||||
|
||||
The guard does not reject a bundle carrying binary files — it does not see
|
||||
them, because the importer walks `.md`. So no coordination message to
|
||||
`llm-ingestion-pipeline-security` is needed for this step.
|
||||
|
||||
The measurement that did NOT pass first time is in the report: Door C merged
|
||||
the concepts and wrote no `assets/` at all, so every pointer in the imported
|
||||
bundle named a missing file. Fixed here, by the content-identity rule that door
|
||||
already owns. The run record is
|
||||
`docs/2026-09-17-bilder-i-bundlen-trinn1.md` § 5.
|
||||
|
||||
## Route
|
||||
|
||||
Raised through `portfolio-optimiser-commons`, which owns the ingest-spec this
|
||||
library implements, and from there upstream. Not edited into `_okf-canonical`
|
||||
from here: that mirror is a read-only pin, and a spec change written by its
|
||||
implementer is not a spec change.
|
||||
Loading…
Add table
Add a link
Reference in a new issue