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