fix(okf): top-level frontmatter title survives a nested sources: title

P15 (order 20260912T220951Z). okf._frontmatter_from_text was linewise
last-write-wins over EVERY line regardless of indentation, so a curated
concept's own top-level `title:` got silently overwritten by the nested
`sources:\n  - title: ...` block's title. Fix: a top-level (unindented)
key always wins over an indented one of the same name; a nested line
with no top-level counterpart is still preserved (SPEC §4).

Red-before/green-after: new test
test_parse_frontmatter_top_level_title_survives_nested_sources_title
(tests/test_okf.py) failed on 45edbf5 (fm["title"] == "N500:2024",
expected the concept's own), green after the fix.

Re-measured on all four vegnormal-okf bases (concept files / distinct
titles): n100-2023 446/446 (was 1) - n200-2024 1133/1133 (was 1) -
n500-2024 270/270 (was 1) - r761-2025 2756/2407 (genuine repeated
process names, not a collapse). directory_listing on krav/N500:
269 documents / 269 distinct titles (was 1).

tests/test_context_sets_loadbearing.py:
- The P14 tripwire test (asserting parse_frontmatter DID collapse
  titles) is INVERTED, not deleted, per the order: it now asserts the
  fix holds, as a live regression guard.
- own_frontmatter() stays (not replaced by parse_frontmatter): measured
  29,500 field reads (type/title/req_number/prosessnr, all four bases)
  agree exactly except for quote-stripping (2,728/29,500, zero value
  mismatches) - own_frontmatter unquotes for fasit comparison,
  parse_frontmatter deliberately doesn't (D1/(a)/(i): unquote_scalar is
  the ONE unquoting rule).

docs/2026-09-12-p14-kontekstsett.md Part B correction: the "22 of 22
cost words absent from n100/n200/n500" claim was false - n500-2024
carries `kroner` as a false positive (substring match inside
"borkroner", drill bits, not money). The original 22-word list was
never persisted, so only ~9 of the 22 survive named. Replaced with a
newly named, persisted 22-word list and the actual re-measured count:
n100 22/22 absent - n200 22/22 - n500 21/22 (kroner via borkroner) -
r761 18/22 (4 genuine cost words). No gate touched (no fasit anchor is
`kroner`).

Verification: full suite 1643 passed / 5 skipped (was 1642/5 on
45edbf5, +1 new test, 0 removed) - `uv run pytest -q`. ruff check +
ruff format --check clean on the three changed source/test files.
Golden transcripts byte-unchanged: shasum -a 1
tests/golden/demo-transcript.stdout = ea8c534773acdbe41ae68f2c55724d69aaf8be4f,
demo-transcript.stderr = ede3e2f685ce6a14ad9888e9de421d1a66f6c611.
No version bump, no push (both forbidden by the order).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-13 07:40:57 +02:00
commit f13dc64a0a
4 changed files with 127 additions and 38 deletions

View file

@ -119,17 +119,42 @@ Den døra er ikke stresstestens sti — stresstesten går `--mandate` over den o
### 2.2 Falsifiseringsgaten — og hvorfor den er så skarp her
Hvert sett bærer minst to spørsmål basen **ikke kan besvare**, der riktig svar er å si det.
MÅLT over alle fire basene, 22 prøvde kostnadsord:
**RETTET 2026-09-13 (P15, ordre `20260912T220951Z`).** Den opprinnelige setningen her — «samtlige 22
er FRAVÆRENDE fra n100/n200/n500» — var USANN og er fjernet. Kun 8 av de 22 opprinnelig prøvde
ordene ble noensinne navngitt (listen over, med et avsluttende «…»), og selve 22-ordslisten ble
ALDRI persistert noe sted i repoet — den kan derfor ikke re-måles verbatim. Det er en
nevner-svikt av Verifiseringslovens ansikt 4-type: et «fraværende»-utsagn uten en gjenfinnbar
liste er ikke et mål, det er en påstand om et mål som fant sted.
- **n100-2023, n200-2024, n500-2024: samtlige 22 er FRAVÆRENDE fra hele basen**`enhetspris`,
`kroner`, `budsjett`, `kostnadsestimat`, `prisskjema`, `timepris`, `nåverdi`, `driftskostnad`
- **r761-2025:** 18 av 22 fraværende; basen bærer `kroner`, `budsjett`, `enhetspris` og `hydrogen`,
fordi prosesskoden ER kontraktsspråk.
**Ny, navngitt og persistert 22-ordsliste** (2026-09-13, denne rettelsen — velges for domene-treffsikkerhet, ikke for å oppnå et bestemt utfall), re-målt med gatens egen
substring-regel (`anchors_are_absent`, `tests/test_context_sets_loadbearing.py`) mot alle fire
basene:
Det er et funn i seg selv, og det er hele grunnen til at gaten er verdt å teste: po sitt oppdrag er
å finne **kostnadsbesparelser**, og tre av fire baser inneholder ikke ett eneste pengeord. En
kjøring som svarer med et kronebeløp mot N100 har ikke lest basen — den har funnet på.
`enhetspris`, `kroner`, `budsjett`, `kostnadsestimat`, `prisskjema`, `timepris`, `nåverdi`,
`driftskostnad`, `anleggskostnad`, `materialkostnad`, `arbeidskostnad`, `investeringskostnad`,
`vedlikeholdskostnad`, `kapitalkostnad`, `finansieringskostnad`, `livssykluskostnad`,
`kontraktssum`, `anbudspris`, `tilbudspris`, `merverdiavgift`, `avskrivning`,
`besparelsespotensial`.
**Faktisk telling per base (446/1133/270/2756 konsepter):**
- **n100-2023: 22 av 22 fraværende.**
- **n200-2024: 22 av 22 fraværende.**
- **n500-2024: 21 av 22 fraværende — IKKE 22.** Basen bærer `kroner`, men som en FALSK POSITIV:
ordet forekommer kun inni `borkroner` (boreutstyr, ikke penger) i
`krav/N500/id-41a2f459-e362-42d8-d725-aaee8290c7c1.md` — «(styrestenger, borkroner), nøyaktighet
ved a…». Delstreng-regelen i Rule U treffer bevisst uten ordgrenser (§ 2.3), og dette er den
konkrete prisen for det: en ekte nulltreff-base kan likevel «bære» et pengeord gjennom et
urelatert sammensatt ord. Funnet endrer INGEN gate — ingen fasit-anchor er `kroner` — men det
gjør STATE-påstanden usann og er derfor rettet her.
- **r761-2025: 18 av 22 fraværende** (uendret konklusjon fra tidligere, men re-målt mot den nye
listen): basen bærer `enhetspris`, `kroner`, `budsjett` og `kapitalkostnad` — alle fire er ekte
kostnadsspråk (f.eks. «kapitalkostnader», «mengder i kroner avviker fra summen»), konsistent med
at prosesskoden ER kontraktsspråk.
Konklusjonen står: tre av fire baser er reelt uten kostnadsspråk (n500s ene treff er en
falsk positiv, ikke et pengeord), og det er hele grunnen til at gaten er verdt å teste. En kjøring
som svarer med et kronebeløp mot N100 har ikke lest basen — den har funnet på.
### 2.3 Regelen for «kan ikke besvares» (DEL 3c) — skrevet ned