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