1
0
Fork 0

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.
This commit is contained in:
Kjell Tore Guttormsen 2026-09-06 23:58:02 +02:00
commit 6e7c8d2b98
5 changed files with 579 additions and 0 deletions

View file

@ -0,0 +1,317 @@
# 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`):
> ```yaml
> ---
> 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`:
```yaml
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.