feat(profiles): a profile may name a per-suffix renderer
Arm E, capability only. A profile MAY name a renderer per suffix; no domain-aware renderer is written here, that stays a Non-Goal, and `_RENDERERS` is empty on purpose so the emptiness reads as a decision rather than an omission. THE LAYERING IS THE DESIGN, not an implementation detail. `extract.py` is the extraction registry and must not import the contract layer, or the dependency runs backwards and the registry stops standing on its own. So `extract_text` gains a keyword-only `renderer: Callable[[str], str] | None`, knowing nothing about profiles, and `inbox.py` -- which already holds the profile at that call site -- resolves a NAME to a function. A test asserts extract.py still contains no reference to the profile layer, because that constraint is the whole reason the parameter is shaped this way. The renderer runs AFTER extraction, never instead of it, so it never has to re-implement a reader and the two cannot drift. The default is identity, which is what keeps the five byte-pinned goldens byte-pinned -- asserted per suffix rather than once. An unknown renderer NAME is refused rather than falling back to identity: a silent fallback would produce a bundle that looks rendered and is not, which is the failure mode this arm exists to make visible. That needed a registered code (`unknown_renderer`) and its test -- slightly beyond the step's named files, but the capability cannot ship without defining what an unknown name does. `tests/test_profile.py`'s exact-field-set assertion went red, as the plan's risk table predicted. Updated deliberately with the reason recorded: that assertion exists so a field cannot arrive without someone deciding it should, and its red run is the mechanism working. Suite 917 -> 926. All five goldens byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
0170c526ad
commit
55a09d6c8e
7 changed files with 213 additions and 3 deletions
|
|
@ -227,4 +227,11 @@ def test_a_profile_is_assembled_from_its_policies() -> None:
|
|||
# exists to forbid. Defaulted to None, so the four shipped profiles
|
||||
# construct unchanged and their bytes do not move.
|
||||
"segmentation",
|
||||
# Arm E's capability, on the same terms. Updated DELIBERATELY: this
|
||||
# assertion pins the exact field set precisely so a field cannot arrive
|
||||
# without someone deciding it should, and the red run it produced is
|
||||
# the mechanism working rather than a regression. `None` means the
|
||||
# profile does not have the capability; every shipped profile still
|
||||
# constructs unchanged and no golden moved.
|
||||
"renderers",
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue