1
0
Fork 0
llm-ingestion-pipeline-secu.../docs/2026-09-07-limitations-44-maaling.md
Kjell Tore Guttormsen 6e7c8d2b98 docs(okf): measure the tags/description gap against the pinned SPEC and corpus
Order 20260906T213322Z: measure and propose, no src, no bump. Measured against
SPEC _okf-canonical @ ad30107 and the corpus _okf-upstream @ 3fcbb9f (denominator
53, extracted at the pin because the work tree had moved on to 9a15b13).

Three claims in the tags/description entry are stale against the 1.3.0 parser.
The parser does have a sequence type -- _consume_block_list parses block lists of
scalars -- what it lacks is tolerance for the flush-left indentation 36/53 of the
corpus uses. Removing tags alone now lets 6/53 pass, not the 4/53 recorded, and
the entry's headline claim does not hold at all: description alone unblocks 0/53,
and with all three surface forms closed 44/53 still stop on generated as a
top-level block mapping. The entry oversells its own reach by a factor of seven.

The SPEC has no depth rule. no-nesting-past-depth-1 is entirely ours; the
conformance floor is only "a parseable YAML frontmatter block" (SS11.1).

Candidates were measured by normalizing the surface form onto a shape the parser
already accepts, then importing parse_frontmatter -- the predicate is never
re-implemented, only the input's spelling is rewritten. P1 (scalar flow sequence)
takes the corpus from 0/53 to 6/53 without spending depth-1; P2 and P3 buy 0/53
each and 6/53 stacked on P1. Recommendation is P1 alone, and the note says out
loud that this does not open the corpus.

The label "punkt 44" is not grep-able: it came from a count-after-insertion, and
the entry is today the 12th of 45 at docs/LIMITATIONS.md:126.

No src change, no version bump, no push. 868 passed, coverage 130/130 + 6/6 gaps,
redos sweep exit 0 -- all after git add.
2026-09-06 23:58:02 +02:00

15 KiB
Raw Blame History

Beslutningsgrunnlag — LIMITATIONS «punkt 44» (tags/description-gapet)

Målt: 2026-09-07, mot HEAD = 44e2b31 (v1.3.0, urørt) og pinnet SPEC _okf-canonical @ ad30107. Korpus: _okf-upstream @ 3fcbb9f. Hva dette er: underlaget for én operatørbeslutning — hvilket predikat, om noe, skal slippe inn den formen tags faktisk har i korpuset. Hva dette ikke er: beslutningen. Ingen fil i src/ er endret, ingen versjon er bumpet, ingenting er pushet (0 upushede commits ved øktstart).


1. Referenten: «punkt 44» er ikke grepbart

Ordren og STATE peker på «LIMITATIONS punkt 44». Målt:

$ grep -c '^- \*\*' docs/LIMITATIONS.md
45
$ grep -n '^- \*\*' docs/LIMITATIONS.md | awk -F: 'NR==44 {print $1}'
756          # -> ZWJ-oppføringen, ikke tags/description

Tallet stammer fra commit 0184df9 (25.08), som skrev «43 -> 44 items» i commit-meldingen: 44 var antallet oppføringer etter innsettingen, ikke oppføringens posisjon. Oppføringen ble satt inn midt i dokumentet og er i dag den 12. av 45, på docs/LIMITATIONS.md:126. Etiketten «punkt 44» løser seg altså ikke opp verken ved posisjon eller ved grep, og den vil peke feil igjen neste gang listen vokser. Referenten er entydig i prosa («tags/description»), men nummeret bør ikke brukes videre.

2. Hva punkt 44 sier i dag, ordrett

docs/LIMITATIONS.md:126-144:

tags and description block the OKF import corpus universally, before the trust layer is even reached. The line-flat frontmatter parser has no sequence-value type at all: tags is present in 53/53 upstream concept documents — 9/53 as a flow sequence ([a, b, c], rejected on the [ indicator) and 44/53 as a block sequence (- a / - b, rejected as "malformed frontmatter line") — 100% rejection regardless of form. description is present in 53/53; 29/53 is a folded plain scalar continuing on an indented second line, which the parser has no continuation-line model for and misreads as "nested mappings are not supported" (the remaining 24/53 are single-line and parse fine). Measured directly on the upstream reference bundles (_okf-upstream/okf @ 3fcbb9f): removing tags alone lets 4/53 documents pass; removing both tags and description together (trust layer untouched) lets the same 4/53 pass, and all four then parse generated correctly as a mapping. Independent of the mapping-form work above: neither 1.2.0's flow mapping nor 1.3.0's sources carriers move anything on this corpus, because tags/description reject before sources is ever read. No sequence-value type or continuation-line model exists in the stdlib-only parser to close this with.

3. SPEC, ordrett fra pinnet kopi (ad30107)

§2 Terminology (SPEC.md:81):

  • Frontmatter: A YAML metadata block delimited by --- at the top of a markdown file.

§4 Concept documents (SPEC.md:157-159):

  1. A YAML frontmatter block, delimited by --- on its own line at the start of the file and a closing --- on its own line.

§4.1 Frontmatter, skjelettet (SPEC.md:163-173):

---
type: <Type name>                  # REQUIRED
title: <Optional display name>
description: <Optional one-line summary>
resource: <Optional canonical URI for the underlying asset>
tags: [<tag>, <tag>, ...]          # Optional

§4.1, de to bærende definisjonene (SPEC.md:194-199):

  • description: A single sentence summarizing the concept. Used by index.md generators, search snippets, and previews.
  • tags: A YAML list of short strings for cross-cutting categorization.

§11 Conformance (SPEC.md:738-742):

A bundle is conformant with OKF v0.2 if:

  1. Every non-reserved .md file in the tree contains a parseable YAML frontmatter block.

3.1 Det spec-en ikke sier

grep -n -i 'nest\|depth\|indent' SPEC.md gir null treff som uttrykker en dybde- eller innrykksregel (13 treff, alle i andre betydninger — «flat list of» i §9 og §13, «indent» ingen). SPEC-en har ingen dybderegel. «Ingen nesting forbi dybde 1» er utelukkende vår egen sikkerhetsegenskap. Konformans-gulvet er «a parseable YAML frontmatter block» — altså hele YAML.

To presiseringer som følger av ordlyden:

  • tags er definert som «A YAML list» — ikke som flow-formen. Skjelettets [<tag>, ...] er ett eksempel, ikke formkravet. Blokkformen er like konform.
  • description er «A single sentence». En setning brutt over to linjer som foldet plain scalar er samme setning; spec-en stiller ingen linjekrav.

4. Måleoppsett

Korpuset er hentet ut ved pinnen, ikke fra arbeidstreet — _okf-upstream står i dag på 9a15b13, og 3fcbb9f er en ekte forgjenger (git merge-base --is-ancestor → 0), med 23 filer endret i okf/ mellom dem.

$ git -C _okf-upstream archive 3fcbb9f okf/bundles | tar -x -C scratchpad/corpus
$ find scratchpad/corpus -name '*.md' ! -name index.md ! -name log.md | wc -l
53

Nevner = 53. Definisjonen er «hver .md under okf/bundles/ som ikke er et reservert strukturnavn» — som er nøyaktig §11.1s «every non-reserved .md file».

Positiv kontroll. Et null-resultat må sjekkes mot et kjent-positivt tilfelle før det konsumeres. Følgende dokument parser i dag, mot samme HEAD:

type: Metric
description: Recognized revenue for a period.
tags:
  - finance          # INNRYKKET blokksekvens
  - revenue
generated: { by: reference_agent/gemini-2.5-pro, at: 2026-06-30T14:00:00Z }
sources:
  - id: revenue-policy
    resource: policies/revenue-recognition.md

tags blir ['finance', 'revenue'], sources blir en liste av dicter. Instrumentet avviser altså ikke alt; tags som blokksekvens fungerer allerede i dag, forutsatt innrykk.

5. Målingen

5.1 Formsensus over de 53 (scratchpad/shapes.py, exit 0)

nøkkel form antall
tags blokksekvens uten innrykk (- a i kolonne 0) 36/53
tags flow-sekvens [a, b, c] 9/53
tags enkeltlinje-skalar 8/53
description skalar + innrykket fortsettelseslinje 29/53
description enkeltlinje-skalar 24/53
generated topp-nivå blokk-mapping ( by: på neste linje) 44/53
generated flow-mapping { ... } 9/53
sources blokksekvens uten innrykk 44/53
sources blokksekvens med innrykk 5/53

5.2 Baseline og kandidater (scratchpad/candidates.py, exit 0)

Kandidatene er målt ved å normalisere overflateformen inn i en form parseren allerede godtar, og så importere parse_frontmatter. Predikatet er aldri re-implementert; bare stavemåten på inputen er skrevet om.

variant passerer dominerende residual
baseline (v1.3.0 som utgitt) 0/53 32× nested mappings, 12× malformed line, 9× flow sequence
P1 skalar-flow-sekvens 6/53 32× nested mappings, 12× malformed line
P2 blokksekvens uten innrykk 0/53 44× nested mappings
P3 foldet plain scalar 0/53 36× malformed line
P1+P3 6/53 36× malformed line
P1+P2 6/53 44× nested mappings
P1+P2+P3 6/53 44× nested mappings, 3× allowlist

De 6 som passerer med P1 er alle i acme_retail; de er 6 av de 9 med flow-sekvens-tags, og de tre siste stoppes av allowlisten (parameters.name ×2, not.term ×1), ikke av tags.

5.3 Taket

Med alle tre predikatene er taket 6/53. Residualet er 44× topp-nivå blokk-mapping på generated og 3× allowlist. En diagnostisk kjøring som også normaliserte topp-nivå blokk-mapping til flow-mapping traff neste vegg med én gang: 44× «a quoted scalar inside a flow mapping is not a supported form» — korpuset skriver at: '2026-07-10T23:16:06+00:00' med enkeltfnutter.

6. Tre påstander i punkt 44 er målt feil

  1. «The line-flat frontmatter parser has no sequence-value type at all.» Usant siden 1.3.0. _consume_block_list (okf.py:662) parser blokklister av rene skalarer, og den positive kontrollen i §4 beviser det. Det som mangler er ikke sekvenstypen, men innrykkskravet: _consume_block_list krever raw[:1] in (" ", "\t"), og korpuset skriver - a i kolonne 0. Feilklassen «44/53 rejected as malformed frontmatter line» er i dag 12/53, fordi de øvrige treffer description-fortsettelsen først.

  2. «removing tags alone lets 4/53 documents pass … removing both … the same 4/53.» Målt i dag: 6/53 med tags fjernet, og 6/53 med begge fjernet. Tallet 4 var riktig for 1.2.0-parseren; 1.3.0s sources-bærere flyttet to dokumenter til.

  3. «tags/description reject before sources is ever read» → derfor er dette «det ENESTE residualet som blokkerer hele korpuset». Den slutningen holder ikke. description alene løsner 0/53 — fjerner man bare description, passerer ingenting. Og lukker man alle tre formene, står 44/53 fortsatt på generated som topp-nivå blokk-mapping. Den bindende skranken på dette korpuset er altså ikke tags/description, men den topp-nivå blokk-mappingen vi bevisst avviser. Punkt 44 overselger sin egen betydning med en faktor på over sju (6 mot 53).

Punkt 44 bør skrives om etter at operatøren har bestemt seg — det er en dokumentasjonsendring som hører sammen med predikatvalget, ikke før det.

7. Kandidatpredikatene

Ordren spurte etter predikatet som slipper inn en ren skalar-flow-sekvens. Det er P1. P2 og P3 tas med fordi målingen viser at P1 alene er en liten gevinst, og beslutningen bør se hva naboene koster.

P1 — flow-sekvens av rene skalarer

Predikat: i _parse_flow_sequence, når første ikke-blanke tegn i et element ikke er {, les elementet som en plain scalar dersom det ikke inneholder noen av { } [ ] : , " ' # og ikke er tomt. Blandet sekvens (skalar + mapping) avvises, slik blokklisten allerede gjør.

  • (a) Slipper inn: tags: [finance, revenue, headline-metric] — 9/53 i korpuset. Passeringen går fra 0/53 til 6/53.
  • (b) Avviser fortsatt: siterte elementer (['a', 'b']), elementer med kolon eller komma i seg, tom sekvens [], uavsluttet [a, b, blandet [a, {b: c}], og nestet [[a]] — alle på tegn-nivå, uten YAML-semantikk.
  • (c) Dybde-1: bruker den ikke opp. Elementene er blad; ingen ny nestingsgrad oppstår. Det er samme dybde blokklisten av skalarer allerede har.
  • (d) Testen som pinner den: tags: [a, b]["a", "b"]; tags: ['a'] raiser; tags: [a, {b: c}] raiser med blandingsfeilen; tags: [] raiser; sources: [{ id: x }] parser uendret (ingen regresjon på G30-bæreren).

P2 — blokksekvens uten innrykk

Predikat: i _consume_block_list, godta også raw[:1] == "-" når linjen starter med - og forrige toppnøkkel hadde tom verdi.

  • (a) Slipper inn: tags: + - a i kolonne 0 — 36/53. Men også sources: i samme form — 44/53.
  • (b) Avviser fortsatt: alt innholdet i elementet avviser i dag; allowlisten og _reject_mapping_construct er uendret.
  • (c) Dybde-1: bruker den ikke opp for skalarelementer, men den er ikke gratis: den åpner samtidig den ikke-innrykkede blokk-mapping-bæreren for sources, hvor elementenes fortsettelseslinjer er innrykket. Det er en større flate enn tags, og den bør vurderes for seg.
  • (d) Testen: tags:\n- a\n- b["a", "b"]; sources:\n- id: x\n title: y → én dict; en linje - a uten forutgående tom toppnøkkel raiser fortsatt; tags:\n- a\n- {b: c} raiser med blandingsfeilen.
  • Målt effekt alene: 0/53. Den løsner ingenting uten P1 eller uten at generated også åpnes.

P3 — foldet plain scalar (fortsettelseslinje)

Predikat: etter en toppnøkkel med ikke-tom, ikke-[/{ verdi, slå sammen etterfølgende innrykkede linjer som ikke starter med - og ikke inneholder en uquotet ": ", med ett mellomrom som skjøt.

  • (a) Slipper inn: description brutt over to linjer — 29/53.
  • (b) Avviser fortsatt: en innrykket linje som ser ut som k: v treffer fremdeles nested-mapping-avvisningen; - treffer fremdeles listeruten. _reject_dangerous_value kjører på den sammenslåtte verdien, ikke på fragmentene.
  • (c) Dybde-1: bruker den ikke opp — resultatet er én skalar. Men den svekker et vern: i dag er enhver innrykket linje uten aktiv listenøkkel et avvist nestet uttrykk. Etter P3 er den regelen betinget av at linjen ikke inneholder ": " — altså samme heuristikk som _reject_mapping_construct, gjenbrukt til å slippe gjennom i stedet for til å avvise.
  • (d) Testen: description: en setning\n som fortsetter → én streng med ett mellomrom; description: x\n y: z raiser fortsatt som nested mapping; description: x\n - a raiser fortsatt.
  • Målt effekt alene: 0/53.

8. Anbefaling

P1 alene. Ikke P2, ikke P3, ikke nå.

Begrunnelsen er tallene, ikke smaken:

  • P1 er den eneste av de tre som flytter passeringstallet i det hele tatt (0 → 6). P2 og P3 gir hver for seg 0/53, og lagt oppå P1 gir de fortsatt 6/53. De koster parserflate og kjøper null målt konformans.
  • P1 er den minste flaten: den er et tegn-nivå-predikat inne i en funksjon som allerede eksisterer, og den bruker ikke opp dybde-1-regelen.
  • P1 lukker et gap STATE allerede fører som bevisst («tags: [a, b] konformansgap»), og den bringer parseren i linje med §4.1s eget skjelett — den ene formen spec-en faktisk skriver ut.
  • P2 og P3 bør ikke besluttes på dette korpuset, fordi korpuset ikke kan skille dem: 44/53 stopper på generated som topp-nivå blokk-mapping uansett. Å bygge P2 og P3 nå ville være å betale for to predikater og måle null.

Den ærlige konsekvensen, som må sies høyt: P1 tar korpuset fra 0/53 til 6/53. Det lukker ikke «hele korpuset». Skal 53/53 nås, er den neste beslutningen en helt annen og mye tyngre en — topp-nivå blokk-mapping (44/53) pluss siterte skalarer (44/53) — og den bruker opp dybde-1-regelen. Det er en sikkerhetsbeslutning, ikke en parserdetalj, og den hører ikke i denne ordren.

9. Verifiseringslogg

Påstand Kommando Exit Resultat
Nevner = 53 find scratchpad/corpus -name '*.md' ! -name index.md ! -name log.md | wc -l 0 53
Pinnen er ekte forgjenger git -C _okf-upstream merge-base --is-ancestor 3fcbb9f HEAD 0 ja (HEAD = 9a15b13)
SPEC-pinnen er ren git -C _okf-canonical rev-parse --short HEAD; git status --porcelain 0 ad30107, rent tre
SPEC har ingen dybderegel grep -n -i 'nest|depth|indent' SPEC.md 0 13 treff, 0 relevante
Baseline 0/53 PYTHONPATH=src .venv/bin/python scratchpad/measure.py 0 0 passerer
Formsensus PYTHONPATH=src .venv/bin/python scratchpad/shapes.py 0 tabell §5.1
Kandidattall PYTHONPATH=src .venv/bin/python scratchpad/candidates.py 0 tabell §5.2
Positiv kontroll inline, se §4 0 parser, tags == ['finance','revenue']
LIMITATIONS-antall grep -c '^- \*\*' docs/LIMITATIONS.md 0 45
Upushet ved øktstart git rev-list --count origin/main..HEAD 0 0

Ikke målt: om P1 påvirker ytelse eller ReDoS-marginen — predikatet er tegn-for-tegn uten regex, men ingen sveip er kjørt, siden ingen kode er skrevet. Kandidatene er målt ved overflatenormalisering, ikke ved en patchet parser: det er en trofast simulering av hva som slippes inn, men den beviser ikke at en implementasjon av P1 avviser nøyaktig (b)-listen. Testene i (d) er det som ville pinne det.