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