Source code for cadrumo.application.operator_actions.models
"""Strict application records for machine-resolvable action outcomes."""from__future__importannotationsfrompydanticimportBaseModel,field_validatorfrom...core.action_argument_resolutionimportActionArgumentResolutionfrom...core.errors.hierarchyimportpydantic_validation_boundaryfrom...core.modelsimportSTRICT_FROZEN_CONFIGas_STRICT_FROZENfrom...core.operator_action_enumsimportActionArgumentStatusfrom...core.precondition_action_invariantsimport(PreconditionActionIdentity,PreconditionEvidence,PreconditionOutcomeInvariant,)
[docs]classConditionEvidence(PreconditionEvidence):"""Typed facts supporting a failed condition from one authority."""
[docs]classActionReference(PreconditionActionIdentity):"""Stable action identity; catalogue resolution is owned by a later layer."""
[docs]classActionArgumentBinding(ActionArgumentResolution):"""One recovery-action argument and the verdict data that can supply it."""
[docs]classPreconditionVerdict(PreconditionOutcomeInvariant[ConditionEvidence,ActionReference,ActionArgumentBinding],):"""Application-owned outcome for one failed precondition. The record identifies the rejected condition and evidence, then carries exactly one actionable recovery reference or an explicit closed no-recovery outcome. It neither resolves an action catalogue nor embeds presentation text or an executable command. """
[docs]classDeclaredNextAction(BaseModel):"""One catalogue action a producer names as the forward step after success. This is the success-path counterpart of :class:`PreconditionVerdict` and is deliberately a separate record: nothing failed, so there is no rejected condition, no evidence, no conditionality axis and no closed no-recovery outcome to state. What remains is the stable action identity plus the concrete arguments the producer can already supply from its own outcome. The record carries no command string, no CLI path and no presentation text. Catalogue lookup, live-command resolution and required-input coverage stay with the operator-surface projection, which refuses a declaration whose target cannot supply every live required input - so a producer can never make an unexecutable step look executable. Producers across packages share this one record rather than each defining a local "next command" carrier, which is how divergent command prose entered the tree in the first place. """model_config=_STRICT_FROZENaction:ActionReferenceargument_bindings:tuple[ActionArgumentBinding,...]=()@field_validator("argument_bindings")@classmethod@pydantic_validation_boundarydef_require_concrete_unique_arguments(cls,value:tuple[ActionArgumentBinding,...],)->tuple[ActionArgumentBinding,...]:"""Refuse a duplicated or unresolved argument on a forward action."""names=tuple(item.argument_nameforiteminvalue)iflen(set(names))!=len(names):raiseValueError("declared next-action argument names must be unique")ifany(item.statusisnotActionArgumentStatus.RESOLVEDforiteminvalue):raiseValueError("declared next actions require fully resolved argument bindings")returntuple(sorted(value,key=lambdaitem:item.argument_name))