"""Generated regions inside a page body, found by delimiter rather than by prose. `xref` owns the links block and `cite` owns the footnotes block. Both used to be located by matching their **heading text** - `^## Beziehungen$` - which made a translated heading a structural fact and put the KB language into the compiler. It also made the region's *end* a guess: the footnotes block ran to the next heading, and before that to the end of the file, which silently deleted whatever sat after it on eight pages. A marker pair answers both questions exactly: ## Beziehungen - **depends-on:** [[Hermes]] Everything between the markers is generated and is replaced wholesale on the next write - heading included, which is why the heading text is a *rendering* value from `kb/CONVENTIONS.md` rather than something the tool searches for. An author never edits inside the markers; anything they put there is overwritten without warning, exactly like `kb/index.md`. The markers are HTML comments: invisible in every renderer this corpus is read through, and the same convention `dist:strip-start`/`-end` already uses in `AGENTS.md`. They cost a reader nothing and cost an LLM about twenty tokens a page - the price of not having to guess where a generated region ends. """ from __future__ import annotations import re from typing import Optional # The two regions the tool owns. `see-also` is deliberately absent: it was the # reciprocal half of the old bidirectional `xref add`, and under authored # directional edges it is a *label* (`see-also`) inside the links block, not a # section of its own. LINKS = "links" FOOTNOTES = "footnotes" BLOCKS = (LINKS, FOOTNOTES) _NAME = r"[a-z][a-z0-9-]*" def open_marker(name: str) -> str: return f"" def close_marker(name: str) -> str: return f"" def _region_re(name: str) -> re.Pattern[str]: """The whole region including both markers and the blank line around it.""" return re.compile( r"\n*" + re.escape(open_marker(name)) + r".*?" + re.escape(close_marker(name)) + r"\n*", re.DOTALL, ) _ANY_OPEN_RE = re.compile(rf"") _ANY_CLOSE_RE = re.compile(rf"") def find(body: str, name: str) -> Optional[str]: """The generated content of `name`'s region, markers excluded, or None.""" match = _region_re(name).search(body) if not match: return None text = match.group(0) start = text.index(open_marker(name)) + len(open_marker(name)) end = text.index(close_marker(name)) return text[start:end].strip("\n") def render(name: str, heading: str, lines: list[str]) -> str: """A whole region, ready to place into a body. Empty `lines` renders "". An empty region is no region at all rather than a heading with nothing under it: a page that cites nothing should not carry an empty Footnotes section, and the same holds for a page with no declared edges. """ if not lines: return "" parts = [open_marker(name), f"## {heading}", "", *lines, close_marker(name)] return "\n".join(parts) def replace(body: str, name: str, region: str) -> str: """Put `region` where `name`'s region is, or append it if there is none. Appending at the end is right for both blocks: they are the page's trailing machine-owned material, and an author's prose never follows them. A region that is `""` removes what was there. """ existing = _region_re(name).search(body) if existing: replacement = f"\n\n{region}\n" if region else "\n" return (body[: existing.start()] + replacement + body[existing.end():]).rstrip("\n") + "\n" if not region: return body return body.rstrip("\n") + "\n\n" + region + "\n" def strip(body: str, name: str) -> str: """The body with `name`'s region removed entirely.""" return replace(body, name, "") def marker_pairs(body: str) -> dict[str, int]: """How many complete open/close pairs each region name has in `body`. The invariant `migrate verify` checks. An agent rewriting prose next to a boundary can drop or duplicate a marker, and the failure is otherwise silent: a lost opening marker turns a generated region into ordinary prose that the next write appends a second copy beside. """ opens = [m.group(1) for m in _ANY_OPEN_RE.finditer(body)] closes = [m.group(1) for m in _ANY_CLOSE_RE.finditer(body)] names = set(opens) | set(closes) return {name: min(opens.count(name), closes.count(name)) for name in sorted(names)} def unbalanced_markers(body: str) -> list[str]: """Region names whose open and close markers do not pair up.""" opens = [m.group(1) for m in _ANY_OPEN_RE.finditer(body)] closes = [m.group(1) for m in _ANY_CLOSE_RE.finditer(body)] return sorted( name for name in set(opens) | set(closes) if opens.count(name) != closes.count(name) )