# 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: # REQUIRED > title: > description: > resource: > tags: [, , ...] # 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 `[, ...]` 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.