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
|
|
@ -336,6 +336,18 @@ def test_unsupported_cell_type(value: object) -> None:
|
|||
assert code_of(excinfo) == "unsupported_cell_type"
|
||||
|
||||
|
||||
def test_unknown_renderer() -> None:
|
||||
from dataclasses import replace
|
||||
|
||||
from llm_ingestion_okf.inbox import _resolve_renderer
|
||||
from llm_ingestion_okf.profiles import DEFAULT
|
||||
|
||||
profile = replace(DEFAULT, renderers={".md": "no-such-renderer"})
|
||||
with pytest.raises(MaterializationError) as excinfo:
|
||||
_resolve_renderer(profile, "note.md")
|
||||
assert code_of(excinfo) == "unknown_renderer"
|
||||
|
||||
|
||||
# --- ExtractionError codes ---
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue