build(deps): pin llm-ingestion-guard v1.3.0 so the gate reads our own goldens
The guard could not read back what this library WRITES. At 1.2.0,
`okf.parse_frontmatter` refused the OKF v0.2 golden outright --
`OKFFrontmatterError: value begins with a disallowed YAML indicator '['`
against `sources: [{ id: golden-v0-2-sales, resource: fixture }]`. Flow is
the only form this library can emit, because its own line-oriented parser
cannot round-trip the block form at all, so a gate that refuses flow
refuses everything Door A produces under `OKF_V0_2`.
The control was run BEFORE the bump, which is the only moment it exists:
the probe raised on 1.2.0, so the new test discriminates rather than
merely passes. `[project.dependencies]` already said `>=1.2,<2.0` and is
unchanged; only `[tool.uv.sources]` and `uv.lock` move.
TWO gate rows moved, not the one the work was scoped around, which is why
the whole documented probe was re-run instead of just the `sources` case:
the BLOCK form of `sources` now passes too, retiring G30. That changes
nothing about what we emit -- our own parser is still the binding
constraint on writing flow -- and `docs/okf-nokkelinventar.md` now carries
a `guard 1.3.0` column beside the 1.2.0 measurement rather than
overwriting it. A third row kept its verdict but changed its reason, so
the quoted message was corrected too.
The Door C boundary is unmoved, verified with a known-positive:
`resource` is allowlisted only inside a `sources` entry, so section
10.2's `executor.resource` and `attester.resource` are still rejected
("not on the OKF mapping allowlist under 'executor'") while top-level
`resource` passes.
`uv.lock` also gains `pypandoc-binary==1.17`. That is a stale lockfile
being corrected, not a new dependency: it was already declared in the
`[extract]` extra, and `uv lock --check` reports the lockfile out of date
on the untouched tree. Core keeps exactly one runtime dependency.
Not addressed, and recorded rather than built: the guard reports that
`sources[].resource` is scanned as text but never URL-validated, because
SPEC 5.1 permits bundle-relative paths and scope descriptions. No
consumer has asked for a gate there.
Guard 1.3.0 installed from 44e2b31, verified anonymously over https
against the remote tag. 1054 -> 1055 tests. `mypy --strict` clean, `ruff`
clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
95eb271f57
commit
2d9fb0f934
5 changed files with 92 additions and 24 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
|
@ -45,6 +45,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Changed
|
||||
|
||||
- **The pinned `llm-ingestion-guard` moves to `v1.3.0`** (`[tool.uv.sources]`
|
||||
and `uv.lock`; the `>=1.2,<2.0` range in `[project.dependencies]` already
|
||||
covered it and is unchanged). What this fixes is that the guard could not
|
||||
read back what this library WRITES: at `1.2.0` the flow-form `sources` in the
|
||||
OKF v0.2 golden was refused outright, and flow is the only form this library
|
||||
is able to emit, because its own line-oriented parser cannot round-trip the
|
||||
block form. Measured before the bump so the test discriminates rather than
|
||||
merely passes, and pinned by
|
||||
`tests/test_guard_adapter.py::test_the_guard_parses_the_flow_form_sources_our_goldens_emit`.
|
||||
Two rows of the gate table in `docs/okf-nokkelinventar.md` moved, not one:
|
||||
the BLOCK form of `sources` now passes too, which retires G30 -- though it
|
||||
changes nothing about what we emit, since our own parser is still the binding
|
||||
constraint. `resource` is allowlisted only inside a `sources` entry, so
|
||||
section 10.2's `executor`/`attester` resource stays rejected through every
|
||||
carrier and the Door C boundary is unmoved. No public API of this library
|
||||
changes; a consumer's cost is a re-run.
|
||||
- **`uv.lock` also picks up `pypandoc-binary==1.17`**, which is a stale lockfile
|
||||
being corrected rather than a new dependency: the package was already declared
|
||||
in the `[extract]` extra, and `uv lock --check` reports the lockfile as out of
|
||||
date on the untouched tree. Core still has exactly one runtime dependency.
|
||||
- **`tools/okf_propose_segments.py` writes no artifact when it has nothing to
|
||||
propose**, exiting `1` (distinct from `2`, "could not do the job") instead of
|
||||
`0` with an empty plan. An empty plan cannot be replayed -- `process_inbox`
|
||||
|
|
|
|||
|
|
@ -49,14 +49,15 @@ V=~/repos/vegnormal-okf/docs/VEGNORMAL_V1.md
|
|||
K1=~/repos/vegnormal-okf/docs/okf-spec-konformitet.md # ordre K1, levert 27.08
|
||||
K3=~/repos/vegnormal-okf/docs/okf-eksempelkorpus.md # ordre K3, levert 28.08
|
||||
R=~/repos/vegnormal-okf/docs/2026-08-26-resonnering-resultater.md
|
||||
G=~/repos/llm-ingestion-pipeline-security/src/llm_ingestion_guard/okf.py # guard-kilden; installert 1.2.0 måles i .venv
|
||||
G=~/repos/llm-ingestion-pipeline-security/src/llm_ingestion_guard/okf.py # guard-kilden; installert versjon måles i .venv
|
||||
```
|
||||
|
||||
Nevnere: `find $E -name '*.md' | wc -l` → **270** · `ls $M/*.md | wc -l` → **271**
|
||||
(270 konsepter + `index.md`) · `find $B -name '*.md' ! -name index.md | wc -l` →
|
||||
**48 983** (hele `build/`, alle armer og grenbaser) · `wc -l $P` → **1047** ·
|
||||
`.venv/bin/python -c "import importlib.metadata as m; print(m.version('llm-ingestion-guard'))"`
|
||||
→ **1.2.0** (= pinnen `pyproject.toml` l.35/113).
|
||||
→ **1.3.0** (området `pyproject.toml` l.35 `>=1.2,<2.0`; taggen som avgjør
|
||||
versjonen er l.139 `tag = "v1.3.0"` — var `v1.2.0` til 2026-09-03).
|
||||
|
||||
### 0.3 Tallene 17, 3 og 33 — produsert av kommando og avstemt mot operatørens
|
||||
|
||||
|
|
@ -89,7 +90,8 @@ find $E -name '*.md' -print0 | xargs -0 grep -l '^sources:' | wc -l # -
|
|||
|
||||
**3 i M (`type`, `title`, `status`) — operatørens tall holder for bundlen armene leste.**
|
||||
I E er det **4**: `sources` skrives 270/270 av emitteren og fjernes før import fordi
|
||||
guarden avviser formen (G30; målt på nytt mot 1.2.0 i § 0.7). Avstemt.
|
||||
guarden avviste formen (G30; **falt ved 1.3.0**, § 0.7 — men M-tallet er målt på
|
||||
en import kjørt under 1.2.0 og er ikke re-målt). Avstemt.
|
||||
|
||||
**33 egne:**
|
||||
|
||||
|
|
@ -185,7 +187,7 @@ datetime with an explicit UTC offset». Nøklene den binder: `generated.at`,
|
|||
nøklene den strammet inn** (`stale_after`, `last_modified`, `usage_window`). Den gamle
|
||||
`YYYY-MM-DD`-formen finnes hos oss bare i produsentnøkler (`kravdato`, `date`).
|
||||
|
||||
### 0.7 Guard 1.2.0 — hva porten faktisk slipper gjennom (probe, ikke målearm)
|
||||
### 0.7 Guard 1.2.0 og 1.3.0 — hva porten faktisk slipper gjennom (probe, ikke målearm)
|
||||
|
||||
Kjørt mot den installerte guarden i `.venv` (samme som Dør C bruker), med kjent-positiv
|
||||
først. Dette er verdiformer, ikke en tiende arm.
|
||||
|
|
@ -208,19 +210,19 @@ for n, d in {
|
|||
except Exception as e: print("REJECT", n, "->", str(e)[:70])'
|
||||
```
|
||||
|
||||
| verdi | guard 1.2.0 | betydning |
|
||||
|---|---|---|
|
||||
| `title` én linje (kjent-positiv) | PASS | spørringen kan skille — `okf.parse_frontmatter` returnerer `({'type': 'Krav', 'title': …}, body)` |
|
||||
| `description` én linje | **PASS** | en énlinjes `description` ville passert Dør C i dag (`okf.parse_frontmatter` → `{'description': 'One sentence …'}`) |
|
||||
| `description` brettet over to linjer | REJECT | `nested mappings are not supported in OKF frontmatter` — G33s «29/53 brettet» står for upstreams bundler (`~/.claude/docs/okf-bundle-prosessen.md` l.130) |
|
||||
| `tags: [a, b]` | REJECT | `value begins with a disallowed YAML indicator '['` |
|
||||
| `tags:` blokkliste | **PASS** | G33s «44/53 blokk avvist … ingen sekvens-verditype» gjelder en eldre guard; 1.2.0 leser `['a', 'b']` (`okf.parse_frontmatter`) |
|
||||
| `sources:` blokk-sekvens av mappinger (E-formen, `bundle.py` l.167) | REJECT | `a mapping is not expressible in OKF frontmatter` — **G30 står ved 1.2.0** (registeret sa «UMÅLT mot 1.2.0», l.133; nå målt) |
|
||||
| `sources: [{ … }]` flow-sekvens (Dør A under `OKF_V0_2`, `materialize.py` l.241) | REJECT | vår egen golden avvises: `.venv/bin/python -c 'from llm_ingestion_guard import okf; okf.parse_frontmatter(open("examples/ingest-golden-okf-v0-2/expected-bundle/ingest-sales.md").read())'` → `OKFFrontmatterError … '['` — den «målte begrensningen» i `$P` l.998–1005 (mot 0.2.0) **står fortsatt** |
|
||||
| `generated: { by, at }` flow-mapping | **PASS** | 1.2.0 leser mappingen som `{'by': …, 'at': …}` — kommentaren i `$P` l.998–1003 («no expressible form») er foreldet for denne nøkkelen |
|
||||
| `generated: true` (Dør B) | PASS | passerer porten som streng `'true'`; porten dømmer form, ikke § 5.2 |
|
||||
| verdi | guard 1.2.0 | guard 1.3.0 | betydning |
|
||||
|---|---|---|---|
|
||||
| `title` én linje (kjent-positiv) | PASS | PASS | spørringen kan skille — `okf.parse_frontmatter` returnerer `({'type': 'Krav', 'title': …}, body)` |
|
||||
| `description` én linje | **PASS** | PASS | en énlinjes `description` ville passert Dør C i dag (`okf.parse_frontmatter` → `{'description': 'One sentence …'}`) |
|
||||
| `description` brettet over to linjer | REJECT | REJECT | `nested mappings are not supported in OKF frontmatter` — G33s «29/53 brettet» står for upstreams bundler (`~/.claude/docs/okf-bundle-prosessen.md` l.130) |
|
||||
| `tags: [a, b]` | REJECT | REJECT | **samme dom, ny begrunnelse ved 1.3.0:** `a flow sequence admits flow mappings only: '[a, b]'` (var: `value begins with a disallowed YAML indicator '['`). En flow-sekvens av skalarer er fortsatt ute; det er flow-sekvensen av *mappinger* som åpnet |
|
||||
| `tags:` blokkliste | **PASS** | PASS | G33s «44/53 blokk avvist … ingen sekvens-verditype» gjelder en eldre guard; 1.2.0 leser `['a', 'b']` (`okf.parse_frontmatter`) |
|
||||
| `sources:` blokk-sekvens av mappinger (E-formen, `bundle.py` l.167) | REJECT | **PASS** | **G30 FALT ved 1.3.0.** Var `a mapping is not expressible in OKF frontmatter`. Men porten er ikke det eneste hinderet: *vår egen* parser er linjeorientert og kan ikke lese blokkformen tilbake, så at guarden nå tar imot den endrer ikke at dette biblioteket bare skriver flow (`CLAUDE.md § Upstream version policy`) |
|
||||
| `sources: [{ … }]` flow-sekvens (Dør A under `OKF_V0_2`, `materialize.py` l.241) | REJECT | **PASS** | vår egen golden passerer nå: `.venv/bin/python -c 'from llm_ingestion_guard import okf; fm, _ = okf.parse_frontmatter(open("examples/ingest-golden-okf-v0-2/expected-bundle/ingest-sales.md").read()); print(fm["sources"])'` → `[{'id': 'golden-v0-2-sales', 'resource': 'fixture'}]`. Ved 1.2.0: `OKFFrontmatterError … '['`. Pinnet av `tests/test_guard_adapter.py::test_the_guard_parses_the_flow_form_sources_our_goldens_emit` |
|
||||
| `generated: { by, at }` flow-mapping | **PASS** | PASS | 1.2.0 leser mappingen som `{'by': …, 'at': …}` — kommentaren i `$P` l.998–1003 («no expressible form») er foreldet for denne nøkkelen |
|
||||
| `generated: true` (Dør B) | PASS | PASS | passerer porten som streng `'true'`; porten dømmer form, ikke § 5.2 |
|
||||
|
||||
Guardens mapping-nøkkel-allowlist: `sed -n 89,92p $G` → `by at from to id title author usage_count last_modified` — nøyaktig § 5.1/§ 5.2s feltnavn, **uten `resource`**, som er hvorfor `sources[].resource` inne i en mapping stopper (registerets G30-forklaring, l.133).
|
||||
Guardens mapping-nøkkel-allowlist **flyttet ved 1.3.0, og foreldrenøkkelen avgjør nå.** Ved 1.2.0: `by at from to id title author usage_count last_modified` — **uten `resource`**, som var hvorfor `sources[].resource` inne i en mapping stoppet (registerets G30-forklaring, l.133). Ved 1.3.0 er `resource` tillatt **kun inne i en `sources`-oppføring** (`_SOURCES_ENTRY_KEYS`, `okf.py` l.102). Målt, med kjent-positiv: `sources: [{ id: a, resource: fixture }]` → PASS · `executor: { id: e, resource: … }` → REJECT `mapping key 'resource' is not on the OKF mapping allowlist under 'executor'` · `attester: { … }` → REJECT tilsvarende · toppnivå `resource:` (§ 4.1) → PASS. § 10.2s kjøretids-`resource` er altså fortsatt ute gjennom hver bærer, som er den grensen Dør C hviler på.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -236,8 +238,8 @@ måling, arm og celle der en finnes; ellers «ikke målt».
|
|||
| `title` | 4.1 recommended (l.192–193) | display name; consumers MAY derive from filename | 0.1 | **bib: ja** — A og B skriver (`inbox.py` l.133); B utleder ved fravær (`structure.py` l.58). **korpus: 270/270** = `<req_number> <seksjonstittel>` (`bundle.py` l.194) | siterbar overskrift (`VEGNORMAL_V1 § 4` l.176) | **indeksen bruker den ikke som lenketekst:** Dør C setter konsept-id som label (`importer.py` l.268), så UUID står to ganger per rad og `title` legges som fasett bak — UUID 27,7 % + `title:`-felt 16,6 % av indeksen (K1 S8b, l.173; 75 837 tegn / 270 rader). Ikke målt som celler | `grep -l '^title:' $M/*.md \| wc -l` → 270 · `grep -c '^- \[krav/N500/id-' $M/index.md` → 270 · `grep -c ' — title: ' $M/index.md` → 270 · `sed -n 268,275p $I/importer.py` |
|
||||
| `description` | 4.1 recommended (l.194–196); 8 SHOULD (l.527) | one-sentence summary; «Used by `index.md` generators, search snippets, and previews» | 0.1 | **bib: ingen dør skriver den.** `STRICT_V1` navngir (l.818) og bærer den i indeksmalen (l.859); `DEFAULT`/`OKF_V0_2`-malen har ingen plass (l.797). **korpus: 0/270 (E), 0/270 (M)** | ikke skrevet; `VEGNORMAL_V1` nevner den ikke (0 av 1073 linjer, kjent-positiv `` `kravtype` `` → 8), `bundle.py` navngir den ikke (0) | **se A.3** — indeksplassen § 8 gir den er fylt med en fasettdump; oppslagsaksen taper 4/13 (F4 9/13 mot R 13/13); hypotesen om årsak er **ikke målt** | `grep -l '^description:' $M/*.md \| wc -l` → 0 · `find $E -name '*.md' -print0 \| xargs -0 grep -l '^description:' \| wc -l` → 0 · `grep -l '^title:' $M/*.md \| wc -l` → 270 (kjent-positiv) · `grep -c '\`description\`' $V` → 0 · `grep -c '"description"' ~/repos/vegnormal-okf/src/vegnormal_okf/bundle.py` → 0 |
|
||||
| `resource` | 4.1 recommended (l.197–199) | URI that uniquely identifies the underlying asset; absent for abstract concepts | 0.1 | **bib: nei** — ingen dør skriver, ingen profil navngir (§ B.2-enumereringen: 35 navn, `resource` ikke blant dem). **korpus: 0** på toppnivå; den ene URI-en vi har står i `sources[].resource` | ikke skrevet; om kilde-API-et tilbyr en per-krav-URI er **ikke undersøkt** | ikke målt | `find $E -name '*.md' -print0 \| xargs -0 grep -c '^resource:' \| awk -F: '{s+=$NF}END{print s}'` → 0 · kjent-positiv `'^ - resource:'` → 270 · `grep -c '\`resource\`' $V` → 1 (l.383, om `sources`) |
|
||||
| `tags` | 4.1 recommended (l.200–201); 3.1 (l.148–151) | YAML list of short strings; tag-browsing views are synthesized from it | 0.1 | **bib: aldri** skrevet eller lest (1 treff i 11 moduler, om HTML-tags). **korpus: 0** | ingen tverrgående kategorisering er definert utover `type`/`kravtype`/`seksjon` (`grep -c '\`tags\`' $V` → 0) | ikke målt. Porten: blokkliste passerer 1.2.0, flow avvises (§ 0.7) | `grep -n tags $I/*.py` → `extract.py:86` (HTML) · `grep -l '^tags:' $M/*.md \| wc -l` → 0 · `grep -c '^tags:' $S` → 3 (spec-ens egne eksempler, kjent-positiv) |
|
||||
| `sources` | 5.1 (l.287–364) | provenance: the materials a concept derives from; list of entries | 0.2 | **bib: navngitt i `OKF_V0_2`** (l.947), **skrevet av Dør A kun under den profilen** som flow-sekvens (`materialize.py` l.241–242; golden 1/1) — **som guard 1.2.0 avviser** (§ 0.7). Parseren leser den ikke tilbake (`materialize.py` l.119, skalar). **korpus: E 270/270** blokk-sekvens av mappinger (`bundle.py` l.167–177); **M 0/270 — fjernet før import** (G30, står ved 1.2.0) | skrives fordi § 5.1 er den konforme formen (`VEGNORMAL_V1 § 9` l.381); fjernes fordi porten avviser mapping-i-sekvens | **proveniensen finnes ikke i noe armene leste:** 0/270 i M. Etter import er `utgave` (B.1) eneste bærer av utgaven. Ikke målt som celler | `find $E -name '*.md' -print0 \| xargs -0 grep -l '^sources:' \| wc -l` → 270 · `grep -l '^sources:' $M/*.md \| wc -l` → 0 · `sed -n 241,242p $I/materialize.py` · `grep -rl '^sources:' examples \| wc -l` → 1 |
|
||||
| `tags` | 4.1 recommended (l.200–201); 3.1 (l.148–151) | YAML list of short strings; tag-browsing views are synthesized from it | 0.1 | **bib: aldri** skrevet eller lest (1 treff i 11 moduler, om HTML-tags). **korpus: 0** | ingen tverrgående kategorisering er definert utover `type`/`kravtype`/`seksjon` (`grep -c '\`tags\`' $V` → 0) | ikke målt. Porten: blokkliste passerer, flow-sekvens av skalarer avvises — likt ved 1.2.0 og 1.3.0, men med ulik begrunnelse (§ 0.7) | `grep -n tags $I/*.py` → `extract.py:86` (HTML) · `grep -l '^tags:' $M/*.md \| wc -l` → 0 · `grep -c '^tags:' $S` → 3 (spec-ens egne eksempler, kjent-positiv) |
|
||||
| `sources` | 5.1 (l.287–364) | provenance: the materials a concept derives from; list of entries | 0.2 | **bib: navngitt i `OKF_V0_2`** (l.947), **skrevet av Dør A kun under den profilen** som flow-sekvens (`materialize.py` l.241–242; golden 1/1) — **som guard 1.2.0 avviste og 1.3.0 slipper gjennom** (§ 0.7). Parseren leser den ikke tilbake (`materialize.py` l.119, skalar). **korpus: E 270/270** blokk-sekvens av mappinger (`bundle.py` l.167–177); **M 0/270 — fjernet før import** (G30; falt ved 1.3.0, så grunnen til å fjerne den er borte — men M-tallet er målt på en import kjørt under 1.2.0 og er ikke re-målt) | skrives fordi § 5.1 er den konforme formen (`VEGNORMAL_V1 § 9` l.381); fjernes fordi porten avviser mapping-i-sekvens | **proveniensen finnes ikke i noe armene leste:** 0/270 i M. Etter import er `utgave` (B.1) eneste bærer av utgaven. Ikke målt som celler | `find $E -name '*.md' -print0 \| xargs -0 grep -l '^sources:' \| wc -l` → 270 · `grep -l '^sources:' $M/*.md \| wc -l` → 0 · `sed -n 241,242p $I/materialize.py` · `grep -rl '^sources:' examples \| wc -l` → 1 |
|
||||
| `sources[].resource` | 5.1 REQUIRED i oppføringen (l.303–306) | artifact a consumer can follow (URL, bundle path, `references/`) **or a scope descriptor** («all queries in project X») | 0.2 | **bib:** Dør A skriver `resource: fixture`-plassholder i golden (l.184–). **korpus: E 270/270** — én absolutt URL til kilde-API-et, samme for alle (K1 N13) | peker tilbake til kilden | ikke målt (0 i M, se `sources`) | `find $E -name '*.md' -print0 \| xargs -0 grep -h '^ - resource:' \| sort -u \| wc -l` → 1 · `… \| grep -c '^ - resource: https://'` → 270 |
|
||||
| `sources[].id` | 5.1 SHOULD når kroppen siterer (l.307–309) | stable key for per-claim attribution via `[^id]` footnotes (l.348–364) | 0.2 | **bib:** golden skriver `id: golden-v0-2-sales`. **korpus: 0** — ingen kropp siterer med fotnote | ikke utløst (K1 N10: 0 `[^`) | ikke målt | `find $E -name '*.md' -print0 \| xargs -0 grep -c '^ id:' \| awk -F: '{s+=$NF}END{print s}'` → 0 · kjent-positiv `'^ title:'` → 270 · `grep -l '\[\^' $M/*.md \| wc -l` → 0 |
|
||||
| `sources[].title` | 5.1 optional (l.310–311) | human-readable label for the source | 0.2 | **bib: nei.** **korpus: E 270/270** = `productTitle` (`bundle.py` l.177) | kildens navn | **dupliserer `utgave` 270/270 byte for byte** (B.1) — etter G30 er duplikatet det som overlever | `n=0; m=0; for f in $(find $E -name '*.md'); do u=$(grep -m1 '^utgave:' "$f" \| sed 's/^utgave: //'); s=$(grep -m1 '^ title:' "$f" \| sed 's/^ title: //'); m=$((m+1)); [ -n "$u" ] && [ "$u" = "$s" ] && n=$((n+1)); done; echo "$n av $m"` → `270 av 270` |
|
||||
|
|
@ -327,7 +329,8 @@ seksjon fra kilden (T7 veiledningsfelle 4/4 mot 2/4; T8 mutert hypotese 5/5 mot
|
|||
det er **modalitet (U2)**, ikke en beskrivelse. Begge står åpne. Fase 2 velger hva som
|
||||
måles; dette dokumentet gjør det ikke.
|
||||
|
||||
**7. Porten.** En énlinjes `description` **passerer guard 1.2.0** (§ 0.7); en brettet
|
||||
**7. Porten.** En énlinjes `description` **passerer** (målt ved både 1.2.0 og 1.3.0,
|
||||
§ 0.7); en brettet
|
||||
avvises. G33s «29/53 brettet» (`~/.claude/docs/okf-bundle-prosessen.md` l.130) gjelder
|
||||
upstreams bundler, ikke en verdi vi selv ville skrevet.
|
||||
|
||||
|
|
|
|||
|
|
@ -136,4 +136,4 @@ ignore_missing_imports = true
|
|||
# true of this tree. Reading a range off one and installing it against the
|
||||
# other is the one combination that fails.
|
||||
[tool.uv.sources]
|
||||
llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", tag = "v1.2.0" }
|
||||
llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", tag = "v1.3.0" }
|
||||
|
|
|
|||
|
|
@ -71,6 +71,35 @@ def test_guard_version_is_inside_the_pin() -> None:
|
|||
assert major == 1 and minor >= 2, guard.__version__
|
||||
|
||||
|
||||
def test_the_guard_parses_the_flow_form_sources_our_goldens_emit() -> None:
|
||||
"""The guard's parser must read back what this library WRITES.
|
||||
|
||||
A known-negative turned known-positive, and the control was run before the
|
||||
bump so it is not a story: on 1.2.0 this exact call raised
|
||||
`OKFFrontmatterError` -- "value begins with a disallowed YAML indicator
|
||||
'['" -- against the v0.2 golden's `sources: [{ id: ..., resource: ... }]`.
|
||||
That is not a cosmetic rejection. This library emits structured
|
||||
frontmatter values in FLOW form as a hard convention, because its own
|
||||
line-oriented parser cannot round-trip the block form at all, so a guard
|
||||
that refuses flow refuses the only shape we are able to produce.
|
||||
|
||||
1.3.0 allowlists `resource` inside a `sources` entry, and the parent key
|
||||
is what decides: section 10's `executor` and `attester` resource stays
|
||||
rejected through every carrier. This test therefore pins the narrow thing
|
||||
that changed, not the whole parse surface -- if a later guard widened
|
||||
`resource` beyond `sources`, the assertion below would still pass and the
|
||||
Door C boundary tests are what would move.
|
||||
"""
|
||||
golden = Path("examples/ingest-golden-okf-v0-2/expected-bundle/ingest-sales.md")
|
||||
# `(mapping, body)`, not a mapping. Measured rather than assumed, and the
|
||||
# unpacking is part of what this test pins: at 1.2.0 the call raised before
|
||||
# returning anything, so the shape was not observable from here at all.
|
||||
frontmatter, _body = guard_okf.parse_frontmatter(golden.read_text(encoding="utf-8"))
|
||||
sources = frontmatter["sources"]
|
||||
assert isinstance(sources, list) and len(sources) == 1
|
||||
assert sources[0] == {"id": "golden-v0-2-sales", "resource": "fixture"}
|
||||
|
||||
|
||||
def test_guard_screen_output_signature_is_what_door_b_calls() -> None:
|
||||
parameters = inspect.signature(guard.screen_output).parameters
|
||||
assert list(parameters) == ["text", "policy", "provenance", "transform_failed"]
|
||||
|
|
|
|||
22
uv.lock
generated
22
uv.lock
generated
|
|
@ -496,8 +496,8 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "llm-ingestion-guard"
|
||||
version = "1.2.0"
|
||||
source = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v1.2.0#9aeceb02c0b422160f6064c2b94505eae5cea418" }
|
||||
version = "1.3.0"
|
||||
source = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v1.3.0#44e2b31afdf2ea11028d5ccc5235b3a7cea2a4ff" }
|
||||
|
||||
[[package]]
|
||||
name = "llm-ingestion-okf"
|
||||
|
|
@ -510,6 +510,7 @@ dependencies = [
|
|||
[package.optional-dependencies]
|
||||
extract = [
|
||||
{ name = "pdfplumber" },
|
||||
{ name = "pypandoc-binary" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
|
|
@ -521,8 +522,9 @@ dev = [
|
|||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "llm-ingestion-guard", git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v1.2.0" },
|
||||
{ name = "llm-ingestion-guard", git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v1.3.0" },
|
||||
{ name = "pdfplumber", marker = "extra == 'extract'", specifier = ">=0.11.10,<0.12" },
|
||||
{ name = "pypandoc-binary", marker = "extra == 'extract'", specifier = "==1.17" },
|
||||
]
|
||||
provides-extras = ["extract"]
|
||||
|
||||
|
|
@ -768,6 +770,20 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pypandoc-binary"
|
||||
version = "1.17"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/80/85/681a54111f0948821a5cf87ce30a88bb0a3f6848af5112c912abac4a2b77/pypandoc_binary-1.17-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:734726dc618ef276343e272e1a6b4567e59c2ef9ef41d5533042deac3b0531f1", size = 25553945, upload-time = "2026-03-14T22:38:47.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/58/8fd107c68522957868c1e785fbea7595608df118e440e424d189668294df/pypandoc_binary-1.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fcfd28f347ed998dda28823fc6bc24f9310e7fdf3ddceaf925bf0563a100ab5b", size = 25553944, upload-time = "2026-03-14T22:38:50.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/27/ac1078239aae14b94c51975b7f46ad8e099e47d7ae26c175a5486b1c0099/pypandoc_binary-1.17-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d6b620b21c9374e3e48aabd518492bf0776b148442ee28816f6aaf52da3d4387", size = 34460960, upload-time = "2026-03-14T22:38:53.391Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/7f/1e5612b52900ebe590862dabeadf546f739b27527dcd8bfd632f8adac1be/pypandoc_binary-1.17-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ada156cb980cd54fd6534231788e668c00dbb591cbd24f0be0bd86812eb8788", size = 36867598, upload-time = "2026-03-14T22:38:56.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/31/a5a867159c4080e5d368f4a53540a727501a2f31affc297dc8e0fced96a7/pypandoc_binary-1.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2f439dcd211183bb3460253ca4511101df6e1acf4a01f45f5617e1fa2ad24279", size = 36867584, upload-time = "2026-03-14T22:38:59.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/2d/6a51cd4e54bdf132c19416801077c34bd40ba182e85d843360d36ae03a2d/pypandoc_binary-1.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f6e6d3e4cfafbe23189a08db3d41f8def260bacd6e7e382bceadab7ba1f17da6", size = 34460949, upload-time = "2026-03-14T22:39:01.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/b9/f47b77ba75ed5d47ec85fcc2ecfbf7f78e3a73347f3a09836634d930de98/pypandoc_binary-1.17-py3-none-win_amd64.whl", hash = "sha256:76fae066cd2d7e78fb97f0ec8e9e36f437b07187b689b0b415ca18216f8f898a", size = 40891661, upload-time = "2026-03-14T22:39:04.782Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pypdfium2"
|
||||
version = "5.13.0"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue