"""Off-load-path record-design coverage and calculation-closure derivations.
:func:`calculation_closure_casilla_ids` and
``calculation_closure_legal_refs`` derive bounded closure projections from
a :class:`ModeloRevision`; ``DisenoCoverageReport`` remains the advisory
full-Dise�o inventory.
"""
from __future__ import annotations
from collections.abc import Callable
from ....core.casilla_id import CasillaId
from .bindings import binding_source_casilla_ids, binding_source_modelo
from .runtime_graph import expression_casilla_refs
from .schema import BindingDefinition, ModeloRevision
from .schema_surfaces import CasillaDefinition
# ---------------------------------------------------------------------------
# Calculation-completeness manifest derivation and Dise�o extraction
# (off-load-path)
# ---------------------------------------------------------------------------
#
# The derivations below run off the snapshot-build hot path; they are
# called only by manifest-authoring scripts, the off-load-path coverage
# report, and the drift re-verification test.
#
# - ``calculation_closure_casilla_ids`` enumerates a revision's calculation
# closure as canonical ``casilla.id`` values only. Reference tokens that do
# not name a declared casilla id remain unresolved and fail the manifest
# derivation instead of being reinterpreted as display metadata.
#
# - ``calculation_closure_record_design_metadata`` projects that canonical
# closure through each closure casilla's own registry ``(segmento, number)``
# metadata. This metadata is reviewed against Dise�o where needed, while
# the manifest row also carries the canonical ``casilla.id`` that consumers
# resolve.
#
# - ``derive_calculation_completeness_casillas`` derives the
# *calculation-completeness manifest* casilla set from that closure:
# the modelo's calculation surface keyed on canonical ``casilla.id`` and
# carrying the registry metadata each closure casilla declares. For a multi-segment modelo it optionally
# verifies the derived record segments against the AEAT Dise�o de
# Registros. This is the set the load-blocking completeness gate
# enforces.
#
# - ``derive_diseno_coverage_casillas`` extracts the *full* Dise�o
# casilla set � every five-digit casilla tag AEAT embeds in a field
# description, accounting-statement data-entry fields included. It
# parses the multi-megabyte Dise�o corpus and is the input to the
# off-load-path advisory coverage report that inventories form-level
# data coverage; it is NOT a load-blocking gate.
"""Match bounded bracketed field identifiers in record-design text.
The extractor accepts the identifier widths used by the authored record
design corpus and leaves unbracketed layouts to the coverage report. It
does not declare a model, field, or legal mapping.
The numeric width is bounded so unrelated bracketed amounts, identifiers,
and offsets are not mistaken for fields.
"""
def _binding_is_cross_modelo(binding: BindingDefinition, modelo_id: str) -> bool:
"""Return whether a binding names a foreign source modelo.
A binding is *cross-modelo* when its typed selector helper reports a
``source_modelo`` that is not the modelo whose closure is being derived.
A binding with no source modelo, or one set to ``modelo_id``, is a
*within-modelo* binding: its ``source_casilla_ids`` / ``source_casilla_id``
name casillas on the modelo being derived, and those casillas belong in
the modelo's own calculation closure.
"""
source_modelo = binding_source_modelo(binding)
if source_modelo is None:
return False
return source_modelo != modelo_id
def _visit_formula_closure_tokens(
revision: ModeloRevision,
visit_token: Callable[[CasillaId], None],
) -> None:
for formula in revision.formulas:
visit_token(formula.target_casilla_id)
for ref in expression_casilla_refs(formula.expression):
visit_token(ref)
def _visit_expectation_closure_tokens(
revision: ModeloRevision,
visit_token: Callable[[CasillaId], None],
) -> None:
for expectation in revision.verification_expectations:
for ref in expectation.computed_casilla_ids:
visit_token(ref)
for ref in expectation.reconciliation_total_casilla_ids.values():
visit_token(ref)
def _visit_binding_closure_tokens(
revision: ModeloRevision,
modelo_id: str,
visit_token: Callable[[CasillaId], None],
) -> None:
for binding in revision.bindings:
if _binding_is_cross_modelo(binding, modelo_id):
continue
for token in binding_source_casilla_ids(binding):
visit_token(token)
def _walk_calculation_closure(
revision: ModeloRevision,
modelo_id: str,
*,
visit_token: Callable[[CasillaId], None],
visit_endpoint: Callable[[CasillaDefinition], None],
) -> None:
"""Walk the within-modelo calculation closure, dispatching each member.
Shared by :func:`calculation_closure_casilla_ids` and
``calculation_closure_record_design_metadata``; ``visit_endpoint`` receives every
formula/binding endpoint casilla and ``visit_token`` every referenced
casilla token (formula targets, transitive expression refs,
verification-expectation operands, and within-modelo binding source
coordinates).
"""
for casilla in revision.casillas:
if casilla.formula is not None or casilla.binding is not None:
visit_endpoint(casilla)
_visit_formula_closure_tokens(revision, visit_token)
_visit_expectation_closure_tokens(revision, visit_token)
_visit_binding_closure_tokens(revision, modelo_id, visit_token)
[docs]
def calculation_closure_casilla_ids(revision: ModeloRevision, modelo_id: str) -> frozenset[CasillaId]:
"""Return canonical casilla ids in a revision's calculation closure.
The *calculation closure* is the set of casillas the cross-connecting
calculation engine traverses **within this modelo revision**:
- every ``formula.target_casilla_id`` casilla;
- every casilla referenced inside any ``formula.expression``, walked
transitively via the runtime-graph ``expression_casilla_refs``
walker;
- every casilla that declares a ``formula`` (a computed endpoint) or
a ``binding`` (a bound endpoint) � the engine-visible casillas;
- every verification-expectation operand casilla
(``computed_casilla_ids`` and the ``reconciliation_total_casilla_ids`` targets);
- every *within-modelo* binding ``source_casilla_ids`` / ``source_casilla_id``
selector casilla, and every *within-modelo*
the relation-prefill provider's ``source_casilla_id``.
A binding ``source_casilla_ids`` / ``source_casilla_id`` selector � and a
the relation-prefill provider's ``source_casilla_id`` � is excluded from this closure
**only when it is genuinely cross-modelo**: when the selector
explicitly names a ``source_modelo`` that differs from ``modelo_id``.
A cross-modelo selector's ``source_casilla_ids`` / ``source_casilla_id``
name casillas on that *foreign* modelo, not on the modelo whose
closure is being derived; the cross-modelo edge enters the current
modelo through the *bound* casilla � the current-modelo casilla that
declares the binding (or, for a relation, ``relation.target_binding``)
� which is already counted above as a binding endpoint. Folding a
foreign-modelo casilla id into this closure would make the
completeness gate demand it from the wrong modelo's registry.
A selector that omits ``source_modelo`` or sets it equal to
``modelo_id`` is a *within-modelo* selector: a ``previous_filing``
self-binding or a ``previous_period`` self-relation names a casilla
on the modelo being derived, so that casilla is a genuine closure
member and is kept.
References are not normalised through record-design metadata. A formula,
binding, relation, or verification token must already be the canonical
``casilla.id`` for this revision. A reference token that matches no
declared casilla id is kept verbatim so the manifest derivation and
registry validation fail loudly on the unresolved canonical reference.
Args:
revision: The :class:`ModeloRevision` whose formula and binding graph
is walked to derive the closure.
modelo_id: The AEAT modelo identifier used to exclude cross-modelo
selector casillas from the closure.
"""
closure: set[CasillaId] = set()
_walk_calculation_closure(
revision,
modelo_id,
visit_token=closure.add,
visit_endpoint=lambda casilla: closure.add(casilla.id),
)
return frozenset(closure)
__all__ = [
"calculation_closure_casilla_ids",
]