feat(segmentation): parse the adjudication state a plan already carries

This commit is contained in:
Kjell Tore Guttormsen 2026-09-02 14:44:06 +02:00
commit 233cdc5671
2 changed files with 181 additions and 0 deletions

View file

@ -99,6 +99,23 @@ _CONVERTED_EXTRACTOR_IDS = frozenset({"docx", "xlsx", "pptx", "odt", "rtf"})
_PDF_DISTRIBUTION = "pdfminer.six"
@dataclass(frozen=True)
class SegmentVerdict:
"""One adjudicator's judgement of one entry, with the time it cost.
Key names are B2's (`docs/plan/office-intake.md` § 5) verbatim, so the
profile that projects this into frontmatter has nothing to translate.
The dwell time is not bookkeeping. A ratified flag carrying no per-item
time is unfalsifiable -- nothing distinguishes a judgement from a click --
and it is the same number that makes adjudication throughput measurable.
"""
adjudicated_by: str
adjudicated_at: str
adjudication_dwell_s: int
@dataclass(frozen=True)
class SegmentAnchor:
"""The text an entry names, plus enough context to find it again.
@ -139,6 +156,7 @@ class SegmentEntry:
parent_id: str | None = None
derived: frozenset[str] = field(default_factory=frozenset)
anchor: SegmentAnchor | None = None
adjudication: SegmentVerdict | None = None
@dataclass(frozen=True)
@ -162,6 +180,16 @@ class SegmentationPlan:
extractor_version: str
adjudicated_at: str
entries: tuple[SegmentEntry, ...]
#: Whether a human has ratified this plan. Absent means NOT adjudicated:
#: the proposer has written the flag since it shipped, and the parser used
#: to drop it, so a plan nobody had looked at parsed into an object
#: identical to a ratified one. Defaulting the other way would let a
#: proposal replay as a judgement, which is the failure this module exists
#: to prevent.
adjudicated: bool = False
#: What produced the proposal, when one did. `None` for a plan authored by
#: hand, which is a real case and not a missing value.
proposed_by: str | None = None
def _require_str(payload: Mapping[str, Any], key: str, *, where: str) -> str:
@ -285,6 +313,33 @@ def _parse_anchor(value: Any, *, where: str) -> SegmentAnchor | None:
return SegmentAnchor(quote=quote, prefix=context["prefix"], suffix=context["suffix"])
def _parse_verdict(value: Any, *, where: str) -> SegmentVerdict | None:
if value is None:
return None
if not isinstance(value, Mapping):
raise SegmentationError(
f"{where} field 'adjudication' must be a mapping carrying "
"'adjudicated_by', 'adjudicated_at' and 'adjudication_dwell_s', "
f"got {value!r}",
code="segmentation_plan_invalid",
)
dwell = value.get("adjudication_dwell_s")
# `bool` is a subclass of `int`, and `True` would silently record a dwell
# of one second. The verdict is the record that makes ratification
# falsifiable, so a nonsense number in it is worse than none.
if not isinstance(dwell, int) or isinstance(dwell, bool) or dwell < 0:
raise SegmentationError(
f"{where} adjudication field 'adjudication_dwell_s' must be a whole "
f"number of seconds, got {dwell!r}",
code="segmentation_plan_invalid",
)
return SegmentVerdict(
adjudicated_by=_require_str(value, "adjudicated_by", where=f"{where} adjudication"),
adjudicated_at=_require_str(value, "adjudicated_at", where=f"{where} adjudication"),
adjudication_dwell_s=dwell,
)
def _parse_entry(payload: Any, *, position: int) -> SegmentEntry:
where = f"segmentation entry {position}"
if not isinstance(payload, Mapping):
@ -318,9 +373,35 @@ def _parse_entry(payload: Any, *, position: int) -> SegmentEntry:
parent_id=parent_id,
derived=_parse_derived(payload.get("derived", ()), where=where),
anchor=_parse_anchor(payload.get("anchor"), where=where),
adjudication=_parse_verdict(payload.get("adjudication"), where=where),
)
def _parse_adjudicated(value: Any) -> bool:
# Not `bool(value)`. A JSON `"false"` is a non-empty string and would
# ratify a plan by accident, which is the one direction this flag must
# never fail in.
if not isinstance(value, bool):
raise SegmentationError(
"the segmentation plan field 'adjudicated' must be a boolean — it records "
f"whether a human ratified this plan, got {value!r}",
code="segmentation_plan_invalid",
)
return value
def _parse_proposed_by(value: Any) -> str | None:
if value is None:
return None
if not isinstance(value, str) or not value:
raise SegmentationError(
"the segmentation plan field 'proposed_by' must be a non-empty string or "
f"absent, got {value!r}",
code="segmentation_plan_invalid",
)
return value
def parse_segmentation_plan(payload: Mapping[str, Any]) -> SegmentationPlan:
"""Validate an authored plan fail-fast, or refuse it with a typed code.
@ -396,6 +477,8 @@ def parse_segmentation_plan(payload: Mapping[str, Any]) -> SegmentationPlan:
extractor_version=_require_str(payload, "extractor_version", where="the segmentation plan"),
adjudicated_at=_require_str(payload, "adjudicated_at", where="the segmentation plan"),
entries=entries,
adjudicated=_parse_adjudicated(payload.get("adjudicated", False)),
proposed_by=_parse_proposed_by(payload.get("proposed_by")),
)