Skip to main content

meerkat_runtime/
meerkat_machine_types.rs

1//! Meerkat runtime command/result and snapshot support types.
2//!
3//! The authority surface now lives in `meerkat_machine.rs`; this module holds
4//! the supporting command/result enums and the durable diagnostic snapshots that
5//! remain useful after cutover.
6
7use std::collections::BTreeMap;
8use std::sync::Arc;
9
10use crate::meerkat_machine::{CommsDrainMode, CommsDrainPhase, DrainExitReason, dsl};
11use indexmap::IndexSet;
12use meerkat_core::RuntimeEpochId;
13use meerkat_core::agent::CommsRuntime;
14use meerkat_core::image_generation::{
15    ImageOperationApprovalReason, ImageOperationDenialReason, ImageOperationId,
16    ImageOperationPhase, ImageOperationTerminalClass, ImageProviderTerminalObservation,
17    ProviderTextDisposition, SessionModelRoutingStatus, SwitchTurnApprovalReason,
18    SwitchTurnControlResult, SwitchTurnIntent, SwitchTurnRequestId,
19};
20use meerkat_core::lifecycle::WaitRequestId;
21use meerkat_core::lifecycle::core_executor::CoreExecutor;
22use meerkat_core::lifecycle::run_primitive::{ModelId, TurnMetadataOverride};
23use meerkat_core::lifecycle::{InputId, RunId};
24use meerkat_core::lifecycle::{RunBoundaryReceipt, RunId as LifecycleRunId};
25use meerkat_core::ops::OperationId;
26use meerkat_core::ops_lifecycle::OperationLifecycleSnapshot;
27use meerkat_core::types::HandlingMode;
28use meerkat_core::types::SessionId;
29use meerkat_machine_derive::CommandManifest;
30use meerkat_machine_schema::catalog::dsl::meerkat_machine::MeerkatMachineInputVariant;
31use serde::{Deserialize, Serialize};
32
33use crate::AcceptOutcome;
34use crate::identifiers::LogicalRuntimeId;
35use crate::ingress_types::{ContentShape, RequestId, ReservationKey};
36use crate::input::Input;
37use crate::input_state::InputLifecycleState;
38use crate::input_state::InputTerminalOutcome;
39use crate::input_state::StoredInputState;
40use crate::runtime_event::RuntimeEventEnvelope;
41use crate::runtime_state::RuntimeState;
42use crate::traits::{
43    DestroyReport, RecoveryReport, RecycleReport, ResetReport, RetireReport,
44    RuntimeControlPlaneError, RuntimeDriverError,
45};
46
47/// Per-turn LLM hot-swap reconfigure request.
48///
49/// `provider_params` and `auth_binding` carry the canonical Inherit/Set/Clear
50/// tri-state via [`TurnMetadataOverride`]: `None` preserves the durable value,
51/// `Some(Set)` overrides it, and `Some(Clear)` removes it. The illegal
52/// "set and clear" fourth state is structurally unrepresentable.
53#[derive(Debug, Clone, Serialize, PartialEq)]
54#[serde(rename_all = "snake_case")]
55pub struct SessionLlmReconfigureRequest {
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub model: Option<String>,
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub provider: Option<String>,
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub provider_params: Option<
62        TurnMetadataOverride<meerkat_core::lifecycle::run_primitive::ProviderParamsOverride>,
63    >,
64    /// Optional realm-scoped connection override resolved through the tri-state:
65    /// `Some(Set)` swaps the binding, `Some(Clear)` removes it, `None` preserves
66    /// the session's existing `SessionLlmIdentity.auth_binding`.
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub auth_binding: Option<TurnMetadataOverride<meerkat_core::AuthBindingRef>>,
69}
70
71#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
72#[serde(rename_all = "snake_case")]
73pub enum SessionLlmCapabilitySurfaceStatus {
74    Resolved,
75    #[default]
76    Unresolved,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
80#[serde(rename_all = "snake_case")]
81pub struct SessionLlmCapabilitySurface {
82    pub supports_temperature: bool,
83    pub supports_thinking: bool,
84    pub supports_reasoning: bool,
85    pub inline_video: bool,
86    pub vision: bool,
87    #[serde(default)]
88    pub image_input: bool,
89    pub image_tool_results: bool,
90    pub supports_web_search: bool,
91    #[serde(default)]
92    pub image_generation: bool,
93    /// Whether the resolved model exposes a realtime bidirectional streaming
94    /// transport. Drives capability-based auto attach/detach in
95    /// `reconfigure_live_topology` and `apply_capability_driven_realtime_transport`.
96    #[serde(default)]
97    pub realtime: bool,
98    #[serde(default, skip_serializing_if = "Option::is_none")]
99    pub call_timeout_secs: Option<u64>,
100}
101
102impl SessionLlmCapabilitySurface {
103    #[must_use]
104    pub fn to_wire_resolved(&self) -> meerkat_contracts::WireResolvedModelCapabilities {
105        meerkat_contracts::WireResolvedModelCapabilities {
106            vision: self.vision,
107            image_input: self.image_input,
108            image_tool_results: self.image_tool_results,
109            inline_video: self.inline_video,
110            realtime: self.realtime,
111            web_search: self.supports_web_search,
112            image_generation: self.image_generation,
113        }
114    }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub struct SessionLlmCapabilityDelta {
119    pub previous: Option<SessionLlmCapabilitySurface>,
120    pub current: Option<SessionLlmCapabilitySurface>,
121    pub changed: bool,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct SessionToolVisibilityDelta {
126    pub previous_capability_base_filter: meerkat_core::ToolFilter,
127    pub current_capability_base_filter: meerkat_core::ToolFilter,
128    pub committed_visible_set_changed: bool,
129    pub revision_bumped: bool,
130}
131
132#[derive(Debug, Clone, PartialEq)]
133pub struct SessionLlmReconfigureReport {
134    pub previous_identity: meerkat_core::SessionLlmIdentity,
135    pub new_identity: meerkat_core::SessionLlmIdentity,
136    pub capability_delta: SessionLlmCapabilityDelta,
137    pub tool_visibility_delta: SessionToolVisibilityDelta,
138    pub rollback_occurred: bool,
139}
140
141#[derive(Debug, Clone)]
142pub struct HydratedSessionLlmState {
143    pub current_identity: meerkat_core::SessionLlmIdentity,
144    pub current_visibility_state: meerkat_core::SessionToolVisibilityState,
145    pub current_capability_surface: Option<SessionLlmCapabilitySurface>,
146    pub capability_surface_status: SessionLlmCapabilitySurfaceStatus,
147    pub base_tool_names: std::collections::BTreeSet<meerkat_core::ToolName>,
148}
149
150#[derive(Debug, Clone)]
151pub struct ResolvedSessionLlmReconfigure {
152    pub target_identity: meerkat_core::SessionLlmIdentity,
153    pub target_capability_surface: SessionLlmCapabilitySurface,
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum ModelRoutingApprovalDisposition {
158    NotRequired,
159    Approved,
160    DeniedByUser,
161    RequiredButUnavailable,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub struct ModelRoutingRealtimePolicy {
166    pub target_realtime_capable: bool,
167    pub allow_realtime_detach: bool,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct SwitchTurnRequest {
172    pub request_id: SwitchTurnRequestId,
173    pub intent: SwitchTurnIntent,
174    pub target_realtime: ModelRoutingRealtimePolicy,
175    pub approval: ModelRoutingApprovalDisposition,
176    pub approval_reason: Option<SwitchTurnApprovalReason>,
177}
178
179#[derive(Debug, Clone, PartialEq, Eq)]
180pub struct ImageOperationRoutingRequest {
181    pub operation_id: ImageOperationId,
182    pub target_model: ModelId,
183    pub target_realtime: ModelRoutingRealtimePolicy,
184    pub approval: ModelRoutingApprovalDisposition,
185    pub approval_reason: Option<ImageOperationApprovalReason>,
186    pub requires_scoped_override: bool,
187}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
190pub enum ImageOperationRoutingResult {
191    Accepted {
192        operation_id: ImageOperationId,
193        phase: ImageOperationPhase,
194    },
195    Denied {
196        operation_id: ImageOperationId,
197        reason: ImageOperationDenialReason,
198    },
199}
200
201#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
202#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
203pub trait SessionLlmReconfigureHost: Send + Sync {
204    async fn hydrate_session_llm_state(
205        &self,
206        session_id: &SessionId,
207    ) -> Result<HydratedSessionLlmState, RuntimeDriverError>;
208
209    async fn resolve_target_session_llm_identity(
210        &self,
211        request: &SessionLlmReconfigureRequest,
212        current_identity: &meerkat_core::SessionLlmIdentity,
213    ) -> Result<ResolvedSessionLlmReconfigure, RuntimeDriverError>;
214
215    async fn apply_live_session_llm_identity(
216        &self,
217        session_id: &SessionId,
218        identity: &meerkat_core::SessionLlmIdentity,
219    ) -> Result<(), RuntimeDriverError>;
220
221    async fn apply_live_session_tool_visibility_state(
222        &self,
223        session_id: &SessionId,
224        visibility_state: Option<meerkat_core::SessionToolVisibilityState>,
225    ) -> Result<(), RuntimeDriverError>;
226
227    async fn persist_live_session(&self, session_id: &SessionId) -> Result<(), RuntimeDriverError>;
228
229    async fn discard_live_session(&self, session_id: &SessionId) -> Result<(), RuntimeDriverError>;
230}
231
232#[derive(Debug, thiserror::Error)]
233pub(crate) enum MeerkatMachineCommandError {
234    #[error(transparent)]
235    Driver(#[from] RuntimeDriverError),
236    #[error(transparent)]
237    Control(#[from] RuntimeControlPlaneError),
238}
239
240/// Unified internal Meerkat machine command surface.
241///
242/// This replaces the old per-domain dispatch split (session, drain,
243/// drain-local, control, ingress) while keeping the public helper
244/// methods and external runtime/machine surface unchanged.
245#[derive(CommandManifest)]
246#[allow(clippy::large_enum_variant)]
247pub(crate) enum MeerkatMachineCommand {
248    RegisterSession {
249        session_id: SessionId,
250    },
251    // Retained for generated manifest/parity coverage; production call sites
252    // prefer typed unregister helpers.
253    #[cfg_attr(not(test), allow(dead_code))]
254    UnregisterSession {
255        session_id: SessionId,
256    },
257    EnsureSessionWithExecutor {
258        session_id: SessionId,
259        executor: Box<dyn CoreExecutor>,
260    },
261    SetSilentIntents {
262        session_id: SessionId,
263        intents: Vec<String>,
264    },
265    CancelAfterBoundary {
266        session_id: SessionId,
267    },
268    StopRuntimeExecutor {
269        session_id: SessionId,
270        reason: String,
271    },
272    CommitServiceTurnTerminalReceipt {
273        session_id: SessionId,
274    },
275    #[cfg_attr(not(test), allow(dead_code))]
276    ContainsSession {
277        session_id: SessionId,
278    },
279    SessionHasExecutor {
280        session_id: SessionId,
281    },
282    SessionHasComms {
283        session_id: SessionId,
284    },
285    OpsLifecycleRegistry {
286        session_id: SessionId,
287    },
288    // Retained for generated composition contracts; production binding requests
289    // normally enter through typed composition helpers.
290    #[cfg_attr(not(test), allow(dead_code))]
291    PrepareBindings {
292        session_id: SessionId,
293    },
294    // Local bootstrap is a shell mechanic, kept in the command catalog so the
295    // generated classifier can spell that boundary explicitly.
296    #[cfg_attr(not(test), allow(dead_code))]
297    PrepareLocalSessionBindings {
298        session_id: SessionId,
299    },
300    InputState {
301        session_id: SessionId,
302        input_id: InputId,
303    },
304    ListActiveInputs {
305        session_id: SessionId,
306    },
307    ReconfigureSessionLlmIdentity {
308        session_id: SessionId,
309        previous_identity: Box<meerkat_core::SessionLlmIdentity>,
310        previous_visibility_state: Box<meerkat_core::SessionToolVisibilityState>,
311        previous_capability_surface: Option<SessionLlmCapabilitySurface>,
312        previous_capability_surface_status: SessionLlmCapabilitySurfaceStatus,
313        view_image_tool_available: bool,
314        previous_view_image_visible: bool,
315        next_view_image_visible: bool,
316        previous_active_visibility_revision: u64,
317        previous_staged_visibility_revision: u64,
318        target_identity: Box<meerkat_core::SessionLlmIdentity>,
319        target_capability_surface: Box<SessionLlmCapabilitySurface>,
320        next_visibility_state: Box<meerkat_core::SessionToolVisibilityState>,
321        next_capability_base_filter: meerkat_core::ToolFilter,
322        next_active_visibility_revision: u64,
323        tool_visibility_delta: Box<SessionToolVisibilityDelta>,
324    },
325    StagePersistentFilter {
326        session_id: SessionId,
327        filter: meerkat_core::ToolFilter,
328        witnesses:
329            std::collections::BTreeMap<meerkat_core::ToolName, meerkat_core::ToolVisibilityWitness>,
330    },
331    RequestDeferredTools {
332        session_id: SessionId,
333        authorities: Vec<meerkat_core::DeferredToolLoadAuthority>,
334    },
335    /// Publish the committed visible tool set through the machine dispatch.
336    ///
337    /// TLA+ source: VisibleSurfacesMatchAppliedStateInvariant —
338    /// the visible-set publication must route through the canonical command
339    /// path and be gated on session existence and non-Destroyed state.
340    PublishCommittedVisibleSet {
341        session_id: SessionId,
342        visibility_state: Box<meerkat_core::SessionToolVisibilityState>,
343    },
344    SetPeerIngressContext {
345        session_id: SessionId,
346        keep_alive: bool,
347        comms_runtime: Option<Arc<dyn CommsRuntime>>,
348        /// Mob-owned path sets this to the spawning mob's id so the DSL
349        /// transitions to `PeerIngressOwnerKind::MobOwned` rather than
350        /// `SessionOwned`. Session-owned and detach paths leave it `None`.
351        mob_id: Option<crate::meerkat_machine::dsl::MobId>,
352    },
353    NotifyDrainExited {
354        session_id: SessionId,
355        reason: DrainExitReason,
356    },
357    AbortAll,
358    Abort {
359        session_id: SessionId,
360    },
361    Wait {
362        session_id: SessionId,
363    },
364    Ingest {
365        runtime_id: LogicalRuntimeId,
366        input: Input,
367    },
368    PublishEvent {
369        event: RuntimeEventEnvelope,
370    },
371    Retire {
372        runtime_id: LogicalRuntimeId,
373    },
374    Recycle {
375        runtime_id: LogicalRuntimeId,
376    },
377    Reset {
378        runtime_id: LogicalRuntimeId,
379    },
380    Recover {
381        runtime_id: LogicalRuntimeId,
382    },
383    Destroy {
384        runtime_id: LogicalRuntimeId,
385    },
386    RuntimeState {
387        runtime_id: LogicalRuntimeId,
388    },
389    ResolvedSessionLlmCapabilities {
390        session_id: SessionId,
391    },
392    ConfigureModelRoutingBaseline {
393        session_id: SessionId,
394        baseline_model: ModelId,
395        realtime_capable: bool,
396    },
397    SessionModelRoutingStatus {
398        session_id: SessionId,
399    },
400    RequestSwitchTurn {
401        session_id: SessionId,
402        request: Box<SwitchTurnRequest>,
403    },
404    AdmitModelRoutingAssistantTurn {
405        session_id: SessionId,
406    },
407    BeginImageOperation {
408        session_id: SessionId,
409        request: Box<ImageOperationRoutingRequest>,
410    },
411    DenyImageOperationPlan {
412        session_id: SessionId,
413        operation_id: ImageOperationId,
414        reason: ImageOperationDenialReason,
415    },
416    ActivateImageOperationOverride {
417        session_id: SessionId,
418        operation_id: ImageOperationId,
419    },
420    ClassifyImageOperationTerminal {
421        session_id: SessionId,
422        operation_id: ImageOperationId,
423        observation: ImageProviderTerminalObservation,
424        provider_text: ProviderTextDisposition,
425    },
426    CompleteImageOperation {
427        session_id: SessionId,
428        operation_id: ImageOperationId,
429        terminal: ImageOperationTerminalClass,
430    },
431    RestoreImageOperationOverride {
432        session_id: SessionId,
433        operation_id: ImageOperationId,
434    },
435    LoadBoundaryReceipt {
436        runtime_id: LogicalRuntimeId,
437        run_id: LifecycleRunId,
438        sequence: u64,
439    },
440    AcceptWithCompletion {
441        session_id: SessionId,
442        input: Input,
443        register_completion: bool,
444    },
445    AcceptWithoutWake {
446        session_id: SessionId,
447        input: Input,
448    },
449}
450
451#[derive(Debug, Clone)]
452pub(crate) struct MeerkatMachineRunFailure {
453    pub source: Option<dsl::RunFailureSourceKind>,
454    pub machine_terminal_failure_observed: bool,
455    pub error: String,
456}
457
458impl MeerkatMachineRunFailure {
459    pub(crate) fn from_machine_terminal_failure(error: impl Into<String>) -> Self {
460        Self {
461            source: None,
462            machine_terminal_failure_observed: true,
463            error: error.into(),
464        }
465    }
466}
467
468#[derive(Debug)]
469#[allow(clippy::large_enum_variant)]
470pub(crate) enum MeerkatMachineCommandResult {
471    AcceptOutcome(AcceptOutcome),
472    AcceptWithCompletion {
473        outcome: AcceptOutcome,
474        handle: Option<crate::completion::CompletionHandle>,
475        #[cfg_attr(not(test), allow(dead_code))]
476        admission_signal: crate::driver::ephemeral::PostAdmissionSignal,
477    },
478    Unit,
479    Bool(bool),
480    Spawned(bool),
481    OpsLifecycleRegistry(Option<Arc<crate::ops_lifecycle::RuntimeOpsLifecycleRegistry>>),
482    Bindings(meerkat_core::SessionRuntimeBindings),
483    InputState(Option<StoredInputState>),
484    ActiveInputs(Vec<InputId>),
485    LlmReconfigured(SessionLlmReconfigureReport),
486    VisibilityRevision(meerkat_core::ToolScopeRevision),
487    VisibilityPublished(meerkat_core::SessionToolVisibilityState),
488    RetireReport(RetireReport),
489    RecycleReport(RecycleReport),
490    ResetReport(ResetReport),
491    RecoveryReport(RecoveryReport),
492    DestroyReport(DestroyReport),
493    RuntimeState(RuntimeState),
494    ResolvedSessionLlmCapabilities(Option<SessionLlmCapabilitySurface>),
495    SessionModelRoutingStatus(SessionModelRoutingStatus),
496    SwitchTurnControlResult(SwitchTurnControlResult),
497    ImageOperationRoutingResult(ImageOperationRoutingResult),
498    ImageOperationPhase(ImageOperationPhase),
499    ImageOperationTerminalClass(ImageOperationTerminalClass),
500    BoundaryReceipt(Option<RunBoundaryReceipt>),
501}
502
503#[doc(hidden)]
504#[must_use]
505pub fn canonical_meerkat_machine_command_manifest() -> IndexSet<&'static str> {
506    canonical_meerkat_machine_command_input_variant_manifest()
507        .into_iter()
508        .map(|variant| variant.as_str())
509        .collect()
510}
511
512#[doc(hidden)]
513#[must_use]
514pub fn canonical_meerkat_machine_command_input_variant_manifest()
515-> IndexSet<MeerkatMachineInputVariant> {
516    canonical_meerkat_machine_command_classifications()
517        .into_iter()
518        .flat_map(|record| record.classification.catalog_input_variants())
519        .collect()
520}
521
522#[doc(hidden)]
523#[must_use]
524pub fn canonical_meerkat_machine_runtime_internal_manifest() -> IndexSet<&'static str> {
525    canonical_meerkat_machine_runtime_internal_input_variant_manifest()
526        .into_iter()
527        .map(|variant| variant.as_str())
528        .collect()
529}
530
531#[doc(hidden)]
532#[must_use]
533pub fn canonical_meerkat_machine_runtime_internal_input_variant_manifest()
534-> IndexSet<MeerkatMachineInputVariant> {
535    canonical_meerkat_machine_runtime_internal_classifications()
536        .into_iter()
537        .map(|record| record.input.input_variant())
538        .collect()
539}
540
541#[doc(hidden)]
542#[must_use]
543pub fn canonical_meerkat_machine_runtime_internal_fieldless_input_variant_manifest()
544-> IndexSet<MeerkatMachineInputVariant> {
545    MeerkatMachineFieldlessRuntimeInternalInput::ALL
546        .iter()
547        .copied()
548        .map(MeerkatMachineFieldlessRuntimeInternalInput::input_variant)
549        .collect()
550}
551
552macro_rules! meerkat_machine_runtime_internal_inputs {
553    ($($reason:ident => [$($variant:ident),+ $(,)?]),+ $(,)?) => {
554        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
555        pub enum MeerkatMachineRuntimeInternalInput {
556            $($($variant),+),+
557        }
558
559        impl MeerkatMachineRuntimeInternalInput {
560            pub const ALL: &'static [Self] = &[
561                $($(Self::$variant),+),+
562            ];
563
564            pub const CLASSIFICATIONS: &'static [MeerkatMachineRuntimeInternalClassificationRecord] = &[
565                $($(
566                    MeerkatMachineRuntimeInternalClassificationRecord {
567                        input: Self::$variant,
568                        reason: MeerkatMachineRuntimeInternalReason::$reason,
569                    },
570                )+)+
571            ];
572
573            #[must_use]
574            pub const fn input_variant(self) -> MeerkatMachineInputVariant {
575                match self {
576                    $($(Self::$variant => MeerkatMachineInputVariant::$variant,)+)+
577                }
578            }
579
580            #[must_use]
581            pub const fn reason(self) -> MeerkatMachineRuntimeInternalReason {
582                match self {
583                    $(
584                        $(Self::$variant)|+ => MeerkatMachineRuntimeInternalReason::$reason,
585                    )+
586                }
587            }
588        }
589    };
590}
591
592#[derive(Debug, Clone, Copy, PartialEq, Eq)]
593pub enum MeerkatMachineRuntimeInternalReason {
594    InputQueueLifecycle,
595    OperationLifecycle,
596    RunExecutionLifecycle,
597    CancellationLifecycle,
598    LiveTopologyReconfiguration,
599    InteractionStreamLifecycle,
600    EventStreamLifecycle,
601    CommsIngressLifecycle,
602    SupervisorTrustLifecycle,
603    MobOperatorAuthorityLifecycle,
604    PeerRequestLifecycle,
605    VisibilityAuthorityLifecycle,
606    DeferredSessionLifecycle,
607    ExtractionLifecycle,
608    McpServerLifecycle,
609    ModelRoutingLifecycle,
610    ExternalSurfaceLifecycle,
611    FailureRecoveryLifecycle,
612    UserInterruptDispatch,
613    SessionUnregisterDrainLifecycle,
614}
615
616#[derive(Debug, Clone, Copy, PartialEq, Eq)]
617pub struct MeerkatMachineRuntimeInternalClassificationRecord {
618    pub input: MeerkatMachineRuntimeInternalInput,
619    pub reason: MeerkatMachineRuntimeInternalReason,
620}
621
622meerkat_machine_runtime_internal_inputs!(
623    InputQueueLifecycle => [
624        AbandonInput,
625        AdvanceSessionContext,
626        BudgetExhausted,
627        ChangeLane,
628        CoalesceInput,
629        ConsumeInput,
630        ConsumeOnAccept,
631        MarkApplied,
632        MarkAppliedPendingConsumption,
633        DeferInputBehindBacklog,
634        PrioritizeInput,
635        QueueAccepted,
636        RecoverAdmittedInput,
637        RecoverInputLifecycle,
638        ResolveAdmissionIdempotency,
639        ResolveAdmissionPlan,
640        ResolveAdmissionValidation,
641        ResolveInputPublicLifecycle,
642        ResolveInputPublicTerminalOutcome,
643        ResolveTranscriptEditAdmission,
644        RegisterAcceptedIdempotency,
645        ResolveStagedRollback,
646        RetryRequested,
647        RollbackStaged,
648        StageForRun,
649        StartConversationRun,
650        StartImmediateAppend,
651        StartImmediateContext,
652        SteerAccepted,
653        SupersedeInput,
654        AuthorizeStoredInputStateSeed,
655        ClassifyInputTerminality,
656        ClassifyRecoveredInputDurability,
657        ClassifyRuntimeLoopQueueAdmission,
658        NormalizeRecoveredInputLifecycle,
659    ],
660    OperationLifecycle => [
661        AbortOp,
662        CancelOp,
663        CancelWaitAll,
664        ClassifyOperationCompletionFeed,
665        ClassifyOperationCompletionWake,
666        ClassifyOperationDurability,
667        ClassifyOperationPublicResult,
668        ClassifyOperationTerminality,
669        ClassifyOperationTransitionIdempotence,
670        ClassifyRecoveredOperationRecord,
671        CollectCompletedOp,
672        CompleteOp,
673        EvictCompletedOp,
674        FailOp,
675        IncrementAttemptCount,
676        OpsBarrierSatisfied,
677        PeerReadyOp,
678        ProgressReportedOp,
679        RecoverCompletionFeedEntry,
680        RegisterOp,
681        RegisterPendingOps,
682        RecoverCompletionConsumerCursors,
683        RecoverOpRecord,
684        RecoverOpsCompletionCursor,
685        ResolveOpLifecycleTransitionRejection,
686        ResolveRuntimeOpsLifecycleDurability,
687        ResolveWaitAllAdmission,
688        RequestWaitAll,
689        RetireCompletedOp,
690        RetireRequestedOp,
691        SatisfyWaitAll,
692        StartOp,
693        TerminateOp,
694    ],
695    RunExecutionLifecycle => [
696        AcknowledgeTerminal,
697        AdvanceAgentCompletionCursor,
698        AdvanceRuntimeInjectedCompletionCursor,
699        AdvanceRuntimeObservedCompletionCursor,
700        BoundaryComplete,
701        BoundaryContinue,
702        ClassifyAssistantOutput,
703        ClassifyCallTimeout,
704        ClassifyTurnTerminalCauseClass,
705        ClassifyTurnTerminality,
706        ClearSessionLlmState,
707        Commit,
708        Fail,
709        HydrateSessionLlmState,
710        LlmReturnedTerminal,
711        LlmReturnedToolCalls,
712        Prepare,
713        PrimitiveApplied,
714        RecordBoundarySeq,
715        ResolveLiveBoundaryContextReceipt,
716        ResolveRuntimeCompletionCleanup,
717        ResolveRuntimeCompletionResult,
718        ResolveRuntimeCompletionWaitFailure,
719        ResolveTurnSurfaceResult,
720        RollbackRun,
721        RunCompleted,
722        RunFailed,
723        RuntimeExecutorExited,
724        TimeBudgetExceeded,
725        ToolCallsResolved,
726        TurnLimitReached,
727    ],
728    CancellationLifecycle => [
729        CancelNow,
730        CancelRun,
731        CancellationObserved,
732        ForceCancelNoRun,
733        RequestCancelAfterBoundary,
734        RunCancelled,
735    ],
736    LiveTopologyReconfiguration => [
737        AbandonLiveOpenAdmission,
738        CompleteUntilChangedSwitchTurnReconfigure,
739        RecordLiveChannelRequestRejected,
740        RecordLiveChannelStatus,
741        RecordLiveCloseClosed,
742        RecordLiveCommandAccepted,
743        RecordLiveCommandRejected,
744        RecordLiveRefreshQueued,
745        ResolveLiveOpenAdmission,
746    ],
747    InteractionStreamLifecycle => [
748        InteractionStreamAttached,
749        InteractionStreamClosedEarly,
750        InteractionStreamCompleted,
751        InteractionStreamExpired,
752        InteractionStreamReserved,
753    ],
754    EventStreamLifecycle => [
755        RecordMobEventStreamOpened,
756        RecordMobEventStreamTerminated,
757        RecordSessionEventStreamOpened,
758        RecordSessionEventStreamTerminated,
759        ResolveMobEventStreamClose,
760        ResolveSessionEventStreamClose,
761    ],
762    CommsIngressLifecycle => [
763        AddDirectPeerEndpoint,
764        ApplyMobPeerOverlay,
765        AttachMobIngress,
766        AttachSessionIngress,
767        AuthorizeSupervisorMobPeerOverlay,
768        BindSupervisor,
769        ClearLocalEndpoint,
770        DetachIngress,
771        PeerResponseRejected,
772        PublishLocalEndpoint,
773        RemoveDirectPeerEndpoint,
774        ResolvePeerIngressDequeue,
775        ResolvePeerIngressReceive,
776        ResolveSupervisorAuthorizeAdmission,
777        ResolveSupervisorBindAdmission,
778        ResolveSupervisorBindMaterialAdmission,
779        ResolveSupervisorBridgeCommandAdmission,
780        SpawnDrain,
781        StopDrain,
782    ],
783    SupervisorTrustLifecycle => [
784        AuthorizeSupervisor,
785        RequestSupervisorTrustPublish,
786        RevokeSupervisor,
787        SupervisorTrustEdgePublishFailed,
788        SupervisorTrustEdgePublished,
789        SupervisorTrustEdgeRevokeFailed,
790        SupervisorTrustEdgeRevoked,
791    ],
792    MobOperatorAuthorityLifecycle => [
793        GrantMobOperatorManageMob,
794        ResolveMobOperatorCreateAuthority,
795        RestoreMobOperatorAuthority,
796        SetMobOperatorCreateAuthority,
797        SetMobOperatorProfileMutation,
798        SetMobOperatorSpawnProfilesInMob,
799    ],
800    PeerRequestLifecycle => [
801        PeerRequestReceived,
802        PeerRequestSendFailed,
803        PeerRequestSent,
804        PeerRequestTimedOut,
805        PeerResponseProgressArrived,
806        PeerResponseReplied,
807        PeerResponseTerminalArrived,
808    ],
809    VisibilityAuthorityLifecycle => [
810        CommitDeferredNames,
811        CommitVisibilityFilter,
812        ClearTurnToolOverlay,
813        ReplaceDeferredToolAuthorityCatalog,
814        ReplaceFilterToolAuthorityCatalog,
815        SetTurnToolOverlay,
816        StageDeferredNames,
817        StageVisibilityFilter,
818        SurfaceSetRemovalTimeout,
819        ReplaceVisibilityState,
820    ],
821    DeferredSessionLifecycle => [
822        AbandonDeferredSessionPromotion,
823        AuthorizeDeferredSessionMachineArchivedResume,
824        AuthorizeDeferredSessionSystemContextAppend,
825        BeginDeferredSessionArchive,
826        BeginDeferredSessionPromotion,
827        DropDeferredSession,
828        FinishDeferredSessionArchive,
829        FinishDeferredSessionPromotion,
830        RestoreDeferredSessionArchive,
831        StageDeferredSession,
832        UpdateDeferredSessionKeepAlive,
833        UpdateDeferredSessionLlmIdentity,
834    ],
835    ExtractionLifecycle => [
836        EnterExtraction,
837        ExtractionFailed,
838        ExtractionStart,
839        ExtractionValidationFailed,
840        ExtractionValidationPassed,
841    ],
842    McpServerLifecycle => [
843        McpServerConnectPending,
844        McpServerConnected,
845        McpServerDisconnected,
846        McpServerFailed,
847        McpServerReload,
848    ],
849    ModelRoutingLifecycle => [
850        ModelRoutingStatus,
851        RequestFiniteSwitchTurn,
852        RequestUntilChangedSwitchTurn,
853        SetModelRoutingBaseline,
854    ],
855    ExternalSurfaceLifecycle => [
856        AdmitSurfaceRequest,
857        CancelSurfaceRequest,
858        ClassifySurfaceRequestTerminal,
859        FinishSurfaceRequestUnpublished,
860        PublishOrCancelSurfaceRequest,
861        PublishSurfaceRequest,
862        RecordLiveWebrtcAnswerAccepted,
863        RecordLiveWebrtcTokenIssued,
864        RecordLiveWebsocketTokenIssued,
865        ResolveLiveWebrtcAnswerAdmission,
866        ResolveLiveWebsocketTokenAdmission,
867        SurfaceApplyBoundary,
868        SurfaceCallFinished,
869        SurfaceCallStarted,
870        SurfaceFinalizeRemovalClean,
871        SurfaceFinalizeRemovalForced,
872        SurfaceMarkPendingFailed,
873        SurfaceMarkPendingSucceeded,
874        SurfaceRegister,
875        SurfaceShutdown,
876        SurfaceSnapshotAligned,
877        SurfaceStageAdd,
878        SurfaceStageReload,
879        SurfaceStageRemove,
880    ],
881    FailureRecoveryLifecycle => [
882        ClassifyLlmFailureRecovery,
883        ClassifyRuntimeLifecycleDurability,
884        ClassifyRuntimeLifecycleState,
885        FatalFailure,
886        RecoverableFailure,
887        RecoverRuntimeAuthority,
888        ResolveVisibleRuntimePhase,
889    ],
890    UserInterruptDispatch => [
891        InterruptCurrentRun,
892        ResolveUserInterruptPublicResult,
893    ],
894    SessionUnregisterDrainLifecycle => [
895        BeginUnregisterSession,
896        CommsDrainExitedForUnregister,
897        CompletionWaitersResolvedForUnregister,
898        RuntimeLoopStoppedForUnregister,
899    ],
900);
901
902macro_rules! meerkat_machine_fieldless_runtime_internal_inputs {
903    ($($authority:ident => [$($variant:ident),+ $(,)?]),+ $(,)?) => {
904        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
905        pub enum MeerkatMachineFieldlessRuntimeInternalInput {
906            $($($variant),+),+
907        }
908
909        impl MeerkatMachineFieldlessRuntimeInternalInput {
910            pub const ALL: &'static [Self] = &[
911                $($(Self::$variant),+),+
912            ];
913
914            #[must_use]
915            pub const fn runtime_internal_input(self) -> MeerkatMachineRuntimeInternalInput {
916                match self {
917                    $($(Self::$variant => MeerkatMachineRuntimeInternalInput::$variant,)+)+
918                }
919            }
920
921            #[must_use]
922            pub const fn input_variant(self) -> MeerkatMachineInputVariant {
923                self.runtime_internal_input().input_variant()
924            }
925
926            #[must_use]
927            pub const fn authority(self) -> MeerkatMachineFieldlessRuntimeInternalAuthority {
928                match self {
929                    $(
930                        $(Self::$variant)|+ => MeerkatMachineFieldlessRuntimeInternalAuthority::$authority,
931                    )+
932                }
933            }
934
935            #[must_use]
936            pub const fn requires_typed_runtime_internal_stager(self) -> bool {
937                matches!(
938                    self.authority(),
939                    MeerkatMachineFieldlessRuntimeInternalAuthority::UserInterruptDispatch
940                )
941            }
942
943            pub(crate) const fn dsl_input_variant(self) -> dsl::MeerkatMachineInputVariant {
944                match self {
945                    $($(Self::$variant => dsl::MeerkatMachineInputVariant::$variant,)+)+
946                }
947            }
948
949            pub(crate) fn dsl_input(self) -> dsl::MeerkatMachineInput {
950                match self {
951                    $($(Self::$variant => dsl::MeerkatMachineInput::$variant,)+)+
952                }
953            }
954
955            pub(crate) fn from_dsl_input_variant(
956                variant: dsl::MeerkatMachineInputVariant,
957            ) -> Option<Self> {
958                Self::ALL
959                    .iter()
960                    .copied()
961                    .find(|input| input.dsl_input_variant() == variant)
962            }
963
964            pub(crate) fn reject_raw_dsl_input(
965                input: &dsl::MeerkatMachineInput,
966            ) -> Result<(), String> {
967                if let Some(fieldless) = Self::from_dsl_input_variant(input.variant())
968                    && fieldless.requires_typed_runtime_internal_stager()
969                {
970                    let variant = fieldless.input_variant();
971                    return Err(format!(
972                        "fieldless runtime-internal input {variant:?} must use typed runtime-internal staging authority"
973                    ));
974                }
975                Ok(())
976            }
977        }
978    };
979}
980
981meerkat_machine_fieldless_runtime_internal_inputs!(
982    RuntimeOwner => [
983        RuntimeExecutorExited,
984        ForceCancelNoRun,
985        CancelWaitAll,
986        StopDrain,
987        SurfaceShutdown,
988        DetachIngress,
989        ClearLocalEndpoint,
990    ],
991    UserInterruptDispatch => [
992        InterruptCurrentRun,
993    ],
994);
995
996#[derive(Debug, Clone, Copy, PartialEq, Eq)]
997pub enum MeerkatMachineFieldlessRuntimeInternalAuthority {
998    RuntimeOwner,
999    UserInterruptDispatch,
1000}
1001
1002#[doc(hidden)]
1003#[must_use]
1004pub fn canonical_meerkat_machine_runtime_internal_classifications()
1005-> Vec<MeerkatMachineRuntimeInternalClassificationRecord> {
1006    MeerkatMachineRuntimeInternalInput::CLASSIFICATIONS.to_vec()
1007}
1008
1009#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1010pub enum MeerkatMachineCommandClassification {
1011    CatalogInput(MeerkatMachineCatalogInput),
1012    CatalogInputs(&'static [MeerkatMachineCatalogInput]),
1013    ShellMechanic(MeerkatMachineShellMechanicReason),
1014}
1015
1016impl MeerkatMachineCommandClassification {
1017    #[must_use]
1018    pub fn catalog_inputs(self) -> Vec<MeerkatMachineCatalogInput> {
1019        match self {
1020            Self::CatalogInput(input) => vec![input],
1021            Self::CatalogInputs(inputs) => inputs.to_vec(),
1022            Self::ShellMechanic(_) => Vec::new(),
1023        }
1024    }
1025
1026    #[must_use]
1027    pub fn catalog_input_variants(self) -> Vec<MeerkatMachineInputVariant> {
1028        self.catalog_inputs()
1029            .into_iter()
1030            .map(MeerkatMachineCatalogInput::input_variant)
1031            .collect()
1032    }
1033}
1034
1035#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1036pub enum MeerkatMachineCatalogInput {
1037    RegisterSession,
1038    UnregisterSession,
1039    EnsureSessionWithExecutor,
1040    SetSilentIntents,
1041    CancelAfterBoundary,
1042    StopRuntimeExecutor,
1043    ServiceTurnCommitted,
1044    ContainsSession,
1045    SessionHasExecutor,
1046    SessionHasComms,
1047    OpsLifecycleRegistry,
1048    PrepareBindings,
1049    InputState,
1050    ListActiveInputs,
1051    ReconfigureSessionLlmIdentity,
1052    StagePersistentFilter,
1053    RequestDeferredTools,
1054    PublishCommittedVisibleSet,
1055    SetPeerIngressContext,
1056    NotifyDrainExited,
1057    AbortAll,
1058    Abort,
1059    Wait,
1060    Ingest,
1061    PublishEvent,
1062    Retire,
1063    Recycle,
1064    Reset,
1065    Recover,
1066    Destroy,
1067    RuntimeState,
1068    ModelRoutingStatus,
1069    SetModelRoutingBaseline,
1070    RequestFiniteSwitchTurn,
1071    RequestUntilChangedSwitchTurn,
1072    AdmitModelRoutingAssistantTurn,
1073    BeginImageOperation,
1074    DenyImageOperationPlan,
1075    ActivateImageOperationOverride,
1076    ClassifyImageOperationTerminal,
1077    CompleteImageOperation,
1078    RestoreImageOperationOverride,
1079    LoadBoundaryReceipt,
1080    AcceptWithCompletion,
1081    AcceptWithoutWake,
1082}
1083
1084impl MeerkatMachineCatalogInput {
1085    pub const ALL: &'static [Self] = &[
1086        Self::RegisterSession,
1087        Self::UnregisterSession,
1088        Self::EnsureSessionWithExecutor,
1089        Self::SetSilentIntents,
1090        Self::CancelAfterBoundary,
1091        Self::StopRuntimeExecutor,
1092        Self::ServiceTurnCommitted,
1093        Self::ContainsSession,
1094        Self::SessionHasExecutor,
1095        Self::SessionHasComms,
1096        Self::OpsLifecycleRegistry,
1097        Self::PrepareBindings,
1098        Self::InputState,
1099        Self::ListActiveInputs,
1100        Self::ReconfigureSessionLlmIdentity,
1101        Self::StagePersistentFilter,
1102        Self::RequestDeferredTools,
1103        Self::PublishCommittedVisibleSet,
1104        Self::SetPeerIngressContext,
1105        Self::NotifyDrainExited,
1106        Self::AbortAll,
1107        Self::Abort,
1108        Self::Wait,
1109        Self::Ingest,
1110        Self::PublishEvent,
1111        Self::Retire,
1112        Self::Recycle,
1113        Self::Reset,
1114        Self::Recover,
1115        Self::Destroy,
1116        Self::RuntimeState,
1117        Self::ModelRoutingStatus,
1118        Self::SetModelRoutingBaseline,
1119        Self::RequestFiniteSwitchTurn,
1120        Self::RequestUntilChangedSwitchTurn,
1121        Self::AdmitModelRoutingAssistantTurn,
1122        Self::BeginImageOperation,
1123        Self::DenyImageOperationPlan,
1124        Self::ActivateImageOperationOverride,
1125        Self::ClassifyImageOperationTerminal,
1126        Self::CompleteImageOperation,
1127        Self::RestoreImageOperationOverride,
1128        Self::LoadBoundaryReceipt,
1129        Self::AcceptWithCompletion,
1130        Self::AcceptWithoutWake,
1131    ];
1132
1133    #[must_use]
1134    pub const fn input_variant(self) -> MeerkatMachineInputVariant {
1135        match self {
1136            Self::RegisterSession => MeerkatMachineInputVariant::RegisterSession,
1137            Self::UnregisterSession => MeerkatMachineInputVariant::UnregisterSession,
1138            Self::EnsureSessionWithExecutor => {
1139                MeerkatMachineInputVariant::EnsureSessionWithExecutor
1140            }
1141            Self::SetSilentIntents => MeerkatMachineInputVariant::SetSilentIntents,
1142            Self::CancelAfterBoundary => MeerkatMachineInputVariant::CancelAfterBoundary,
1143            Self::StopRuntimeExecutor => MeerkatMachineInputVariant::StopRuntimeExecutor,
1144            Self::ServiceTurnCommitted => MeerkatMachineInputVariant::ServiceTurnCommitted,
1145            Self::ContainsSession => MeerkatMachineInputVariant::ContainsSession,
1146            Self::SessionHasExecutor => MeerkatMachineInputVariant::SessionHasExecutor,
1147            Self::SessionHasComms => MeerkatMachineInputVariant::SessionHasComms,
1148            Self::OpsLifecycleRegistry => MeerkatMachineInputVariant::OpsLifecycleRegistry,
1149            Self::PrepareBindings => MeerkatMachineInputVariant::PrepareBindings,
1150            Self::InputState => MeerkatMachineInputVariant::InputState,
1151            Self::ListActiveInputs => MeerkatMachineInputVariant::ListActiveInputs,
1152            Self::ReconfigureSessionLlmIdentity => {
1153                MeerkatMachineInputVariant::ReconfigureSessionLlmIdentity
1154            }
1155            Self::StagePersistentFilter => MeerkatMachineInputVariant::StagePersistentFilter,
1156            Self::RequestDeferredTools => MeerkatMachineInputVariant::RequestDeferredTools,
1157            Self::PublishCommittedVisibleSet => {
1158                MeerkatMachineInputVariant::PublishCommittedVisibleSet
1159            }
1160            Self::SetPeerIngressContext => MeerkatMachineInputVariant::SetPeerIngressContext,
1161            Self::NotifyDrainExited => MeerkatMachineInputVariant::NotifyDrainExited,
1162            Self::AbortAll => MeerkatMachineInputVariant::AbortAll,
1163            Self::Abort => MeerkatMachineInputVariant::Abort,
1164            Self::Wait => MeerkatMachineInputVariant::Wait,
1165            Self::Ingest => MeerkatMachineInputVariant::Ingest,
1166            Self::PublishEvent => MeerkatMachineInputVariant::PublishEvent,
1167            Self::Retire => MeerkatMachineInputVariant::Retire,
1168            Self::Recycle => MeerkatMachineInputVariant::Recycle,
1169            Self::Reset => MeerkatMachineInputVariant::Reset,
1170            Self::Recover => MeerkatMachineInputVariant::Recover,
1171            Self::Destroy => MeerkatMachineInputVariant::Destroy,
1172            Self::RuntimeState => MeerkatMachineInputVariant::RuntimeState,
1173            Self::ModelRoutingStatus => MeerkatMachineInputVariant::ModelRoutingStatus,
1174            Self::SetModelRoutingBaseline => MeerkatMachineInputVariant::SetModelRoutingBaseline,
1175            Self::RequestFiniteSwitchTurn => MeerkatMachineInputVariant::RequestFiniteSwitchTurn,
1176            Self::RequestUntilChangedSwitchTurn => {
1177                MeerkatMachineInputVariant::RequestUntilChangedSwitchTurn
1178            }
1179            Self::AdmitModelRoutingAssistantTurn => {
1180                MeerkatMachineInputVariant::AdmitModelRoutingAssistantTurn
1181            }
1182            Self::BeginImageOperation => MeerkatMachineInputVariant::BeginImageOperation,
1183            Self::DenyImageOperationPlan => MeerkatMachineInputVariant::DenyImageOperationPlan,
1184            Self::ActivateImageOperationOverride => {
1185                MeerkatMachineInputVariant::ActivateImageOperationOverride
1186            }
1187            Self::ClassifyImageOperationTerminal => {
1188                MeerkatMachineInputVariant::ClassifyImageOperationTerminal
1189            }
1190            Self::CompleteImageOperation => MeerkatMachineInputVariant::CompleteImageOperation,
1191            Self::RestoreImageOperationOverride => {
1192                MeerkatMachineInputVariant::RestoreImageOperationOverride
1193            }
1194            Self::LoadBoundaryReceipt => MeerkatMachineInputVariant::LoadBoundaryReceipt,
1195            Self::AcceptWithCompletion => MeerkatMachineInputVariant::AcceptWithCompletion,
1196            Self::AcceptWithoutWake => MeerkatMachineInputVariant::AcceptWithoutWake,
1197        }
1198    }
1199
1200    #[must_use]
1201    pub const fn as_str(self) -> &'static str {
1202        match self {
1203            Self::RegisterSession => "RegisterSession",
1204            Self::UnregisterSession => "UnregisterSession",
1205            Self::EnsureSessionWithExecutor => "EnsureSessionWithExecutor",
1206            Self::SetSilentIntents => "SetSilentIntents",
1207            Self::CancelAfterBoundary => "CancelAfterBoundary",
1208            Self::StopRuntimeExecutor => "StopRuntimeExecutor",
1209            Self::ServiceTurnCommitted => "ServiceTurnCommitted",
1210            Self::ContainsSession => "ContainsSession",
1211            Self::SessionHasExecutor => "SessionHasExecutor",
1212            Self::SessionHasComms => "SessionHasComms",
1213            Self::OpsLifecycleRegistry => "OpsLifecycleRegistry",
1214            Self::PrepareBindings => "PrepareBindings",
1215            Self::InputState => "InputState",
1216            Self::ListActiveInputs => "ListActiveInputs",
1217            Self::ReconfigureSessionLlmIdentity => "ReconfigureSessionLlmIdentity",
1218            Self::StagePersistentFilter => "StagePersistentFilter",
1219            Self::RequestDeferredTools => "RequestDeferredTools",
1220            Self::PublishCommittedVisibleSet => "PublishCommittedVisibleSet",
1221            Self::SetPeerIngressContext => "SetPeerIngressContext",
1222            Self::NotifyDrainExited => "NotifyDrainExited",
1223            Self::AbortAll => "AbortAll",
1224            Self::Abort => "Abort",
1225            Self::Wait => "Wait",
1226            Self::Ingest => "Ingest",
1227            Self::PublishEvent => "PublishEvent",
1228            Self::Retire => "Retire",
1229            Self::Recycle => "Recycle",
1230            Self::Reset => "Reset",
1231            Self::Recover => "Recover",
1232            Self::Destroy => "Destroy",
1233            Self::RuntimeState => "RuntimeState",
1234            Self::ModelRoutingStatus => "ModelRoutingStatus",
1235            Self::SetModelRoutingBaseline => "SetModelRoutingBaseline",
1236            Self::RequestFiniteSwitchTurn => "RequestFiniteSwitchTurn",
1237            Self::RequestUntilChangedSwitchTurn => "RequestUntilChangedSwitchTurn",
1238            Self::AdmitModelRoutingAssistantTurn => "AdmitModelRoutingAssistantTurn",
1239            Self::BeginImageOperation => "BeginImageOperation",
1240            Self::DenyImageOperationPlan => "DenyImageOperationPlan",
1241            Self::ActivateImageOperationOverride => "ActivateImageOperationOverride",
1242            Self::ClassifyImageOperationTerminal => "ClassifyImageOperationTerminal",
1243            Self::CompleteImageOperation => "CompleteImageOperation",
1244            Self::RestoreImageOperationOverride => "RestoreImageOperationOverride",
1245            Self::LoadBoundaryReceipt => "LoadBoundaryReceipt",
1246            Self::AcceptWithCompletion => "AcceptWithCompletion",
1247            Self::AcceptWithoutWake => "AcceptWithoutWake",
1248        }
1249    }
1250}
1251
1252impl MeerkatMachineCommandVariant {
1253    #[must_use]
1254    pub const fn catalog_input(self) -> Option<MeerkatMachineCatalogInput> {
1255        match self {
1256            Self::ConfigureModelRoutingBaseline
1257            | Self::RequestSwitchTurn
1258            | Self::ResolvedSessionLlmCapabilities
1259            | Self::SessionModelRoutingStatus
1260            | Self::PrepareLocalSessionBindings => None,
1261            Self::RegisterSession => Some(MeerkatMachineCatalogInput::RegisterSession),
1262            Self::UnregisterSession => Some(MeerkatMachineCatalogInput::UnregisterSession),
1263            Self::EnsureSessionWithExecutor => {
1264                Some(MeerkatMachineCatalogInput::EnsureSessionWithExecutor)
1265            }
1266            Self::SetSilentIntents => Some(MeerkatMachineCatalogInput::SetSilentIntents),
1267            Self::CancelAfterBoundary => Some(MeerkatMachineCatalogInput::CancelAfterBoundary),
1268            Self::StopRuntimeExecutor => Some(MeerkatMachineCatalogInput::StopRuntimeExecutor),
1269            Self::CommitServiceTurnTerminalReceipt => {
1270                Some(MeerkatMachineCatalogInput::ServiceTurnCommitted)
1271            }
1272            Self::ContainsSession => Some(MeerkatMachineCatalogInput::ContainsSession),
1273            Self::SessionHasExecutor => Some(MeerkatMachineCatalogInput::SessionHasExecutor),
1274            Self::SessionHasComms => Some(MeerkatMachineCatalogInput::SessionHasComms),
1275            Self::OpsLifecycleRegistry => Some(MeerkatMachineCatalogInput::OpsLifecycleRegistry),
1276            Self::PrepareBindings => Some(MeerkatMachineCatalogInput::PrepareBindings),
1277            Self::InputState => Some(MeerkatMachineCatalogInput::InputState),
1278            Self::ListActiveInputs => Some(MeerkatMachineCatalogInput::ListActiveInputs),
1279            Self::ReconfigureSessionLlmIdentity => {
1280                Some(MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity)
1281            }
1282            Self::StagePersistentFilter => Some(MeerkatMachineCatalogInput::StagePersistentFilter),
1283            Self::RequestDeferredTools => Some(MeerkatMachineCatalogInput::RequestDeferredTools),
1284            Self::PublishCommittedVisibleSet => {
1285                Some(MeerkatMachineCatalogInput::PublishCommittedVisibleSet)
1286            }
1287            Self::SetPeerIngressContext => Some(MeerkatMachineCatalogInput::SetPeerIngressContext),
1288            Self::NotifyDrainExited => Some(MeerkatMachineCatalogInput::NotifyDrainExited),
1289            Self::AbortAll => Some(MeerkatMachineCatalogInput::AbortAll),
1290            Self::Abort => Some(MeerkatMachineCatalogInput::Abort),
1291            Self::Wait => Some(MeerkatMachineCatalogInput::Wait),
1292            Self::Ingest => Some(MeerkatMachineCatalogInput::Ingest),
1293            Self::PublishEvent => Some(MeerkatMachineCatalogInput::PublishEvent),
1294            Self::Retire => Some(MeerkatMachineCatalogInput::Retire),
1295            Self::Recycle => Some(MeerkatMachineCatalogInput::Recycle),
1296            Self::Reset => Some(MeerkatMachineCatalogInput::Reset),
1297            Self::Recover => Some(MeerkatMachineCatalogInput::Recover),
1298            Self::Destroy => Some(MeerkatMachineCatalogInput::Destroy),
1299            Self::RuntimeState => Some(MeerkatMachineCatalogInput::RuntimeState),
1300            Self::AdmitModelRoutingAssistantTurn => {
1301                Some(MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn)
1302            }
1303            Self::BeginImageOperation => Some(MeerkatMachineCatalogInput::BeginImageOperation),
1304            Self::DenyImageOperationPlan => {
1305                Some(MeerkatMachineCatalogInput::DenyImageOperationPlan)
1306            }
1307            Self::ActivateImageOperationOverride => {
1308                Some(MeerkatMachineCatalogInput::ActivateImageOperationOverride)
1309            }
1310            Self::ClassifyImageOperationTerminal => {
1311                Some(MeerkatMachineCatalogInput::ClassifyImageOperationTerminal)
1312            }
1313            Self::CompleteImageOperation => {
1314                Some(MeerkatMachineCatalogInput::CompleteImageOperation)
1315            }
1316            Self::RestoreImageOperationOverride => {
1317                Some(MeerkatMachineCatalogInput::RestoreImageOperationOverride)
1318            }
1319            Self::LoadBoundaryReceipt => Some(MeerkatMachineCatalogInput::LoadBoundaryReceipt),
1320            Self::AcceptWithCompletion => Some(MeerkatMachineCatalogInput::AcceptWithCompletion),
1321            Self::AcceptWithoutWake => Some(MeerkatMachineCatalogInput::AcceptWithoutWake),
1322        }
1323    }
1324}
1325
1326#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1327pub enum MeerkatMachineShellMechanicReason {
1328    ModelRoutingShellConfiguration,
1329    TurnControlOverlayRequest,
1330    RealtimeTransportObservation,
1331    SessionModelRoutingObservation,
1332    LocalSessionBindingBootstrap,
1333}
1334
1335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1336pub struct MeerkatMachineCommandClassificationRecord {
1337    pub command: MeerkatMachineCommandVariant,
1338    pub classification: MeerkatMachineCommandClassification,
1339}
1340
1341#[doc(hidden)]
1342#[must_use]
1343pub fn canonical_meerkat_machine_command_classifications()
1344-> Vec<MeerkatMachineCommandClassificationRecord> {
1345    MeerkatMachineCommand::command_variant_manifest()
1346        .iter()
1347        .copied()
1348        .map(|variant| MeerkatMachineCommandClassificationRecord {
1349            command: variant,
1350            classification: meerkat_machine_command_classification(variant),
1351        })
1352        .collect()
1353}
1354
1355const fn meerkat_machine_command_classification(
1356    variant: MeerkatMachineCommandVariant,
1357) -> MeerkatMachineCommandClassification {
1358    match variant {
1359        MeerkatMachineCommandVariant::ConfigureModelRoutingBaseline => {
1360            MeerkatMachineCommandClassification::CatalogInput(
1361                MeerkatMachineCatalogInput::SetModelRoutingBaseline,
1362            )
1363        }
1364        MeerkatMachineCommandVariant::RequestSwitchTurn => {
1365            MeerkatMachineCommandClassification::CatalogInputs(&[
1366                MeerkatMachineCatalogInput::RequestFiniteSwitchTurn,
1367                MeerkatMachineCatalogInput::RequestUntilChangedSwitchTurn,
1368            ])
1369        }
1370        MeerkatMachineCommandVariant::ResolvedSessionLlmCapabilities => {
1371            MeerkatMachineCommandClassification::ShellMechanic(
1372                MeerkatMachineShellMechanicReason::SessionModelRoutingObservation,
1373            )
1374        }
1375        MeerkatMachineCommandVariant::SessionModelRoutingStatus => {
1376            MeerkatMachineCommandClassification::CatalogInput(
1377                MeerkatMachineCatalogInput::ModelRoutingStatus,
1378            )
1379        }
1380        MeerkatMachineCommandVariant::PrepareLocalSessionBindings => {
1381            MeerkatMachineCommandClassification::ShellMechanic(
1382                MeerkatMachineShellMechanicReason::LocalSessionBindingBootstrap,
1383            )
1384        }
1385        MeerkatMachineCommandVariant::RegisterSession => {
1386            MeerkatMachineCommandClassification::CatalogInput(
1387                MeerkatMachineCatalogInput::RegisterSession,
1388            )
1389        }
1390        MeerkatMachineCommandVariant::UnregisterSession => {
1391            MeerkatMachineCommandClassification::CatalogInput(
1392                MeerkatMachineCatalogInput::UnregisterSession,
1393            )
1394        }
1395        MeerkatMachineCommandVariant::EnsureSessionWithExecutor => {
1396            MeerkatMachineCommandClassification::CatalogInput(
1397                MeerkatMachineCatalogInput::EnsureSessionWithExecutor,
1398            )
1399        }
1400        MeerkatMachineCommandVariant::SetSilentIntents => {
1401            MeerkatMachineCommandClassification::CatalogInput(
1402                MeerkatMachineCatalogInput::SetSilentIntents,
1403            )
1404        }
1405        MeerkatMachineCommandVariant::CancelAfterBoundary => {
1406            MeerkatMachineCommandClassification::CatalogInput(
1407                MeerkatMachineCatalogInput::CancelAfterBoundary,
1408            )
1409        }
1410        MeerkatMachineCommandVariant::StopRuntimeExecutor => {
1411            MeerkatMachineCommandClassification::CatalogInput(
1412                MeerkatMachineCatalogInput::StopRuntimeExecutor,
1413            )
1414        }
1415        MeerkatMachineCommandVariant::CommitServiceTurnTerminalReceipt => {
1416            MeerkatMachineCommandClassification::CatalogInput(
1417                MeerkatMachineCatalogInput::ServiceTurnCommitted,
1418            )
1419        }
1420        MeerkatMachineCommandVariant::ContainsSession => {
1421            MeerkatMachineCommandClassification::CatalogInput(
1422                MeerkatMachineCatalogInput::ContainsSession,
1423            )
1424        }
1425        MeerkatMachineCommandVariant::SessionHasExecutor => {
1426            MeerkatMachineCommandClassification::CatalogInput(
1427                MeerkatMachineCatalogInput::SessionHasExecutor,
1428            )
1429        }
1430        MeerkatMachineCommandVariant::SessionHasComms => {
1431            MeerkatMachineCommandClassification::CatalogInput(
1432                MeerkatMachineCatalogInput::SessionHasComms,
1433            )
1434        }
1435        MeerkatMachineCommandVariant::OpsLifecycleRegistry => {
1436            MeerkatMachineCommandClassification::CatalogInput(
1437                MeerkatMachineCatalogInput::OpsLifecycleRegistry,
1438            )
1439        }
1440        MeerkatMachineCommandVariant::PrepareBindings => {
1441            MeerkatMachineCommandClassification::CatalogInput(
1442                MeerkatMachineCatalogInput::PrepareBindings,
1443            )
1444        }
1445        MeerkatMachineCommandVariant::InputState => {
1446            MeerkatMachineCommandClassification::CatalogInput(
1447                MeerkatMachineCatalogInput::InputState,
1448            )
1449        }
1450        MeerkatMachineCommandVariant::ListActiveInputs => {
1451            MeerkatMachineCommandClassification::CatalogInput(
1452                MeerkatMachineCatalogInput::ListActiveInputs,
1453            )
1454        }
1455        MeerkatMachineCommandVariant::ReconfigureSessionLlmIdentity => {
1456            MeerkatMachineCommandClassification::CatalogInput(
1457                MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity,
1458            )
1459        }
1460        MeerkatMachineCommandVariant::StagePersistentFilter => {
1461            MeerkatMachineCommandClassification::CatalogInput(
1462                MeerkatMachineCatalogInput::StagePersistentFilter,
1463            )
1464        }
1465        MeerkatMachineCommandVariant::RequestDeferredTools => {
1466            MeerkatMachineCommandClassification::CatalogInput(
1467                MeerkatMachineCatalogInput::RequestDeferredTools,
1468            )
1469        }
1470        MeerkatMachineCommandVariant::PublishCommittedVisibleSet => {
1471            MeerkatMachineCommandClassification::CatalogInput(
1472                MeerkatMachineCatalogInput::PublishCommittedVisibleSet,
1473            )
1474        }
1475        MeerkatMachineCommandVariant::SetPeerIngressContext => {
1476            MeerkatMachineCommandClassification::CatalogInput(
1477                MeerkatMachineCatalogInput::SetPeerIngressContext,
1478            )
1479        }
1480        MeerkatMachineCommandVariant::NotifyDrainExited => {
1481            MeerkatMachineCommandClassification::CatalogInput(
1482                MeerkatMachineCatalogInput::NotifyDrainExited,
1483            )
1484        }
1485        MeerkatMachineCommandVariant::AbortAll => {
1486            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::AbortAll)
1487        }
1488        MeerkatMachineCommandVariant::Abort => {
1489            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Abort)
1490        }
1491        MeerkatMachineCommandVariant::Wait => {
1492            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Wait)
1493        }
1494        MeerkatMachineCommandVariant::Ingest => {
1495            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Ingest)
1496        }
1497        MeerkatMachineCommandVariant::PublishEvent => {
1498            MeerkatMachineCommandClassification::CatalogInput(
1499                MeerkatMachineCatalogInput::PublishEvent,
1500            )
1501        }
1502        MeerkatMachineCommandVariant::Retire => {
1503            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Retire)
1504        }
1505        MeerkatMachineCommandVariant::Recycle => {
1506            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recycle)
1507        }
1508        MeerkatMachineCommandVariant::Reset => {
1509            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Reset)
1510        }
1511        MeerkatMachineCommandVariant::Recover => {
1512            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recover)
1513        }
1514        MeerkatMachineCommandVariant::Destroy => {
1515            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Destroy)
1516        }
1517        MeerkatMachineCommandVariant::RuntimeState => {
1518            MeerkatMachineCommandClassification::CatalogInput(
1519                MeerkatMachineCatalogInput::RuntimeState,
1520            )
1521        }
1522        MeerkatMachineCommandVariant::AdmitModelRoutingAssistantTurn => {
1523            MeerkatMachineCommandClassification::CatalogInput(
1524                MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn,
1525            )
1526        }
1527        MeerkatMachineCommandVariant::BeginImageOperation => {
1528            MeerkatMachineCommandClassification::CatalogInput(
1529                MeerkatMachineCatalogInput::BeginImageOperation,
1530            )
1531        }
1532        MeerkatMachineCommandVariant::DenyImageOperationPlan => {
1533            MeerkatMachineCommandClassification::CatalogInput(
1534                MeerkatMachineCatalogInput::DenyImageOperationPlan,
1535            )
1536        }
1537        MeerkatMachineCommandVariant::ActivateImageOperationOverride => {
1538            MeerkatMachineCommandClassification::CatalogInput(
1539                MeerkatMachineCatalogInput::ActivateImageOperationOverride,
1540            )
1541        }
1542        MeerkatMachineCommandVariant::ClassifyImageOperationTerminal => {
1543            MeerkatMachineCommandClassification::CatalogInput(
1544                MeerkatMachineCatalogInput::ClassifyImageOperationTerminal,
1545            )
1546        }
1547        MeerkatMachineCommandVariant::CompleteImageOperation => {
1548            MeerkatMachineCommandClassification::CatalogInput(
1549                MeerkatMachineCatalogInput::CompleteImageOperation,
1550            )
1551        }
1552        MeerkatMachineCommandVariant::RestoreImageOperationOverride => {
1553            MeerkatMachineCommandClassification::CatalogInput(
1554                MeerkatMachineCatalogInput::RestoreImageOperationOverride,
1555            )
1556        }
1557        MeerkatMachineCommandVariant::LoadBoundaryReceipt => {
1558            MeerkatMachineCommandClassification::CatalogInput(
1559                MeerkatMachineCatalogInput::LoadBoundaryReceipt,
1560            )
1561        }
1562        MeerkatMachineCommandVariant::AcceptWithCompletion => {
1563            MeerkatMachineCommandClassification::CatalogInput(
1564                MeerkatMachineCatalogInput::AcceptWithCompletion,
1565            )
1566        }
1567        MeerkatMachineCommandVariant::AcceptWithoutWake => {
1568            MeerkatMachineCommandClassification::CatalogInput(
1569                MeerkatMachineCatalogInput::AcceptWithoutWake,
1570            )
1571        }
1572    }
1573}
1574
1575/// Snapshot of completion waiters registered for one input.
1576///
1577/// This is a supporting-carrier view, not canonical semantic truth.
1578#[derive(Debug, Clone, PartialEq, Eq)]
1579pub struct MeerkatCompletionWaiterSnapshot {
1580    pub input_id: InputId,
1581    pub waiter_count: usize,
1582}
1583
1584/// Snapshot of the runtime completion waiter carrier.
1585///
1586/// Completion waiters are supporting carrier state rather than primary
1587/// authority, but they remain useful for diagnostics and recovery inspection.
1588#[derive(Debug, Clone, PartialEq, Eq)]
1589pub struct MeerkatCompletionWaitersSnapshot {
1590    pub input_count: usize,
1591    pub waiter_count: usize,
1592    pub waiting_inputs: Vec<MeerkatCompletionWaiterSnapshot>,
1593}
1594
1595/// Runtime driver flavor for a registered Meerkat session entry.
1596#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1597pub enum MeerkatDriverKind {
1598    Ephemeral,
1599    Persistent,
1600}
1601
1602/// Snapshot of the hidden runtime epoch cursor state.
1603#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1604pub struct MeerkatCursorSnapshot {
1605    pub agent_applied_cursor: u64,
1606    pub runtime_observed_seq: u64,
1607    pub runtime_last_injected_seq: u64,
1608}
1609
1610/// Snapshot of the hidden runtime binding for one Meerkat session.
1611#[derive(Debug, Clone)]
1612pub struct MeerkatBindingSnapshot {
1613    pub session_id: SessionId,
1614    pub runtime_id: LogicalRuntimeId,
1615    pub driver_kind: MeerkatDriverKind,
1616    pub driver_present: bool,
1617    pub completions_present: bool,
1618    pub ops_registry_present: bool,
1619    pub epoch_id: RuntimeEpochId,
1620    pub cursor_state: MeerkatCursorSnapshot,
1621}
1622
1623/// Snapshot of runtime control-plane truth for one session.
1624#[derive(Debug, Clone)]
1625pub struct MeerkatControlSnapshot {
1626    pub phase: RuntimeState,
1627    pub current_run_id: Option<RunId>,
1628    pub pre_run_phase: Option<RuntimeState>,
1629}
1630
1631/// Snapshot of one admitted runtime input.
1632#[derive(Debug, Clone)]
1633pub struct MeerkatAdmittedInputSnapshot {
1634    pub input_id: InputId,
1635    pub content_shape: Option<ContentShape>,
1636    pub request_id: Option<RequestId>,
1637    pub reservation_key: Option<ReservationKey>,
1638    pub handling_mode: Option<HandlingMode>,
1639    /// #338: machine-owned per-input live-interrupt verdict, carried from the
1640    /// admission `RuntimeInputSemantics`. The live-projection consumer reads
1641    /// this typed fact instead of re-scanning `handling_mode == Steer`.
1642    pub live_interrupt_required: bool,
1643    pub lifecycle: Option<InputLifecycleState>,
1644    pub terminal_outcome: Option<InputTerminalOutcome>,
1645    pub last_run_id: Option<RunId>,
1646    pub last_boundary_sequence: Option<u64>,
1647    pub is_prompt: bool,
1648}
1649
1650/// Snapshot of runtime ingress truth for one session.
1651#[derive(Debug, Clone)]
1652pub struct MeerkatInputsSnapshot {
1653    pub admission_order: Vec<MeerkatAdmittedInputSnapshot>,
1654    pub queue: Vec<InputId>,
1655    pub steer_queue: Vec<InputId>,
1656    pub current_run_id: Option<RunId>,
1657    pub current_run_contributors: Vec<InputId>,
1658    pub post_admission_signal: String,
1659    pub silent_intent_overrides: Vec<String>,
1660}
1661
1662/// Lightweight lifecycle snapshot used by archive/retire cleanup.
1663///
1664/// This intentionally avoids the heavier diagnostic regions that are not
1665/// needed for archive decisions. Control and input fields still come from the
1666/// generated runtime authority/driver projections.
1667#[derive(Debug, Clone)]
1668pub struct MeerkatArchiveSnapshot {
1669    pub control: MeerkatControlSnapshot,
1670    pub queue: Vec<InputId>,
1671    pub steer_queue: Vec<InputId>,
1672    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
1673}
1674
1675/// Snapshot of the canonical input-ledger carrier for one session.
1676///
1677/// These counts sit below the top-level Meerkat phase machine, but they drive
1678/// the exact values returned by control-plane reports such as
1679/// `DestroyReport.inputs_abandoned`.
1680#[derive(Debug, Clone)]
1681pub struct MeerkatLedgerSnapshot {
1682    pub input_count: usize,
1683    pub non_terminal_count: usize,
1684    pub accepted_count: usize,
1685    pub queued_count: usize,
1686    pub staged_count: usize,
1687    pub applied_count: usize,
1688    pub applied_pending_consumption_count: usize,
1689    pub consumed_count: usize,
1690    pub superseded_count: usize,
1691    pub coalesced_count: usize,
1692    pub abandoned_count: usize,
1693}
1694
1695/// Snapshot of runtime-owned async operation truth for one session.
1696#[derive(Debug, Clone)]
1697pub struct MeerkatOpsSnapshot {
1698    pub operation_count: usize,
1699    pub active_count: usize,
1700    pub wait_request_id: Option<WaitRequestId>,
1701    pub pending_wait_present: bool,
1702    pub pending_wait_request_id: Option<WaitRequestId>,
1703    pub wait_operation_ids: Vec<OperationId>,
1704    pub operations: Vec<OperationLifecycleSnapshot>,
1705}
1706
1707/// Diagnostic comms-drain projection for one session.
1708///
1709/// `phase` and `mode` are read from generated MeerkatMachine authority; the
1710/// handle flag is shell task mechanics only.
1711#[derive(Debug, Clone)]
1712pub struct MeerkatDrainSnapshot {
1713    pub slot_present: bool,
1714    pub phase: Option<CommsDrainPhase>,
1715    pub mode: Option<CommsDrainMode>,
1716    pub handle_present: bool,
1717}
1718
1719/// Schema-aligned projection of the Meerkat formal state derived from the
1720/// live runtime.
1721///
1722/// This stays diagnostic and intentionally stringifies field values so the
1723/// parity harness can compare full field vectors even where the runtime does
1724/// not expose a first-class Rust type for every formal field.
1725#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1726pub struct MeerkatFormalStateProjection {
1727    /// Formal fields that have a live runtime-backed projection today.
1728    pub available_fields: BTreeMap<String, String>,
1729    /// Formal fields that still have no canonical runtime source.
1730    pub unavailable_fields: Vec<String>,
1731}
1732
1733/// Diagnostic snapshot of the current Meerkat runtime spine.
1734///
1735/// This is an observational scaffold over the existing runtime-owned Meerkat
1736/// regions. It is intentionally not the final MeerkatMachine reducer.
1737#[derive(Debug, Clone)]
1738pub struct MeerkatMachineSpineSnapshot {
1739    pub binding: MeerkatBindingSnapshot,
1740    pub control: MeerkatControlSnapshot,
1741    pub inputs: MeerkatInputsSnapshot,
1742    pub ledger: MeerkatLedgerSnapshot,
1743    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
1744    pub ops: MeerkatOpsSnapshot,
1745    pub drain: MeerkatDrainSnapshot,
1746    pub formal_state: MeerkatFormalStateProjection,
1747}
1748
1749impl MeerkatMachineSpineSnapshot {
1750    /// Validate TLA+ structural invariants against the current spine snapshot.
1751    ///
1752    /// Returns `Ok(())` if all invariants hold, or `Err(violations)` with a
1753    /// list of human-readable violation descriptions. This is release-mode
1754    /// validation — not `debug_assert`.
1755    pub fn validate_spine_invariants(&self) -> Result<(), Vec<String>> {
1756        let mut violations = Vec::new();
1757
1758        // --- Control/binding invariants ---
1759
1760        // RunningHasActiveRunInvariant: Running => HasActiveRun
1761        if self.control.phase == RuntimeState::Running && self.control.current_run_id.is_none() {
1762            violations
1763                .push("RunningHasActiveRunInvariant: phase is Running but no active run_id".into());
1764        }
1765
1766        // ActiveRunPhaseInvariant: HasActiveRun => phase in {Running, Retired}
1767        if self.control.current_run_id.is_some()
1768            && !matches!(
1769                self.control.phase,
1770                RuntimeState::Running | RuntimeState::Retired
1771            )
1772        {
1773            violations.push(format!(
1774                "ActiveRunPhaseInvariant: active run_id present but phase is {:?}",
1775                self.control.phase
1776            ));
1777        }
1778
1779        // DestroyedShapeInvariant: Destroyed => empty queues, no waiting inputs
1780        if self.control.phase == RuntimeState::Destroyed {
1781            if !self.inputs.queue.is_empty() {
1782                violations.push("DestroyedShapeInvariant: Destroyed but queue is non-empty".into());
1783            }
1784            if !self.inputs.steer_queue.is_empty() {
1785                violations
1786                    .push("DestroyedShapeInvariant: Destroyed but steer_queue is non-empty".into());
1787            }
1788            if self.completion_waiters.input_count > 0 {
1789                violations.push(
1790                    "DestroyedShapeInvariant: Destroyed but completion waiters remain".into(),
1791                );
1792            }
1793        }
1794
1795        // --- Input invariants ---
1796
1797        // QueueSteerDisjointInvariant
1798        let queue_set: std::collections::HashSet<_> = self.inputs.queue.iter().collect();
1799        let steer_set: std::collections::HashSet<_> = self.inputs.steer_queue.iter().collect();
1800        if !queue_set.is_disjoint(&steer_set) {
1801            violations
1802                .push("QueueSteerDisjointInvariant: queue and steer_queue share entries".into());
1803        }
1804
1805        // QueueHandlingInvariant: all queue entries must have handling_mode=Queue, lifecycle=Queued
1806        for qid in &self.inputs.queue {
1807            if let Some(snap) = self
1808                .inputs
1809                .admission_order
1810                .iter()
1811                .find(|a| &a.input_id == qid)
1812            {
1813                if snap.handling_mode != Some(HandlingMode::Queue) {
1814                    violations.push(format!(
1815                        "QueueHandlingInvariant: queue entry {qid} has handling_mode {:?}",
1816                        snap.handling_mode
1817                    ));
1818                }
1819                if snap.lifecycle != Some(InputLifecycleState::Queued) {
1820                    violations.push(format!(
1821                        "QueueHandlingInvariant: queue entry {qid} has lifecycle {:?}",
1822                        snap.lifecycle
1823                    ));
1824                }
1825            }
1826        }
1827
1828        // SteerHandlingInvariant: all steer_queue entries must have handling_mode=Steer, lifecycle=Queued
1829        for sid in &self.inputs.steer_queue {
1830            if let Some(snap) = self
1831                .inputs
1832                .admission_order
1833                .iter()
1834                .find(|a| &a.input_id == sid)
1835            {
1836                if snap.handling_mode != Some(HandlingMode::Steer) {
1837                    violations.push(format!(
1838                        "SteerHandlingInvariant: steer_queue entry {sid} has handling_mode {:?}",
1839                        snap.handling_mode
1840                    ));
1841                }
1842                if snap.lifecycle != Some(InputLifecycleState::Queued) {
1843                    violations.push(format!(
1844                        "SteerHandlingInvariant: steer_queue entry {sid} has lifecycle {:?}",
1845                        snap.lifecycle
1846                    ));
1847                }
1848            }
1849        }
1850
1851        // ContributorLifecycleInvariant: all current_run_contributors must
1852        // still belong to the active run lifecycle slice.
1853        for cid in &self.inputs.current_run_contributors {
1854            if let Some(snap) = self
1855                .inputs
1856                .admission_order
1857                .iter()
1858                .find(|a| &a.input_id == cid)
1859                && !matches!(
1860                    snap.lifecycle,
1861                    Some(
1862                        InputLifecycleState::Staged
1863                            | InputLifecycleState::Applied
1864                            | InputLifecycleState::AppliedPendingConsumption
1865                    )
1866                )
1867            {
1868                violations.push(format!(
1869                    "ContributorLifecycleInvariant: contributor {cid} has lifecycle {:?}",
1870                    snap.lifecycle
1871                ));
1872            }
1873        }
1874
1875        // TerminalInputsNotQueuedInvariant: terminal inputs must not be in queue or steer_queue
1876        for snap in &self.inputs.admission_order {
1877            if snap.terminal_outcome.is_some() {
1878                if queue_set.contains(&snap.input_id) {
1879                    violations.push(format!(
1880                        "TerminalInputsNotQueuedInvariant: terminal input {} in queue",
1881                        snap.input_id
1882                    ));
1883                }
1884                if steer_set.contains(&snap.input_id) {
1885                    violations.push(format!(
1886                        "TerminalInputsNotQueuedInvariant: terminal input {} in steer_queue",
1887                        snap.input_id
1888                    ));
1889                }
1890            }
1891        }
1892
1893        // CurrentRunContributorsInvariant: HasActiveRun => contributors non-empty
1894        if self.control.current_run_id.is_some() && self.inputs.current_run_contributors.is_empty()
1895        {
1896            violations
1897                .push("CurrentRunContributorsInvariant: active run but no contributors".into());
1898        }
1899
1900        // ContributorRunIdentityInvariant: when control owns an active run,
1901        // every current contributor must point at that same run in the
1902        // ingress bookkeeping.
1903        if let Some(control_run_id) = &self.control.current_run_id {
1904            for cid in &self.inputs.current_run_contributors {
1905                if let Some(snap) = self
1906                    .inputs
1907                    .admission_order
1908                    .iter()
1909                    .find(|a| &a.input_id == cid)
1910                    && snap.last_run_id.as_ref() != Some(control_run_id)
1911                {
1912                    violations.push(format!(
1913                        "ContributorRunIdentityInvariant: contributor {cid} has last_run_id {:?}, expected {:?}",
1914                        snap.last_run_id, control_run_id
1915                    ));
1916                }
1917            }
1918        }
1919
1920        // --- Ops invariants ---
1921
1922        // WaitAllAlignmentInvariant
1923        let wait_active = self.ops.wait_request_id.is_some();
1924        if wait_active && self.ops.wait_operation_ids.is_empty() {
1925            violations
1926                .push("WaitAllAlignmentInvariant: wait_active but no wait_operation_ids".into());
1927        }
1928        if !wait_active && !self.ops.wait_operation_ids.is_empty() {
1929            violations.push(
1930                "WaitAllAlignmentInvariant: wait_operation_ids present but no wait_request_id"
1931                    .into(),
1932            );
1933        }
1934
1935        // --- Drain invariants ---
1936
1937        // DrainModeInvariant: drain.phase != Inactive => mode is set and not Disabled
1938        if let Some(phase) = self.drain.phase
1939            && phase != CommsDrainPhase::Inactive
1940            && self.drain.mode.is_none()
1941        {
1942            violations.push("DrainModeInvariant: drain.phase is active but mode is None".into());
1943        }
1944
1945        if violations.is_empty() {
1946            Ok(())
1947        } else {
1948            Err(violations)
1949        }
1950    }
1951}