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::{
11    CommsDrainMode, CommsDrainPhase, DrainExitReason, RuntimeExecutorAttachmentWitness, dsl,
12};
13use indexmap::IndexSet;
14use meerkat_core::RuntimeEpochId;
15use meerkat_core::agent::CommsRuntime;
16use meerkat_core::image_generation::{
17    ImageOperationApprovalReason, ImageOperationDenialReason, ImageOperationId,
18    ImageOperationPhase, ImageOperationTerminalClass, ImageProviderTerminalObservation,
19    ProviderTextDisposition, SessionModelRoutingStatus, SwitchTurnApprovalReason,
20    SwitchTurnControlResult, SwitchTurnIntent, SwitchTurnRequestId,
21};
22use meerkat_core::lifecycle::WaitRequestId;
23use meerkat_core::lifecycle::core_executor::CoreExecutor;
24use meerkat_core::lifecycle::run_primitive::{ModelId, TurnMetadataOverride};
25use meerkat_core::lifecycle::{InputId, RunId};
26use meerkat_core::lifecycle::{RunBoundaryReceipt, RunId as LifecycleRunId};
27use meerkat_core::ops::OperationId;
28use meerkat_core::ops_lifecycle::OperationLifecycleSnapshot;
29use meerkat_core::types::HandlingMode;
30use meerkat_core::types::SessionId;
31use meerkat_machine_derive::CommandManifest;
32use meerkat_machine_schema::catalog::dsl::meerkat_machine::MeerkatMachineInputVariant;
33use serde::{Deserialize, Serialize};
34
35use crate::AcceptOutcome;
36use crate::identifiers::LogicalRuntimeId;
37use crate::ingress_types::{ContentShape, RequestId, ReservationKey};
38use crate::input::Input;
39use crate::input_state::InputLifecycleState;
40use crate::input_state::InputTerminalOutcome;
41use crate::input_state::StoredInputState;
42use crate::runtime_event::RuntimeEventEnvelope;
43use crate::runtime_state::RuntimeState;
44use crate::traits::{
45    DestroyReport, RecoveryReport, RecycleReport, ResetReport, RetireReport,
46    RuntimeControlPlaneError, RuntimeDriverError,
47};
48
49/// Per-turn LLM hot-swap reconfigure request.
50///
51/// `provider_params` and `auth_binding` carry the canonical Inherit/Set/Clear
52/// tri-state via [`TurnMetadataOverride`]: `None` preserves the durable value,
53/// `Some(Set)` overrides it, and `Some(Clear)` removes it. The illegal
54/// "set and clear" fourth state is structurally unrepresentable.
55#[derive(Debug, Clone, Serialize, PartialEq)]
56#[serde(rename_all = "snake_case")]
57pub struct SessionLlmReconfigureRequest {
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub model: Option<String>,
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub provider: Option<String>,
62    /// Exact configured server route for a self-hosted model. Provider/model
63    /// alone cannot distinguish two local servers exposing the same model id.
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub self_hosted_server_id: Option<String>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub provider_params: Option<
68        TurnMetadataOverride<meerkat_core::lifecycle::run_primitive::ProviderParamsOverride>,
69    >,
70    /// Optional realm-scoped connection override resolved through the tri-state:
71    /// `Some(Set)` swaps the binding, `Some(Clear)` removes it, `None` preserves
72    /// the session's existing `SessionLlmIdentity.auth_binding`.
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub auth_binding: Option<TurnMetadataOverride<meerkat_core::AuthBindingRef>>,
75}
76
77#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
78#[serde(rename_all = "snake_case")]
79pub enum SessionLlmCapabilitySurfaceStatus {
80    Resolved,
81    #[default]
82    Unresolved,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
86#[serde(rename_all = "snake_case")]
87pub struct SessionLlmCapabilitySurface {
88    pub supports_temperature: bool,
89    pub supports_thinking: bool,
90    pub supports_reasoning: bool,
91    pub inline_video: bool,
92    pub vision: bool,
93    #[serde(default)]
94    pub image_input: bool,
95    pub image_tool_results: bool,
96    pub supports_web_search: bool,
97    #[serde(default)]
98    pub image_generation: bool,
99    /// Whether the resolved model exposes a realtime bidirectional streaming
100    /// transport. Drives capability-based auto attach/detach in
101    /// `reconfigure_live_topology` and `apply_capability_driven_realtime_transport`.
102    #[serde(default)]
103    pub realtime: bool,
104    #[serde(default, skip_serializing_if = "Option::is_none")]
105    pub call_timeout_secs: Option<u64>,
106}
107
108impl SessionLlmCapabilitySurface {
109    #[must_use]
110    pub fn to_wire_resolved(&self) -> meerkat_contracts::WireResolvedModelCapabilities {
111        meerkat_contracts::WireResolvedModelCapabilities {
112            vision: self.vision,
113            image_input: self.image_input,
114            image_tool_results: self.image_tool_results,
115            inline_video: self.inline_video,
116            realtime: self.realtime,
117            web_search: self.supports_web_search,
118            image_generation: self.image_generation,
119        }
120    }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct SessionLlmCapabilityDelta {
125    pub previous: Option<SessionLlmCapabilitySurface>,
126    pub current: Option<SessionLlmCapabilitySurface>,
127    pub changed: bool,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct SessionToolVisibilityDelta {
132    pub previous_capability_base_filter: meerkat_core::ToolFilter,
133    pub current_capability_base_filter: meerkat_core::ToolFilter,
134    pub committed_visible_set_changed: bool,
135    pub revision_bumped: bool,
136}
137
138#[derive(Debug, Clone, PartialEq)]
139pub struct SessionLlmReconfigureReport {
140    pub previous_identity: meerkat_core::SessionLlmIdentity,
141    pub new_identity: meerkat_core::SessionLlmIdentity,
142    pub capability_delta: SessionLlmCapabilityDelta,
143    pub tool_visibility_delta: SessionToolVisibilityDelta,
144    pub rollback_occurred: bool,
145}
146
147#[derive(Debug, Clone)]
148pub struct HydratedSessionLlmState {
149    pub current_identity: meerkat_core::SessionLlmIdentity,
150    pub current_visibility_state: meerkat_core::SessionToolVisibilityState,
151    pub current_capability_surface: Option<SessionLlmCapabilitySurface>,
152    pub capability_surface_status: SessionLlmCapabilitySurfaceStatus,
153    pub base_tool_names: std::collections::BTreeSet<meerkat_core::ToolName>,
154}
155
156#[derive(Debug, Clone)]
157pub struct ResolvedSessionLlmReconfigure {
158    pub target_identity: meerkat_core::SessionLlmIdentity,
159    pub target_capability_surface: SessionLlmCapabilitySurface,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum ModelRoutingApprovalDisposition {
164    NotRequired,
165    Approved,
166    DeniedByUser,
167    RequiredButUnavailable,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub struct ModelRoutingRealtimePolicy {
172    pub target_realtime_capable: bool,
173    pub allow_realtime_detach: bool,
174}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
177pub struct SwitchTurnRequest {
178    pub request_id: SwitchTurnRequestId,
179    pub intent: SwitchTurnIntent,
180    pub target_realtime: ModelRoutingRealtimePolicy,
181    pub approval: ModelRoutingApprovalDisposition,
182    pub approval_reason: Option<SwitchTurnApprovalReason>,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct ImageOperationRoutingRequest {
187    pub operation_id: ImageOperationId,
188    pub target_model: ModelId,
189    pub target_realtime: ModelRoutingRealtimePolicy,
190    pub approval: ModelRoutingApprovalDisposition,
191    pub approval_reason: Option<ImageOperationApprovalReason>,
192    pub requires_scoped_override: bool,
193}
194
195#[derive(Debug, Clone, PartialEq, Eq)]
196pub enum ImageOperationRoutingResult {
197    Accepted {
198        operation_id: ImageOperationId,
199        phase: ImageOperationPhase,
200    },
201    Denied {
202        operation_id: ImageOperationId,
203        reason: ImageOperationDenialReason,
204    },
205}
206
207#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
208#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
209pub trait SessionLlmReconfigureHost: Send + Sync {
210    /// Acquire the stable session-service boundary that must enclose the
211    /// complete hydrate -> generated-machine commit -> live mutation ->
212    /// persistence transaction. Runtime-loop callers already hold the same
213    /// boundary; direct control-plane callers acquire it through this hook.
214    async fn acquire_turn_finalization_boundary(
215        &self,
216        _session_id: &SessionId,
217    ) -> Result<
218        Box<dyn meerkat_core::lifecycle::CoreExecutorTurnFinalizationGuard>,
219        RuntimeDriverError,
220    > {
221        Ok(Box::new(()))
222    }
223
224    async fn hydrate_session_llm_state(
225        &self,
226        session_id: &SessionId,
227    ) -> Result<HydratedSessionLlmState, RuntimeDriverError>;
228
229    async fn resolve_target_session_llm_identity(
230        &self,
231        request: &SessionLlmReconfigureRequest,
232        current_identity: &meerkat_core::SessionLlmIdentity,
233    ) -> Result<ResolvedSessionLlmReconfigure, RuntimeDriverError>;
234
235    /// Caller must hold the turn-finalization boundary.
236    async fn apply_live_session_llm_identity(
237        &self,
238        session_id: &SessionId,
239        identity: &meerkat_core::SessionLlmIdentity,
240    ) -> Result<(), RuntimeDriverError>;
241
242    /// Caller must hold the turn-finalization boundary.
243    async fn apply_live_session_tool_visibility_state(
244        &self,
245        session_id: &SessionId,
246        visibility_state: Option<meerkat_core::SessionToolVisibilityState>,
247    ) -> Result<(), RuntimeDriverError>;
248
249    /// Caller must hold the turn-finalization boundary.
250    async fn persist_live_session(&self, session_id: &SessionId) -> Result<(), RuntimeDriverError>;
251
252    /// Caller must hold the turn-finalization boundary.
253    async fn discard_live_session(&self, session_id: &SessionId) -> Result<(), RuntimeDriverError>;
254}
255
256#[derive(Debug, thiserror::Error)]
257pub(crate) enum MeerkatMachineCommandError {
258    #[error(transparent)]
259    Driver(#[from] RuntimeDriverError),
260    #[error(transparent)]
261    Control(#[from] RuntimeControlPlaneError),
262}
263
264/// Unified internal Meerkat machine command surface.
265///
266/// This replaces the old per-domain dispatch split (session, drain,
267/// drain-local, control, ingress) while keeping the public helper
268/// methods and external runtime/machine surface unchanged.
269#[derive(CommandManifest)]
270#[allow(clippy::large_enum_variant)]
271pub(crate) enum MeerkatMachineCommand {
272    RegisterSession {
273        session_id: SessionId,
274    },
275    // Retained for generated manifest/parity coverage; production call sites
276    // prefer typed unregister helpers.
277    #[cfg_attr(not(test), allow(dead_code))]
278    UnregisterSession {
279        session_id: SessionId,
280    },
281    EnsureSessionWithExecutor {
282        session_id: SessionId,
283        executor: Box<dyn CoreExecutor>,
284    },
285    SetSilentIntents {
286        session_id: SessionId,
287        intents: Vec<String>,
288    },
289    CancelAfterBoundary {
290        session_id: SessionId,
291    },
292    StopRuntimeExecutor {
293        session_id: SessionId,
294        reason: String,
295    },
296    CommitServiceTurnTerminalReceipt {
297        session_id: SessionId,
298        session_snapshot: Vec<u8>,
299    },
300    #[cfg_attr(not(test), allow(dead_code))]
301    ContainsSession {
302        session_id: SessionId,
303    },
304    SessionHasExecutor {
305        session_id: SessionId,
306    },
307    SessionHasComms {
308        session_id: SessionId,
309    },
310    OpsLifecycleRegistry {
311        session_id: SessionId,
312    },
313    // Retained for generated composition contracts; production binding requests
314    // normally enter through typed composition helpers.
315    #[cfg_attr(not(test), allow(dead_code))]
316    PrepareBindings {
317        session_id: SessionId,
318    },
319    // Local bootstrap is a shell mechanic, kept in the command catalog so the
320    // generated classifier can spell that boundary explicitly.
321    #[cfg_attr(not(test), allow(dead_code))]
322    PrepareLocalSessionBindings {
323        session_id: SessionId,
324    },
325    InputState {
326        session_id: SessionId,
327        input_id: InputId,
328    },
329    // Read-only reconciliation lookup: resolve the machine-owned
330    // idempotency-key binding to its input and return that input's stored
331    // state. Same authority read as `InputState`; the key resolution is a
332    // mechanical mirror of the generated admission map (never a dedup
333    // decision — `ResolveAdmissionIdempotency` on the accept path stays the
334    // only mutator).
335    InputStateByIdempotencyKey {
336        session_id: SessionId,
337        idempotency_key: String,
338    },
339    // Restart-first-class terminal-status read: same machine-owned authority
340    // read as `InputState`, but when the session is not registered a
341    // persistent machine answers from the durably committed input-state
342    // witnesses in the RuntimeStore instead of failing NotReady.
343    InteractionTerminalStatus {
344        session_id: SessionId,
345        selector: crate::terminal_status::InteractionSelector,
346    },
347    // Run-id-keyed terminal-status read over the same witnesses: the durable
348    // run-terminal truth is the set of inputs whose `last_run_id` references
349    // the run, evaluated by the canonical pure evaluator.
350    RunTerminalStatus {
351        session_id: SessionId,
352        run_id: RunId,
353    },
354    ListActiveInputs {
355        session_id: SessionId,
356    },
357    ReconfigureSessionLlmIdentity {
358        session_id: SessionId,
359        previous_identity: Box<meerkat_core::SessionLlmIdentity>,
360        previous_visibility_state: Box<meerkat_core::SessionToolVisibilityState>,
361        previous_capability_surface: Option<SessionLlmCapabilitySurface>,
362        previous_capability_surface_status: SessionLlmCapabilitySurfaceStatus,
363        view_image_tool_available: bool,
364        previous_view_image_visible: bool,
365        next_view_image_visible: bool,
366        previous_active_visibility_revision: u64,
367        previous_staged_visibility_revision: u64,
368        target_identity: Box<meerkat_core::SessionLlmIdentity>,
369        target_capability_surface: Box<SessionLlmCapabilitySurface>,
370        next_visibility_state: Box<meerkat_core::SessionToolVisibilityState>,
371        next_capability_base_filter: meerkat_core::ToolFilter,
372        next_active_visibility_revision: u64,
373        tool_visibility_delta: Box<SessionToolVisibilityDelta>,
374    },
375    StagePersistentFilter {
376        session_id: SessionId,
377        filter: meerkat_core::ToolFilter,
378        witnesses:
379            std::collections::BTreeMap<meerkat_core::ToolName, meerkat_core::ToolVisibilityWitness>,
380    },
381    RequestDeferredTools {
382        session_id: SessionId,
383        authorities: Vec<meerkat_core::DeferredToolLoadAuthority>,
384    },
385    /// Publish the committed visible tool set through the machine dispatch.
386    ///
387    /// TLA+ source: VisibleSurfacesMatchAppliedStateInvariant —
388    /// the visible-set publication must route through the canonical command
389    /// path and be gated on session existence and non-Destroyed state.
390    PublishCommittedVisibleSet {
391        session_id: SessionId,
392        visibility_state: Box<meerkat_core::SessionToolVisibilityState>,
393    },
394    SetPeerIngressContext {
395        session_id: SessionId,
396        keep_alive: bool,
397        comms_runtime: Option<Arc<dyn CommsRuntime>>,
398        /// When present, the peer-ingress mutation is authorized only for
399        /// this exact committed executor attachment. A delayed publisher for
400        /// attachment A can therefore never configure replacement B.
401        expected_attachment: Option<crate::RuntimeExecutorAttachmentWitness>,
402        /// Mob-owned path sets this to the spawning mob's id so the DSL
403        /// transitions to `PeerIngressOwnerKind::MobOwned` rather than
404        /// `SessionOwned`. Session-owned and detach paths leave it `None`.
405        mob_id: Option<crate::meerkat_machine::dsl::MobId>,
406    },
407    NotifyDrainExited {
408        session_id: SessionId,
409        reason: DrainExitReason,
410    },
411    AbortAll,
412    Abort {
413        session_id: SessionId,
414    },
415    Wait {
416        session_id: SessionId,
417    },
418    Ingest {
419        runtime_id: LogicalRuntimeId,
420        input: Input,
421    },
422    PublishEvent {
423        event: RuntimeEventEnvelope,
424    },
425    Retire {
426        runtime_id: LogicalRuntimeId,
427    },
428    Recycle {
429        runtime_id: LogicalRuntimeId,
430    },
431    Reset {
432        runtime_id: LogicalRuntimeId,
433    },
434    Recover {
435        runtime_id: LogicalRuntimeId,
436    },
437    Destroy {
438        runtime_id: LogicalRuntimeId,
439    },
440    RuntimeState {
441        runtime_id: LogicalRuntimeId,
442    },
443    ResolvedSessionLlmCapabilities {
444        session_id: SessionId,
445    },
446    ConfigureModelRoutingBaseline {
447        session_id: SessionId,
448        baseline_model: ModelId,
449        realtime_capable: bool,
450    },
451    SessionModelRoutingStatus {
452        session_id: SessionId,
453    },
454    RequestSwitchTurn {
455        session_id: SessionId,
456        request: Box<SwitchTurnRequest>,
457    },
458    AdmitModelRoutingAssistantTurn {
459        session_id: SessionId,
460    },
461    BeginImageOperation {
462        session_id: SessionId,
463        request: Box<ImageOperationRoutingRequest>,
464    },
465    DenyImageOperationPlan {
466        session_id: SessionId,
467        operation_id: ImageOperationId,
468        reason: ImageOperationDenialReason,
469    },
470    ActivateImageOperationOverride {
471        session_id: SessionId,
472        operation_id: ImageOperationId,
473    },
474    ClassifyImageOperationTerminal {
475        session_id: SessionId,
476        operation_id: ImageOperationId,
477        observation: ImageProviderTerminalObservation,
478        provider_text: ProviderTextDisposition,
479    },
480    CompleteImageOperation {
481        session_id: SessionId,
482        operation_id: ImageOperationId,
483        terminal: ImageOperationTerminalClass,
484    },
485    RestoreImageOperationOverride {
486        session_id: SessionId,
487        operation_id: ImageOperationId,
488    },
489    LoadBoundaryReceipt {
490        runtime_id: LogicalRuntimeId,
491        run_id: LifecycleRunId,
492        sequence: u64,
493    },
494    AcceptWithCompletion {
495        session_id: SessionId,
496        input: Input,
497        register_completion: bool,
498        member_residency: MemberResidencyExpectation,
499        expected_attachment: Option<RuntimeExecutorAttachmentWitness>,
500    },
501    AcceptWithoutWake {
502        session_id: SessionId,
503        input: Input,
504    },
505}
506
507#[derive(Debug, Clone)]
508pub(crate) enum MemberResidencyExpectation {
509    Unfenced,
510    PeerOnly,
511    Placed(meerkat_contracts::wire::supervisor_bridge::BridgeMemberIncarnation),
512}
513
514#[derive(Debug, Clone)]
515pub(crate) struct MeerkatMachineRunFailure {
516    pub source: Option<dsl::RunFailureSourceKind>,
517    pub machine_terminal_failure_observed: bool,
518    pub machine_terminal_error: Option<meerkat_core::TurnErrorMetadata>,
519    pub error: String,
520}
521
522impl MeerkatMachineRunFailure {
523    pub(crate) fn from_machine_terminal_failure(error: meerkat_core::TurnErrorMetadata) -> Self {
524        let detail = error
525            .detail
526            .clone()
527            .unwrap_or_else(|| "machine terminal failure".to_string());
528        Self {
529            source: None,
530            machine_terminal_failure_observed: true,
531            machine_terminal_error: Some(error),
532            error: detail,
533        }
534    }
535}
536
537#[derive(Debug)]
538#[allow(clippy::large_enum_variant)]
539pub(crate) enum MeerkatMachineCommandResult {
540    AcceptOutcome(AcceptOutcome),
541    AcceptWithCompletion {
542        outcome: AcceptOutcome,
543        handle: Option<crate::completion::CompletionHandle>,
544        #[cfg_attr(not(test), allow(dead_code))]
545        admission_signal: crate::driver::ephemeral::PostAdmissionSignal,
546    },
547    Unit,
548    Bool(bool),
549    Spawned(bool),
550    OpsLifecycleRegistry(Option<Arc<crate::ops_lifecycle::RuntimeOpsLifecycleRegistry>>),
551    Bindings(meerkat_core::SessionRuntimeBindings),
552    InputState(Option<StoredInputState>),
553    InteractionTerminalStatus(
554        Option<crate::terminal_status::Sourced<crate::terminal_status::InteractionTerminalReport>>,
555    ),
556    RunTerminalStatus(crate::terminal_status::Sourced<crate::terminal_status::RunTerminalReport>),
557    ActiveInputs(Vec<InputId>),
558    LlmReconfigured(SessionLlmReconfigureReport),
559    VisibilityRevision(meerkat_core::ToolScopeRevision),
560    VisibilityPublished(meerkat_core::SessionToolVisibilityState),
561    RetireReport(RetireReport),
562    RecycleReport(RecycleReport),
563    ResetReport(ResetReport),
564    RecoveryReport(RecoveryReport),
565    DestroyReport(DestroyReport),
566    RuntimeState(RuntimeState),
567    ResolvedSessionLlmCapabilities(Option<SessionLlmCapabilitySurface>),
568    SessionModelRoutingStatus(SessionModelRoutingStatus),
569    SwitchTurnControlResult(SwitchTurnControlResult),
570    ImageOperationRoutingResult(ImageOperationRoutingResult),
571    ImageOperationPhase(ImageOperationPhase),
572    ImageOperationTerminalClass(ImageOperationTerminalClass),
573    BoundaryReceipt(Option<RunBoundaryReceipt>),
574}
575
576#[doc(hidden)]
577#[must_use]
578pub fn canonical_meerkat_machine_command_manifest() -> IndexSet<&'static str> {
579    canonical_meerkat_machine_command_input_variant_manifest()
580        .into_iter()
581        .map(|variant| variant.as_str())
582        .collect()
583}
584
585#[doc(hidden)]
586#[must_use]
587pub fn canonical_meerkat_machine_command_input_variant_manifest()
588-> IndexSet<MeerkatMachineInputVariant> {
589    canonical_meerkat_machine_command_classifications()
590        .into_iter()
591        .flat_map(|record| record.classification.catalog_input_variants())
592        .collect()
593}
594
595#[doc(hidden)]
596#[must_use]
597pub fn canonical_meerkat_machine_runtime_internal_manifest() -> IndexSet<&'static str> {
598    canonical_meerkat_machine_runtime_internal_input_variant_manifest()
599        .into_iter()
600        .map(|variant| variant.as_str())
601        .collect()
602}
603
604#[doc(hidden)]
605#[must_use]
606pub fn canonical_meerkat_machine_runtime_internal_input_variant_manifest()
607-> IndexSet<MeerkatMachineInputVariant> {
608    canonical_meerkat_machine_runtime_internal_classifications()
609        .into_iter()
610        .map(|record| record.input.input_variant())
611        .collect()
612}
613
614#[doc(hidden)]
615#[must_use]
616pub fn canonical_meerkat_machine_runtime_internal_fieldless_input_variant_manifest()
617-> IndexSet<MeerkatMachineInputVariant> {
618    MeerkatMachineFieldlessRuntimeInternalInput::ALL
619        .iter()
620        .copied()
621        .map(MeerkatMachineFieldlessRuntimeInternalInput::input_variant)
622        .collect()
623}
624
625macro_rules! meerkat_machine_runtime_internal_inputs {
626    ($($reason:ident => [$($variant:ident),+ $(,)?]),+ $(,)?) => {
627        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
628        pub enum MeerkatMachineRuntimeInternalInput {
629            $($($variant),+),+
630        }
631
632        impl MeerkatMachineRuntimeInternalInput {
633            pub const ALL: &'static [Self] = &[
634                $($(Self::$variant),+),+
635            ];
636
637            pub const CLASSIFICATIONS: &'static [MeerkatMachineRuntimeInternalClassificationRecord] = &[
638                $($(
639                    MeerkatMachineRuntimeInternalClassificationRecord {
640                        input: Self::$variant,
641                        reason: MeerkatMachineRuntimeInternalReason::$reason,
642                    },
643                )+)+
644            ];
645
646            #[must_use]
647            pub const fn input_variant(self) -> MeerkatMachineInputVariant {
648                match self {
649                    $($(Self::$variant => MeerkatMachineInputVariant::$variant,)+)+
650                }
651            }
652
653            #[must_use]
654            pub const fn reason(self) -> MeerkatMachineRuntimeInternalReason {
655                match self {
656                    $(
657                        $(Self::$variant)|+ => MeerkatMachineRuntimeInternalReason::$reason,
658                    )+
659                }
660            }
661        }
662    };
663}
664
665#[derive(Debug, Clone, Copy, PartialEq, Eq)]
666pub enum MeerkatMachineRuntimeInternalReason {
667    InputQueueLifecycle,
668    OperationLifecycle,
669    RunExecutionLifecycle,
670    CancellationLifecycle,
671    LiveTopologyReconfiguration,
672    InteractionStreamLifecycle,
673    EventStreamLifecycle,
674    CommsIngressLifecycle,
675    SupervisorTrustLifecycle,
676    MobOperatorAuthorityLifecycle,
677    PeerRequestLifecycle,
678    VisibilityAuthorityLifecycle,
679    DeferredSessionLifecycle,
680    ExtractionLifecycle,
681    McpServerLifecycle,
682    ModelRoutingLifecycle,
683    ExternalSurfaceLifecycle,
684    FailureRecoveryLifecycle,
685    UserInterruptDispatch,
686    SessionUnregisterDrainLifecycle,
687}
688
689#[derive(Debug, Clone, Copy, PartialEq, Eq)]
690pub struct MeerkatMachineRuntimeInternalClassificationRecord {
691    pub input: MeerkatMachineRuntimeInternalInput,
692    pub reason: MeerkatMachineRuntimeInternalReason,
693}
694
695meerkat_machine_runtime_internal_inputs!(
696    InputQueueLifecycle => [
697        AbandonInput,
698        AdvanceSessionContext,
699        BudgetExhausted,
700        ChangeLane,
701        CoalesceInput,
702        ConsumeInput,
703        ConsumeOnAccept,
704        MarkApplied,
705        MarkAppliedPendingConsumption,
706        DeferInputBehindBacklog,
707        PrioritizeInput,
708        QueueAccepted,
709        RecoverAdmittedInput,
710        RecoverInputLifecycle,
711        ResolveAdmissionIdempotency,
712        ResolveAdmissionPlan,
713        ResolveAdmissionValidation,
714        ResolveInputPublicLifecycle,
715        ResolveInputPublicTerminalOutcome,
716        ResolveTranscriptEditAdmission,
717        RegisterAcceptedIdempotency,
718        ResolveStagedRollback,
719        RetryRequested,
720        RollbackStaged,
721        StageForRun,
722        StartConversationRun,
723        StartImmediateAppend,
724        StartImmediateContext,
725        SteerAccepted,
726        SupersedeInput,
727        AuthorizeStoredInputStateSeed,
728        ClassifyInputTerminality,
729        ClassifyRecoveredInputDurability,
730        ClassifyRuntimeLoopQueueAdmission,
731        NormalizeRecoveredInputLifecycle,
732    ],
733    OperationLifecycle => [
734        AbortOp,
735        CancelOp,
736        CancelWaitAll,
737        ClassifyOperationCompletionFeed,
738        ClassifyOperationCompletionWake,
739        ClassifyOperationDurability,
740        ClassifyOperationPublicResult,
741        ClassifyOperationTerminality,
742        ClassifyOperationTransitionIdempotence,
743        ClassifyRecoveredOperationRecord,
744        CollectCompletedOp,
745        CompleteOp,
746        EvictCompletedOp,
747        FailOp,
748        IncrementAttemptCount,
749        OpsBarrierSatisfied,
750        PeerReadyOp,
751        ProgressReportedOp,
752        RecoverCompletionFeedEntry,
753        RegisterOp,
754        RegisterPendingOps,
755        RecoverCompletionConsumerCursors,
756        RecoverOpRecord,
757        RecoverOpsCompletionCursor,
758        ResolveOpLifecycleTransitionRejection,
759        ResolveRuntimeOpsLifecycleDurability,
760        ResolveWaitAllAdmission,
761        RequestWaitAll,
762        RetireCompletedOp,
763        RetireRequestedOp,
764        RollbackUnreturnedOp,
765        SatisfyWaitAll,
766        StartOp,
767        TerminateOp,
768    ],
769    RunExecutionLifecycle => [
770        AcknowledgeTerminal,
771        CallbackPending,
772        AdvanceAgentCompletionCursor,
773        AdvanceRuntimeInjectedCompletionCursor,
774        AdvanceRuntimeObservedCompletionCursor,
775        BoundaryComplete,
776        BoundaryContinue,
777        ClassifyAssistantOutput,
778        ClassifyCallTimeout,
779        ClassifyTurnTerminalCauseClass,
780        ClassifyTurnTerminality,
781        ClearSessionLlmState,
782        Commit,
783        Fail,
784        HydrateSessionLlmState,
785        LlmReturnedTerminal,
786        LlmReturnedToolCalls,
787        Prepare,
788        PrimitiveApplied,
789        RecordBoundarySeq,
790        ResolveLiveBoundaryContextReceipt,
791        ResolveRuntimeCompletionCleanup,
792        ResolveRuntimeCompletionResult,
793        ResolveRuntimeCompletionWaitFailure,
794        ResolveTurnSurfaceResult,
795        RollbackRun,
796        RunCompleted,
797        RunFailed,
798        RuntimeExecutorExited,
799        TimeBudgetExceeded,
800        ToolCallsResolved,
801        TurnLimitReached,
802    ],
803    CancellationLifecycle => [
804        AbortCancelAfterBoundaryDispatch,
805        CancelNow,
806        CancelRun,
807        CancellationObserved,
808        ForceCancelNoRun,
809        RequestCancelAfterBoundary,
810        RunCancelled,
811    ],
812    LiveTopologyReconfiguration => [
813        AbandonLiveOpenAdmission,
814        CompleteUntilChangedSwitchTurnReconfigure,
815        RecordLiveChannelRequestRejected,
816        RecordLiveChannelStatus,
817        RecordLiveCloseClosed,
818        RecordLiveCommandAccepted,
819        RecordLiveCommandRejected,
820        RecordLiveRefreshQueued,
821        ResolveLiveOpenAdmission,
822    ],
823    InteractionStreamLifecycle => [
824        InteractionStreamAbandoned,
825        InteractionStreamAttached,
826        InteractionStreamClosedEarly,
827        InteractionStreamCompleted,
828        InteractionStreamExpired,
829        InteractionStreamReserved,
830    ],
831    EventStreamLifecycle => [
832        RecordMobEventStreamOpened,
833        RecordMobEventStreamTerminated,
834        RecordSessionEventStreamOpened,
835        RecordSessionEventStreamTerminated,
836        ResolveMobEventStreamClose,
837        ResolveSessionEventStreamClose,
838    ],
839    CommsIngressLifecycle => [
840        AddDirectPeerEndpoint,
841        ApplyMobPeerOverlay,
842        AttachMobIngress,
843        AttachSessionIngress,
844        AuthorizeSupervisorMobPeerOverlay,
845        BindSupervisor,
846        ClearLocalEndpoint,
847        DetachIngress,
848        PeerResponseRejected,
849        PublishLocalEndpoint,
850        RemoveDirectPeerEndpoint,
851        ResolvePeerIngressDequeue,
852        ResolvePeerIngressReceive,
853        ResolveSupervisorAuthorizeAdmission,
854        ResolveSupervisorBindAdmission,
855        ResolveSupervisorBindMaterialAdmission,
856        ResolveSupervisorBridgeCommandAdmission,
857        SpawnDrain,
858        StopDrain,
859    ],
860    SupervisorTrustLifecycle => [
861        AuthorizeSupervisor,
862        PrepareTerminalSupervisorCleanupBindings,
863        RecoverRevokedSupervisorReceipt,
864        RecoverSupervisorBinding,
865        RecoverSupervisorRevocationPending,
866        RecoverSupervisorRotationOperation,
867        RecoverSupervisorRotationTerminalReceipt,
868        RefreshSupervisorBindingRoute,
869        RequestSupervisorTrustPublish,
870        ResolveSupervisorCleanupCommandAdmission,
871        ResumeSupervisorRotation,
872        RevokeSupervisor,
873        SubmitSupervisorRotation,
874        SupervisorRotationNextPublished,
875        SupervisorRotationPreviousRevoked,
876        SupervisorTrustEdgePublishFailed,
877        SupervisorTrustEdgePublished,
878        SupervisorTrustEdgeRevokeFailed,
879        SupervisorTrustEdgeRevoked,
880        ObserveSupervisorRotation,
881    ],
882    MobOperatorAuthorityLifecycle => [
883        GrantMobOperatorManageMob,
884        ResolveMobOperatorCreateAuthority,
885        RestoreMobOperatorAuthority,
886        SetMobOperatorCreateAuthority,
887        SetMobOperatorProfileMutation,
888        SetMobOperatorSpawnProfilesInMob,
889    ],
890    PeerRequestLifecycle => [
891        PeerRequestReceived,
892        PeerRequestSendFailed,
893        PeerRequestSent,
894        PeerRequestTimedOut,
895        PeerResponseProgressArrived,
896        PeerResponseReplied,
897        PeerResponseTerminalArrived,
898    ],
899    VisibilityAuthorityLifecycle => [
900        CommitDeferredNames,
901        CommitVisibilityFilter,
902        ClearTurnToolOverlay,
903        ReplaceDeferredToolAuthorityCatalog,
904        ReplaceFilterToolAuthorityCatalog,
905        SetTurnToolOverlay,
906        StageDeferredNames,
907        StageVisibilityFilter,
908        SurfaceSetRemovalTimeout,
909        ReplaceVisibilityState,
910    ],
911    DeferredSessionLifecycle => [
912        AbandonDeferredSessionPromotion,
913        AuthorizeDeferredSessionMachineArchivedResume,
914        AuthorizeDeferredSessionSystemContextAppend,
915        BeginDeferredSessionArchive,
916        BeginDeferredSessionPromotion,
917        DropDeferredSession,
918        FinishDeferredSessionArchive,
919        FinishDeferredSessionPromotion,
920        RestoreDeferredSessionArchive,
921        StageDeferredSession,
922        UpdateDeferredSessionKeepAlive,
923        UpdateDeferredSessionLlmIdentity,
924    ],
925    ExtractionLifecycle => [
926        EnterExtraction,
927        ExtractionFailed,
928        ExtractionStart,
929        ExtractionValidationFailed,
930        ExtractionValidationPassed,
931    ],
932    McpServerLifecycle => [
933        McpServerConnectPending,
934        McpServerConnected,
935        McpServerDisconnected,
936        McpServerFailed,
937        McpServerReload,
938    ],
939    ModelRoutingLifecycle => [
940        CommitStickyModelFallback,
941        ModelRoutingStatus,
942        RequestFiniteSwitchTurn,
943        RequestUntilChangedSwitchTurn,
944        SetModelRoutingBaseline,
945    ],
946    ExternalSurfaceLifecycle => [
947        AdmitSurfaceRequest,
948        CancelSurfaceRequest,
949        ClassifySurfaceRequestTerminal,
950        FinishSurfaceRequestUnpublished,
951        PublishOrCancelSurfaceRequest,
952        PublishSurfaceRequest,
953        RecordLiveWebrtcAnswerAccepted,
954        RecordLiveWebrtcTokenIssued,
955        RecordLiveWebsocketTokenIssued,
956        ResolveLiveWebrtcAnswerAdmission,
957        ResolveLiveWebsocketTokenAdmission,
958        SurfaceApplyBoundary,
959        SurfaceCallFinished,
960        SurfaceCallStarted,
961        SurfaceFinalizeRemovalClean,
962        SurfaceFinalizeRemovalForced,
963        SurfaceMarkPendingFailed,
964        SurfaceMarkPendingSucceeded,
965        SurfaceRegister,
966        SurfaceShutdown,
967        SurfaceSnapshotAligned,
968        SurfaceStageAdd,
969        SurfaceStageReload,
970        SurfaceStageRemove,
971    ],
972    FailureRecoveryLifecycle => [
973        AuthorizeInteractionTerminalOutboxAdoption,
974        ClassifyLlmFailureRecovery,
975        ClassifyRuntimeAuthorityReconciliation,
976        ClassifyRuntimeLifecycleDurability,
977        ClassifyRuntimeLifecycleState,
978        FatalFailure,
979        RecoverableFailure,
980        RecoverRuntimeCompletionResultCorrelation,
981        ResolveVisibleRuntimePhase,
982    ],
983    UserInterruptDispatch => [
984        InterruptCurrentRun,
985        ResolveUserInterruptPublicResult,
986    ],
987    SessionUnregisterDrainLifecycle => [
988        BeginUnregisterUnservedAttachment,
989        BeginUnregisterSession,
990        CommsDrainExitedForUnregister,
991        CompletionWaitersResolvedForUnregister,
992        RuntimeLoopStoppedForUnregister,
993    ],
994);
995
996macro_rules! meerkat_machine_fieldless_runtime_internal_inputs {
997    ($($authority:ident => [$($variant:ident),+ $(,)?]),+ $(,)?) => {
998        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
999        pub enum MeerkatMachineFieldlessRuntimeInternalInput {
1000            $($($variant),+),+
1001        }
1002
1003        impl MeerkatMachineFieldlessRuntimeInternalInput {
1004            pub const ALL: &'static [Self] = &[
1005                $($(Self::$variant),+),+
1006            ];
1007
1008            #[must_use]
1009            pub const fn runtime_internal_input(self) -> MeerkatMachineRuntimeInternalInput {
1010                match self {
1011                    $($(Self::$variant => MeerkatMachineRuntimeInternalInput::$variant,)+)+
1012                }
1013            }
1014
1015            #[must_use]
1016            pub const fn input_variant(self) -> MeerkatMachineInputVariant {
1017                self.runtime_internal_input().input_variant()
1018            }
1019
1020            #[must_use]
1021            pub const fn authority(self) -> MeerkatMachineFieldlessRuntimeInternalAuthority {
1022                match self {
1023                    $(
1024                        $(Self::$variant)|+ => MeerkatMachineFieldlessRuntimeInternalAuthority::$authority,
1025                    )+
1026                }
1027            }
1028
1029            #[must_use]
1030            pub const fn requires_typed_runtime_internal_stager(self) -> bool {
1031                matches!(
1032                    self.authority(),
1033                    MeerkatMachineFieldlessRuntimeInternalAuthority::UserInterruptDispatch
1034                )
1035            }
1036
1037            pub(crate) const fn dsl_input_variant(self) -> dsl::MeerkatMachineInputVariant {
1038                match self {
1039                    $($(Self::$variant => dsl::MeerkatMachineInputVariant::$variant,)+)+
1040                }
1041            }
1042
1043            pub(crate) fn dsl_input(self) -> dsl::MeerkatMachineInput {
1044                match self {
1045                    $($(Self::$variant => dsl::MeerkatMachineInput::$variant,)+)+
1046                }
1047            }
1048
1049            pub(crate) fn from_dsl_input_variant(
1050                variant: dsl::MeerkatMachineInputVariant,
1051            ) -> Option<Self> {
1052                Self::ALL
1053                    .iter()
1054                    .copied()
1055                    .find(|input| input.dsl_input_variant() == variant)
1056            }
1057
1058            pub(crate) fn reject_raw_dsl_input(
1059                input: &dsl::MeerkatMachineInput,
1060            ) -> Result<(), String> {
1061                if let Some(fieldless) = Self::from_dsl_input_variant(input.variant())
1062                    && fieldless.requires_typed_runtime_internal_stager()
1063                {
1064                    let variant = fieldless.input_variant();
1065                    return Err(format!(
1066                        "fieldless runtime-internal input {variant:?} must use typed runtime-internal staging authority"
1067                    ));
1068                }
1069                Ok(())
1070            }
1071        }
1072    };
1073}
1074
1075meerkat_machine_fieldless_runtime_internal_inputs!(
1076    RuntimeOwner => [
1077        RuntimeExecutorExited,
1078        ForceCancelNoRun,
1079        CancelWaitAll,
1080        StopDrain,
1081        SurfaceShutdown,
1082        DetachIngress,
1083        ClearLocalEndpoint,
1084    ],
1085    UserInterruptDispatch => [
1086        InterruptCurrentRun,
1087    ],
1088);
1089
1090#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1091pub enum MeerkatMachineFieldlessRuntimeInternalAuthority {
1092    RuntimeOwner,
1093    UserInterruptDispatch,
1094}
1095
1096#[doc(hidden)]
1097#[must_use]
1098pub fn canonical_meerkat_machine_runtime_internal_classifications()
1099-> Vec<MeerkatMachineRuntimeInternalClassificationRecord> {
1100    MeerkatMachineRuntimeInternalInput::CLASSIFICATIONS.to_vec()
1101}
1102
1103/// Closed typed manifest of supervisor bridge wire command kinds
1104/// (`meerkat_contracts::wire::supervisor_bridge::BridgeCommand` variants).
1105///
1106/// The wire enum is `#[non_exhaustive]`, so no match over it outside
1107/// `meerkat-contracts` can be compiler-checked for completeness. This manifest
1108/// is the runtime's completeness authority instead:
1109/// `supervisor_bridge_command_manifest_matches_wire_enum` pins it against the
1110/// wire enum's variant list and
1111/// `supervisor_bridge_command_realization_matches_drain_dispatch` pins each
1112/// kind's realization posture against the `comms_drain` bridge dispatch match,
1113/// so a new wire command or dispatch arm cannot land without a typed
1114/// admission classification here.
1115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1116pub enum SupervisorBridgeCommandKind {
1117    BindMember,
1118    AuthorizeSupervisor,
1119    ObserveSupervisorRotation,
1120    RevokeSupervisor,
1121    DeliverMemberInput,
1122    ObserveMember,
1123    InterruptMember,
1124    HardCancelMember,
1125    CancelTrackedMemberInput,
1126    RetireMember,
1127    DestroyMember,
1128    WireMember,
1129    UnwireMember,
1130    DeclareMemberOutboundTaint,
1131    // Multi-host (protocol V4) member-addressed family (plan §6.4/§16.4):
1132    // admission-classified ahead of realization; the comms drain stays
1133    // fail-closed until the execution arms land.
1134    ReadMemberHistory,
1135    PollMemberEvents,
1136    OpenMemberLiveChannel,
1137    CloseMemberLiveChannel,
1138    MemberLiveChannelStatus,
1139    ControlMemberLiveChannel,
1140    // Multi-host (protocol V4) host-addressed + member-originated families:
1141    // admitted by `MobHostBindingAuthority` on the host daemon / `MobMachine`
1142    // on the controlling host, never by MeerkatMachine on a member runtime.
1143    BindHost,
1144    RebindHost,
1145    RevokeHost,
1146    MaterializeMember,
1147    ReleaseMember,
1148    InstallPeerTrust,
1149    RemovePeerTrust,
1150    HostStatus,
1151    MemberOperatorRequest,
1152}
1153
1154impl SupervisorBridgeCommandKind {
1155    pub const ALL: &'static [Self] = &[
1156        Self::BindMember,
1157        Self::AuthorizeSupervisor,
1158        Self::ObserveSupervisorRotation,
1159        Self::RevokeSupervisor,
1160        Self::DeliverMemberInput,
1161        Self::ObserveMember,
1162        Self::InterruptMember,
1163        Self::HardCancelMember,
1164        Self::CancelTrackedMemberInput,
1165        Self::RetireMember,
1166        Self::DestroyMember,
1167        Self::WireMember,
1168        Self::UnwireMember,
1169        Self::DeclareMemberOutboundTaint,
1170        Self::ReadMemberHistory,
1171        Self::PollMemberEvents,
1172        Self::OpenMemberLiveChannel,
1173        Self::CloseMemberLiveChannel,
1174        Self::MemberLiveChannelStatus,
1175        Self::ControlMemberLiveChannel,
1176        Self::BindHost,
1177        Self::RebindHost,
1178        Self::RevokeHost,
1179        Self::MaterializeMember,
1180        Self::ReleaseMember,
1181        Self::InstallPeerTrust,
1182        Self::RemovePeerTrust,
1183        Self::HostStatus,
1184        Self::MemberOperatorRequest,
1185    ];
1186
1187    /// Wire enum variant identifier this kind mirrors.
1188    #[must_use]
1189    pub const fn variant_name(self) -> &'static str {
1190        match self {
1191            Self::BindMember => "BindMember",
1192            Self::AuthorizeSupervisor => "AuthorizeSupervisor",
1193            Self::ObserveSupervisorRotation => "ObserveSupervisorRotation",
1194            Self::RevokeSupervisor => "RevokeSupervisor",
1195            Self::DeliverMemberInput => "DeliverMemberInput",
1196            Self::ObserveMember => "ObserveMember",
1197            Self::InterruptMember => "InterruptMember",
1198            Self::HardCancelMember => "HardCancelMember",
1199            Self::CancelTrackedMemberInput => "CancelTrackedMemberInput",
1200            Self::RetireMember => "RetireMember",
1201            Self::DestroyMember => "DestroyMember",
1202            Self::WireMember => "WireMember",
1203            Self::UnwireMember => "UnwireMember",
1204            Self::DeclareMemberOutboundTaint => "DeclareMemberOutboundTaint",
1205            Self::ReadMemberHistory => "ReadMemberHistory",
1206            Self::PollMemberEvents => "PollMemberEvents",
1207            Self::OpenMemberLiveChannel => "OpenMemberLiveChannel",
1208            Self::CloseMemberLiveChannel => "CloseMemberLiveChannel",
1209            Self::MemberLiveChannelStatus => "MemberLiveChannelStatus",
1210            Self::ControlMemberLiveChannel => "ControlMemberLiveChannel",
1211            Self::BindHost => "BindHost",
1212            Self::RebindHost => "RebindHost",
1213            Self::RevokeHost => "RevokeHost",
1214            Self::MaterializeMember => "MaterializeMember",
1215            Self::ReleaseMember => "ReleaseMember",
1216            Self::InstallPeerTrust => "InstallPeerTrust",
1217            Self::RemovePeerTrust => "RemovePeerTrust",
1218            Self::HostStatus => "HostStatus",
1219            Self::MemberOperatorRequest => "MemberOperatorRequest",
1220        }
1221    }
1222
1223    /// MeerkatMachine admission route for this command on a member runtime.
1224    #[must_use]
1225    pub const fn admission_route(self) -> SupervisorBridgeCommandAdmissionRoute {
1226        match self {
1227            Self::BindMember => SupervisorBridgeCommandAdmissionRoute::SupervisorBind,
1228            Self::AuthorizeSupervisor => SupervisorBridgeCommandAdmissionRoute::SupervisorAuthorize,
1229            Self::ObserveSupervisorRotation => {
1230                SupervisorBridgeCommandAdmissionRoute::SupervisorRotationObservation
1231            }
1232            Self::RevokeSupervisor
1233            | Self::DeliverMemberInput
1234            | Self::ObserveMember
1235            | Self::InterruptMember
1236            | Self::HardCancelMember
1237            | Self::CancelTrackedMemberInput
1238            | Self::RetireMember
1239            | Self::DestroyMember
1240            | Self::WireMember
1241            | Self::UnwireMember
1242            | Self::DeclareMemberOutboundTaint
1243            | Self::ReadMemberHistory
1244            | Self::PollMemberEvents
1245            | Self::OpenMemberLiveChannel
1246            | Self::CloseMemberLiveChannel
1247            | Self::MemberLiveChannelStatus
1248            | Self::ControlMemberLiveChannel => {
1249                SupervisorBridgeCommandAdmissionRoute::SupervisorBridgeCommand
1250            }
1251            Self::BindHost
1252            | Self::RebindHost
1253            | Self::RevokeHost
1254            | Self::MaterializeMember
1255            | Self::ReleaseMember
1256            | Self::InstallPeerTrust
1257            | Self::RemovePeerTrust
1258            | Self::HostStatus
1259            | Self::MemberOperatorRequest => {
1260                SupervisorBridgeCommandAdmissionRoute::NotMemberAddressed
1261            }
1262        }
1263    }
1264
1265    /// Current realization posture: member-runtime comms drain arm,
1266    /// off-drain responder, or fail-closed unsupported.
1267    #[must_use]
1268    pub const fn realization(self) -> SupervisorBridgeCommandRealization {
1269        match self {
1270            Self::BindMember
1271            | Self::AuthorizeSupervisor
1272            | Self::ObserveSupervisorRotation
1273            | Self::RevokeSupervisor
1274            | Self::DeliverMemberInput
1275            | Self::ObserveMember
1276            | Self::InterruptMember
1277            | Self::RetireMember
1278            | Self::DestroyMember
1279            | Self::WireMember
1280            | Self::UnwireMember
1281            | Self::DeclareMemberOutboundTaint
1282            // Phase 6 (§7.4): member-input cancellation + the observation
1283            // pair gained member-drain arms (DEC-P6E-1/3/6/7).
1284            | Self::HardCancelMember
1285            | Self::CancelTrackedMemberInput
1286            | Self::ReadMemberHistory
1287            | Self::PollMemberEvents
1288            // Phase 6b (§16): the live-channel family gained member-drain
1289            // arms served through the MemberLiveHost slot (ADJ-P6B-1); an
1290            // absent slot rejects typed LiveTransportUnavailable at the arm.
1291            | Self::OpenMemberLiveChannel
1292            | Self::CloseMemberLiveChannel
1293            | Self::MemberLiveChannelStatus
1294            | Self::ControlMemberLiveChannel => SupervisorBridgeCommandRealization::Realized,
1295            // Host-addressed bind/rebind (phase 2), materialize/release/
1296            // status (phase 3), and the peer-trust pair (phase 4) are served
1297            // by the mob host daemon's serving loop, never a member drain
1298            // arm (ADJ-13; admission is MobHostBindingAuthority-owned — the
1299            // trust mutation itself applies through the member session's
1300            // machine-gated direct-peer-endpoint seam).
1301            Self::BindHost
1302            | Self::RebindHost
1303            | Self::RevokeHost
1304            | Self::MaterializeMember
1305            | Self::ReleaseMember
1306            | Self::InstallPeerTrust
1307            | Self::RemovePeerTrust
1308            | Self::HostStatus => SupervisorBridgeCommandRealization::RealizedOffDrain {
1309                by: OffDrainResponder::HostDaemon,
1310            },
1311            // Member-originated operator requests are served by the
1312            // controlling host's supervisor-inbox responder (ADJ-13;
1313            // admission is MobMachine's ResolveMemberOperatorAdmission).
1314            Self::MemberOperatorRequest => SupervisorBridgeCommandRealization::RealizedOffDrain {
1315                by: OffDrainResponder::ControllingSupervisorInbox,
1316            },
1317        }
1318    }
1319}
1320
1321/// MeerkatMachine admission route for a supervisor bridge command arriving at
1322/// a member runtime.
1323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1324pub enum SupervisorBridgeCommandAdmissionRoute {
1325    /// Bind bootstrap ladder (`ResolveSupervisorBindAdmission` +
1326    /// `ResolveSupervisorBindMaterialAdmission`): establishes initial
1327    /// supervisor authority, so it cannot require a bound supervisor.
1328    SupervisorBind,
1329    /// Supervisor rotation ladder (`ResolveSupervisorAuthorizeAdmission`).
1330    SupervisorAuthorize,
1331    /// Typed observation of the machine-owned supervisor rotation operation.
1332    SupervisorRotationObservation,
1333    /// Bound-supervisor command ladder
1334    /// (`ResolveSupervisorBridgeCommandAdmission`).
1335    ///
1336    /// v1 posture (multi-host plan §6.4, deliberate and recorded): the ladder
1337    /// guards on bound supervisor identity + epoch + sender only. There is no
1338    /// member-side fence or topology-epoch validation on delivery — fences
1339    /// are validated supervisor-side on submit (`StaleFenceToken`).
1340    SupervisorBridgeCommand,
1341    /// Never admitted by MeerkatMachine: host-addressed commands are admitted
1342    /// by `MobHostBindingAuthority` on the host daemon; the member-originated
1343    /// operator request is admitted by `MobMachine` on the controlling host.
1344    NotMemberAddressed,
1345}
1346
1347impl SupervisorBridgeCommandAdmissionRoute {
1348    /// Generated MeerkatMachine admission inputs staged for this route.
1349    #[must_use]
1350    pub const fn admission_inputs(self) -> &'static [MeerkatMachineRuntimeInternalInput] {
1351        match self {
1352            Self::SupervisorBind => &[
1353                MeerkatMachineRuntimeInternalInput::ResolveSupervisorBindAdmission,
1354                MeerkatMachineRuntimeInternalInput::ResolveSupervisorBindMaterialAdmission,
1355            ],
1356            Self::SupervisorAuthorize => {
1357                &[MeerkatMachineRuntimeInternalInput::ResolveSupervisorAuthorizeAdmission]
1358            }
1359            Self::SupervisorRotationObservation => {
1360                &[MeerkatMachineRuntimeInternalInput::ObserveSupervisorRotation]
1361            }
1362            Self::SupervisorBridgeCommand => {
1363                &[MeerkatMachineRuntimeInternalInput::ResolveSupervisorBridgeCommandAdmission]
1364            }
1365            Self::NotMemberAddressed => &[],
1366        }
1367    }
1368}
1369
1370/// Realization posture of a supervisor bridge command in the member runtime's
1371/// comms drain (the `comms_drain.rs` bridge dispatch match) or an off-drain
1372/// responder.
1373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1374pub enum SupervisorBridgeCommandRealization {
1375    /// A dispatch arm stages admission and realizes the command today.
1376    Realized,
1377    /// Served OFF the member runtime's comms drain by a dedicated responder
1378    /// (ADJ-13): the drain keeps ZERO arms for these kinds — the drain-scan
1379    /// gate asserts `Realized` ⇔ drain arm, so an off-drain-served command
1380    /// is never recorded as a false `Realized` and never left as an
1381    /// under-describing `FailClosedUnsupported`.
1382    RealizedOffDrain { by: OffDrainResponder },
1383    /// No dispatch arm anywhere: the fail-closed `_ =>` arm replies
1384    /// `BridgeRejectionCause::Unsupported` (the `HardCancelMember` precedent).
1385    /// Admission classification is declared ahead of realization; when the
1386    /// execution arm lands it must flip this record to `Realized` (drain arm)
1387    /// or `RealizedOffDrain` (dedicated responder) in the same change-set
1388    /// (`supervisor_bridge_command_realization_matches_drain_dispatch`
1389    /// enforces the flip).
1390    FailClosedUnsupported,
1391}
1392
1393/// Which off-drain responder realizes a `RealizedOffDrain` command (ADJ-13).
1394#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1395pub enum OffDrainResponder {
1396    /// The mob host daemon's `MobHostActor` serving loop — host-addressed
1397    /// commands admitted by `MobHostBindingAuthority`.
1398    HostDaemon,
1399    /// The controlling host's supervisor-inbox responder — the
1400    /// member-originated operator family admitted by `MobMachine`
1401    /// (`ResolveMemberOperatorAdmission`).
1402    ControllingSupervisorInbox,
1403}
1404
1405/// Per-wire-command admission classification record.
1406#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1407pub struct SupervisorBridgeCommandClassificationRecord {
1408    pub kind: SupervisorBridgeCommandKind,
1409    pub route: SupervisorBridgeCommandAdmissionRoute,
1410    pub realization: SupervisorBridgeCommandRealization,
1411}
1412
1413#[doc(hidden)]
1414#[must_use]
1415pub fn canonical_supervisor_bridge_command_classifications()
1416-> Vec<SupervisorBridgeCommandClassificationRecord> {
1417    SupervisorBridgeCommandKind::ALL
1418        .iter()
1419        .copied()
1420        .map(|kind| SupervisorBridgeCommandClassificationRecord {
1421            kind,
1422            route: kind.admission_route(),
1423            realization: kind.realization(),
1424        })
1425        .collect()
1426}
1427
1428#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1429pub enum MeerkatMachineCommandClassification {
1430    CatalogInput(MeerkatMachineCatalogInput),
1431    CatalogInputs(&'static [MeerkatMachineCatalogInput]),
1432    ShellMechanic(MeerkatMachineShellMechanicReason),
1433}
1434
1435impl MeerkatMachineCommandClassification {
1436    #[must_use]
1437    pub fn catalog_inputs(self) -> Vec<MeerkatMachineCatalogInput> {
1438        match self {
1439            Self::CatalogInput(input) => vec![input],
1440            Self::CatalogInputs(inputs) => inputs.to_vec(),
1441            Self::ShellMechanic(_) => Vec::new(),
1442        }
1443    }
1444
1445    #[must_use]
1446    pub fn catalog_input_variants(self) -> Vec<MeerkatMachineInputVariant> {
1447        self.catalog_inputs()
1448            .into_iter()
1449            .map(MeerkatMachineCatalogInput::input_variant)
1450            .collect()
1451    }
1452}
1453
1454#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1455pub enum MeerkatMachineCatalogInput {
1456    RegisterSession,
1457    UnregisterSession,
1458    EnsureSessionWithExecutor,
1459    SetSilentIntents,
1460    CancelAfterBoundary,
1461    StopRuntimeExecutor,
1462    ServiceTurnCommitted,
1463    ContainsSession,
1464    SessionHasExecutor,
1465    SessionHasComms,
1466    OpsLifecycleRegistry,
1467    PrepareBindings,
1468    InputState,
1469    ListActiveInputs,
1470    ReconfigureSessionLlmIdentity,
1471    StagePersistentFilter,
1472    RequestDeferredTools,
1473    PublishCommittedVisibleSet,
1474    SetPeerIngressContext,
1475    NotifyDrainExited,
1476    AbortAll,
1477    Abort,
1478    Wait,
1479    Ingest,
1480    PublishEvent,
1481    Retire,
1482    Recycle,
1483    Reset,
1484    Recover,
1485    Destroy,
1486    RuntimeState,
1487    ModelRoutingStatus,
1488    SetModelRoutingBaseline,
1489    RequestFiniteSwitchTurn,
1490    RequestUntilChangedSwitchTurn,
1491    AdmitModelRoutingAssistantTurn,
1492    BeginImageOperation,
1493    DenyImageOperationPlan,
1494    ActivateImageOperationOverride,
1495    ClassifyImageOperationTerminal,
1496    CompleteImageOperation,
1497    RestoreImageOperationOverride,
1498    LoadBoundaryReceipt,
1499    AcceptWithCompletion,
1500    AcceptWithoutWake,
1501}
1502
1503impl MeerkatMachineCatalogInput {
1504    pub const ALL: &'static [Self] = &[
1505        Self::RegisterSession,
1506        Self::UnregisterSession,
1507        Self::EnsureSessionWithExecutor,
1508        Self::SetSilentIntents,
1509        Self::CancelAfterBoundary,
1510        Self::StopRuntimeExecutor,
1511        Self::ServiceTurnCommitted,
1512        Self::ContainsSession,
1513        Self::SessionHasExecutor,
1514        Self::SessionHasComms,
1515        Self::OpsLifecycleRegistry,
1516        Self::PrepareBindings,
1517        Self::InputState,
1518        Self::ListActiveInputs,
1519        Self::ReconfigureSessionLlmIdentity,
1520        Self::StagePersistentFilter,
1521        Self::RequestDeferredTools,
1522        Self::PublishCommittedVisibleSet,
1523        Self::SetPeerIngressContext,
1524        Self::NotifyDrainExited,
1525        Self::AbortAll,
1526        Self::Abort,
1527        Self::Wait,
1528        Self::Ingest,
1529        Self::PublishEvent,
1530        Self::Retire,
1531        Self::Recycle,
1532        Self::Reset,
1533        Self::Recover,
1534        Self::Destroy,
1535        Self::RuntimeState,
1536        Self::ModelRoutingStatus,
1537        Self::SetModelRoutingBaseline,
1538        Self::RequestFiniteSwitchTurn,
1539        Self::RequestUntilChangedSwitchTurn,
1540        Self::AdmitModelRoutingAssistantTurn,
1541        Self::BeginImageOperation,
1542        Self::DenyImageOperationPlan,
1543        Self::ActivateImageOperationOverride,
1544        Self::ClassifyImageOperationTerminal,
1545        Self::CompleteImageOperation,
1546        Self::RestoreImageOperationOverride,
1547        Self::LoadBoundaryReceipt,
1548        Self::AcceptWithCompletion,
1549        Self::AcceptWithoutWake,
1550    ];
1551
1552    #[must_use]
1553    pub const fn input_variant(self) -> MeerkatMachineInputVariant {
1554        match self {
1555            Self::RegisterSession => MeerkatMachineInputVariant::RegisterSession,
1556            Self::UnregisterSession => MeerkatMachineInputVariant::UnregisterSession,
1557            Self::EnsureSessionWithExecutor => {
1558                MeerkatMachineInputVariant::EnsureSessionWithExecutor
1559            }
1560            Self::SetSilentIntents => MeerkatMachineInputVariant::SetSilentIntents,
1561            Self::CancelAfterBoundary => MeerkatMachineInputVariant::CancelAfterBoundary,
1562            Self::StopRuntimeExecutor => MeerkatMachineInputVariant::StopRuntimeExecutor,
1563            Self::ServiceTurnCommitted => MeerkatMachineInputVariant::ServiceTurnCommitted,
1564            Self::ContainsSession => MeerkatMachineInputVariant::ContainsSession,
1565            Self::SessionHasExecutor => MeerkatMachineInputVariant::SessionHasExecutor,
1566            Self::SessionHasComms => MeerkatMachineInputVariant::SessionHasComms,
1567            Self::OpsLifecycleRegistry => MeerkatMachineInputVariant::OpsLifecycleRegistry,
1568            Self::PrepareBindings => MeerkatMachineInputVariant::PrepareBindings,
1569            Self::InputState => MeerkatMachineInputVariant::InputState,
1570            Self::ListActiveInputs => MeerkatMachineInputVariant::ListActiveInputs,
1571            Self::ReconfigureSessionLlmIdentity => {
1572                MeerkatMachineInputVariant::ReconfigureSessionLlmIdentity
1573            }
1574            Self::StagePersistentFilter => MeerkatMachineInputVariant::StagePersistentFilter,
1575            Self::RequestDeferredTools => MeerkatMachineInputVariant::RequestDeferredTools,
1576            Self::PublishCommittedVisibleSet => {
1577                MeerkatMachineInputVariant::PublishCommittedVisibleSet
1578            }
1579            Self::SetPeerIngressContext => MeerkatMachineInputVariant::SetPeerIngressContext,
1580            Self::NotifyDrainExited => MeerkatMachineInputVariant::NotifyDrainExited,
1581            Self::AbortAll => MeerkatMachineInputVariant::AbortAll,
1582            Self::Abort => MeerkatMachineInputVariant::Abort,
1583            Self::Wait => MeerkatMachineInputVariant::Wait,
1584            Self::Ingest => MeerkatMachineInputVariant::Ingest,
1585            Self::PublishEvent => MeerkatMachineInputVariant::PublishEvent,
1586            Self::Retire => MeerkatMachineInputVariant::Retire,
1587            Self::Recycle => MeerkatMachineInputVariant::Recycle,
1588            Self::Reset => MeerkatMachineInputVariant::Reset,
1589            Self::Recover => MeerkatMachineInputVariant::Recover,
1590            Self::Destroy => MeerkatMachineInputVariant::Destroy,
1591            Self::RuntimeState => MeerkatMachineInputVariant::RuntimeState,
1592            Self::ModelRoutingStatus => MeerkatMachineInputVariant::ModelRoutingStatus,
1593            Self::SetModelRoutingBaseline => MeerkatMachineInputVariant::SetModelRoutingBaseline,
1594            Self::RequestFiniteSwitchTurn => MeerkatMachineInputVariant::RequestFiniteSwitchTurn,
1595            Self::RequestUntilChangedSwitchTurn => {
1596                MeerkatMachineInputVariant::RequestUntilChangedSwitchTurn
1597            }
1598            Self::AdmitModelRoutingAssistantTurn => {
1599                MeerkatMachineInputVariant::AdmitModelRoutingAssistantTurn
1600            }
1601            Self::BeginImageOperation => MeerkatMachineInputVariant::BeginImageOperation,
1602            Self::DenyImageOperationPlan => MeerkatMachineInputVariant::DenyImageOperationPlan,
1603            Self::ActivateImageOperationOverride => {
1604                MeerkatMachineInputVariant::ActivateImageOperationOverride
1605            }
1606            Self::ClassifyImageOperationTerminal => {
1607                MeerkatMachineInputVariant::ClassifyImageOperationTerminal
1608            }
1609            Self::CompleteImageOperation => MeerkatMachineInputVariant::CompleteImageOperation,
1610            Self::RestoreImageOperationOverride => {
1611                MeerkatMachineInputVariant::RestoreImageOperationOverride
1612            }
1613            Self::LoadBoundaryReceipt => MeerkatMachineInputVariant::LoadBoundaryReceipt,
1614            Self::AcceptWithCompletion => MeerkatMachineInputVariant::AcceptWithCompletion,
1615            Self::AcceptWithoutWake => MeerkatMachineInputVariant::AcceptWithoutWake,
1616        }
1617    }
1618
1619    #[must_use]
1620    pub const fn as_str(self) -> &'static str {
1621        match self {
1622            Self::RegisterSession => "RegisterSession",
1623            Self::UnregisterSession => "UnregisterSession",
1624            Self::EnsureSessionWithExecutor => "EnsureSessionWithExecutor",
1625            Self::SetSilentIntents => "SetSilentIntents",
1626            Self::CancelAfterBoundary => "CancelAfterBoundary",
1627            Self::StopRuntimeExecutor => "StopRuntimeExecutor",
1628            Self::ServiceTurnCommitted => "ServiceTurnCommitted",
1629            Self::ContainsSession => "ContainsSession",
1630            Self::SessionHasExecutor => "SessionHasExecutor",
1631            Self::SessionHasComms => "SessionHasComms",
1632            Self::OpsLifecycleRegistry => "OpsLifecycleRegistry",
1633            Self::PrepareBindings => "PrepareBindings",
1634            Self::InputState => "InputState",
1635            Self::ListActiveInputs => "ListActiveInputs",
1636            Self::ReconfigureSessionLlmIdentity => "ReconfigureSessionLlmIdentity",
1637            Self::StagePersistentFilter => "StagePersistentFilter",
1638            Self::RequestDeferredTools => "RequestDeferredTools",
1639            Self::PublishCommittedVisibleSet => "PublishCommittedVisibleSet",
1640            Self::SetPeerIngressContext => "SetPeerIngressContext",
1641            Self::NotifyDrainExited => "NotifyDrainExited",
1642            Self::AbortAll => "AbortAll",
1643            Self::Abort => "Abort",
1644            Self::Wait => "Wait",
1645            Self::Ingest => "Ingest",
1646            Self::PublishEvent => "PublishEvent",
1647            Self::Retire => "Retire",
1648            Self::Recycle => "Recycle",
1649            Self::Reset => "Reset",
1650            Self::Recover => "Recover",
1651            Self::Destroy => "Destroy",
1652            Self::RuntimeState => "RuntimeState",
1653            Self::ModelRoutingStatus => "ModelRoutingStatus",
1654            Self::SetModelRoutingBaseline => "SetModelRoutingBaseline",
1655            Self::RequestFiniteSwitchTurn => "RequestFiniteSwitchTurn",
1656            Self::RequestUntilChangedSwitchTurn => "RequestUntilChangedSwitchTurn",
1657            Self::AdmitModelRoutingAssistantTurn => "AdmitModelRoutingAssistantTurn",
1658            Self::BeginImageOperation => "BeginImageOperation",
1659            Self::DenyImageOperationPlan => "DenyImageOperationPlan",
1660            Self::ActivateImageOperationOverride => "ActivateImageOperationOverride",
1661            Self::ClassifyImageOperationTerminal => "ClassifyImageOperationTerminal",
1662            Self::CompleteImageOperation => "CompleteImageOperation",
1663            Self::RestoreImageOperationOverride => "RestoreImageOperationOverride",
1664            Self::LoadBoundaryReceipt => "LoadBoundaryReceipt",
1665            Self::AcceptWithCompletion => "AcceptWithCompletion",
1666            Self::AcceptWithoutWake => "AcceptWithoutWake",
1667        }
1668    }
1669}
1670
1671impl MeerkatMachineCommandVariant {
1672    #[must_use]
1673    pub const fn catalog_input(self) -> Option<MeerkatMachineCatalogInput> {
1674        match self {
1675            Self::ConfigureModelRoutingBaseline
1676            | Self::RequestSwitchTurn
1677            | Self::ResolvedSessionLlmCapabilities
1678            | Self::SessionModelRoutingStatus
1679            | Self::PrepareLocalSessionBindings => None,
1680            Self::RegisterSession => Some(MeerkatMachineCatalogInput::RegisterSession),
1681            Self::UnregisterSession => Some(MeerkatMachineCatalogInput::UnregisterSession),
1682            Self::EnsureSessionWithExecutor => {
1683                Some(MeerkatMachineCatalogInput::EnsureSessionWithExecutor)
1684            }
1685            Self::SetSilentIntents => Some(MeerkatMachineCatalogInput::SetSilentIntents),
1686            Self::CancelAfterBoundary => Some(MeerkatMachineCatalogInput::CancelAfterBoundary),
1687            Self::StopRuntimeExecutor => Some(MeerkatMachineCatalogInput::StopRuntimeExecutor),
1688            Self::CommitServiceTurnTerminalReceipt => {
1689                Some(MeerkatMachineCatalogInput::ServiceTurnCommitted)
1690            }
1691            Self::ContainsSession => Some(MeerkatMachineCatalogInput::ContainsSession),
1692            Self::SessionHasExecutor => Some(MeerkatMachineCatalogInput::SessionHasExecutor),
1693            Self::SessionHasComms => Some(MeerkatMachineCatalogInput::SessionHasComms),
1694            Self::OpsLifecycleRegistry => Some(MeerkatMachineCatalogInput::OpsLifecycleRegistry),
1695            Self::PrepareBindings => Some(MeerkatMachineCatalogInput::PrepareBindings),
1696            Self::InputState => Some(MeerkatMachineCatalogInput::InputState),
1697            // Same machine-owned read route as `InputState` (the key
1698            // resolves through the generated admission map first).
1699            Self::InputStateByIdempotencyKey => Some(MeerkatMachineCatalogInput::InputState),
1700            // Terminal-status queries are the same machine-owned read route
1701            // as `InputState`: they evaluate the identical DSL-owned seed
1702            // facts (live snapshot or their durable store witnesses).
1703            Self::InteractionTerminalStatus => Some(MeerkatMachineCatalogInput::InputState),
1704            Self::RunTerminalStatus => Some(MeerkatMachineCatalogInput::InputState),
1705            Self::ListActiveInputs => Some(MeerkatMachineCatalogInput::ListActiveInputs),
1706            Self::ReconfigureSessionLlmIdentity => {
1707                Some(MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity)
1708            }
1709            Self::StagePersistentFilter => Some(MeerkatMachineCatalogInput::StagePersistentFilter),
1710            Self::RequestDeferredTools => Some(MeerkatMachineCatalogInput::RequestDeferredTools),
1711            Self::PublishCommittedVisibleSet => {
1712                Some(MeerkatMachineCatalogInput::PublishCommittedVisibleSet)
1713            }
1714            Self::SetPeerIngressContext => Some(MeerkatMachineCatalogInput::SetPeerIngressContext),
1715            Self::NotifyDrainExited => Some(MeerkatMachineCatalogInput::NotifyDrainExited),
1716            Self::AbortAll => Some(MeerkatMachineCatalogInput::AbortAll),
1717            Self::Abort => Some(MeerkatMachineCatalogInput::Abort),
1718            Self::Wait => Some(MeerkatMachineCatalogInput::Wait),
1719            Self::Ingest => Some(MeerkatMachineCatalogInput::Ingest),
1720            Self::PublishEvent => Some(MeerkatMachineCatalogInput::PublishEvent),
1721            Self::Retire => Some(MeerkatMachineCatalogInput::Retire),
1722            Self::Recycle => Some(MeerkatMachineCatalogInput::Recycle),
1723            Self::Reset => Some(MeerkatMachineCatalogInput::Reset),
1724            Self::Recover => Some(MeerkatMachineCatalogInput::Recover),
1725            Self::Destroy => Some(MeerkatMachineCatalogInput::Destroy),
1726            Self::RuntimeState => Some(MeerkatMachineCatalogInput::RuntimeState),
1727            Self::AdmitModelRoutingAssistantTurn => {
1728                Some(MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn)
1729            }
1730            Self::BeginImageOperation => Some(MeerkatMachineCatalogInput::BeginImageOperation),
1731            Self::DenyImageOperationPlan => {
1732                Some(MeerkatMachineCatalogInput::DenyImageOperationPlan)
1733            }
1734            Self::ActivateImageOperationOverride => {
1735                Some(MeerkatMachineCatalogInput::ActivateImageOperationOverride)
1736            }
1737            Self::ClassifyImageOperationTerminal => {
1738                Some(MeerkatMachineCatalogInput::ClassifyImageOperationTerminal)
1739            }
1740            Self::CompleteImageOperation => {
1741                Some(MeerkatMachineCatalogInput::CompleteImageOperation)
1742            }
1743            Self::RestoreImageOperationOverride => {
1744                Some(MeerkatMachineCatalogInput::RestoreImageOperationOverride)
1745            }
1746            Self::LoadBoundaryReceipt => Some(MeerkatMachineCatalogInput::LoadBoundaryReceipt),
1747            Self::AcceptWithCompletion => Some(MeerkatMachineCatalogInput::AcceptWithCompletion),
1748            Self::AcceptWithoutWake => Some(MeerkatMachineCatalogInput::AcceptWithoutWake),
1749        }
1750    }
1751}
1752
1753#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1754pub enum MeerkatMachineShellMechanicReason {
1755    ModelRoutingShellConfiguration,
1756    TurnControlOverlayRequest,
1757    RealtimeTransportObservation,
1758    SessionModelRoutingObservation,
1759    LocalSessionBindingBootstrap,
1760}
1761
1762#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1763pub struct MeerkatMachineCommandClassificationRecord {
1764    pub command: MeerkatMachineCommandVariant,
1765    pub classification: MeerkatMachineCommandClassification,
1766}
1767
1768#[doc(hidden)]
1769#[must_use]
1770pub fn canonical_meerkat_machine_command_classifications()
1771-> Vec<MeerkatMachineCommandClassificationRecord> {
1772    MeerkatMachineCommand::command_variant_manifest()
1773        .iter()
1774        .copied()
1775        .map(|variant| MeerkatMachineCommandClassificationRecord {
1776            command: variant,
1777            classification: meerkat_machine_command_classification(variant),
1778        })
1779        .collect()
1780}
1781
1782const fn meerkat_machine_command_classification(
1783    variant: MeerkatMachineCommandVariant,
1784) -> MeerkatMachineCommandClassification {
1785    match variant {
1786        MeerkatMachineCommandVariant::ConfigureModelRoutingBaseline => {
1787            MeerkatMachineCommandClassification::CatalogInput(
1788                MeerkatMachineCatalogInput::SetModelRoutingBaseline,
1789            )
1790        }
1791        MeerkatMachineCommandVariant::RequestSwitchTurn => {
1792            MeerkatMachineCommandClassification::CatalogInputs(&[
1793                MeerkatMachineCatalogInput::RequestFiniteSwitchTurn,
1794                MeerkatMachineCatalogInput::RequestUntilChangedSwitchTurn,
1795            ])
1796        }
1797        MeerkatMachineCommandVariant::ResolvedSessionLlmCapabilities => {
1798            MeerkatMachineCommandClassification::ShellMechanic(
1799                MeerkatMachineShellMechanicReason::SessionModelRoutingObservation,
1800            )
1801        }
1802        MeerkatMachineCommandVariant::SessionModelRoutingStatus => {
1803            MeerkatMachineCommandClassification::CatalogInput(
1804                MeerkatMachineCatalogInput::ModelRoutingStatus,
1805            )
1806        }
1807        MeerkatMachineCommandVariant::PrepareLocalSessionBindings => {
1808            MeerkatMachineCommandClassification::ShellMechanic(
1809                MeerkatMachineShellMechanicReason::LocalSessionBindingBootstrap,
1810            )
1811        }
1812        MeerkatMachineCommandVariant::RegisterSession => {
1813            MeerkatMachineCommandClassification::CatalogInput(
1814                MeerkatMachineCatalogInput::RegisterSession,
1815            )
1816        }
1817        MeerkatMachineCommandVariant::UnregisterSession => {
1818            MeerkatMachineCommandClassification::CatalogInput(
1819                MeerkatMachineCatalogInput::UnregisterSession,
1820            )
1821        }
1822        MeerkatMachineCommandVariant::EnsureSessionWithExecutor => {
1823            MeerkatMachineCommandClassification::CatalogInput(
1824                MeerkatMachineCatalogInput::EnsureSessionWithExecutor,
1825            )
1826        }
1827        MeerkatMachineCommandVariant::SetSilentIntents => {
1828            MeerkatMachineCommandClassification::CatalogInput(
1829                MeerkatMachineCatalogInput::SetSilentIntents,
1830            )
1831        }
1832        MeerkatMachineCommandVariant::CancelAfterBoundary => {
1833            MeerkatMachineCommandClassification::CatalogInput(
1834                MeerkatMachineCatalogInput::CancelAfterBoundary,
1835            )
1836        }
1837        MeerkatMachineCommandVariant::StopRuntimeExecutor => {
1838            MeerkatMachineCommandClassification::CatalogInput(
1839                MeerkatMachineCatalogInput::StopRuntimeExecutor,
1840            )
1841        }
1842        MeerkatMachineCommandVariant::CommitServiceTurnTerminalReceipt => {
1843            MeerkatMachineCommandClassification::CatalogInput(
1844                MeerkatMachineCatalogInput::ServiceTurnCommitted,
1845            )
1846        }
1847        MeerkatMachineCommandVariant::ContainsSession => {
1848            MeerkatMachineCommandClassification::CatalogInput(
1849                MeerkatMachineCatalogInput::ContainsSession,
1850            )
1851        }
1852        MeerkatMachineCommandVariant::SessionHasExecutor => {
1853            MeerkatMachineCommandClassification::CatalogInput(
1854                MeerkatMachineCatalogInput::SessionHasExecutor,
1855            )
1856        }
1857        MeerkatMachineCommandVariant::SessionHasComms => {
1858            MeerkatMachineCommandClassification::CatalogInput(
1859                MeerkatMachineCatalogInput::SessionHasComms,
1860            )
1861        }
1862        MeerkatMachineCommandVariant::OpsLifecycleRegistry => {
1863            MeerkatMachineCommandClassification::CatalogInput(
1864                MeerkatMachineCatalogInput::OpsLifecycleRegistry,
1865            )
1866        }
1867        MeerkatMachineCommandVariant::PrepareBindings => {
1868            MeerkatMachineCommandClassification::CatalogInput(
1869                MeerkatMachineCatalogInput::PrepareBindings,
1870            )
1871        }
1872        MeerkatMachineCommandVariant::InputState => {
1873            MeerkatMachineCommandClassification::CatalogInput(
1874                MeerkatMachineCatalogInput::InputState,
1875            )
1876        }
1877        // Same machine-owned read route as `InputState`: the idempotency
1878        // key resolves through the generated admission map, then the stored
1879        // input state is read identically.
1880        MeerkatMachineCommandVariant::InputStateByIdempotencyKey => {
1881            MeerkatMachineCommandClassification::CatalogInput(
1882                MeerkatMachineCatalogInput::InputState,
1883            )
1884        }
1885        // Terminal-status reads evaluate the same DSL-owned seed facts as
1886        // `InputState` (live snapshot for registered sessions, durable store
1887        // witnesses otherwise) — same machine-owned read route.
1888        MeerkatMachineCommandVariant::InteractionTerminalStatus
1889        | MeerkatMachineCommandVariant::RunTerminalStatus => {
1890            MeerkatMachineCommandClassification::CatalogInput(
1891                MeerkatMachineCatalogInput::InputState,
1892            )
1893        }
1894        MeerkatMachineCommandVariant::ListActiveInputs => {
1895            MeerkatMachineCommandClassification::CatalogInput(
1896                MeerkatMachineCatalogInput::ListActiveInputs,
1897            )
1898        }
1899        MeerkatMachineCommandVariant::ReconfigureSessionLlmIdentity => {
1900            MeerkatMachineCommandClassification::CatalogInput(
1901                MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity,
1902            )
1903        }
1904        MeerkatMachineCommandVariant::StagePersistentFilter => {
1905            MeerkatMachineCommandClassification::CatalogInput(
1906                MeerkatMachineCatalogInput::StagePersistentFilter,
1907            )
1908        }
1909        MeerkatMachineCommandVariant::RequestDeferredTools => {
1910            MeerkatMachineCommandClassification::CatalogInput(
1911                MeerkatMachineCatalogInput::RequestDeferredTools,
1912            )
1913        }
1914        MeerkatMachineCommandVariant::PublishCommittedVisibleSet => {
1915            MeerkatMachineCommandClassification::CatalogInput(
1916                MeerkatMachineCatalogInput::PublishCommittedVisibleSet,
1917            )
1918        }
1919        MeerkatMachineCommandVariant::SetPeerIngressContext => {
1920            MeerkatMachineCommandClassification::CatalogInput(
1921                MeerkatMachineCatalogInput::SetPeerIngressContext,
1922            )
1923        }
1924        MeerkatMachineCommandVariant::NotifyDrainExited => {
1925            MeerkatMachineCommandClassification::CatalogInput(
1926                MeerkatMachineCatalogInput::NotifyDrainExited,
1927            )
1928        }
1929        MeerkatMachineCommandVariant::AbortAll => {
1930            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::AbortAll)
1931        }
1932        MeerkatMachineCommandVariant::Abort => {
1933            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Abort)
1934        }
1935        MeerkatMachineCommandVariant::Wait => {
1936            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Wait)
1937        }
1938        MeerkatMachineCommandVariant::Ingest => {
1939            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Ingest)
1940        }
1941        MeerkatMachineCommandVariant::PublishEvent => {
1942            MeerkatMachineCommandClassification::CatalogInput(
1943                MeerkatMachineCatalogInput::PublishEvent,
1944            )
1945        }
1946        MeerkatMachineCommandVariant::Retire => {
1947            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Retire)
1948        }
1949        MeerkatMachineCommandVariant::Recycle => {
1950            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recycle)
1951        }
1952        MeerkatMachineCommandVariant::Reset => {
1953            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Reset)
1954        }
1955        MeerkatMachineCommandVariant::Recover => {
1956            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recover)
1957        }
1958        MeerkatMachineCommandVariant::Destroy => {
1959            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Destroy)
1960        }
1961        MeerkatMachineCommandVariant::RuntimeState => {
1962            MeerkatMachineCommandClassification::CatalogInput(
1963                MeerkatMachineCatalogInput::RuntimeState,
1964            )
1965        }
1966        MeerkatMachineCommandVariant::AdmitModelRoutingAssistantTurn => {
1967            MeerkatMachineCommandClassification::CatalogInput(
1968                MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn,
1969            )
1970        }
1971        MeerkatMachineCommandVariant::BeginImageOperation => {
1972            MeerkatMachineCommandClassification::CatalogInput(
1973                MeerkatMachineCatalogInput::BeginImageOperation,
1974            )
1975        }
1976        MeerkatMachineCommandVariant::DenyImageOperationPlan => {
1977            MeerkatMachineCommandClassification::CatalogInput(
1978                MeerkatMachineCatalogInput::DenyImageOperationPlan,
1979            )
1980        }
1981        MeerkatMachineCommandVariant::ActivateImageOperationOverride => {
1982            MeerkatMachineCommandClassification::CatalogInput(
1983                MeerkatMachineCatalogInput::ActivateImageOperationOverride,
1984            )
1985        }
1986        MeerkatMachineCommandVariant::ClassifyImageOperationTerminal => {
1987            MeerkatMachineCommandClassification::CatalogInput(
1988                MeerkatMachineCatalogInput::ClassifyImageOperationTerminal,
1989            )
1990        }
1991        MeerkatMachineCommandVariant::CompleteImageOperation => {
1992            MeerkatMachineCommandClassification::CatalogInput(
1993                MeerkatMachineCatalogInput::CompleteImageOperation,
1994            )
1995        }
1996        MeerkatMachineCommandVariant::RestoreImageOperationOverride => {
1997            MeerkatMachineCommandClassification::CatalogInput(
1998                MeerkatMachineCatalogInput::RestoreImageOperationOverride,
1999            )
2000        }
2001        MeerkatMachineCommandVariant::LoadBoundaryReceipt => {
2002            MeerkatMachineCommandClassification::CatalogInput(
2003                MeerkatMachineCatalogInput::LoadBoundaryReceipt,
2004            )
2005        }
2006        MeerkatMachineCommandVariant::AcceptWithCompletion => {
2007            MeerkatMachineCommandClassification::CatalogInput(
2008                MeerkatMachineCatalogInput::AcceptWithCompletion,
2009            )
2010        }
2011        MeerkatMachineCommandVariant::AcceptWithoutWake => {
2012            MeerkatMachineCommandClassification::CatalogInput(
2013                MeerkatMachineCatalogInput::AcceptWithoutWake,
2014            )
2015        }
2016    }
2017}
2018
2019/// Snapshot of completion waiters registered for one input.
2020///
2021/// This is a supporting-carrier view, not canonical semantic truth.
2022#[derive(Debug, Clone, PartialEq, Eq)]
2023pub struct MeerkatCompletionWaiterSnapshot {
2024    pub input_id: InputId,
2025    pub waiter_count: usize,
2026}
2027
2028/// Snapshot of the runtime completion waiter carrier.
2029///
2030/// Completion waiters are supporting carrier state rather than primary
2031/// authority, but they remain useful for diagnostics and recovery inspection.
2032#[derive(Debug, Clone, PartialEq, Eq)]
2033pub struct MeerkatCompletionWaitersSnapshot {
2034    pub input_count: usize,
2035    pub waiter_count: usize,
2036    pub waiting_inputs: Vec<MeerkatCompletionWaiterSnapshot>,
2037}
2038
2039/// Runtime driver flavor for a registered Meerkat session entry.
2040#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2041pub enum MeerkatDriverKind {
2042    Ephemeral,
2043    Persistent,
2044}
2045
2046/// Snapshot of the hidden runtime epoch cursor state.
2047#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2048pub struct MeerkatCursorSnapshot {
2049    pub agent_applied_cursor: u64,
2050    pub runtime_observed_seq: u64,
2051    pub runtime_last_injected_seq: u64,
2052}
2053
2054/// Snapshot of the hidden runtime binding for one Meerkat session.
2055#[derive(Debug, Clone)]
2056pub struct MeerkatBindingSnapshot {
2057    pub session_id: SessionId,
2058    pub runtime_id: LogicalRuntimeId,
2059    pub driver_kind: MeerkatDriverKind,
2060    pub driver_present: bool,
2061    pub completions_present: bool,
2062    pub ops_registry_present: bool,
2063    pub epoch_id: RuntimeEpochId,
2064    pub cursor_state: MeerkatCursorSnapshot,
2065}
2066
2067/// Snapshot of runtime control-plane truth for one session.
2068#[derive(Debug, Clone)]
2069pub struct MeerkatControlSnapshot {
2070    pub phase: RuntimeState,
2071    pub current_run_id: Option<RunId>,
2072    pub pre_run_phase: Option<RuntimeState>,
2073}
2074
2075/// Snapshot of one admitted runtime input.
2076#[derive(Debug, Clone, PartialEq, Eq)]
2077pub struct MeerkatAdmittedInputSnapshot {
2078    pub input_id: InputId,
2079    pub content_shape: Option<ContentShape>,
2080    pub request_id: Option<RequestId>,
2081    pub reservation_key: Option<ReservationKey>,
2082    pub handling_mode: Option<HandlingMode>,
2083    /// #338: machine-owned per-input live-interrupt verdict, carried from the
2084    /// admission `RuntimeInputSemantics`. The live-projection consumer reads
2085    /// this typed fact instead of re-scanning `handling_mode == Steer`.
2086    pub live_interrupt_required: bool,
2087    pub lifecycle: Option<InputLifecycleState>,
2088    pub terminal_outcome: Option<InputTerminalOutcome>,
2089    pub last_run_id: Option<RunId>,
2090    pub last_boundary_sequence: Option<u64>,
2091    pub is_prompt: bool,
2092}
2093
2094/// Snapshot of runtime ingress truth for one session.
2095#[derive(Debug, Clone, PartialEq, Eq)]
2096pub struct MeerkatInputsSnapshot {
2097    pub admission_order: Vec<MeerkatAdmittedInputSnapshot>,
2098    pub queue: Vec<InputId>,
2099    pub steer_queue: Vec<InputId>,
2100    pub current_run_id: Option<RunId>,
2101    pub current_run_contributors: Vec<InputId>,
2102    pub post_admission_signal: String,
2103    pub silent_intent_overrides: Vec<String>,
2104}
2105
2106/// Lightweight lifecycle snapshot used by archive/retire cleanup.
2107///
2108/// This intentionally avoids the heavier diagnostic regions that are not
2109/// needed for archive decisions. Control and input fields still come from the
2110/// generated runtime authority/driver projections.
2111#[derive(Debug, Clone)]
2112pub struct MeerkatArchiveSnapshot {
2113    pub control: MeerkatControlSnapshot,
2114    pub queue: Vec<InputId>,
2115    pub steer_queue: Vec<InputId>,
2116    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
2117}
2118
2119/// Snapshot of the canonical input-ledger carrier for one session.
2120///
2121/// These counts sit below the top-level Meerkat phase machine, but they drive
2122/// the exact values returned by control-plane reports such as
2123/// `DestroyReport.inputs_abandoned`.
2124#[derive(Debug, Clone)]
2125pub struct MeerkatLedgerSnapshot {
2126    pub input_count: usize,
2127    pub non_terminal_count: usize,
2128    pub accepted_count: usize,
2129    pub queued_count: usize,
2130    pub staged_count: usize,
2131    pub applied_count: usize,
2132    pub applied_pending_consumption_count: usize,
2133    pub consumed_count: usize,
2134    pub superseded_count: usize,
2135    pub coalesced_count: usize,
2136    pub abandoned_count: usize,
2137}
2138
2139/// Snapshot of runtime-owned async operation truth for one session.
2140#[derive(Debug, Clone)]
2141pub struct MeerkatOpsSnapshot {
2142    pub operation_count: usize,
2143    pub active_count: usize,
2144    pub wait_request_id: Option<WaitRequestId>,
2145    pub pending_wait_present: bool,
2146    pub pending_wait_request_id: Option<WaitRequestId>,
2147    pub wait_operation_ids: Vec<OperationId>,
2148    pub operations: Vec<OperationLifecycleSnapshot>,
2149}
2150
2151/// Diagnostic comms-drain projection for one session.
2152///
2153/// `phase` and `mode` are read from generated MeerkatMachine authority; the
2154/// handle flag is shell task mechanics only.
2155#[derive(Debug, Clone)]
2156pub struct MeerkatDrainSnapshot {
2157    pub slot_present: bool,
2158    pub phase: Option<CommsDrainPhase>,
2159    pub mode: Option<CommsDrainMode>,
2160    pub handle_present: bool,
2161}
2162
2163/// Schema-aligned projection of the Meerkat formal state derived from the
2164/// live runtime.
2165///
2166/// This stays diagnostic and intentionally stringifies field values so the
2167/// parity harness can compare full field vectors even where the runtime does
2168/// not expose a first-class Rust type for every formal field.
2169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2170pub struct MeerkatFormalStateProjection {
2171    /// Formal fields that have a live runtime-backed projection today.
2172    pub available_fields: BTreeMap<String, String>,
2173    /// Formal fields that still have no canonical runtime source.
2174    pub unavailable_fields: Vec<String>,
2175}
2176
2177/// Diagnostic snapshot of the current Meerkat runtime spine.
2178///
2179/// This is an observational scaffold over the existing runtime-owned Meerkat
2180/// regions. It is intentionally not the final MeerkatMachine reducer.
2181#[derive(Debug, Clone)]
2182pub struct MeerkatMachineSpineSnapshot {
2183    pub binding: MeerkatBindingSnapshot,
2184    pub control: MeerkatControlSnapshot,
2185    pub inputs: MeerkatInputsSnapshot,
2186    pub ledger: MeerkatLedgerSnapshot,
2187    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
2188    pub ops: MeerkatOpsSnapshot,
2189    pub drain: MeerkatDrainSnapshot,
2190    pub formal_state: MeerkatFormalStateProjection,
2191}
2192
2193impl MeerkatMachineSpineSnapshot {
2194    /// Validate TLA+ structural invariants against the current spine snapshot.
2195    ///
2196    /// Returns `Ok(())` if all invariants hold, or `Err(violations)` with a
2197    /// list of human-readable violation descriptions. This is release-mode
2198    /// validation — not `debug_assert`.
2199    pub fn validate_spine_invariants(&self) -> Result<(), Vec<String>> {
2200        let mut violations = Vec::new();
2201
2202        // --- Control/binding invariants ---
2203
2204        // RunningHasActiveRunInvariant: Running => HasActiveRun
2205        if self.control.phase == RuntimeState::Running && self.control.current_run_id.is_none() {
2206            violations
2207                .push("RunningHasActiveRunInvariant: phase is Running but no active run_id".into());
2208        }
2209
2210        // ActiveRunPhaseInvariant: HasActiveRun => phase in {Running, Retired}
2211        if self.control.current_run_id.is_some()
2212            && !matches!(
2213                self.control.phase,
2214                RuntimeState::Running | RuntimeState::Retired
2215            )
2216        {
2217            violations.push(format!(
2218                "ActiveRunPhaseInvariant: active run_id present but phase is {:?}",
2219                self.control.phase
2220            ));
2221        }
2222
2223        // DestroyedShapeInvariant: Destroyed => empty queues, no waiting inputs
2224        if self.control.phase == RuntimeState::Destroyed {
2225            if !self.inputs.queue.is_empty() {
2226                violations.push("DestroyedShapeInvariant: Destroyed but queue is non-empty".into());
2227            }
2228            if !self.inputs.steer_queue.is_empty() {
2229                violations
2230                    .push("DestroyedShapeInvariant: Destroyed but steer_queue is non-empty".into());
2231            }
2232            if self.completion_waiters.input_count > 0 {
2233                violations.push(
2234                    "DestroyedShapeInvariant: Destroyed but completion waiters remain".into(),
2235                );
2236            }
2237        }
2238
2239        // --- Input invariants ---
2240
2241        // QueueSteerDisjointInvariant
2242        let queue_set: std::collections::HashSet<_> = self.inputs.queue.iter().collect();
2243        let steer_set: std::collections::HashSet<_> = self.inputs.steer_queue.iter().collect();
2244        if !queue_set.is_disjoint(&steer_set) {
2245            violations
2246                .push("QueueSteerDisjointInvariant: queue and steer_queue share entries".into());
2247        }
2248
2249        // QueueHandlingInvariant: all queue entries must have handling_mode=Queue, lifecycle=Queued
2250        for qid in &self.inputs.queue {
2251            if let Some(snap) = self
2252                .inputs
2253                .admission_order
2254                .iter()
2255                .find(|a| &a.input_id == qid)
2256            {
2257                if snap.handling_mode != Some(HandlingMode::Queue) {
2258                    violations.push(format!(
2259                        "QueueHandlingInvariant: queue entry {qid} has handling_mode {:?}",
2260                        snap.handling_mode
2261                    ));
2262                }
2263                if snap.lifecycle != Some(InputLifecycleState::Queued) {
2264                    violations.push(format!(
2265                        "QueueHandlingInvariant: queue entry {qid} has lifecycle {:?}",
2266                        snap.lifecycle
2267                    ));
2268                }
2269            }
2270        }
2271
2272        // SteerHandlingInvariant: all steer_queue entries must have handling_mode=Steer, lifecycle=Queued
2273        for sid in &self.inputs.steer_queue {
2274            if let Some(snap) = self
2275                .inputs
2276                .admission_order
2277                .iter()
2278                .find(|a| &a.input_id == sid)
2279            {
2280                if snap.handling_mode != Some(HandlingMode::Steer) {
2281                    violations.push(format!(
2282                        "SteerHandlingInvariant: steer_queue entry {sid} has handling_mode {:?}",
2283                        snap.handling_mode
2284                    ));
2285                }
2286                if snap.lifecycle != Some(InputLifecycleState::Queued) {
2287                    violations.push(format!(
2288                        "SteerHandlingInvariant: steer_queue entry {sid} has lifecycle {:?}",
2289                        snap.lifecycle
2290                    ));
2291                }
2292            }
2293        }
2294
2295        // ContributorLifecycleInvariant: all current_run_contributors must
2296        // still belong to the active run lifecycle slice.
2297        for cid in &self.inputs.current_run_contributors {
2298            if let Some(snap) = self
2299                .inputs
2300                .admission_order
2301                .iter()
2302                .find(|a| &a.input_id == cid)
2303                && !matches!(
2304                    snap.lifecycle,
2305                    Some(
2306                        InputLifecycleState::Staged
2307                            | InputLifecycleState::Applied
2308                            | InputLifecycleState::AppliedPendingConsumption
2309                    )
2310                )
2311            {
2312                violations.push(format!(
2313                    "ContributorLifecycleInvariant: contributor {cid} has lifecycle {:?}",
2314                    snap.lifecycle
2315                ));
2316            }
2317        }
2318
2319        // TerminalInputsNotQueuedInvariant: terminal inputs must not be in queue or steer_queue
2320        for snap in &self.inputs.admission_order {
2321            if snap.terminal_outcome.is_some() {
2322                if queue_set.contains(&snap.input_id) {
2323                    violations.push(format!(
2324                        "TerminalInputsNotQueuedInvariant: terminal input {} in queue",
2325                        snap.input_id
2326                    ));
2327                }
2328                if steer_set.contains(&snap.input_id) {
2329                    violations.push(format!(
2330                        "TerminalInputsNotQueuedInvariant: terminal input {} in steer_queue",
2331                        snap.input_id
2332                    ));
2333                }
2334            }
2335        }
2336
2337        // CurrentRunContributorsInvariant: HasActiveRun => contributors non-empty
2338        if self.control.current_run_id.is_some() && self.inputs.current_run_contributors.is_empty()
2339        {
2340            violations
2341                .push("CurrentRunContributorsInvariant: active run but no contributors".into());
2342        }
2343
2344        // ContributorRunIdentityInvariant: when control owns an active run,
2345        // every current contributor must point at that same run in the
2346        // ingress bookkeeping.
2347        if let Some(control_run_id) = &self.control.current_run_id {
2348            for cid in &self.inputs.current_run_contributors {
2349                if let Some(snap) = self
2350                    .inputs
2351                    .admission_order
2352                    .iter()
2353                    .find(|a| &a.input_id == cid)
2354                    && snap.last_run_id.as_ref() != Some(control_run_id)
2355                {
2356                    violations.push(format!(
2357                        "ContributorRunIdentityInvariant: contributor {cid} has last_run_id {:?}, expected {:?}",
2358                        snap.last_run_id, control_run_id
2359                    ));
2360                }
2361            }
2362        }
2363
2364        // --- Ops invariants ---
2365
2366        // WaitAllAlignmentInvariant
2367        let wait_active = self.ops.wait_request_id.is_some();
2368        if wait_active && self.ops.wait_operation_ids.is_empty() {
2369            violations
2370                .push("WaitAllAlignmentInvariant: wait_active but no wait_operation_ids".into());
2371        }
2372        if !wait_active && !self.ops.wait_operation_ids.is_empty() {
2373            violations.push(
2374                "WaitAllAlignmentInvariant: wait_operation_ids present but no wait_request_id"
2375                    .into(),
2376            );
2377        }
2378
2379        // --- Drain invariants ---
2380
2381        // DrainModeInvariant: drain.phase != Inactive => mode is set and not Disabled
2382        if let Some(phase) = self.drain.phase
2383            && phase != CommsDrainPhase::Inactive
2384            && self.drain.mode.is_none()
2385        {
2386            violations.push("DrainModeInvariant: drain.phase is active but mode is None".into());
2387        }
2388
2389        if violations.is_empty() {
2390            Ok(())
2391        } else {
2392            Err(violations)
2393        }
2394    }
2395}