cadrumo.application.flows.copy module

The render-time copy assembler: references in, display copy out.

Every copy slot on a flow definition is a CopyRef, never a literal string; this module resolves those references against the bundled sources at render time and composes the per-page copy bundle both frontends consume. Locale keys resolve through the canonical tr() catalogue lookup built in; the SCHEMA_FIELD and TERMINOLOGY_CONCEPT kinds resolve through resolvers a domain flow registers at import time, keeping the substrate blind to which schema or concept corpus backs them.

Resolution is loud: an unresolvable reference raises FlowCopyResolutionError naming the reference kind and id — a page never renders a silent blank or a raw key leak.

PAGE_REQUIREMENT_LOCALE_KEYS: Final[Mapping[bool, str]]

The required/optional badge keys, keyed by whether the page is required.

Both frontends pick one of these behind a variable and hand it to tr(), so no literal reaches the call site and the static scanner cannot see either key there. A catalogue strip would then prune both as orphans and the badge would raise at render time on a key nobody could find a reference to.

The scanner does read module-level constants whose name carries the _LOCALE_KEYS suffix, so centralising the pair here is what keeps them visible. That makes the name load-bearing rather than descriptive: renaming this constant to something outside the convention silently removes both keys from the scan.

register_copy_source(kind, resolver)[source]

Register a resolver for a non-locale copy source kind.

LOCALE_KEY is built in and may not be overridden. A kind may carry several resolvers — distinct domains legitimately serve disjoint reference namespaces of the same kind (profile schema fields and modelo casilla labels are both SCHEMA_FIELD) — and resolution tries them in registration order, first non-None winning. Registering the same resolver object twice is refused so an accidental double import surfaces loudly.

Return type:

None

Parameters:
resolve_copy(ref)[source]

Resolve one reference to display copy, or refuse loudly.

Return type:

str

Parameters:

ref (CopyRef)

resolve_optional_copy(ref)[source]

Resolve an optional copy slot; None stays None.

Return type:

str | None

Parameters:

ref (CopyRef | None)

class ChoiceCopy(**data)[source]

Bases: BaseModel

Rendered copy for one choice row.

Parameters:

data (Any)

value: str
label: str
description: str | None
provenance: str | None
class LegalRefCopy(**data)[source]

Bases: BaseModel

Resolved copy for one legal citation on the provenance zone.

ref is the registry legal-ref token, carried through verbatim; label is the resolved display text, or None when the citation declared no label. The pairing is structured, not pre-rendered prose — how a ref/label pair reads on screen is the frontend’s concern.

Parameters:

data (Any)

ref: str
label: str | None
class PageCopy(**data)[source]

Bases: BaseModel

The fully-assembled display bundle for one page.

Frontends render this bundle and nothing else — no frontend ever resolves a reference itself, so page copy is identical across the full-screen, line-mode, and any future surface by construction.

Parameters:

data (Any)

prompt: str
help: str | None
format_hint: str | None
failure_modes: tuple[str, ...]
legal_zone: tuple[LegalRefCopy, ...]
choices: tuple[ChoiceCopy, ...]
assemble_page_copy(page)[source]

Resolve every copy slot on page into one display bundle.

Return type:

PageCopy

Parameters:

page (FlowPage)

assemble_section_titles(definition)[source]

Resolve every section’s title copy, keyed by section id.

A section id is an internal slug (residence, identidad) that is never display copy; FlowSection.title is the reference that carries the operator-facing, locale-resolved title. Every frontend that shows a section — the question panel’s group-box title, the progress header, the review grouping — resolves it here so no surface can fall back to rendering the slug.

Return type:

dict[str, str]

Parameters:

definition (FlowDefinition)