feat(description): an STS section's description is its own first spec point
K3-19 c. The NISO-STS reader records, per titled <sec>, the FIRST <p> of its FIRST direct-child <sec sec-type="spec"> as `OutlineMark.description`. The plan entry carries it (`description`, only where the source has one, so every other row's plan keeps its bytes), `parse_segmentation_plan` refuses an empty, multi-line or non-string value, and the door writes it as the concept's `description` after the gate has seen it: it is document text persisted outside the body the gate screens, so it is kept only on the non-blocking floor and as the sanitized text. SPEC SS 4.1 makes `description` RECOMMENDED and sets no length, in SS 4.1, SS 8 or SS 11. The limit is ours and structural -- one paragraph, whole -- because a cut inside it writes a sentence the source never wrote. Measured on R761: 2 026 of 2 761 titled sections carry a direct-child spec point; the first <p> runs 17 / 109 / 273 / 521 / 942 characters (min / median / p90 / p99 / max). A section with none gets no key; nothing is derived from the title. A stated `--frontmatter description=...` replaces it. The extracted text does not move: the description is read beside it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0dbc331b76
commit
de7849e35b
6 changed files with 96 additions and 4 deletions
|
|
@ -157,6 +157,11 @@ class SegmentEntry:
|
|||
derived: frozenset[str] = field(default_factory=frozenset)
|
||||
anchor: SegmentAnchor | None = None
|
||||
adjudication: SegmentVerdict | None = None
|
||||
#: The SOURCE's own one-line summary of this segment, where it declares one
|
||||
#: (NISO-STS: the section's first spec point). Carried in the plan beside
|
||||
#: `title` for the same reason `title` is: a plan is the record a rebuild
|
||||
#: replays, and the door writes it as the concept's `description`.
|
||||
description: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -363,6 +368,20 @@ def _parse_entry(payload: Any, *, position: int) -> SegmentEntry:
|
|||
f"{where} field 'parent_id' must be a non-empty string or absent, got {parent_id!r}",
|
||||
code="segmentation_plan_invalid",
|
||||
)
|
||||
description = payload.get("description")
|
||||
if description is not None and (
|
||||
not isinstance(description, str)
|
||||
or not description
|
||||
or "\n" in description
|
||||
or "\r" in description
|
||||
):
|
||||
# Written into line-oriented frontmatter, so a line break would inject
|
||||
# a key and an empty value would state a summary that says nothing.
|
||||
raise SegmentationError(
|
||||
f"{where} field 'description' must be a non-empty single-line string or "
|
||||
f"absent, got {description!r}",
|
||||
code="segmentation_plan_invalid",
|
||||
)
|
||||
return SegmentEntry(
|
||||
segment_id=segment_id,
|
||||
path=normalize_segment_path(payload["path"], where=where),
|
||||
|
|
@ -374,6 +393,7 @@ def _parse_entry(payload: Any, *, position: int) -> SegmentEntry:
|
|||
derived=_parse_derived(payload.get("derived", ()), where=where),
|
||||
anchor=_parse_anchor(payload.get("anchor"), where=where),
|
||||
adjudication=_parse_verdict(payload.get("adjudication"), where=where),
|
||||
description=description,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue