llm-ingestion-okf/tests/test_parent_reaches_reader.py
Kjell Tore Guttormsen 88cf67f12e test(fixtures): the STS fixtures and fixture codes are fictitious
Three STS fixtures still carried the section titles and labels of one real
reference document, and three identifiers were copies of its codes with a
letter or a word swapped. They now describe an invented kitchen counter and
cookbook series: the titles, labels and descriptions of sts-identity.xml,
sts-inherit.xml and sts-empty-label.xml, the P350/P351 document codes, the
99-0001 delivery prefix and chapter 7 of the image and accounting corpora.
Generated fixtures are regenerated and the witness inventory's per-document
totals are identical before and after; only names and text move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 14:52:03 +02:00

246 lines
9.6 KiB
Python

"""`parent` reaches the reader: the concept body, the payload, and the checker.
Round 20 gave a heading-only section a `parent:` key naming the nearest
ancestor that holds text (`okf build --shell-parent`), and no reader could see
it. `okf consume` did not read the key, so the payload did not carry it; the
body held no link, so a reader who opened the file found a `segment_id` it had
no way to follow without enumerating the bundle, which the consumption
contract's SS 9.2 forbids.
Two expressions of one relation, each on its own ground in the canonical spec:
- **The body carries a link.** SS 5.1: "Lineage is expressed through links, not
a dedicated field." SS 6.1: the bundle-relative form, beginning with `/`, "is
the recommended form", and the kind of relationship "is conveyed by the
surrounding prose, not by the link itself". Written only where the body is
its heading alone -- a body holding text already has something to read, and
the segmented goldens' declared parents are bodies holding text.
- **The payload carries the pointer resolved.** SS 4.1 makes `parent:` a
permitted extension. The excerpt names the concept the pointer resolves to
and that concept's title, never the raw `segment_id`: a reader holding
`p1977` cannot open anything.
`sts-inherit.xml` is hand-written in an invented setting and carries no
sentence from any source.
"""
from __future__ import annotations
import re
from pathlib import Path
from typing import Any
import pytest
from llm_ingestion_okf import cli, consume, contract_check, skill
PROJECT_ROOT = Path(__file__).resolve().parents[1]
FIXTURE = Path(__file__).parent / "fixtures" / "sts-inherit.xml"
TEMPLATE = PROJECT_ROOT / "skills" / "okf-consume-template" / "SKILL.md"
CONTRACT = PROJECT_ROOT / "docs" / "consumption-contract.md"
#: shell title -> the section its link and its excerpt name. The same pairs
#: `tests/test_shell_parent.py` holds for the plan.
LINKED = {
"Kjokkenbenk": "Vedlikehold av utstyr",
"Utskifting": "Vedlikehold av utstyr",
"Utskifting av list": "Utskifting av enkeltdeler",
"Utskifting av hengsel": "Utskifting av enkeltdeler",
}
#: The one line a linked body gains, in the spec's recommended form.
LINK = re.compile(r"^Enclosing section: \[(?P<title>[^\]]+)\]\((?P<target>/[^)\s]+\.md)\)$")
def _build(tmp_path: Path, name: str, files: dict[str, bytes], *extra: str) -> Path:
inbox = tmp_path / f"{name}-inbox"
inbox.mkdir()
for filename, data in files.items():
(inbox / filename).write_bytes(data)
bundle = tmp_path / name
assert (
cli.main(
[
"build",
str(inbox),
"--bundle",
str(bundle),
"--bundle-id",
"inherit-fixture",
"--okf-version",
"0.2",
*extra,
]
)
== 0
)
return bundle
def _flagged(tmp_path: Path) -> Path:
return _build(tmp_path, "flagged", {FIXTURE.name: FIXTURE.read_bytes()}, "--shell-parent")
def _concepts(bundle: Path) -> dict[str, tuple[str, dict[str, str], str]]:
"""title -> (concept id, frontmatter, body), for one document's concepts."""
found: dict[str, tuple[str, dict[str, str], str]] = {}
for path in sorted(bundle.rglob("*.md")):
if path.name in ("index.md", "log.md"):
continue
head, _, body = path.read_text(encoding="utf-8").partition("\n---\n")
values = dict(line.split(": ", 1) for line in head.split("\n")[1:] if ": " in line)
concept_id = path.relative_to(bundle).as_posix()[: -len(".md")]
found[values["title"]] = (concept_id, values, body)
return found
# --- The body ------------------------------------------------------------------
def test_a_shell_s_body_links_once_to_its_enclosing_section(tmp_path: Path) -> None:
bundle = _flagged(tmp_path)
concepts = _concepts(bundle)
for title, (_, _, body) in concepts.items():
links = [match for line in body.split("\n") if (match := LINK.match(line))]
if title not in LINKED:
assert links == [], title
continue
assert len(links) == 1, title
ancestor_id = concepts[LINKED[title]][0]
assert links[0]["title"] == LINKED[title]
assert links[0]["target"] == f"/{ancestor_id}.md"
# Not broken: the target is a concept of this bundle.
assert (bundle / links[0]["target"].lstrip("/")).is_file()
# --- The payload ---------------------------------------------------------------
def test_an_excerpt_carries_the_parent_it_can_follow(tmp_path: Path) -> None:
bundle = _flagged(tmp_path)
concepts = _concepts(bundle)
payload = consume.build_payload(bundle, question="Utskifting av list")
by_title = {excerpt["title"]: excerpt for excerpt in payload["excerpts"]} # type: ignore[attr-defined]
shell = by_title["Utskifting av list"]
assert shell["parent"] == {
"concept_id": concepts["Utskifting av enkeltdeler"][0],
"title": "Utskifting av enkeltdeler",
}
# Conditional, never empty: an excerpt whose concept names no parent
# carries neither member.
for title, excerpt in by_title.items():
if title not in LINKED:
assert "parent" not in excerpt and "parent_unresolved" not in excerpt, title
def test_a_pointer_that_resolves_to_nothing_is_named_and_not_dropped(tmp_path: Path) -> None:
bundle = _flagged(tmp_path)
concepts = _concepts(bundle)
shell_id, values, _ = concepts["Utskifting av list"]
path = bundle / f"{shell_id}.md"
path.write_text(
path.read_text(encoding="utf-8").replace(f"parent: {values['parent']}\n", "parent: p999\n"),
encoding="utf-8",
)
payload = consume.build_payload(bundle, question="Utskifting av list")
shell = next(e for e in payload["excerpts"] if e["title"] == "Utskifting av list") # type: ignore[attr-defined]
assert "parent" not in shell
assert shell["parent_unresolved"] is True
def test_a_pointer_resolves_inside_its_own_document(tmp_path: Path) -> None:
"""Segment ids are per plan, so `p1` exists once in EVERY document. A
pointer resolved across the bundle would be ambiguous in two documents and
wrong in one."""
other = (
FIXTURE.read_bytes()
.replace(b"Fikstur for arvet kontekst", b"Annen fikstur")
.replace("alt utstyr i kjøkkenet".encode(), b"utstyr i kjeller")
)
bundle = _build(
tmp_path,
"two",
{"a.xml": FIXTURE.read_bytes(), "b.xml": other},
"--shell-parent",
)
payload = consume.build_payload(
bundle, question="Utskifting av list og hengsel i kjokkenbenk", k=50
)
carried = [e for e in payload["excerpts"] if "parent" in e] # type: ignore[attr-defined]
documents = {str(e["concept_id"]).split("/")[0] for e in carried}
assert documents == {"a", "b"}
for excerpt in carried:
assert (
str(excerpt["parent"]["concept_id"]).split("/")[0]
== str(excerpt["concept_id"]).split("/")[0]
)
# --- The checker ---------------------------------------------------------------
def _pair(tmp_path: Path) -> tuple[str, dict[str, Any]]:
bundle = _flagged(tmp_path)
text, _ = skill.render(bundle, out=bundle.parent / "unwritten")
payload = consume.build_payload(bundle, question="Utskifting av list")
return text, dict(payload)
def test_the_checker_has_nineteen_rules_and_accepts_a_followable_parent(tmp_path: Path) -> None:
text, payload = _pair(tmp_path)
assert any("parent" in excerpt for excerpt in payload["excerpts"])
report = contract_check.check(text, payload)
assert report.findings == ()
assert report.rules_evaluated == len(contract_check.RULES) == 19
assert "19 rules" in report.render()
@pytest.mark.parametrize(
"parent",
[
# The raw segment id round 20 wrote: nothing a reader can open.
"p4",
# A concept id naming nothing in this bundle.
{"concept_id": "nowhere/in-this-bundle", "title": "Utskifting av enkeltdeler"},
# No title, so nothing a citation can be made of.
{"concept_id": "SELF-PARENT"},
# A section naming itself.
"SELF",
],
)
def test_known_negative_a_parent_a_reader_cannot_follow_is_a_finding(
tmp_path: Path, parent: object
) -> None:
text, payload = _pair(tmp_path)
shell = next(e for e in payload["excerpts"] if "parent" in e)
if parent == "SELF":
parent = {"concept_id": shell["concept_id"], "title": shell["title"]}
elif isinstance(parent, dict) and parent.get("concept_id") == "SELF-PARENT":
parent = {"concept_id": shell["parent"]["concept_id"]}
shell["parent"] = parent
found = [finding.code for finding in contract_check.check(text, payload).findings]
assert found == ["parent_unfollowable"]
def test_a_payload_carrying_no_parent_meets_the_rule_as_before(tmp_path: Path) -> None:
text, payload = _pair(tmp_path)
for excerpt in payload["excerpts"]:
excerpt.pop("parent", None)
assert contract_check.check(text, payload).findings == ()
# --- The documents a reader is told by -----------------------------------------
def test_the_contract_names_the_field_and_what_the_checker_reads() -> None:
section = CONTRACT.read_text(encoding="utf-8").split("## 8. The payload shape")[1]
section = section.split("## 9.")[0]
assert '"parent"' in section
assert "`parent_unresolved`" in section
assert "are not read by the checker" not in section
def test_the_skill_template_tells_the_reader_what_parent_is() -> None:
text = TEMPLATE.read_text(encoding="utf-8")
assert "`parent`" in text
assert "`parent_unresolved`" in text