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 on45edbf5(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 on45edbf5, +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:
parent
45edbf5957
commit
f13dc64a0a
4 changed files with 127 additions and 38 deletions
|
|
@ -105,13 +105,34 @@ def _split_frontmatter(text: str) -> tuple[list[str], str, bool]:
|
|||
|
||||
def _frontmatter_from_text(text: str) -> dict[str, str]:
|
||||
"""``parse_frontmatter``'s rule, applied to already-read text: every frontmatter line that
|
||||
carries a colon becomes one ``key: value`` pair, last write winning. Unterminated blocks are
|
||||
parsed as if closed — the pre-split behaviour, preserved deliberately."""
|
||||
carries a colon becomes one ``key: value`` pair, last write winning AMONG LINES AT THE SAME
|
||||
LEVEL. Unterminated blocks are parsed as if closed — the pre-split behaviour, preserved
|
||||
deliberately.
|
||||
|
||||
A TOP-LEVEL key (no leading whitespace — ``_split_frontmatter`` preserves indentation
|
||||
verbatim, so this is a real structural signal, not a guess) always wins over an indented one
|
||||
of the same name: a nested line is a block-sequence/-mapping entry under some OTHER top-level
|
||||
key (``sources:\\n - title: …`` names the SOURCE, not the concept) and must never overwrite
|
||||
the concept's own field, however late it appears in the scan. Measured 2026-09-13 on
|
||||
vegnormal-okf concepts: without this, every ``sources:``-bearing file's nested ``title``
|
||||
replaced its own, collapsing 4605 concepts to 4 distinct titles. A nested line with no
|
||||
top-level counterpart is still preserved (OKF SPEC §4, "unknown fields are preserved")."""
|
||||
fm: dict[str, str] = {}
|
||||
top_level: set[str] = set()
|
||||
for line in _split_frontmatter(text)[0]:
|
||||
if not line:
|
||||
continue
|
||||
key, sep, val = line.partition(":")
|
||||
if sep:
|
||||
fm[key.strip()] = val.strip()
|
||||
if not sep:
|
||||
continue
|
||||
key = key.strip()
|
||||
if line[0].isspace():
|
||||
if key in top_level:
|
||||
continue
|
||||
fm[key] = val.strip()
|
||||
else:
|
||||
fm[key] = val.strip()
|
||||
top_level.add(key)
|
||||
return fm
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue