feat(consume): parent reaches the reader -- excerpt field, body link, checker rule
K3-21 A. `okf consume` resolves a concept's `parent:` pointer -- a
`segment_id`, unique only inside one document's plan -- among the concepts
sharing its `source_file` (`consume.link_parents`, one pass, no file opened
again) and an excerpt carries `parent: { concept_id, title }`. Conditional
like `req_number`: a concept with no `parent` key moves no byte. A pointer
that lands nowhere is named `parent_unresolved: true`, never dropped.
The door writes ONE line into a heading-only body whose entry has a parent:
`Enclosing section: [<title>](/<bundle-relative path>)` (SPEC SS 5.1 lineage
through links, SS 6.1 the recommended absolute form and the kind in the
prose). Only such a body, so the segmented goldens' declared parents -- bodies
holding text -- are untouched. Appended AFTER structure derivation and
screened on its own (`_screened`, the `description` rule): read as body text
the link was derived into a second, unresolved `references` edge, measured on
the fixture. `segmentation.heading_only` is the one predicate the proposer and
the door share.
`okf check` gains its seventeenth rule, `parent_unfollowable`: a `parent`
that is not a concept_id and title, names its own excerpt, or names a concept
in neither `excerpts` nor `withheld` (together every considered concept).
Contract SS 8 point 6 added, the figure carries `parent`, and "additional
members are not read by the checker" now says the checker reads only the
members SS 8 names. The template tells the reader what `parent` is and that
SS 2.2 lets it read that one concept; `skill.CONDITIONAL_FIELDS` gains
`parent`. README and CLAUDE.md say what consume now reads.
Moved on purpose, each named: the SS 7.4 known-positive IS the contract
document, so `budget.known_positive` moves in every payload (13 238 / 12 893
/ 345 -> 14 455 / 14 083 / 372); `skills/okf-consume/` regenerated from the
segmented golden, whose plan declares s1 and s2 under s0 -- its example
payload now carries both parents; `test_bundle_identity` 16 -> 17 rules;
`test_shell_parent`'s byte test also accounts for the link line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a5cd7c5688
commit
4f7bd61500
16 changed files with 319 additions and 58 deletions
|
|
@ -52,6 +52,7 @@ from .segmentation import (
|
|||
SegmentationPlan,
|
||||
SegmentEntry,
|
||||
assert_plan_applies,
|
||||
heading_only,
|
||||
observed_extractor_version,
|
||||
slice_segments,
|
||||
)
|
||||
|
|
@ -101,6 +102,33 @@ def inbox_filename(slug: str, *, profile: BundleProfile = DEFAULT) -> str:
|
|||
)
|
||||
|
||||
|
||||
#: The words around the one link a heading-only body gains. SPEC SS 6.1: the
|
||||
#: kind of relationship "is conveyed by the surrounding prose, not by the link
|
||||
#: itself", so the line names the relation, in two words, because generic code
|
||||
#: writes them into a body in the source's own language.
|
||||
ENCLOSING_SECTION = "Enclosing section"
|
||||
|
||||
|
||||
def _link_enclosing(body: str, parent: SegmentEntry, gate: Gate) -> str:
|
||||
"""`body` plus one line linking the section that encloses it.
|
||||
|
||||
SPEC SS 5.1: "Lineage is expressed through links, not a dedicated field";
|
||||
the `parent:` key stays beside it as a SS 4.1 extension. Bundle-relative
|
||||
and absolute, SS 6.1's "recommended form", because the parent sits in
|
||||
another directory and a relative link would count `..` across a layout the
|
||||
next round may change.
|
||||
|
||||
The line carries the parent's title, which is document text, and joins a
|
||||
body the gate has already judged -- so it is screened on its own, the rule
|
||||
`_screened` applies to a `description`, and dropped rather than refused
|
||||
when the gate would not persist it.
|
||||
"""
|
||||
line = _screened(gate, f"{ENCLOSING_SECTION}: [{parent.title}](/{parent.path})")
|
||||
if line is None:
|
||||
return body
|
||||
return f"{body.rstrip(chr(10))}\n\n{line}\n"
|
||||
|
||||
|
||||
def _normalize_body(text: str) -> str:
|
||||
# LF-only with exactly one trailing newline is a byte-level guarantee, and
|
||||
# dropped files legitimately arrive with CRLF — normalising is the
|
||||
|
|
@ -820,6 +848,16 @@ def _render_segments(
|
|||
extractor_version=observed_extractor_version(extractor_id),
|
||||
)
|
||||
sliced = slice_segments(text, plan)
|
||||
# A body that is its heading alone gains ONE line linking the section its
|
||||
# `parent` names. Only such a body: one holding text already has something
|
||||
# to read, and the segmented goldens' declared parents are bodies holding
|
||||
# text. Decided on the SLICE, the window the proposer's own predicate read.
|
||||
enclosing = {entry.segment_id: entry for entry in plan.entries}
|
||||
linked = {
|
||||
entry.segment_id
|
||||
for entry, body in sliced
|
||||
if entry.parent_id is not None and heading_only(body)
|
||||
}
|
||||
|
||||
decisions = [(entry, gate(body)) for entry, body in sliced]
|
||||
refused = [
|
||||
|
|
@ -837,11 +875,21 @@ def _render_segments(
|
|||
if profile.index.facets is not None:
|
||||
structure = derive_document_structure(decision.sanitized_text, source_file=source_file)
|
||||
_validate_facets(structure, profile)
|
||||
body = decision.sanitized_text
|
||||
if entry.segment_id in linked:
|
||||
# AFTER the structure is derived, and that order is the rule: read
|
||||
# as body text, the link is a bundle-local target, so derivation
|
||||
# would restate the `parent` relation as a `references` edge -- one
|
||||
# relation under two kinds, the second rendered unresolved because
|
||||
# nothing resolves the absolute form. Measured on the fixture
|
||||
# before this order was chosen.
|
||||
assert entry.parent_id is not None
|
||||
body = _link_enclosing(body, enclosing[entry.parent_id], gate)
|
||||
outputs.append(
|
||||
(
|
||||
entry.path,
|
||||
render_inbox_concept(
|
||||
decision.sanitized_text,
|
||||
body,
|
||||
okf_type=entry.okf_type,
|
||||
# DECLARED by the adjudicator, never derived from the
|
||||
# segment's own first line: the plan is the record of the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue