fix(structure): the index resolves a parent naming a segment of its own document

K3-21 C. `resolve_structure` asks `_segment_lookup` first for a `parent`
edge: `(source_file, segment_id)` -> concept name, keyed off each concept's
own frontmatter (`DocumentStructure.declared`, no file read again), so a
pointer lands only inside the pointing concept's document -- `p1` exists in
every document of a multi-document bundle. A value no segment answers to is a
document number and is looked up exactly as before; a pointer naming nothing
keeps `UNRESOLVED_MARKER`. The rendering rule is untouched: a resolved
relation renders as its subject, so `parent: p1977?` becomes `parent: p1977`.

Moved on purpose, each named: both segmented goldens' index files
(`examples/ingest-golden-segmented{,-okf-v0-2}/expected-bundle/krav/1-{1,2}/
index.md`), whose declared parents s1 and s2 -> s0 rendered `parent: s0?`
while s0 stood in the bundle -- four lines, `?` removed. The four goldens
`test_the_four_existing_goldens_are_untouched` guards are not among them.
`skills/okf-consume/` regenerated, because the golden's index bytes -- and so
its ref -- moved. `test_shell_parent`'s byte test now expects the resolved
facet. README and CLAUDE.md no longer say the index renders it unresolved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-11 13:55:03 +02:00
commit 59703699a5
10 changed files with 50 additions and 17 deletions

View file

@ -411,6 +411,28 @@ def _lookup(documents: Mapping[str, DocumentStructure]) -> dict[str, str]:
return {key: next(iter(owners)) for key, owners in claims.items() if len(owners) == 1}
#: The key a segmentation plan's entry id is written under (`inbox.py`), and
#: the value a declared `parent` names.
SEGMENT_ID_KEY = "segment_id"
def _segment_lookup(documents: Mapping[str, DocumentStructure]) -> dict[tuple[str, str], str]:
"""`(source_file, segment_id)` -> concept name, for every concept a plan wrote.
A plan entry's `parent_id` names another entry of the SAME plan, and a plan
is one document's: `p1` exists in every document of a multi-document bundle,
so the key carries the source file. `declared` is the concept's own
frontmatter, verbatim, which is where the door wrote `segment_id` -- no
file is read again. A key claimed twice is dropped, `_lookup`'s rule.
"""
claims: dict[tuple[str, str], set[str]] = {}
for name, document in documents.items():
segment_id = document.declared.get(SEGMENT_ID_KEY, "").strip()
if segment_id:
claims.setdefault((document.source_file, segment_id), set()).add(name)
return {key: next(iter(owners)) for key, owners in claims.items() if len(owners) == 1}
def resolve_structure(documents: Mapping[str, DocumentStructure]) -> BundleStructure:
"""Resolve every pointer in `documents` against the bundle as a whole.
@ -426,17 +448,24 @@ def resolve_structure(documents: Mapping[str, DocumentStructure]) -> BundleStruc
function proposes on its own same number, ordered versions always is.
"""
by_key = _lookup(documents)
by_segment = _segment_lookup(documents)
edges: list[StructureEdge] = []
for name in sorted(documents):
document = documents[name]
if document.parent_number is not None:
# One key, two meanings (`inbox.py`): a plan's DECLARED `parent_id`
# names a segment of the same document, and `structure`'s DERIVED
# parent is a document number. The segment is asked first and only
# inside the pointing concept's own document; a value no segment
# answers to is a number, looked up exactly as before.
edges.append(
StructureEdge(
source=name,
kind="parent",
subject=document.parent_number,
target=by_key.get(document.parent_number.upper()),
target=by_segment.get((document.source_file, document.parent_number))
or by_key.get(document.parent_number.upper()),
derived="parent" in document.derived,
)
)