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        CommitStickyModelFallback,
893        ModelRoutingStatus,
894        RequestFiniteSwitchTurn,
895        RequestUntilChangedSwitchTurn,
896        SetModelRoutingBaseline,
897    ],
898    ExternalSurfaceLifecycle => [
899        AdmitSurfaceRequest,
900        CancelSurfaceRequest,
901        ClassifySurfaceRequestTerminal,
902        FinishSurfaceRequestUnpublished,
903        PublishOrCancelSurfaceRequest,
904        PublishSurfaceRequest,
905        RecordLiveWebrtcAnswerAccepted,
906        RecordLiveWebrtcTokenIssued,
907        RecordLiveWebsocketTokenIssued,
908        ResolveLiveWebrtcAnswerAdmission,
909        ResolveLiveWebsocketTokenAdmission,
910        SurfaceApplyBoundary,
911        SurfaceCallFinished,
912        SurfaceCallStarted,
913        SurfaceFinalizeRemovalClean,
914        SurfaceFinalizeRemovalForced,
915        SurfaceMarkPendingFailed,
916        SurfaceMarkPendingSucceeded,
917        SurfaceRegister,
918        SurfaceShutdown,
919        SurfaceSnapshotAligned,
920        SurfaceStageAdd,
921        SurfaceStageReload,
922        SurfaceStageRemove,
923    ],
924    FailureRecoveryLifecycle => [
925        ClassifyLlmFailureRecovery,
926        ClassifyRuntimeLifecycleDurability,
927        ClassifyRuntimeLifecycleState,
928        FatalFailure,
929        RecoverableFailure,
930        RecoverRuntimeAuthority,
931        ResolveVisibleRuntimePhase,
932    ],
933    UserInterruptDispatch => [
934        InterruptCurrentRun,
935        ResolveUserInterruptPublicResult,
936    ],
937    SessionUnregisterDrainLifecycle => [
938        BeginUnregisterSession,
939        CommsDrainExitedForUnregister,
940        CompletionWaitersResolvedForUnregister,
941        RuntimeLoopStoppedForUnregister,
942    ],
943);
944
945macro_rules! meerkat_machine_fieldless_runtime_internal_inputs {
946    ($($authority:ident => [$($variant:ident),+ $(,)?]),+ $(,)?) => {
947        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
948        pub enum MeerkatMachineFieldlessRuntimeInternalInput {
949            $($($variant),+),+
950        }
951
952        impl MeerkatMachineFieldlessRuntimeInternalInput {
953            pub const ALL: &'static [Self] = &[
954                $($(Self::$variant),+),+
955            ];
956
957            #[must_use]
958            pub const fn runtime_internal_input(self) -> MeerkatMachineRuntimeInternalInput {
959                match self {
960                    $($(Self::$variant => MeerkatMachineRuntimeInternalInput::$variant,)+)+
961                }
962            }
963
964            #[must_use]
965            pub const fn input_variant(self) -> MeerkatMachineInputVariant {
966                self.runtime_internal_input().input_variant()
967            }
968
969            #[must_use]
970            pub const fn authority(self) -> MeerkatMachineFieldlessRuntimeInternalAuthority {
971                match self {
972                    $(
973                        $(Self::$variant)|+ => MeerkatMachineFieldlessRuntimeInternalAuthority::$authority,
974                    )+
975                }
976            }
977
978            #[must_use]
979            pub const fn requires_typed_runtime_internal_stager(self) -> bool {
980                matches!(
981                    self.authority(),
982                    MeerkatMachineFieldlessRuntimeInternalAuthority::UserInterruptDispatch
983                )
984            }
985
986            pub(crate) const fn dsl_input_variant(self) -> dsl::MeerkatMachineInputVariant {
987                match self {
988                    $($(Self::$variant => dsl::MeerkatMachineInputVariant::$variant,)+)+
989                }
990            }
991
992            pub(crate) fn dsl_input(self) -> dsl::MeerkatMachineInput {
993                match self {
994                    $($(Self::$variant => dsl::MeerkatMachineInput::$variant,)+)+
995                }
996            }
997
998            pub(crate) fn from_dsl_input_variant(
999                variant: dsl::MeerkatMachineInputVariant,
1000            ) -> Option<Self> {
1001                Self::ALL
1002                    .iter()
1003                    .copied()
1004                    .find(|input| input.dsl_input_variant() == variant)
1005            }
1006
1007            pub(crate) fn reject_raw_dsl_input(
1008                input: &dsl::MeerkatMachineInput,
1009            ) -> Result<(), String> {
1010                if let Some(fieldless) = Self::from_dsl_input_variant(input.variant())
1011                    && fieldless.requires_typed_runtime_internal_stager()
1012                {
1013                    let variant = fieldless.input_variant();
1014                    return Err(format!(
1015                        "fieldless runtime-internal input {variant:?} must use typed runtime-internal staging authority"
1016                    ));
1017                }
1018                Ok(())
1019            }
1020        }
1021    };
1022}
1023
1024meerkat_machine_fieldless_runtime_internal_inputs!(
1025    RuntimeOwner => [
1026        RuntimeExecutorExited,
1027        ForceCancelNoRun,
1028        CancelWaitAll,
1029        StopDrain,
1030        SurfaceShutdown,
1031        DetachIngress,
1032        ClearLocalEndpoint,
1033    ],
1034    UserInterruptDispatch => [
1035        InterruptCurrentRun,
1036    ],
1037);
1038
1039#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1040pub enum MeerkatMachineFieldlessRuntimeInternalAuthority {
1041    RuntimeOwner,
1042    UserInterruptDispatch,
1043}
1044
1045#[doc(hidden)]
1046#[must_use]
1047pub fn canonical_meerkat_machine_runtime_internal_classifications()
1048-> Vec<MeerkatMachineRuntimeInternalClassificationRecord> {
1049    MeerkatMachineRuntimeInternalInput::CLASSIFICATIONS.to_vec()
1050}
1051
1052#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1053pub enum MeerkatMachineCommandClassification {
1054    CatalogInput(MeerkatMachineCatalogInput),
1055    CatalogInputs(&'static [MeerkatMachineCatalogInput]),
1056    ShellMechanic(MeerkatMachineShellMechanicReason),
1057}
1058
1059impl MeerkatMachineCommandClassification {
1060    #[must_use]
1061    pub fn catalog_inputs(self) -> Vec<MeerkatMachineCatalogInput> {
1062        match self {
1063            Self::CatalogInput(input) => vec![input],
1064            Self::CatalogInputs(inputs) => inputs.to_vec(),
1065            Self::ShellMechanic(_) => Vec::new(),
1066        }
1067    }
1068
1069    #[must_use]
1070    pub fn catalog_input_variants(self) -> Vec<MeerkatMachineInputVariant> {
1071        self.catalog_inputs()
1072            .into_iter()
1073            .map(MeerkatMachineCatalogInput::input_variant)
1074            .collect()
1075    }
1076}
1077
1078#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1079pub enum MeerkatMachineCatalogInput {
1080    RegisterSession,
1081    UnregisterSession,
1082    EnsureSessionWithExecutor,
1083    SetSilentIntents,
1084    CancelAfterBoundary,
1085    StopRuntimeExecutor,
1086    ServiceTurnCommitted,
1087    ContainsSession,
1088    SessionHasExecutor,
1089    SessionHasComms,
1090    OpsLifecycleRegistry,
1091    PrepareBindings,
1092    InputState,
1093    ListActiveInputs,
1094    ReconfigureSessionLlmIdentity,
1095    StagePersistentFilter,
1096    RequestDeferredTools,
1097    PublishCommittedVisibleSet,
1098    SetPeerIngressContext,
1099    NotifyDrainExited,
1100    AbortAll,
1101    Abort,
1102    Wait,
1103    Ingest,
1104    PublishEvent,
1105    Retire,
1106    Recycle,
1107    Reset,
1108    Recover,
1109    Destroy,
1110    RuntimeState,
1111    ModelRoutingStatus,
1112    SetModelRoutingBaseline,
1113    RequestFiniteSwitchTurn,
1114    RequestUntilChangedSwitchTurn,
1115    AdmitModelRoutingAssistantTurn,
1116    BeginImageOperation,
1117    DenyImageOperationPlan,
1118    ActivateImageOperationOverride,
1119    ClassifyImageOperationTerminal,
1120    CompleteImageOperation,
1121    RestoreImageOperationOverride,
1122    LoadBoundaryReceipt,
1123    AcceptWithCompletion,
1124    AcceptWithoutWake,
1125}
1126
1127impl MeerkatMachineCatalogInput {
1128    pub const ALL: &'static [Self] = &[
1129        Self::RegisterSession,
1130        Self::UnregisterSession,
1131        Self::EnsureSessionWithExecutor,
1132        Self::SetSilentIntents,
1133        Self::CancelAfterBoundary,
1134        Self::StopRuntimeExecutor,
1135        Self::ServiceTurnCommitted,
1136        Self::ContainsSession,
1137        Self::SessionHasExecutor,
1138        Self::SessionHasComms,
1139        Self::OpsLifecycleRegistry,
1140        Self::PrepareBindings,
1141        Self::InputState,
1142        Self::ListActiveInputs,
1143        Self::ReconfigureSessionLlmIdentity,
1144        Self::StagePersistentFilter,
1145        Self::RequestDeferredTools,
1146        Self::PublishCommittedVisibleSet,
1147        Self::SetPeerIngressContext,
1148        Self::NotifyDrainExited,
1149        Self::AbortAll,
1150        Self::Abort,
1151        Self::Wait,
1152        Self::Ingest,
1153        Self::PublishEvent,
1154        Self::Retire,
1155        Self::Recycle,
1156        Self::Reset,
1157        Self::Recover,
1158        Self::Destroy,
1159        Self::RuntimeState,
1160        Self::ModelRoutingStatus,
1161        Self::SetModelRoutingBaseline,
1162        Self::RequestFiniteSwitchTurn,
1163        Self::RequestUntilChangedSwitchTurn,
1164        Self::AdmitModelRoutingAssistantTurn,
1165        Self::BeginImageOperation,
1166        Self::DenyImageOperationPlan,
1167        Self::ActivateImageOperationOverride,
1168        Self::ClassifyImageOperationTerminal,
1169        Self::CompleteImageOperation,
1170        Self::RestoreImageOperationOverride,
1171        Self::LoadBoundaryReceipt,
1172        Self::AcceptWithCompletion,
1173        Self::AcceptWithoutWake,
1174    ];
1175
1176    #[must_use]
1177    pub const fn input_variant(self) -> MeerkatMachineInputVariant {
1178        match self {
1179            Self::RegisterSession => MeerkatMachineInputVariant::RegisterSession,
1180            Self::UnregisterSession => MeerkatMachineInputVariant::UnregisterSession,
1181            Self::EnsureSessionWithExecutor => {
1182                MeerkatMachineInputVariant::EnsureSessionWithExecutor
1183            }
1184            Self::SetSilentIntents => MeerkatMachineInputVariant::SetSilentIntents,
1185            Self::CancelAfterBoundary => MeerkatMachineInputVariant::CancelAfterBoundary,
1186            Self::StopRuntimeExecutor => MeerkatMachineInputVariant::StopRuntimeExecutor,
1187            Self::ServiceTurnCommitted => MeerkatMachineInputVariant::ServiceTurnCommitted,
1188            Self::ContainsSession => MeerkatMachineInputVariant::ContainsSession,
1189            Self::SessionHasExecutor => MeerkatMachineInputVariant::SessionHasExecutor,
1190            Self::SessionHasComms => MeerkatMachineInputVariant::SessionHasComms,
1191            Self::OpsLifecycleRegistry => MeerkatMachineInputVariant::OpsLifecycleRegistry,
1192            Self::PrepareBindings => MeerkatMachineInputVariant::PrepareBindings,
1193            Self::InputState => MeerkatMachineInputVariant::InputState,
1194            Self::ListActiveInputs => MeerkatMachineInputVariant::ListActiveInputs,
1195            Self::ReconfigureSessionLlmIdentity => {
1196                MeerkatMachineInputVariant::ReconfigureSessionLlmIdentity
1197            }
1198            Self::StagePersistentFilter => MeerkatMachineInputVariant::StagePersistentFilter,
1199            Self::RequestDeferredTools => MeerkatMachineInputVariant::RequestDeferredTools,
1200            Self::PublishCommittedVisibleSet => {
1201                MeerkatMachineInputVariant::PublishCommittedVisibleSet
1202            }
1203            Self::SetPeerIngressContext => MeerkatMachineInputVariant::SetPeerIngressContext,
1204            Self::NotifyDrainExited => MeerkatMachineInputVariant::NotifyDrainExited,
1205            Self::AbortAll => MeerkatMachineInputVariant::AbortAll,
1206            Self::Abort => MeerkatMachineInputVariant::Abort,
1207            Self::Wait => MeerkatMachineInputVariant::Wait,
1208            Self::Ingest => MeerkatMachineInputVariant::Ingest,
1209            Self::PublishEvent => MeerkatMachineInputVariant::PublishEvent,
1210            Self::Retire => MeerkatMachineInputVariant::Retire,
1211            Self::Recycle => MeerkatMachineInputVariant::Recycle,
1212            Self::Reset => MeerkatMachineInputVariant::Reset,
1213            Self::Recover => MeerkatMachineInputVariant::Recover,
1214            Self::Destroy => MeerkatMachineInputVariant::Destroy,
1215            Self::RuntimeState => MeerkatMachineInputVariant::RuntimeState,
1216            Self::ModelRoutingStatus => MeerkatMachineInputVariant::ModelRoutingStatus,
1217            Self::SetModelRoutingBaseline => MeerkatMachineInputVariant::SetModelRoutingBaseline,
1218            Self::RequestFiniteSwitchTurn => MeerkatMachineInputVariant::RequestFiniteSwitchTurn,
1219            Self::RequestUntilChangedSwitchTurn => {
1220                MeerkatMachineInputVariant::RequestUntilChangedSwitchTurn
1221            }
1222            Self::AdmitModelRoutingAssistantTurn => {
1223                MeerkatMachineInputVariant::AdmitModelRoutingAssistantTurn
1224            }
1225            Self::BeginImageOperation => MeerkatMachineInputVariant::BeginImageOperation,
1226            Self::DenyImageOperationPlan => MeerkatMachineInputVariant::DenyImageOperationPlan,
1227            Self::ActivateImageOperationOverride => {
1228                MeerkatMachineInputVariant::ActivateImageOperationOverride
1229            }
1230            Self::ClassifyImageOperationTerminal => {
1231                MeerkatMachineInputVariant::ClassifyImageOperationTerminal
1232            }
1233            Self::CompleteImageOperation => MeerkatMachineInputVariant::CompleteImageOperation,
1234            Self::RestoreImageOperationOverride => {
1235                MeerkatMachineInputVariant::RestoreImageOperationOverride
1236            }
1237            Self::LoadBoundaryReceipt => MeerkatMachineInputVariant::LoadBoundaryReceipt,
1238            Self::AcceptWithCompletion => MeerkatMachineInputVariant::AcceptWithCompletion,
1239            Self::AcceptWithoutWake => MeerkatMachineInputVariant::AcceptWithoutWake,
1240        }
1241    }
1242
1243    #[must_use]
1244    pub const fn as_str(self) -> &'static str {
1245        match self {
1246            Self::RegisterSession => "RegisterSession",
1247            Self::UnregisterSession => "UnregisterSession",
1248            Self::EnsureSessionWithExecutor => "EnsureSessionWithExecutor",
1249            Self::SetSilentIntents => "SetSilentIntents",
1250            Self::CancelAfterBoundary => "CancelAfterBoundary",
1251            Self::StopRuntimeExecutor => "StopRuntimeExecutor",
1252            Self::ServiceTurnCommitted => "ServiceTurnCommitted",
1253            Self::ContainsSession => "ContainsSession",
1254            Self::SessionHasExecutor => "SessionHasExecutor",
1255            Self::SessionHasComms => "SessionHasComms",
1256            Self::OpsLifecycleRegistry => "OpsLifecycleRegistry",
1257            Self::PrepareBindings => "PrepareBindings",
1258            Self::InputState => "InputState",
1259            Self::ListActiveInputs => "ListActiveInputs",
1260            Self::ReconfigureSessionLlmIdentity => "ReconfigureSessionLlmIdentity",
1261            Self::StagePersistentFilter => "StagePersistentFilter",
1262            Self::RequestDeferredTools => "RequestDeferredTools",
1263            Self::PublishCommittedVisibleSet => "PublishCommittedVisibleSet",
1264            Self::SetPeerIngressContext => "SetPeerIngressContext",
1265            Self::NotifyDrainExited => "NotifyDrainExited",
1266            Self::AbortAll => "AbortAll",
1267            Self::Abort => "Abort",
1268            Self::Wait => "Wait",
1269            Self::Ingest => "Ingest",
1270            Self::PublishEvent => "PublishEvent",
1271            Self::Retire => "Retire",
1272            Self::Recycle => "Recycle",
1273            Self::Reset => "Reset",
1274            Self::Recover => "Recover",
1275            Self::Destroy => "Destroy",
1276            Self::RuntimeState => "RuntimeState",
1277            Self::ModelRoutingStatus => "ModelRoutingStatus",
1278            Self::SetModelRoutingBaseline => "SetModelRoutingBaseline",
1279            Self::RequestFiniteSwitchTurn => "RequestFiniteSwitchTurn",
1280            Self::RequestUntilChangedSwitchTurn => "RequestUntilChangedSwitchTurn",
1281            Self::AdmitModelRoutingAssistantTurn => "AdmitModelRoutingAssistantTurn",
1282            Self::BeginImageOperation => "BeginImageOperation",
1283            Self::DenyImageOperationPlan => "DenyImageOperationPlan",
1284            Self::ActivateImageOperationOverride => "ActivateImageOperationOverride",
1285            Self::ClassifyImageOperationTerminal => "ClassifyImageOperationTerminal",
1286            Self::CompleteImageOperation => "CompleteImageOperation",
1287            Self::RestoreImageOperationOverride => "RestoreImageOperationOverride",
1288            Self::LoadBoundaryReceipt => "LoadBoundaryReceipt",
1289            Self::AcceptWithCompletion => "AcceptWithCompletion",
1290            Self::AcceptWithoutWake => "AcceptWithoutWake",
1291        }
1292    }
1293}
1294
1295impl MeerkatMachineCommandVariant {
1296    #[must_use]
1297    pub const fn catalog_input(self) -> Option<MeerkatMachineCatalogInput> {
1298        match self {
1299            Self::ConfigureModelRoutingBaseline
1300            | Self::RequestSwitchTurn
1301            | Self::ResolvedSessionLlmCapabilities
1302            | Self::SessionModelRoutingStatus
1303            | Self::PrepareLocalSessionBindings => None,
1304            Self::RegisterSession => Some(MeerkatMachineCatalogInput::RegisterSession),
1305            Self::UnregisterSession => Some(MeerkatMachineCatalogInput::UnregisterSession),
1306            Self::EnsureSessionWithExecutor => {
1307                Some(MeerkatMachineCatalogInput::EnsureSessionWithExecutor)
1308            }
1309            Self::SetSilentIntents => Some(MeerkatMachineCatalogInput::SetSilentIntents),
1310            Self::CancelAfterBoundary => Some(MeerkatMachineCatalogInput::CancelAfterBoundary),
1311            Self::StopRuntimeExecutor => Some(MeerkatMachineCatalogInput::StopRuntimeExecutor),
1312            Self::CommitServiceTurnTerminalReceipt => {
1313                Some(MeerkatMachineCatalogInput::ServiceTurnCommitted)
1314            }
1315            Self::ContainsSession => Some(MeerkatMachineCatalogInput::ContainsSession),
1316            Self::SessionHasExecutor => Some(MeerkatMachineCatalogInput::SessionHasExecutor),
1317            Self::SessionHasComms => Some(MeerkatMachineCatalogInput::SessionHasComms),
1318            Self::OpsLifecycleRegistry => Some(MeerkatMachineCatalogInput::OpsLifecycleRegistry),
1319            Self::PrepareBindings => Some(MeerkatMachineCatalogInput::PrepareBindings),
1320            Self::InputState => Some(MeerkatMachineCatalogInput::InputState),
1321            // Same machine-owned read route as `InputState` (the key
1322            // resolves through the generated admission map first).
1323            Self::InputStateByIdempotencyKey => Some(MeerkatMachineCatalogInput::InputState),
1324            // Terminal-status queries are the same machine-owned read route
1325            // as `InputState`: they evaluate the identical DSL-owned seed
1326            // facts (live snapshot or their durable store witnesses).
1327            Self::InteractionTerminalStatus => Some(MeerkatMachineCatalogInput::InputState),
1328            Self::RunTerminalStatus => Some(MeerkatMachineCatalogInput::InputState),
1329            Self::ListActiveInputs => Some(MeerkatMachineCatalogInput::ListActiveInputs),
1330            Self::ReconfigureSessionLlmIdentity => {
1331                Some(MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity)
1332            }
1333            Self::StagePersistentFilter => Some(MeerkatMachineCatalogInput::StagePersistentFilter),
1334            Self::RequestDeferredTools => Some(MeerkatMachineCatalogInput::RequestDeferredTools),
1335            Self::PublishCommittedVisibleSet => {
1336                Some(MeerkatMachineCatalogInput::PublishCommittedVisibleSet)
1337            }
1338            Self::SetPeerIngressContext => Some(MeerkatMachineCatalogInput::SetPeerIngressContext),
1339            Self::NotifyDrainExited => Some(MeerkatMachineCatalogInput::NotifyDrainExited),
1340            Self::AbortAll => Some(MeerkatMachineCatalogInput::AbortAll),
1341            Self::Abort => Some(MeerkatMachineCatalogInput::Abort),
1342            Self::Wait => Some(MeerkatMachineCatalogInput::Wait),
1343            Self::Ingest => Some(MeerkatMachineCatalogInput::Ingest),
1344            Self::PublishEvent => Some(MeerkatMachineCatalogInput::PublishEvent),
1345            Self::Retire => Some(MeerkatMachineCatalogInput::Retire),
1346            Self::Recycle => Some(MeerkatMachineCatalogInput::Recycle),
1347            Self::Reset => Some(MeerkatMachineCatalogInput::Reset),
1348            Self::Recover => Some(MeerkatMachineCatalogInput::Recover),
1349            Self::Destroy => Some(MeerkatMachineCatalogInput::Destroy),
1350            Self::RuntimeState => Some(MeerkatMachineCatalogInput::RuntimeState),
1351            Self::AdmitModelRoutingAssistantTurn => {
1352                Some(MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn)
1353            }
1354            Self::BeginImageOperation => Some(MeerkatMachineCatalogInput::BeginImageOperation),
1355            Self::DenyImageOperationPlan => {
1356                Some(MeerkatMachineCatalogInput::DenyImageOperationPlan)
1357            }
1358            Self::ActivateImageOperationOverride => {
1359                Some(MeerkatMachineCatalogInput::ActivateImageOperationOverride)
1360            }
1361            Self::ClassifyImageOperationTerminal => {
1362                Some(MeerkatMachineCatalogInput::ClassifyImageOperationTerminal)
1363            }
1364            Self::CompleteImageOperation => {
1365                Some(MeerkatMachineCatalogInput::CompleteImageOperation)
1366            }
1367            Self::RestoreImageOperationOverride => {
1368                Some(MeerkatMachineCatalogInput::RestoreImageOperationOverride)
1369            }
1370            Self::LoadBoundaryReceipt => Some(MeerkatMachineCatalogInput::LoadBoundaryReceipt),
1371            Self::AcceptWithCompletion => Some(MeerkatMachineCatalogInput::AcceptWithCompletion),
1372            Self::AcceptWithoutWake => Some(MeerkatMachineCatalogInput::AcceptWithoutWake),
1373        }
1374    }
1375}
1376
1377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1378pub enum MeerkatMachineShellMechanicReason {
1379    ModelRoutingShellConfiguration,
1380    TurnControlOverlayRequest,
1381    RealtimeTransportObservation,
1382    SessionModelRoutingObservation,
1383    LocalSessionBindingBootstrap,
1384}
1385
1386#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1387pub struct MeerkatMachineCommandClassificationRecord {
1388    pub command: MeerkatMachineCommandVariant,
1389    pub classification: MeerkatMachineCommandClassification,
1390}
1391
1392#[doc(hidden)]
1393#[must_use]
1394pub fn canonical_meerkat_machine_command_classifications()
1395-> Vec<MeerkatMachineCommandClassificationRecord> {
1396    MeerkatMachineCommand::command_variant_manifest()
1397        .iter()
1398        .copied()
1399        .map(|variant| MeerkatMachineCommandClassificationRecord {
1400            command: variant,
1401            classification: meerkat_machine_command_classification(variant),
1402        })
1403        .collect()
1404}
1405
1406const fn meerkat_machine_command_classification(
1407    variant: MeerkatMachineCommandVariant,
1408) -> MeerkatMachineCommandClassification {
1409    match variant {
1410        MeerkatMachineCommandVariant::ConfigureModelRoutingBaseline => {
1411            MeerkatMachineCommandClassification::CatalogInput(
1412                MeerkatMachineCatalogInput::SetModelRoutingBaseline,
1413            )
1414        }
1415        MeerkatMachineCommandVariant::RequestSwitchTurn => {
1416            MeerkatMachineCommandClassification::CatalogInputs(&[
1417                MeerkatMachineCatalogInput::RequestFiniteSwitchTurn,
1418                MeerkatMachineCatalogInput::RequestUntilChangedSwitchTurn,
1419            ])
1420        }
1421        MeerkatMachineCommandVariant::ResolvedSessionLlmCapabilities => {
1422            MeerkatMachineCommandClassification::ShellMechanic(
1423                MeerkatMachineShellMechanicReason::SessionModelRoutingObservation,
1424            )
1425        }
1426        MeerkatMachineCommandVariant::SessionModelRoutingStatus => {
1427            MeerkatMachineCommandClassification::CatalogInput(
1428                MeerkatMachineCatalogInput::ModelRoutingStatus,
1429            )
1430        }
1431        MeerkatMachineCommandVariant::PrepareLocalSessionBindings => {
1432            MeerkatMachineCommandClassification::ShellMechanic(
1433                MeerkatMachineShellMechanicReason::LocalSessionBindingBootstrap,
1434            )
1435        }
1436        MeerkatMachineCommandVariant::RegisterSession => {
1437            MeerkatMachineCommandClassification::CatalogInput(
1438                MeerkatMachineCatalogInput::RegisterSession,
1439            )
1440        }
1441        MeerkatMachineCommandVariant::UnregisterSession => {
1442            MeerkatMachineCommandClassification::CatalogInput(
1443                MeerkatMachineCatalogInput::UnregisterSession,
1444            )
1445        }
1446        MeerkatMachineCommandVariant::EnsureSessionWithExecutor => {
1447            MeerkatMachineCommandClassification::CatalogInput(
1448                MeerkatMachineCatalogInput::EnsureSessionWithExecutor,
1449            )
1450        }
1451        MeerkatMachineCommandVariant::SetSilentIntents => {
1452            MeerkatMachineCommandClassification::CatalogInput(
1453                MeerkatMachineCatalogInput::SetSilentIntents,
1454            )
1455        }
1456        MeerkatMachineCommandVariant::CancelAfterBoundary => {
1457            MeerkatMachineCommandClassification::CatalogInput(
1458                MeerkatMachineCatalogInput::CancelAfterBoundary,
1459            )
1460        }
1461        MeerkatMachineCommandVariant::StopRuntimeExecutor => {
1462            MeerkatMachineCommandClassification::CatalogInput(
1463                MeerkatMachineCatalogInput::StopRuntimeExecutor,
1464            )
1465        }
1466        MeerkatMachineCommandVariant::CommitServiceTurnTerminalReceipt => {
1467            MeerkatMachineCommandClassification::CatalogInput(
1468                MeerkatMachineCatalogInput::ServiceTurnCommitted,
1469            )
1470        }
1471        MeerkatMachineCommandVariant::ContainsSession => {
1472            MeerkatMachineCommandClassification::CatalogInput(
1473                MeerkatMachineCatalogInput::ContainsSession,
1474            )
1475        }
1476        MeerkatMachineCommandVariant::SessionHasExecutor => {
1477            MeerkatMachineCommandClassification::CatalogInput(
1478                MeerkatMachineCatalogInput::SessionHasExecutor,
1479            )
1480        }
1481        MeerkatMachineCommandVariant::SessionHasComms => {
1482            MeerkatMachineCommandClassification::CatalogInput(
1483                MeerkatMachineCatalogInput::SessionHasComms,
1484            )
1485        }
1486        MeerkatMachineCommandVariant::OpsLifecycleRegistry => {
1487            MeerkatMachineCommandClassification::CatalogInput(
1488                MeerkatMachineCatalogInput::OpsLifecycleRegistry,
1489            )
1490        }
1491        MeerkatMachineCommandVariant::PrepareBindings => {
1492            MeerkatMachineCommandClassification::CatalogInput(
1493                MeerkatMachineCatalogInput::PrepareBindings,
1494            )
1495        }
1496        MeerkatMachineCommandVariant::InputState => {
1497            MeerkatMachineCommandClassification::CatalogInput(
1498                MeerkatMachineCatalogInput::InputState,
1499            )
1500        }
1501        // Same machine-owned read route as `InputState`: the idempotency
1502        // key resolves through the generated admission map, then the stored
1503        // input state is read identically.
1504        MeerkatMachineCommandVariant::InputStateByIdempotencyKey => {
1505            MeerkatMachineCommandClassification::CatalogInput(
1506                MeerkatMachineCatalogInput::InputState,
1507            )
1508        }
1509        // Terminal-status reads evaluate the same DSL-owned seed facts as
1510        // `InputState` (live snapshot for registered sessions, durable store
1511        // witnesses otherwise) — same machine-owned read route.
1512        MeerkatMachineCommandVariant::InteractionTerminalStatus
1513        | MeerkatMachineCommandVariant::RunTerminalStatus => {
1514            MeerkatMachineCommandClassification::CatalogInput(
1515                MeerkatMachineCatalogInput::InputState,
1516            )
1517        }
1518        MeerkatMachineCommandVariant::ListActiveInputs => {
1519            MeerkatMachineCommandClassification::CatalogInput(
1520                MeerkatMachineCatalogInput::ListActiveInputs,
1521            )
1522        }
1523        MeerkatMachineCommandVariant::ReconfigureSessionLlmIdentity => {
1524            MeerkatMachineCommandClassification::CatalogInput(
1525                MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity,
1526            )
1527        }
1528        MeerkatMachineCommandVariant::StagePersistentFilter => {
1529            MeerkatMachineCommandClassification::CatalogInput(
1530                MeerkatMachineCatalogInput::StagePersistentFilter,
1531            )
1532        }
1533        MeerkatMachineCommandVariant::RequestDeferredTools => {
1534            MeerkatMachineCommandClassification::CatalogInput(
1535                MeerkatMachineCatalogInput::RequestDeferredTools,
1536            )
1537        }
1538        MeerkatMachineCommandVariant::PublishCommittedVisibleSet => {
1539            MeerkatMachineCommandClassification::CatalogInput(
1540                MeerkatMachineCatalogInput::PublishCommittedVisibleSet,
1541            )
1542        }
1543        MeerkatMachineCommandVariant::SetPeerIngressContext => {
1544            MeerkatMachineCommandClassification::CatalogInput(
1545                MeerkatMachineCatalogInput::SetPeerIngressContext,
1546            )
1547        }
1548        MeerkatMachineCommandVariant::NotifyDrainExited => {
1549            MeerkatMachineCommandClassification::CatalogInput(
1550                MeerkatMachineCatalogInput::NotifyDrainExited,
1551            )
1552        }
1553        MeerkatMachineCommandVariant::AbortAll => {
1554            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::AbortAll)
1555        }
1556        MeerkatMachineCommandVariant::Abort => {
1557            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Abort)
1558        }
1559        MeerkatMachineCommandVariant::Wait => {
1560            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Wait)
1561        }
1562        MeerkatMachineCommandVariant::Ingest => {
1563            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Ingest)
1564        }
1565        MeerkatMachineCommandVariant::PublishEvent => {
1566            MeerkatMachineCommandClassification::CatalogInput(
1567                MeerkatMachineCatalogInput::PublishEvent,
1568            )
1569        }
1570        MeerkatMachineCommandVariant::Retire => {
1571            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Retire)
1572        }
1573        MeerkatMachineCommandVariant::Recycle => {
1574            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recycle)
1575        }
1576        MeerkatMachineCommandVariant::Reset => {
1577            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Reset)
1578        }
1579        MeerkatMachineCommandVariant::Recover => {
1580            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recover)
1581        }
1582        MeerkatMachineCommandVariant::Destroy => {
1583            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Destroy)
1584        }
1585        MeerkatMachineCommandVariant::RuntimeState => {
1586            MeerkatMachineCommandClassification::CatalogInput(
1587                MeerkatMachineCatalogInput::RuntimeState,
1588            )
1589        }
1590        MeerkatMachineCommandVariant::AdmitModelRoutingAssistantTurn => {
1591            MeerkatMachineCommandClassification::CatalogInput(
1592                MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn,
1593            )
1594        }
1595        MeerkatMachineCommandVariant::BeginImageOperation => {
1596            MeerkatMachineCommandClassification::CatalogInput(
1597                MeerkatMachineCatalogInput::BeginImageOperation,
1598            )
1599        }
1600        MeerkatMachineCommandVariant::DenyImageOperationPlan => {
1601            MeerkatMachineCommandClassification::CatalogInput(
1602                MeerkatMachineCatalogInput::DenyImageOperationPlan,
1603            )
1604        }
1605        MeerkatMachineCommandVariant::ActivateImageOperationOverride => {
1606            MeerkatMachineCommandClassification::CatalogInput(
1607                MeerkatMachineCatalogInput::ActivateImageOperationOverride,
1608            )
1609        }
1610        MeerkatMachineCommandVariant::ClassifyImageOperationTerminal => {
1611            MeerkatMachineCommandClassification::CatalogInput(
1612                MeerkatMachineCatalogInput::ClassifyImageOperationTerminal,
1613            )
1614        }
1615        MeerkatMachineCommandVariant::CompleteImageOperation => {
1616            MeerkatMachineCommandClassification::CatalogInput(
1617                MeerkatMachineCatalogInput::CompleteImageOperation,
1618            )
1619        }
1620        MeerkatMachineCommandVariant::RestoreImageOperationOverride => {
1621            MeerkatMachineCommandClassification::CatalogInput(
1622                MeerkatMachineCatalogInput::RestoreImageOperationOverride,
1623            )
1624        }
1625        MeerkatMachineCommandVariant::LoadBoundaryReceipt => {
1626            MeerkatMachineCommandClassification::CatalogInput(
1627                MeerkatMachineCatalogInput::LoadBoundaryReceipt,
1628            )
1629        }
1630        MeerkatMachineCommandVariant::AcceptWithCompletion => {
1631            MeerkatMachineCommandClassification::CatalogInput(
1632                MeerkatMachineCatalogInput::AcceptWithCompletion,
1633            )
1634        }
1635        MeerkatMachineCommandVariant::AcceptWithoutWake => {
1636            MeerkatMachineCommandClassification::CatalogInput(
1637                MeerkatMachineCatalogInput::AcceptWithoutWake,
1638            )
1639        }
1640    }
1641}
1642
1643/// Snapshot of completion waiters registered for one input.
1644///
1645/// This is a supporting-carrier view, not canonical semantic truth.
1646#[derive(Debug, Clone, PartialEq, Eq)]
1647pub struct MeerkatCompletionWaiterSnapshot {
1648    pub input_id: InputId,
1649    pub waiter_count: usize,
1650}
1651
1652/// Snapshot of the runtime completion waiter carrier.
1653///
1654/// Completion waiters are supporting carrier state rather than primary
1655/// authority, but they remain useful for diagnostics and recovery inspection.
1656#[derive(Debug, Clone, PartialEq, Eq)]
1657pub struct MeerkatCompletionWaitersSnapshot {
1658    pub input_count: usize,
1659    pub waiter_count: usize,
1660    pub waiting_inputs: Vec<MeerkatCompletionWaiterSnapshot>,
1661}
1662
1663/// Runtime driver flavor for a registered Meerkat session entry.
1664#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1665pub enum MeerkatDriverKind {
1666    Ephemeral,
1667    Persistent,
1668}
1669
1670/// Snapshot of the hidden runtime epoch cursor state.
1671#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1672pub struct MeerkatCursorSnapshot {
1673    pub agent_applied_cursor: u64,
1674    pub runtime_observed_seq: u64,
1675    pub runtime_last_injected_seq: u64,
1676}
1677
1678/// Snapshot of the hidden runtime binding for one Meerkat session.
1679#[derive(Debug, Clone)]
1680pub struct MeerkatBindingSnapshot {
1681    pub session_id: SessionId,
1682    pub runtime_id: LogicalRuntimeId,
1683    pub driver_kind: MeerkatDriverKind,
1684    pub driver_present: bool,
1685    pub completions_present: bool,
1686    pub ops_registry_present: bool,
1687    pub epoch_id: RuntimeEpochId,
1688    pub cursor_state: MeerkatCursorSnapshot,
1689}
1690
1691/// Snapshot of runtime control-plane truth for one session.
1692#[derive(Debug, Clone)]
1693pub struct MeerkatControlSnapshot {
1694    pub phase: RuntimeState,
1695    pub current_run_id: Option<RunId>,
1696    pub pre_run_phase: Option<RuntimeState>,
1697}
1698
1699/// Snapshot of one admitted runtime input.
1700#[derive(Debug, Clone)]
1701pub struct MeerkatAdmittedInputSnapshot {
1702    pub input_id: InputId,
1703    pub content_shape: Option<ContentShape>,
1704    pub request_id: Option<RequestId>,
1705    pub reservation_key: Option<ReservationKey>,
1706    pub handling_mode: Option<HandlingMode>,
1707    /// #338: machine-owned per-input live-interrupt verdict, carried from the
1708    /// admission `RuntimeInputSemantics`. The live-projection consumer reads
1709    /// this typed fact instead of re-scanning `handling_mode == Steer`.
1710    pub live_interrupt_required: bool,
1711    pub lifecycle: Option<InputLifecycleState>,
1712    pub terminal_outcome: Option<InputTerminalOutcome>,
1713    pub last_run_id: Option<RunId>,
1714    pub last_boundary_sequence: Option<u64>,
1715    pub is_prompt: bool,
1716}
1717
1718/// Snapshot of runtime ingress truth for one session.
1719#[derive(Debug, Clone)]
1720pub struct MeerkatInputsSnapshot {
1721    pub admission_order: Vec<MeerkatAdmittedInputSnapshot>,
1722    pub queue: Vec<InputId>,
1723    pub steer_queue: Vec<InputId>,
1724    pub current_run_id: Option<RunId>,
1725    pub current_run_contributors: Vec<InputId>,
1726    pub post_admission_signal: String,
1727    pub silent_intent_overrides: Vec<String>,
1728}
1729
1730/// Lightweight lifecycle snapshot used by archive/retire cleanup.
1731///
1732/// This intentionally avoids the heavier diagnostic regions that are not
1733/// needed for archive decisions. Control and input fields still come from the
1734/// generated runtime authority/driver projections.
1735#[derive(Debug, Clone)]
1736pub struct MeerkatArchiveSnapshot {
1737    pub control: MeerkatControlSnapshot,
1738    pub queue: Vec<InputId>,
1739    pub steer_queue: Vec<InputId>,
1740    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
1741}
1742
1743/// Snapshot of the canonical input-ledger carrier for one session.
1744///
1745/// These counts sit below the top-level Meerkat phase machine, but they drive
1746/// the exact values returned by control-plane reports such as
1747/// `DestroyReport.inputs_abandoned`.
1748#[derive(Debug, Clone)]
1749pub struct MeerkatLedgerSnapshot {
1750    pub input_count: usize,
1751    pub non_terminal_count: usize,
1752    pub accepted_count: usize,
1753    pub queued_count: usize,
1754    pub staged_count: usize,
1755    pub applied_count: usize,
1756    pub applied_pending_consumption_count: usize,
1757    pub consumed_count: usize,
1758    pub superseded_count: usize,
1759    pub coalesced_count: usize,
1760    pub abandoned_count: usize,
1761}
1762
1763/// Snapshot of runtime-owned async operation truth for one session.
1764#[derive(Debug, Clone)]
1765pub struct MeerkatOpsSnapshot {
1766    pub operation_count: usize,
1767    pub active_count: usize,
1768    pub wait_request_id: Option<WaitRequestId>,
1769    pub pending_wait_present: bool,
1770    pub pending_wait_request_id: Option<WaitRequestId>,
1771    pub wait_operation_ids: Vec<OperationId>,
1772    pub operations: Vec<OperationLifecycleSnapshot>,
1773}
1774
1775/// Diagnostic comms-drain projection for one session.
1776///
1777/// `phase` and `mode` are read from generated MeerkatMachine authority; the
1778/// handle flag is shell task mechanics only.
1779#[derive(Debug, Clone)]
1780pub struct MeerkatDrainSnapshot {
1781    pub slot_present: bool,
1782    pub phase: Option<CommsDrainPhase>,
1783    pub mode: Option<CommsDrainMode>,
1784    pub handle_present: bool,
1785}
1786
1787/// Schema-aligned projection of the Meerkat formal state derived from the
1788/// live runtime.
1789///
1790/// This stays diagnostic and intentionally stringifies field values so the
1791/// parity harness can compare full field vectors even where the runtime does
1792/// not expose a first-class Rust type for every formal field.
1793#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1794pub struct MeerkatFormalStateProjection {
1795    /// Formal fields that have a live runtime-backed projection today.
1796    pub available_fields: BTreeMap<String, String>,
1797    /// Formal fields that still have no canonical runtime source.
1798    pub unavailable_fields: Vec<String>,
1799}
1800
1801/// Diagnostic snapshot of the current Meerkat runtime spine.
1802///
1803/// This is an observational scaffold over the existing runtime-owned Meerkat
1804/// regions. It is intentionally not the final MeerkatMachine reducer.
1805#[derive(Debug, Clone)]
1806pub struct MeerkatMachineSpineSnapshot {
1807    pub binding: MeerkatBindingSnapshot,
1808    pub control: MeerkatControlSnapshot,
1809    pub inputs: MeerkatInputsSnapshot,
1810    pub ledger: MeerkatLedgerSnapshot,
1811    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
1812    pub ops: MeerkatOpsSnapshot,
1813    pub drain: MeerkatDrainSnapshot,
1814    pub formal_state: MeerkatFormalStateProjection,
1815}
1816
1817impl MeerkatMachineSpineSnapshot {
1818    /// Validate TLA+ structural invariants against the current spine snapshot.
1819    ///
1820    /// Returns `Ok(())` if all invariants hold, or `Err(violations)` with a
1821    /// list of human-readable violation descriptions. This is release-mode
1822    /// validation — not `debug_assert`.
1823    pub fn validate_spine_invariants(&self) -> Result<(), Vec<String>> {
1824        let mut violations = Vec::new();
1825
1826        // --- Control/binding invariants ---
1827
1828        // RunningHasActiveRunInvariant: Running => HasActiveRun
1829        if self.control.phase == RuntimeState::Running && self.control.current_run_id.is_none() {
1830            violations
1831                .push("RunningHasActiveRunInvariant: phase is Running but no active run_id".into());
1832        }
1833
1834        // ActiveRunPhaseInvariant: HasActiveRun => phase in {Running, Retired}
1835        if self.control.current_run_id.is_some()
1836            && !matches!(
1837                self.control.phase,
1838                RuntimeState::Running | RuntimeState::Retired
1839            )
1840        {
1841            violations.push(format!(
1842                "ActiveRunPhaseInvariant: active run_id present but phase is {:?}",
1843                self.control.phase
1844            ));
1845        }
1846
1847        // DestroyedShapeInvariant: Destroyed => empty queues, no waiting inputs
1848        if self.control.phase == RuntimeState::Destroyed {
1849            if !self.inputs.queue.is_empty() {
1850                violations.push("DestroyedShapeInvariant: Destroyed but queue is non-empty".into());
1851            }
1852            if !self.inputs.steer_queue.is_empty() {
1853                violations
1854                    .push("DestroyedShapeInvariant: Destroyed but steer_queue is non-empty".into());
1855            }
1856            if self.completion_waiters.input_count > 0 {
1857                violations.push(
1858                    "DestroyedShapeInvariant: Destroyed but completion waiters remain".into(),
1859                );
1860            }
1861        }
1862
1863        // --- Input invariants ---
1864
1865        // QueueSteerDisjointInvariant
1866        let queue_set: std::collections::HashSet<_> = self.inputs.queue.iter().collect();
1867        let steer_set: std::collections::HashSet<_> = self.inputs.steer_queue.iter().collect();
1868        if !queue_set.is_disjoint(&steer_set) {
1869            violations
1870                .push("QueueSteerDisjointInvariant: queue and steer_queue share entries".into());
1871        }
1872
1873        // QueueHandlingInvariant: all queue entries must have handling_mode=Queue, lifecycle=Queued
1874        for qid in &self.inputs.queue {
1875            if let Some(snap) = self
1876                .inputs
1877                .admission_order
1878                .iter()
1879                .find(|a| &a.input_id == qid)
1880            {
1881                if snap.handling_mode != Some(HandlingMode::Queue) {
1882                    violations.push(format!(
1883                        "QueueHandlingInvariant: queue entry {qid} has handling_mode {:?}",
1884                        snap.handling_mode
1885                    ));
1886                }
1887                if snap.lifecycle != Some(InputLifecycleState::Queued) {
1888                    violations.push(format!(
1889                        "QueueHandlingInvariant: queue entry {qid} has lifecycle {:?}",
1890                        snap.lifecycle
1891                    ));
1892                }
1893            }
1894        }
1895
1896        // SteerHandlingInvariant: all steer_queue entries must have handling_mode=Steer, lifecycle=Queued
1897        for sid in &self.inputs.steer_queue {
1898            if let Some(snap) = self
1899                .inputs
1900                .admission_order
1901                .iter()
1902                .find(|a| &a.input_id == sid)
1903            {
1904                if snap.handling_mode != Some(HandlingMode::Steer) {
1905                    violations.push(format!(
1906                        "SteerHandlingInvariant: steer_queue entry {sid} has handling_mode {:?}",
1907                        snap.handling_mode
1908                    ));
1909                }
1910                if snap.lifecycle != Some(InputLifecycleState::Queued) {
1911                    violations.push(format!(
1912                        "SteerHandlingInvariant: steer_queue entry {sid} has lifecycle {:?}",
1913                        snap.lifecycle
1914                    ));
1915                }
1916            }
1917        }
1918
1919        // ContributorLifecycleInvariant: all current_run_contributors must
1920        // still belong to the active run lifecycle slice.
1921        for cid in &self.inputs.current_run_contributors {
1922            if let Some(snap) = self
1923                .inputs
1924                .admission_order
1925                .iter()
1926                .find(|a| &a.input_id == cid)
1927                && !matches!(
1928                    snap.lifecycle,
1929                    Some(
1930                        InputLifecycleState::Staged
1931                            | InputLifecycleState::Applied
1932                            | InputLifecycleState::AppliedPendingConsumption
1933                    )
1934                )
1935            {
1936                violations.push(format!(
1937                    "ContributorLifecycleInvariant: contributor {cid} has lifecycle {:?}",
1938                    snap.lifecycle
1939                ));
1940            }
1941        }
1942
1943        // TerminalInputsNotQueuedInvariant: terminal inputs must not be in queue or steer_queue
1944        for snap in &self.inputs.admission_order {
1945            if snap.terminal_outcome.is_some() {
1946                if queue_set.contains(&snap.input_id) {
1947                    violations.push(format!(
1948                        "TerminalInputsNotQueuedInvariant: terminal input {} in queue",
1949                        snap.input_id
1950                    ));
1951                }
1952                if steer_set.contains(&snap.input_id) {
1953                    violations.push(format!(
1954                        "TerminalInputsNotQueuedInvariant: terminal input {} in steer_queue",
1955                        snap.input_id
1956                    ));
1957                }
1958            }
1959        }
1960
1961        // CurrentRunContributorsInvariant: HasActiveRun => contributors non-empty
1962        if self.control.current_run_id.is_some() && self.inputs.current_run_contributors.is_empty()
1963        {
1964            violations
1965                .push("CurrentRunContributorsInvariant: active run but no contributors".into());
1966        }
1967
1968        // ContributorRunIdentityInvariant: when control owns an active run,
1969        // every current contributor must point at that same run in the
1970        // ingress bookkeeping.
1971        if let Some(control_run_id) = &self.control.current_run_id {
1972            for cid in &self.inputs.current_run_contributors {
1973                if let Some(snap) = self
1974                    .inputs
1975                    .admission_order
1976                    .iter()
1977                    .find(|a| &a.input_id == cid)
1978                    && snap.last_run_id.as_ref() != Some(control_run_id)
1979                {
1980                    violations.push(format!(
1981                        "ContributorRunIdentityInvariant: contributor {cid} has last_run_id {:?}, expected {:?}",
1982                        snap.last_run_id, control_run_id
1983                    ));
1984                }
1985            }
1986        }
1987
1988        // --- Ops invariants ---
1989
1990        // WaitAllAlignmentInvariant
1991        let wait_active = self.ops.wait_request_id.is_some();
1992        if wait_active && self.ops.wait_operation_ids.is_empty() {
1993            violations
1994                .push("WaitAllAlignmentInvariant: wait_active but no wait_operation_ids".into());
1995        }
1996        if !wait_active && !self.ops.wait_operation_ids.is_empty() {
1997            violations.push(
1998                "WaitAllAlignmentInvariant: wait_operation_ids present but no wait_request_id"
1999                    .into(),
2000            );
2001        }
2002
2003        // --- Drain invariants ---
2004
2005        // DrainModeInvariant: drain.phase != Inactive => mode is set and not Disabled
2006        if let Some(phase) = self.drain.phase
2007            && phase != CommsDrainPhase::Inactive
2008            && self.drain.mode.is_none()
2009        {
2010            violations.push("DrainModeInvariant: drain.phase is active but mode is None".into());
2011        }
2012
2013        if violations.is_empty() {
2014            Ok(())
2015        } else {
2016            Err(violations)
2017        }
2018    }
2019}