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