"""Strict, frontend-neutral records for the Modelo Edit Contract V1.
Behind :mod:`cadrumo.application.modelo`, this family owns edit admission and
parsing, authoritative preflight, an exact compare-and-swap mutation
baseline, typed scalar and repeatable-row intents, and the safe result receipt
for a guarded calculation edit. It
never owns TUI state, operation lifecycle or custody, registry meaning,
calculation formulas, or persistence adapters -- see
:class:`~domain.calculations.registry.schema.ModeloRevision` for the
registry-owned schema these edits address.
A :class:`ModeloWorkspaceBaselineV1` read-consistency token is never accepted
as mutation authority; admission independently re-resolves every coordinate
this family authorizes against.
"""
from __future__ import annotations
from datetime import datetime
from enum import StrEnum
from typing import Annotated, Literal
from pydantic import Field, field_validator, model_validator
from ...core.casilla_id import CasillaId
from ...core.errors.hierarchy import pydantic_validation_boundary
from ...core.filing_year import FilingYear
from ...core.identity.bucket import BucketId
from ...core.identity.digest import ContentDigest
from ...core.identity.hex_ids import CalculationRevisionId, ModeloEditBaselineId, WorkUnitId
from ...core.period import Period
from ...core.time.utc import validate_utc_aware
from ...domain.calculations.registry.ids import BindingId, RevisionId
from ...domain.calculations.registry.schema_base import CasillaDataTypeValue
from ...domain.filing.schema import ModeloScalar
from ...domain.modelos.codes import ModeloCode
from ...domain.modelos.row_models import ModeloDetailRow
from ..operations.models import OperationId
from ..operator_actions.models import ActionReference
from .edit_contract import (
EditModel,
)
from .edit_contract import (
ModeloEditCompatibilityTupleV1 as _ModeloEditCompatibilityTupleV1,
)
from .edit_contract import (
ModeloEditExecutionEffect as _ModeloEditExecutionEffect,
)
from .edit_contract import (
ModeloEditMutationFamily as _ModeloEditMutationFamily,
)
from .edit_contract import (
ModeloEditMutationResultReceiptV1 as _ModeloEditMutationResultReceiptV1,
)
_MAX_FINDINGS = 500
_MAX_INTENTS = 500
_MAX_SURFACE_ENTRIES = 2000
_MAX_MESSAGE_ARGUMENTS = 16
_MAX_EVIDENCE_REFERENCES = 64
_MAX_ROW_VALUES = 200
type _BoundedText = Annotated[str, Field(min_length=1, max_length=256)]
type _BoundedCode = Annotated[str, Field(min_length=1, max_length=128, pattern=r"^[a-z][a-z0-9_.-]*$")]
type _ClientRowCorrelationId = Annotated[str, Field(min_length=1, max_length=128)]
type _BoundedRefList = Annotated[tuple[_BoundedText, ...], Field(max_length=_MAX_EVIDENCE_REFERENCES)]
[docs]
class ModeloEditScalarIntentKind(StrEnum):
"""The two distinct scalar edit intents; address absence means UNCHANGED.
``REMOVE_OVERRIDE`` is deliberately NOT a member: it addresses the
``CalculationRevision.binding_overrides`` store, which is keyed by
``BindingId``, never ``CasillaId`` -- no casilla-addressed scalar intent
can reach it. See :class:`ModeloEditBindingIntentKind`.
"""
SET_TYPED_VALUE = "set_typed_value"
CLEAR_DECLARED_VALUE = "clear_declared_value"
[docs]
class ModeloEditRowIntentKind(StrEnum):
"""The closed repeatable-row edit intents; MOVE_ROW requires reorderable groups."""
ADD_ROW = "add_row"
UPDATE_ROW = "update_row"
DELETE_ROW = "delete_row"
MOVE_ROW = "move_row"
[docs]
class ModeloEditDetailRowIntentKind(StrEnum):
"""The closed ``ModeloDetailRow`` edit intents, addressed by natural key.
``MOVE_ROW`` is deliberately NOT a member. A row's
physical record occurrence number in the exported fichero is NOT a
function of caller-supplied order at all: every row-producer resolver in
:mod:`domain.calculations.registry.detail_record_bindings`
(``resolve_atribucion_binding_row_values``, ``_build_related_party_rows``,
``_build_foreign_asset_rows``, and their siblings) sorts its rows by a
content key -- ``(country_code, tax_id)`` or equivalent -- BEFORE
``enumerate(..., 1)`` assigns row indices, so two calls supplying the
same rows in different orders render byte-identical ficheros (each builder's
sort is proven in
``cadrumo.domain.calculations.registry.tests.test_detail_record_row_builders``).
The AEAT diseno de registro for these record families identifies each
repeated record by its declared content (member/counterparty NIF, asset
identifier, clave/subclave) rather than by a required sequence -- there
is no "declared order" for AEAT to read or for a reorder to change.
Consistently, the calculation revision's content address is also
order-BLIND (``_canonical_detail_rows`` sorts by ``(row_type,
nif-like)`` specifically so "operators can supply rows in any order"). A
pure reorder therefore computes the SAME revision id AND renders the
SAME fichero bytes as the existing revision, so the guarded
compare-and-swap persistence layer's duplicate-result branch would
silently absorb it and return the existing revision -- the requested
reorder would never actually persist, and even if it did nothing
observable would change. Building MOVE_ROW against the current
content-address shape would ship a control with no addressable effect.
"""
ADD_ROW = "add_row"
UPDATE_ROW = "update_row"
DELETE_ROW = "delete_row"
[docs]
class ModeloEditBindingIntentKind(StrEnum):
"""The two distinct binding-override edit intents; address absence means UNCHANGED.
Mirrors ``SET_TYPED_VALUE``/``CLEAR_DECLARED_VALUE`` in shape, but these
apply to the ``BindingId``-keyed ``binding_overrides`` store rather than a
casilla's declared value -- the operator-facing ``--binding KEY=VALUE``
CLI override and its withdrawal.
"""
SET_OVERRIDE_VALUE = "set_override_value"
REMOVE_OVERRIDE = "remove_override"
[docs]
class ModeloEditNonWritableReason(StrEnum):
"""Why one permitted-surface address is not writable in this baseline."""
COMPUTED_BY_FORMULA = "computed_by_formula"
SCHEMA_DECLARED_READ_ONLY = "schema_declared_read_only"
CAPABILITY_UNAVAILABLE = "capability_unavailable"
[docs]
class ModeloEditFindingSeverity(StrEnum):
"""The closed severity denominator for one preflight finding."""
ERROR = "error"
WARNING = "warning"
INFO = "info"
[docs]
class ModeloEditRefusalCode(StrEnum):
"""The closed refusal-boundary denominator for the edit contract.
``STALE_EDIT_BASELINE`` never appears on :class:`ModeloEditDomainRefusalV1`;
it is carried only by the structured :class:`ModeloEditStaleBaselineRefusalV1`,
the same split ``ModeloWorkspaceDomainRefusalV1`` draws for its own
two-axis mismatch.
"""
TARGET_ABSENT = "target_absent"
TARGET_AMBIGUOUS = "target_ambiguous"
ADMISSION_DENIED = "admission_denied"
UNSUPPORTED_SCHEMA_KIND = "unsupported_schema_kind"
DISALLOWED_INTENT = "disallowed_intent"
PARSE_FAILED = "parse_failed"
VALIDATION_FAILED = "validation_failed"
CAPABILITY_UNAVAILABLE = "capability_unavailable"
WORK_CATALOGUE_CONFLICT = "work_catalogue_conflict"
CALCULATION_HEAD_CONFLICT = "calculation_head_conflict"
REGISTRY_SCHEMA_CONFLICT = "registry_schema_conflict"
SURFACE_CONFLICT = "surface_conflict"
BASELINE_EXPIRED = "baseline_expired"
STALE_EDIT_BASELINE = "stale_edit_baseline"
[docs]
class ModeloEditScalarAddressV1(EditModel):
"""A canonical semantic scalar address, keyed by the registry casilla identity."""
kind: Literal["scalar"] = "scalar"
casilla_id: CasillaId
[docs]
class ModeloEditBindingAddressV1(EditModel):
"""A canonical binding-override address, keyed by the registry binding identity.
Distinct from :class:`ModeloEditScalarAddressV1`: a binding override
addresses ``CalculationRevision.binding_overrides``, which is keyed by
``BindingId``, never ``CasillaId`` -- most overridable bindings (a
fichero-BOE record-field ``manual_input`` binding, most notably) have no
casilla at all to address through.
"""
kind: Literal["binding"] = "binding"
binding_id: BindingId
[docs]
class ModeloEditExistingRowAddressV1(EditModel):
"""The application-issued canonical coordinate of one persisted repeated row."""
kind: Literal["existing_row"] = "existing_row"
binding_id: BindingId
row_index: Annotated[int, Field(ge=1)]
[docs]
class ModeloEditNewRowCorrelationV1(EditModel):
"""An opaque client correlation for a row not yet assigned persistence identity.
Carries no persistence meaning; the canonical row coordinate is minted by
the result and mapped back to this correlation. Positional widget indexes
are never identity.
"""
kind: Literal["new_row"] = "new_row"
binding_id: BindingId
client_correlation_id: _ClientRowCorrelationId
type ModeloEditRowAddressV1 = Annotated[
ModeloEditExistingRowAddressV1 | ModeloEditNewRowCorrelationV1,
Field(discriminator="kind"),
]
[docs]
class ModeloEditDetailRowAddressV1(EditModel):
"""The natural-key address of one ``ModeloDetailRow``, never position or a minted id.
``detail_row_kind`` is the discriminated ``ModeloDetailRow.row_type`` value
(e.g. ``"miembro"``, ``"contraparte"``). ``natural_key`` is the row's own
already-declared identity field, joined with ``|`` for a compound key
(M349 operador/rectificación key on ``nif_comunitario|clave_operacion``,
since one counterparty can carry more than one operation type) -- never a
minted or positional identity. This mirrors the codebase's established
whole-set-replacement convention for repeating declared rows
(``RetencionObservationRepository.replace_observations``): rows are
addressed by the business key they already carry, and an absent key is
sufficient to express removal with no separate "explicitly deleted" axis,
because a row (unlike a scalar) has no ambiguous middle state between
"declared" and "absent".
"""
kind: Literal["detail_row"] = "detail_row"
detail_row_kind: _BoundedCode
natural_key: Annotated[str, Field(min_length=1, max_length=256)]
type ModeloEditAddressV1 = Annotated[
ModeloEditScalarAddressV1
| ModeloEditBindingAddressV1
| ModeloEditExistingRowAddressV1
| ModeloEditNewRowCorrelationV1
| ModeloEditDetailRowAddressV1,
Field(discriminator="kind"),
]
#: The registry's casilla data-type vocabulary, reached at its definition rather
#: than mirrored. Metadata only, not a value: the parse service selects its grammar
#: from this axis, and the permitted surface carries no casilla value. The mirror this
#: replaced was a hand-maintained copy of the same nineteen members, so a member added
#: to the registry never reached it.
[docs]
class ModeloEditWritableScalarSurfaceEntryV1(EditModel):
"""One scalar address the baseline admits for writing, with its allowed intents."""
kind: Literal["writable_scalar"] = "writable_scalar"
casilla_id: CasillaId
data_type: CasillaDataTypeValue
allowed_intents: Annotated[tuple[ModeloEditScalarIntentKind, ...], Field(min_length=1, max_length=3)]
@field_validator("allowed_intents")
@classmethod
def _require_unique_allowed_intents(
cls, value: tuple[ModeloEditScalarIntentKind, ...]
) -> tuple[ModeloEditScalarIntentKind, ...]:
if len(set(value)) != len(value):
raise ValueError("writable scalar surface entry must declare each allowed intent at most once")
return value
[docs]
class ModeloEditNonWritableScalarSurfaceEntryV1(EditModel):
"""One scalar address the baseline exposes as read-only, with its reason."""
kind: Literal["non_writable_scalar"] = "non_writable_scalar"
casilla_id: CasillaId
reason: ModeloEditNonWritableReason
[docs]
class ModeloEditWritableRowGroupSurfaceEntryV1(EditModel):
"""One repeated-row group the baseline admits for writing."""
kind: Literal["writable_row_group"] = "writable_row_group"
binding_id: BindingId
allowed_intents: Annotated[tuple[ModeloEditRowIntentKind, ...], Field(min_length=1, max_length=4)]
reorderable: bool
max_rows: Annotated[int, Field(ge=1)] | None = None
@field_validator("allowed_intents")
@classmethod
def _require_unique_allowed_row_intents(
cls, value: tuple[ModeloEditRowIntentKind, ...]
) -> tuple[ModeloEditRowIntentKind, ...]:
if len(set(value)) != len(value):
raise ValueError("writable row-group surface entry must declare each allowed intent at most once")
return value
@model_validator(mode="after")
def _require_reorderable_for_move(self) -> ModeloEditWritableRowGroupSurfaceEntryV1:
if ModeloEditRowIntentKind.MOVE_ROW in self.allowed_intents and not self.reorderable:
raise ValueError("MOVE_ROW may be allowed only when the row group is declared reorderable")
return self
[docs]
class ModeloEditNonWritableRowGroupSurfaceEntryV1(EditModel):
"""One repeated-row group the baseline exposes as read-only, with its reason."""
kind: Literal["non_writable_row_group"] = "non_writable_row_group"
binding_id: BindingId
reason: ModeloEditNonWritableReason
[docs]
class ModeloEditWritableBindingOverrideSurfaceEntryV1(EditModel):
"""One binding the baseline admits the operator-facing ``--binding`` override for.
Distinct from a scalar or row-group entry: it addresses
``CalculationRevision.binding_overrides`` (``BindingId``-keyed), never a
casilla, because most eligible bindings -- a fichero-BOE record-field
``manual_input`` binding, most notably -- have no casilla to address
through at all.
"""
kind: Literal["writable_binding_override"] = "writable_binding_override"
binding_id: BindingId
allowed_intents: Annotated[tuple[ModeloEditBindingIntentKind, ...], Field(min_length=1, max_length=2)]
@field_validator("allowed_intents")
@classmethod
def _require_unique_allowed_binding_intents(
cls, value: tuple[ModeloEditBindingIntentKind, ...]
) -> tuple[ModeloEditBindingIntentKind, ...]:
if len(set(value)) != len(value):
raise ValueError("writable binding-override surface entry must declare each allowed intent at most once")
return value
[docs]
class ModeloEditNonWritableBindingOverrideSurfaceEntryV1(EditModel):
"""One binding the baseline exposes as override-locked, with its reason."""
kind: Literal["non_writable_binding_override"] = "non_writable_binding_override"
binding_id: BindingId
reason: ModeloEditNonWritableReason
[docs]
class ModeloEditWritableDetailRowSurfaceEntryV1(EditModel):
"""One ``ModeloDetailRow`` kind the baseline admits for writing, natural-key addressed.
Distinct from the (permanently empty) ``BindingId``-keyed row-group axis:
this addresses the genuine repeatable, taxpayer-typed ``ModeloDetailRow``
mechanism, admitted per the same real, enforced per-modelo eligibility
table that
:func:`~._calculation_modelo_adjustments.require_detail_rows_declared_for_their_owning_modelo`
refuses a mismatched row against.
"""
kind: Literal["writable_detail_row"] = "writable_detail_row"
detail_row_kind: _BoundedCode
allowed_intents: Annotated[tuple[ModeloEditDetailRowIntentKind, ...], Field(min_length=1, max_length=3)]
@field_validator("allowed_intents")
@classmethod
def _require_unique_allowed_detail_row_intents(
cls, value: tuple[ModeloEditDetailRowIntentKind, ...]
) -> tuple[ModeloEditDetailRowIntentKind, ...]:
if len(set(value)) != len(value):
raise ValueError("writable detail-row surface entry must declare each allowed intent at most once")
return value
type ModeloEditPermittedSurfaceEntryV1 = Annotated[
ModeloEditWritableScalarSurfaceEntryV1
| ModeloEditNonWritableScalarSurfaceEntryV1
| ModeloEditWritableRowGroupSurfaceEntryV1
| ModeloEditNonWritableRowGroupSurfaceEntryV1
| ModeloEditWritableBindingOverrideSurfaceEntryV1
| ModeloEditNonWritableBindingOverrideSurfaceEntryV1
| ModeloEditWritableDetailRowSurfaceEntryV1,
Field(discriminator="kind"),
]
def _surface_entry_address(entry: ModeloEditPermittedSurfaceEntryV1) -> tuple[str, str]:
if isinstance(entry, (ModeloEditWritableScalarSurfaceEntryV1, ModeloEditNonWritableScalarSurfaceEntryV1)):
return ("scalar", entry.casilla_id)
if isinstance(
entry, (ModeloEditWritableBindingOverrideSurfaceEntryV1, ModeloEditNonWritableBindingOverrideSurfaceEntryV1)
):
return ("binding_override", entry.binding_id)
if isinstance(entry, ModeloEditWritableDetailRowSurfaceEntryV1):
return ("detail_row", entry.detail_row_kind)
return ("row_group", entry.binding_id)
[docs]
class ModeloEditSchemaIdentityV1(EditModel):
"""The edit contract's own schema identity: a compare-and-swap coordinate, not a display fact.
Deliberately its own type rather than a reuse of
:class:`~.workspace_models.ModeloWorkspaceSchemaIdentityV1` -- the two
types share a shape (an id, a fingerprint, and a manifest digest) but not
a meaning. ``completeness_manifest_digest`` digests the registry's
:class:`~domain.calculations.registry.schema_surfaces.CalculationCompletenessManifest`
(the required calculation-closure casilla set: a TAX-SEMANTIC
completeness declaration), so a compare-and-swap re-check catches the
registry's declared completeness rules changing between admission and
commit. The Workspace type's ``field_manifest_digest`` digests a
structurally unrelated concept -- the field-CLASSIFICATION manifest,
a deterministic walk over the public registry TYPE denominator for
display rendering. Reusing one field name for both silently made two
genuinely different values compare unequal under one name; see the
edit-contract ADR amendment this type was added by.
"""
schema_id: _BoundedCode
schema_fingerprint: ContentDigest
completeness_manifest_digest: ContentDigest
[docs]
class ModeloEditBaselineV1(EditModel):
"""One admitted, independently re-resolved compare-and-swap edit coordinate.
Contains only safe coordinates and no values. It is a compare-and-swap
coordinate, not actor authorization or proof that the operation remains
available.
NEVER COMPARED BY RECORD EQUALITY. This carries its own admission identity
and lifetime -- ``issued_at``, ``expires_at`` and ``baseline_id`` -- so two
admissions of an UNCHANGED tree are never equal, and ``==`` between two of
these can only ever report "different". Staleness is asked of
:func:`~application.modelo.edit_services.reconfirm_modelo_edit_baseline`,
which judges the coordinate axes the guarded commit point judges.
The distinction matters because
:class:`~application.modelo.workspace_models.ModeloWorkspaceBaselineV1`
shares the name and has the OPPOSITE contract: it is content-derived,
carries no timestamp, and is compared by equality on purpose. An editor
session once carried that pattern here, and its stale signal was
permanently on -- worse than absent, because a warning that always fires
teaches the operator to dismiss it.
"""
edit_contract_version: Literal[1] = 1
compatibility: _ModeloEditCompatibilityTupleV1
bucket_id: BucketId
modelo: ModeloCode
filing_year: FilingYear
period: Period
work_unit_id: WorkUnitId
work_catalogue_revision: ContentDigest
calculation_catalogue_revision: ContentDigest
current_calculation_revision_id: CalculationRevisionId | None
law_selected_revision_id: RevisionId
schema_identity: ModeloEditSchemaIdentityV1
schema_version: Annotated[int, Field(ge=1)]
permitted_surface: Annotated[tuple[ModeloEditPermittedSurfaceEntryV1, ...], Field(max_length=_MAX_SURFACE_ENTRIES)]
permitted_surface_digest: ContentDigest
mutation_family: _ModeloEditMutationFamily
issued_at: datetime
expires_at: datetime
baseline_id: ModeloEditBaselineId
@field_validator("permitted_surface")
@classmethod
def _require_unique_surface_addresses(
cls, value: tuple[ModeloEditPermittedSurfaceEntryV1, ...]
) -> tuple[ModeloEditPermittedSurfaceEntryV1, ...]:
addresses = [_surface_entry_address(entry) for entry in value]
if len(set(addresses)) != len(addresses):
raise ValueError("edit baseline permitted surface must address each casilla or row group once")
return value
@model_validator(mode="after")
@pydantic_validation_boundary
def _require_ordered_validity_window(self) -> ModeloEditBaselineV1:
validate_utc_aware(self.issued_at)
validate_utc_aware(self.expires_at)
if self.expires_at <= self.issued_at:
raise ValueError("edit baseline expiry must be strictly after its issue time")
return self
[docs]
class ModeloEditAdmittedV1(EditModel):
"""A successful admission carrying the exact re-resolved baseline."""
outcome: Literal["admitted"] = "admitted"
baseline: ModeloEditBaselineV1
[docs]
class ModeloEditParsedValueV1(EditModel):
"""A successfully parsed canonical typed value for one scalar address.
Never echoes the transient raw lexeme that produced it.
"""
outcome: Literal["parsed"] = "parsed"
address: ModeloEditScalarAddressV1
value: ModeloScalar
[docs]
class ModeloEditPreflightEvaluatedV1(EditModel):
"""A completed preflight evaluation carrying every finding.
A green (empty-error) preflight is review material, not authorization;
execution independently repeats every concurrency and capability check.
"""
outcome: Literal["evaluated"] = "evaluated"
baseline_id: ModeloEditBaselineId
findings: Annotated[tuple[ModeloEditFindingV1, ...], Field(max_length=_MAX_FINDINGS)]
[docs]
class ModeloEditVersionRefusalV1(EditModel):
"""Minimal refusal produced before a rejected request target is parsed."""
kind: Literal["unsupported_version"] = "unsupported_version"
requested_version: Annotated[int, Field(ge=1)] | None
supported_version: Literal[1] = 1
[docs]
class ModeloEditCompatibilityRefusalV1(EditModel):
"""Refusal produced when the requested compatibility tuple is unsupported."""
kind: Literal["unsupported_compatibility"] = "unsupported_compatibility"
edit_contract_version: Literal[1] = 1
requested_axis: _BoundedCode
responsible_owner: _BoundedCode
reconsideration_condition: _BoundedText
[docs]
class ModeloEditStaleBaselineRefusalV1(EditModel):
"""The compare-and-swap refusal naming every coordinate that disagreed.
Writes nothing and settles the domain effect as ``NONE``. Never silently
rebases, merges, or retries against a new baseline.
"""
kind: Literal["stale_edit_baseline"] = "stale_edit_baseline"
edit_contract_version: Literal[1] = 1
baseline_id: ModeloEditBaselineId
mismatching_coordinates: Annotated[tuple[_BoundedCode, ...], Field(min_length=1, max_length=8)]
responsible_owner: _BoundedCode
reconsideration_condition: _BoundedText
recovery_action: ActionReference | None = None
@field_validator("mismatching_coordinates")
@classmethod
def _require_unique_mismatching_coordinates(cls, value: tuple[str, ...]) -> tuple[str, ...]:
if len(set(value)) != len(value):
raise ValueError("stale edit baseline refusal coordinates must be unique")
return value
[docs]
class ModeloEditDomainRefusalV1(EditModel):
"""Typed post-admission refusal without a partial projection or raw exception."""
kind: Literal["domain"] = "domain"
edit_contract_version: Literal[1] = 1
code: ModeloEditRefusalCode
address: ModeloEditAddressV1 | None = None
facts: Annotated[tuple[_BoundedText, ...], Field(max_length=_MAX_MESSAGE_ARGUMENTS)] = ()
evidence: _BoundedRefList = ()
responsible_owner: _BoundedCode
reconsideration_condition: _BoundedText
recovery_action: ActionReference | None = None
@model_validator(mode="after")
def _reject_untyped_stale_baseline(self) -> ModeloEditDomainRefusalV1:
if self.code is ModeloEditRefusalCode.STALE_EDIT_BASELINE:
raise ValueError("a stale edit baseline requires the typed compare-and-swap refusal")
return self
[docs]
class ModeloEditUnsupportedIntentReason(StrEnum):
"""One closed reason per intent kind this V1 executor cannot yet reach.
Named for what capability is missing, never for the internal plan Step
that will supply it: a plan Step id is process metadata, and this reason
ships inside a public runtime contract. The Step-to-reason mapping is
recorded where cross-referencing code to project tracking belongs -- the
Step Record that implements each reason, never the reverse.
"""
SET_OVERRIDE_VALUE_NOT_YET_WIRED = "set_override_value_not_yet_wired"
REMOVE_OVERRIDE_NOT_YET_WIRED = "remove_override_not_yet_wired"
ADD_ROW_NOT_YET_WIRED = "add_row_not_yet_wired"
UPDATE_ROW_NOT_YET_WIRED = "update_row_not_yet_wired"
DELETE_ROW_NOT_YET_WIRED = "delete_row_not_yet_wired"
MOVE_ROW_NOT_YET_WIRED = "move_row_not_yet_wired"
RECALCULATE_NOT_YET_WIRED = "recalculate_not_yet_wired"
[docs]
class ModeloEditUnsupportedIntentRefusalV1(EditModel):
"""A syntactically admitted intent this V1 executor cannot yet execute.
Distinct from :class:`ModeloEditDomainRefusalV1`'s ``DISALLOWED_INTENT``:
that code means the baseline's permitted surface never admitted the
address. This refusal means the address and intent ARE admitted, but the
calculation boundary this executor delegates to has no input shape for
this intent kind yet, so a caller can tell a not-yet-built path from a
genuinely invalid one.
"""
kind: Literal["unsupported_intent"] = "unsupported_intent"
edit_contract_version: Literal[1] = 1
address: ModeloEditAddressV1 | None = None
reason: ModeloEditUnsupportedIntentReason
responsible_owner: _BoundedCode
reconsideration_condition: _BoundedText
type ModeloEditRefusalV1 = Annotated[
ModeloEditVersionRefusalV1
| ModeloEditCompatibilityRefusalV1
| ModeloEditStaleBaselineRefusalV1
| ModeloEditDomainRefusalV1
| ModeloEditUnsupportedIntentRefusalV1,
Field(discriminator="kind"),
]
[docs]
class ModeloEditRefusedV1(EditModel):
"""The result arm that exposes a refusal without calling it a partial success."""
outcome: Literal["refused"] = "refused"
refusal: ModeloEditRefusalV1
type ModeloEditAdmissionResultV1 = Annotated[
ModeloEditAdmittedV1 | ModeloEditRefusedV1,
Field(discriminator="outcome"),
]
type ModeloEditParseResultV1 = Annotated[
ModeloEditParsedValueV1 | ModeloEditRefusedV1,
Field(discriminator="outcome"),
]
type ModeloEditPreflightResultV1 = Annotated[
ModeloEditPreflightEvaluatedV1 | ModeloEditRefusedV1,
Field(discriminator="outcome"),
]
[docs]
class ModeloEditFindingV1(EditModel):
"""One preflight finding at a semantic address or global scope."""
code: _BoundedCode
severity: ModeloEditFindingSeverity
address: ModeloEditAddressV1 | None = None
message_arguments: Annotated[tuple[_BoundedText, ...], Field(max_length=_MAX_MESSAGE_ARGUMENTS)] = ()
evidence: _BoundedRefList = ()
ModeloEditPreflightEvaluatedV1.model_rebuild()
[docs]
class ModeloScalarEditIntentV1(EditModel):
"""One scalar edit intent; zero, false, and empty text remain distinct states."""
address: ModeloEditScalarAddressV1
kind: ModeloEditScalarIntentKind
value: ModeloScalar | None = None
@model_validator(mode="after")
def _require_value_only_for_set(self) -> ModeloScalarEditIntentV1:
if self.kind is ModeloEditScalarIntentKind.SET_TYPED_VALUE and self.value is None:
raise ValueError("SET_TYPED_VALUE scalar intent requires a typed value")
if self.kind is not ModeloEditScalarIntentKind.SET_TYPED_VALUE and self.value is not None:
raise ValueError("only SET_TYPED_VALUE may carry a scalar intent value")
return self
[docs]
class ModeloBindingEditIntentV1(EditModel):
"""One binding-override edit intent; zero and false remain distinct from UNCHANGED.
Addresses ``CalculationRevision.binding_overrides`` directly by
``BindingId``. Never carries a value for ``REMOVE_OVERRIDE``: withdrawing
an override is expressed by absence of a value, not a typed zero.
"""
address: ModeloEditBindingAddressV1
kind: ModeloEditBindingIntentKind
value: ModeloScalar | None = None
@model_validator(mode="after")
def _require_value_only_for_set_override(self) -> ModeloBindingEditIntentV1:
if self.kind is ModeloEditBindingIntentKind.SET_OVERRIDE_VALUE and self.value is None:
raise ValueError("SET_OVERRIDE_VALUE binding intent requires a typed value")
if self.kind is not ModeloEditBindingIntentKind.SET_OVERRIDE_VALUE and self.value is not None:
raise ValueError("only SET_OVERRIDE_VALUE may carry a binding intent value")
return self
def _validate_add_row_intent_shape(
address: ModeloEditRowAddressV1,
row: tuple[ModeloScalarEditIntentV1, ...] | None,
move_to_index: int | None,
) -> None:
"""Require an ADD_ROW to carry a new-row identity and complete values."""
if not isinstance(address, ModeloEditNewRowCorrelationV1) or not row:
raise ValueError("ADD_ROW requires a new-row correlation address and a complete typed row")
if move_to_index is not None:
raise ValueError("ADD_ROW may not carry a move_to_index")
def _validate_update_row_intent_shape(
address: ModeloEditRowAddressV1,
row: tuple[ModeloScalarEditIntentV1, ...] | None,
move_to_index: int | None,
) -> None:
"""Require an UPDATE_ROW to carry an existing-row identity and values."""
if isinstance(address, ModeloEditNewRowCorrelationV1) or not row:
raise ValueError("UPDATE_ROW requires the canonical existing-row address and a complete typed row")
if move_to_index is not None:
raise ValueError("UPDATE_ROW may not carry a move_to_index")
def _validate_delete_row_intent_shape(
address: ModeloEditRowAddressV1,
row: tuple[ModeloScalarEditIntentV1, ...] | None,
move_to_index: int | None,
) -> None:
"""Require a DELETE_ROW to carry only an existing-row identity."""
if isinstance(address, ModeloEditNewRowCorrelationV1) or row is not None or move_to_index is not None:
raise ValueError("DELETE_ROW requires only the canonical existing-row address")
def _validate_move_row_intent_shape(
address: ModeloEditRowAddressV1,
row: tuple[ModeloScalarEditIntentV1, ...] | None,
move_to_index: int | None,
) -> None:
"""Require a MOVE_ROW to carry an existing-row identity and destination."""
if isinstance(address, ModeloEditNewRowCorrelationV1) or row is not None or move_to_index is None:
raise ValueError("MOVE_ROW requires the canonical existing-row address and a move_to_index")
[docs]
class ModeloRowEditIntentV1(EditModel):
"""One repeatable-row edit intent addressed by canonical or correlation identity."""
address: ModeloEditRowAddressV1
kind: ModeloEditRowIntentKind
row: Annotated[tuple[ModeloScalarEditIntentV1, ...], Field(max_length=_MAX_ROW_VALUES)] | None = None
move_to_index: Annotated[int, Field(ge=1)] | None = None
@model_validator(mode="after")
def _require_shape_matches_kind(self) -> ModeloRowEditIntentV1:
if self.kind is ModeloEditRowIntentKind.ADD_ROW:
_validate_add_row_intent_shape(self.address, self.row, self.move_to_index)
elif self.kind is ModeloEditRowIntentKind.UPDATE_ROW:
_validate_update_row_intent_shape(self.address, self.row, self.move_to_index)
elif self.kind is ModeloEditRowIntentKind.DELETE_ROW:
_validate_delete_row_intent_shape(self.address, self.row, self.move_to_index)
else:
_validate_move_row_intent_shape(self.address, self.row, self.move_to_index)
return self
[docs]
class ModeloDetailRowEditIntentV1(EditModel):
"""One ``ModeloDetailRow`` edit intent, addressed by the row's own natural key.
ADD_ROW and UPDATE_ROW submit a complete typed ``ModeloDetailRow``.
DELETE_ROW names only the natural key: removal is expressed by the row's
absence from the executor's reconstructed set, with no separate
"explicitly deleted" axis, per :class:`ModeloEditDetailRowAddressV1`.
No MOVE intent exists -- see :class:`ModeloEditDetailRowIntentKind`.
"""
address: ModeloEditDetailRowAddressV1
kind: ModeloEditDetailRowIntentKind
row: ModeloDetailRow | None = None
@model_validator(mode="after")
def _require_shape_matches_kind(self) -> ModeloDetailRowEditIntentV1:
if self.kind in (ModeloEditDetailRowIntentKind.ADD_ROW, ModeloEditDetailRowIntentKind.UPDATE_ROW):
if self.row is None:
raise ValueError("ADD_ROW/UPDATE_ROW detail-row intent requires a complete typed row")
else:
if self.row is not None:
raise ValueError("DELETE_ROW detail-row intent requires only the natural-key address")
return self
def _intent_address_key(
address: ModeloEditScalarAddressV1
| ModeloEditBindingAddressV1
| ModeloEditRowAddressV1
| ModeloEditDetailRowAddressV1,
) -> tuple[str, str]:
if isinstance(address, ModeloEditScalarAddressV1):
return ("scalar", address.casilla_id)
if isinstance(address, ModeloEditBindingAddressV1):
return ("binding_override", address.binding_id)
if isinstance(address, ModeloEditDetailRowAddressV1):
return ("detail_row", f"{address.detail_row_kind}:{address.natural_key}")
if isinstance(address, ModeloEditExistingRowAddressV1):
return ("existing_row", f"{address.binding_id}:{address.row_index}")
return ("new_row", f"{address.binding_id}:{address.client_correlation_id}")
[docs]
class ModeloEditSubmissionV1(EditModel):
"""One baseline plus its complete normalized ordered intent set.
Carries no frontend state. Duplicate or contradictory address intents
refuse before execution.
"""
edit_contract_version: Literal[1] = 1
baseline: ModeloEditBaselineV1
mutation_family: _ModeloEditMutationFamily
scalar_intents: Annotated[tuple[ModeloScalarEditIntentV1, ...], Field(max_length=_MAX_INTENTS)] = ()
binding_intents: Annotated[tuple[ModeloBindingEditIntentV1, ...], Field(max_length=_MAX_INTENTS)] = ()
row_intents: Annotated[tuple[ModeloRowEditIntentV1, ...], Field(max_length=_MAX_INTENTS)] = ()
detail_row_intents: Annotated[tuple[ModeloDetailRowEditIntentV1, ...], Field(max_length=_MAX_INTENTS)] = ()
@model_validator(mode="after")
def _require_consistent_submission(self) -> ModeloEditSubmissionV1:
if self.mutation_family is not self.baseline.mutation_family:
raise ValueError("edit submission mutation family must match its baseline")
keys = [_intent_address_key(intent.address) for intent in self.scalar_intents]
keys.extend(_intent_address_key(intent.address) for intent in self.binding_intents)
keys.extend(_intent_address_key(intent.address) for intent in self.row_intents)
keys.extend(_intent_address_key(intent.address) for intent in self.detail_row_intents)
if len(set(keys)) != len(keys):
raise ValueError(
"edit submission must not address the same casilla, binding, row, or detail row more than once"
)
return self
[docs]
class ModeloEditApplyRequestV1(EditModel):
"""The guarded apply request only the enrolled operation executor may invoke."""
edit_contract_version: Literal[1] = 1
operation_id: OperationId
submission: ModeloEditSubmissionV1
[docs]
class ModeloEditExecutionUpdatedV1(EditModel):
"""The successful compare-and-swap arm carrying the authoritative receipt."""
effect: Literal[_ModeloEditExecutionEffect.UPDATED] = _ModeloEditExecutionEffect.UPDATED
receipt: _ModeloEditMutationResultReceiptV1
[docs]
class ModeloEditExecutionNoEffectV1(EditModel):
"""The failed compare-and-swap arm; writes nothing and names the refusal."""
effect: Literal[_ModeloEditExecutionEffect.NONE] = _ModeloEditExecutionEffect.NONE
refusal: ModeloEditRefusalV1
type ModeloEditExecutionResultV1 = Annotated[
ModeloEditExecutionUpdatedV1 | ModeloEditExecutionNoEffectV1,
Field(discriminator="effect"),
]
__all__ = [
"ModeloBindingEditIntentV1",
"ModeloDetailRowEditIntentV1",
"ModeloEditAddressV1",
"ModeloEditAdmissionResultV1",
"ModeloEditAdmittedV1",
"ModeloEditApplyRequestV1",
"ModeloEditBaselineV1",
"ModeloEditBindingAddressV1",
"ModeloEditBindingIntentKind",
"ModeloEditCompatibilityRefusalV1",
"ModeloEditDetailRowAddressV1",
"ModeloEditDetailRowIntentKind",
"ModeloEditDomainRefusalV1",
"ModeloEditExecutionNoEffectV1",
"ModeloEditExecutionResultV1",
"ModeloEditExecutionUpdatedV1",
"ModeloEditExistingRowAddressV1",
"ModeloEditFindingSeverity",
"ModeloEditFindingV1",
"ModeloEditNewRowCorrelationV1",
"ModeloEditNonWritableBindingOverrideSurfaceEntryV1",
"ModeloEditNonWritableReason",
"ModeloEditNonWritableRowGroupSurfaceEntryV1",
"ModeloEditNonWritableScalarSurfaceEntryV1",
"ModeloEditParseResultV1",
"ModeloEditParsedValueV1",
"ModeloEditPermittedSurfaceEntryV1",
"ModeloEditPreflightEvaluatedV1",
"ModeloEditPreflightResultV1",
"ModeloEditRefusalCode",
"ModeloEditRefusalV1",
"ModeloEditRefusedV1",
"ModeloEditRowAddressV1",
"ModeloEditRowIntentKind",
"ModeloEditScalarAddressV1",
"ModeloEditScalarIntentKind",
"ModeloEditSchemaIdentityV1",
"ModeloEditStaleBaselineRefusalV1",
"ModeloEditSubmissionV1",
"ModeloEditUnsupportedIntentReason",
"ModeloEditUnsupportedIntentRefusalV1",
"ModeloEditVersionRefusalV1",
"ModeloEditWritableBindingOverrideSurfaceEntryV1",
"ModeloEditWritableDetailRowSurfaceEntryV1",
"ModeloEditWritableRowGroupSurfaceEntryV1",
"ModeloEditWritableScalarSurfaceEntryV1",
"ModeloRowEditIntentV1",
"ModeloScalarEditIntentV1",
]