"""`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 = { "Rekkverk": "Vedlikehold av utstyr", "Utskifting": "Vedlikehold av utstyr", "Utskifting av list": "Utskifting av enkeltdeler", "Utskifting av stolpe": "Utskifting av enkeltdeler", } #: The one line a linked body gains, in the spec's recommended form. LINK = re.compile(r"^Enclosing section: \[(?P[^\]]+)\]\((?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(b"alt utstyr langs vegen", b"utstyr i tunnel") ) 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 stolpe i rekkverk", 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_seventeen_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) == 17 assert "17 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