meerkat-machine-schema 0.7.13

Formal machine schemas and transition definitions for Meerkat
Documentation
pub mod catalog;
mod composition;
pub mod identity;
mod machine;
mod seam;
pub mod types;

pub use identity::{
    NamedTypeBinding, RustTypeAtom, TypePathEnumPayloadAtom, TypePathEnumPayloadField,
    TypePathEnumStructuralVariant, TypePathStructField, TypePathStructFieldAtom,
};
pub use types::{CommsRuntimeId, McpServerId, MobId, PeerCorrelationId};

pub use catalog::{
    CompositionCoverageManifest, CoverageAnchor, CoverageClaims, CoverageSchemaTarget,
    MachineCoverageManifest, MachineProductionOwnerRelation, ScenarioCoverage,
    SemanticCoverageEntry, SymbolRef, canonical_composition_coverage_manifests,
    canonical_composition_schemas, canonical_machine_coverage_manifests,
    canonical_machine_production_owner_relations, canonical_machine_schemas,
    meerkat_mob_seam_composition, scheduler_rule_coverage_name,
};
pub use composition::{
    ActorKind, ActorPriority, ActorSchema, ClosurePolicy, CommsTrustAuthorityOperation,
    CommsTrustAuthorityProtocol, CommsTrustAuthoritySourceKind, CompositionDriver,
    CompositionDriverRustBinding, CompositionInvariant, CompositionInvariantKind,
    CompositionSchema, CompositionSchemaError, CompositionStateLimits, CompositionTransactionPlan,
    CompositionWitness, CompositionWitnessField, CompositionWitnessInput, CompositionWitnessState,
    CompositionWitnessTransition, CompositionWitnessTransitionOrder, DriverDispatchRoute,
    DurableMarkerFieldBinding, DurableMarkerProtocol, DurableMarkerRelationProtocol,
    EffectHandoffProtocol, EffectTeardownClass, EntryInput, FeedbackFieldBinding,
    FeedbackFieldSource, FeedbackInputRef, HandleBridgeFeedbackBinding, MachineInstance,
    ProtocolGenerationMode, ProtocolHelperReturnShape, ProtocolRustBinding, Route,
    RouteBindingSource, RouteDelivery, RouteFieldBinding, RouteTarget, RouteTargetKind,
    RouteTargetSelector, RouteVariantId, SchedulerRule, TeardownObligationClass, WatchedEffect,
};
pub use machine::{
    CommandPlanSchema, EffectClosureSchema, EffectDisposition, EffectDispositionRule, EffectEmit,
    EnumSchema, Expr, FieldInit, FieldSchema, FieldType, Guard, HelperSchema, InitSchema,
    InputMatch, InvariantSchema, MachineSchema, MachineSchemaError, Quantifier, RustBinding,
    StateSchema, TransitionSchema, TriggerKind, TriggerMatch, TypeRef, Update, VariantSchema,
};
pub use seam::SeamClassification;

#[cfg(test)]
mod tests {
    use super::{Update, canonical_machine_coverage_manifests, canonical_machine_schemas};
    use crate::catalog::dsl::dsl_schedule_lifecycle_machine as schedule_lifecycle_machine;

    #[test]
    fn schedule_and_occurrence_machines_are_registered_in_canonical_catalog() {
        let machine_names: Vec<_> = canonical_machine_schemas()
            .into_iter()
            .map(|schema| schema.machine)
            .collect();
        let coverage_names: Vec<_> = canonical_machine_coverage_manifests()
            .into_iter()
            .map(|manifest| manifest.machine)
            .collect();

        assert!(
            machine_names
                .iter()
                .any(|name| name.as_str() == "ScheduleLifecycleMachine"),
            "schedule lifecycle machine must be a canonical schema"
        );
        assert!(
            machine_names
                .iter()
                .any(|name| name.as_str() == "OccurrenceLifecycleMachine"),
            "occurrence lifecycle machine must be a canonical schema"
        );
        assert!(
            coverage_names
                .iter()
                .any(|name| name.as_str() == "ScheduleLifecycleMachine"),
            "schedule lifecycle machine must have coverage metadata"
        );
        assert!(
            coverage_names
                .iter()
                .any(|name| name.as_str() == "OccurrenceLifecycleMachine"),
            "occurrence lifecycle machine must have coverage metadata"
        );
    }

    #[test]
    fn schedule_delete_transitions_bump_revision_and_supersede_pending_occurrences() {
        let machine = schedule_lifecycle_machine();

        for transition_name in ["DeleteActive", "DeletePaused"] {
            let transition = machine
                .transitions
                .iter()
                .find(|transition| transition.name.as_str() == transition_name);

            assert!(transition.is_some(), "missing {transition_name} transition");
            let Some(transition) = transition else {
                return;
            };

            assert!(
                transition.updates.iter().any(|update| matches!(
                    update,
                    Update::Increment { field, amount } if field.as_str() == "revision" && *amount == 1
                )),
                "{transition_name} should advance the revision"
            );
            assert!(
                transition
                    .emit
                    .iter()
                    .any(|effect| effect.variant.as_str() == "SupersedePendingOccurrences"),
                "{transition_name} should supersede older pending occurrences"
            );
        }
    }

    #[test]
    fn canonical_registry_excludes_absorbed_flow_machine_schemas() {
        let machine_names: Vec<_> = canonical_machine_schemas()
            .into_iter()
            .map(|schema| schema.machine)
            .collect();

        for compat_name in ["FlowRunMachine", "FlowFrameMachine", "LoopIterationMachine"] {
            assert!(
                !machine_names
                    .iter()
                    .any(|name| name.as_str() == compat_name),
                "{compat_name} should be absorbed into MobMachine, not canonical"
            );
        }
    }
}