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        SatisfyWaitAll,
765        StartOp,
766        TerminateOp,
767    ],
768    RunExecutionLifecycle => [
769        AcknowledgeTerminal,
770        CallbackPending,
771        AdvanceAgentCompletionCursor,
772        AdvanceRuntimeInjectedCompletionCursor,
773        AdvanceRuntimeObservedCompletionCursor,
774        BoundaryComplete,
775        BoundaryContinue,
776        ClassifyAssistantOutput,
777        ClassifyCallTimeout,
778        ClassifyTurnTerminalCauseClass,
779        ClassifyTurnTerminality,
780        ClearSessionLlmState,
781        Commit,
782        Fail,
783        HydrateSessionLlmState,
784        LlmReturnedTerminal,
785        LlmReturnedToolCalls,
786        Prepare,
787        PrimitiveApplied,
788        RecordBoundarySeq,
789        ResolveLiveBoundaryContextReceipt,
790        ResolveRuntimeCompletionCleanup,
791        ResolveRuntimeCompletionResult,
792        ResolveRuntimeCompletionWaitFailure,
793        ResolveTurnSurfaceResult,
794        RollbackRun,
795        RunCompleted,
796        RunFailed,
797        RuntimeExecutorExited,
798        TimeBudgetExceeded,
799        ToolCallsResolved,
800        TurnLimitReached,
801    ],
802    CancellationLifecycle => [
803        AbortCancelAfterBoundaryDispatch,
804        CancelNow,
805        CancelRun,
806        CancellationObserved,
807        ForceCancelNoRun,
808        RequestCancelAfterBoundary,
809        RunCancelled,
810    ],
811    LiveTopologyReconfiguration => [
812        AbandonLiveOpenAdmission,
813        CompleteUntilChangedSwitchTurnReconfigure,
814        RecordLiveChannelRequestRejected,
815        RecordLiveChannelStatus,
816        RecordLiveCloseClosed,
817        RecordLiveCommandAccepted,
818        RecordLiveCommandRejected,
819        RecordLiveRefreshQueued,
820        ResolveLiveOpenAdmission,
821    ],
822    InteractionStreamLifecycle => [
823        InteractionStreamAbandoned,
824        InteractionStreamAttached,
825        InteractionStreamClosedEarly,
826        InteractionStreamCompleted,
827        InteractionStreamExpired,
828        InteractionStreamReserved,
829    ],
830    EventStreamLifecycle => [
831        RecordMobEventStreamOpened,
832        RecordMobEventStreamTerminated,
833        RecordSessionEventStreamOpened,
834        RecordSessionEventStreamTerminated,
835        ResolveMobEventStreamClose,
836        ResolveSessionEventStreamClose,
837    ],
838    CommsIngressLifecycle => [
839        AddDirectPeerEndpoint,
840        ApplyMobPeerOverlay,
841        AttachMobIngress,
842        AttachSessionIngress,
843        AuthorizeSupervisorMobPeerOverlay,
844        BindSupervisor,
845        ClearLocalEndpoint,
846        DetachIngress,
847        PeerResponseRejected,
848        PublishLocalEndpoint,
849        RemoveDirectPeerEndpoint,
850        ResolvePeerIngressDequeue,
851        ResolvePeerIngressReceive,
852        ResolveSupervisorAuthorizeAdmission,
853        ResolveSupervisorBindAdmission,
854        ResolveSupervisorBindMaterialAdmission,
855        ResolveSupervisorBridgeCommandAdmission,
856        SpawnDrain,
857        StopDrain,
858    ],
859    SupervisorTrustLifecycle => [
860        AuthorizeSupervisor,
861        PrepareTerminalSupervisorCleanupBindings,
862        RecoverRevokedSupervisorReceipt,
863        RecoverSupervisorBinding,
864        RecoverSupervisorRevocationPending,
865        RecoverSupervisorRotationOperation,
866        RecoverSupervisorRotationTerminalReceipt,
867        RefreshSupervisorBindingRoute,
868        RequestSupervisorTrustPublish,
869        ResolveSupervisorCleanupCommandAdmission,
870        ResumeSupervisorRotation,
871        RevokeSupervisor,
872        SubmitSupervisorRotation,
873        SupervisorRotationNextPublished,
874        SupervisorRotationPreviousRevoked,
875        SupervisorTrustEdgePublishFailed,
876        SupervisorTrustEdgePublished,
877        SupervisorTrustEdgeRevokeFailed,
878        SupervisorTrustEdgeRevoked,
879        ObserveSupervisorRotation,
880    ],
881    MobOperatorAuthorityLifecycle => [
882        GrantMobOperatorManageMob,
883        ResolveMobOperatorCreateAuthority,
884        RestoreMobOperatorAuthority,
885        SetMobOperatorCreateAuthority,
886        SetMobOperatorProfileMutation,
887        SetMobOperatorSpawnProfilesInMob,
888    ],
889    PeerRequestLifecycle => [
890        PeerRequestReceived,
891        PeerRequestSendFailed,
892        PeerRequestSent,
893        PeerRequestTimedOut,
894        PeerResponseProgressArrived,
895        PeerResponseReplied,
896        PeerResponseTerminalArrived,
897    ],
898    VisibilityAuthorityLifecycle => [
899        CommitDeferredNames,
900        CommitVisibilityFilter,
901        ClearTurnToolOverlay,
902        ReplaceDeferredToolAuthorityCatalog,
903        ReplaceFilterToolAuthorityCatalog,
904        SetTurnToolOverlay,
905        StageDeferredNames,
906        StageVisibilityFilter,
907        SurfaceSetRemovalTimeout,
908        ReplaceVisibilityState,
909    ],
910    DeferredSessionLifecycle => [
911        AbandonDeferredSessionPromotion,
912        AuthorizeDeferredSessionMachineArchivedResume,
913        AuthorizeDeferredSessionSystemContextAppend,
914        BeginDeferredSessionArchive,
915        BeginDeferredSessionPromotion,
916        DropDeferredSession,
917        FinishDeferredSessionArchive,
918        FinishDeferredSessionPromotion,
919        RestoreDeferredSessionArchive,
920        StageDeferredSession,
921        UpdateDeferredSessionKeepAlive,
922        UpdateDeferredSessionLlmIdentity,
923    ],
924    ExtractionLifecycle => [
925        EnterExtraction,
926        ExtractionFailed,
927        ExtractionStart,
928        ExtractionValidationFailed,
929        ExtractionValidationPassed,
930    ],
931    McpServerLifecycle => [
932        McpServerConnectPending,
933        McpServerConnected,
934        McpServerDisconnected,
935        McpServerFailed,
936        McpServerReload,
937    ],
938    ModelRoutingLifecycle => [
939        CommitStickyModelFallback,
940        ModelRoutingStatus,
941        RequestFiniteSwitchTurn,
942        RequestUntilChangedSwitchTurn,
943        SetModelRoutingBaseline,
944    ],
945    ExternalSurfaceLifecycle => [
946        AdmitSurfaceRequest,
947        CancelSurfaceRequest,
948        ClassifySurfaceRequestTerminal,
949        FinishSurfaceRequestUnpublished,
950        PublishOrCancelSurfaceRequest,
951        PublishSurfaceRequest,
952        RecordLiveWebrtcAnswerAccepted,
953        RecordLiveWebrtcTokenIssued,
954        RecordLiveWebsocketTokenIssued,
955        ResolveLiveWebrtcAnswerAdmission,
956        ResolveLiveWebsocketTokenAdmission,
957        SurfaceApplyBoundary,
958        SurfaceCallFinished,
959        SurfaceCallStarted,
960        SurfaceFinalizeRemovalClean,
961        SurfaceFinalizeRemovalForced,
962        SurfaceMarkPendingFailed,
963        SurfaceMarkPendingSucceeded,
964        SurfaceRegister,
965        SurfaceShutdown,
966        SurfaceSnapshotAligned,
967        SurfaceStageAdd,
968        SurfaceStageReload,
969        SurfaceStageRemove,
970    ],
971    FailureRecoveryLifecycle => [
972        AuthorizeInteractionTerminalOutboxAdoption,
973        ClassifyLlmFailureRecovery,
974        ClassifyRuntimeAuthorityReconciliation,
975        ClassifyRuntimeLifecycleDurability,
976        ClassifyRuntimeLifecycleState,
977        FatalFailure,
978        RecoverableFailure,
979        RecoverRuntimeCompletionResultCorrelation,
980        ResolveVisibleRuntimePhase,
981    ],
982    UserInterruptDispatch => [
983        InterruptCurrentRun,
984        ResolveUserInterruptPublicResult,
985    ],
986    SessionUnregisterDrainLifecycle => [
987        BeginUnregisterUnservedAttachment,
988        BeginUnregisterSession,
989        CommsDrainExitedForUnregister,
990        CompletionWaitersResolvedForUnregister,
991        RuntimeLoopStoppedForUnregister,
992    ],
993);
994
995macro_rules! meerkat_machine_fieldless_runtime_internal_inputs {
996    ($($authority:ident => [$($variant:ident),+ $(,)?]),+ $(,)?) => {
997        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
998        pub enum MeerkatMachineFieldlessRuntimeInternalInput {
999            $($($variant),+),+
1000        }
1001
1002        impl MeerkatMachineFieldlessRuntimeInternalInput {
1003            pub const ALL: &'static [Self] = &[
1004                $($(Self::$variant),+),+
1005            ];
1006
1007            #[must_use]
1008            pub const fn runtime_internal_input(self) -> MeerkatMachineRuntimeInternalInput {
1009                match self {
1010                    $($(Self::$variant => MeerkatMachineRuntimeInternalInput::$variant,)+)+
1011                }
1012            }
1013
1014            #[must_use]
1015            pub const fn input_variant(self) -> MeerkatMachineInputVariant {
1016                self.runtime_internal_input().input_variant()
1017            }
1018
1019            #[must_use]
1020            pub const fn authority(self) -> MeerkatMachineFieldlessRuntimeInternalAuthority {
1021                match self {
1022                    $(
1023                        $(Self::$variant)|+ => MeerkatMachineFieldlessRuntimeInternalAuthority::$authority,
1024                    )+
1025                }
1026            }
1027
1028            #[must_use]
1029            pub const fn requires_typed_runtime_internal_stager(self) -> bool {
1030                matches!(
1031                    self.authority(),
1032                    MeerkatMachineFieldlessRuntimeInternalAuthority::UserInterruptDispatch
1033                )
1034            }
1035
1036            pub(crate) const fn dsl_input_variant(self) -> dsl::MeerkatMachineInputVariant {
1037                match self {
1038                    $($(Self::$variant => dsl::MeerkatMachineInputVariant::$variant,)+)+
1039                }
1040            }
1041
1042            pub(crate) fn dsl_input(self) -> dsl::MeerkatMachineInput {
1043                match self {
1044                    $($(Self::$variant => dsl::MeerkatMachineInput::$variant,)+)+
1045                }
1046            }
1047
1048            pub(crate) fn from_dsl_input_variant(
1049                variant: dsl::MeerkatMachineInputVariant,
1050            ) -> Option<Self> {
1051                Self::ALL
1052                    .iter()
1053                    .copied()
1054                    .find(|input| input.dsl_input_variant() == variant)
1055            }
1056
1057            pub(crate) fn reject_raw_dsl_input(
1058                input: &dsl::MeerkatMachineInput,
1059            ) -> Result<(), String> {
1060                if let Some(fieldless) = Self::from_dsl_input_variant(input.variant())
1061                    && fieldless.requires_typed_runtime_internal_stager()
1062                {
1063                    let variant = fieldless.input_variant();
1064                    return Err(format!(
1065                        "fieldless runtime-internal input {variant:?} must use typed runtime-internal staging authority"
1066                    ));
1067                }
1068                Ok(())
1069            }
1070        }
1071    };
1072}
1073
1074meerkat_machine_fieldless_runtime_internal_inputs!(
1075    RuntimeOwner => [
1076        RuntimeExecutorExited,
1077        ForceCancelNoRun,
1078        CancelWaitAll,
1079        StopDrain,
1080        SurfaceShutdown,
1081        DetachIngress,
1082        ClearLocalEndpoint,
1083    ],
1084    UserInterruptDispatch => [
1085        InterruptCurrentRun,
1086    ],
1087);
1088
1089#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1090pub enum MeerkatMachineFieldlessRuntimeInternalAuthority {
1091    RuntimeOwner,
1092    UserInterruptDispatch,
1093}
1094
1095#[doc(hidden)]
1096#[must_use]
1097pub fn canonical_meerkat_machine_runtime_internal_classifications()
1098-> Vec<MeerkatMachineRuntimeInternalClassificationRecord> {
1099    MeerkatMachineRuntimeInternalInput::CLASSIFICATIONS.to_vec()
1100}
1101
1102/// Closed typed manifest of supervisor bridge wire command kinds
1103/// (`meerkat_contracts::wire::supervisor_bridge::BridgeCommand` variants).
1104///
1105/// The wire enum is `#[non_exhaustive]`, so no match over it outside
1106/// `meerkat-contracts` can be compiler-checked for completeness. This manifest
1107/// is the runtime's completeness authority instead:
1108/// `supervisor_bridge_command_manifest_matches_wire_enum` pins it against the
1109/// wire enum's variant list and
1110/// `supervisor_bridge_command_realization_matches_drain_dispatch` pins each
1111/// kind's realization posture against the `comms_drain` bridge dispatch match,
1112/// so a new wire command or dispatch arm cannot land without a typed
1113/// admission classification here.
1114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1115pub enum SupervisorBridgeCommandKind {
1116    BindMember,
1117    AuthorizeSupervisor,
1118    ObserveSupervisorRotation,
1119    RevokeSupervisor,
1120    DeliverMemberInput,
1121    ObserveMember,
1122    InterruptMember,
1123    HardCancelMember,
1124    CancelTrackedMemberInput,
1125    RetireMember,
1126    DestroyMember,
1127    WireMember,
1128    UnwireMember,
1129    DeclareMemberOutboundTaint,
1130    // Multi-host (protocol V4) member-addressed family (plan §6.4/§16.4):
1131    // admission-classified ahead of realization; the comms drain stays
1132    // fail-closed until the execution arms land.
1133    ReadMemberHistory,
1134    PollMemberEvents,
1135    OpenMemberLiveChannel,
1136    CloseMemberLiveChannel,
1137    MemberLiveChannelStatus,
1138    ControlMemberLiveChannel,
1139    // Multi-host (protocol V4) host-addressed + member-originated families:
1140    // admitted by `MobHostBindingAuthority` on the host daemon / `MobMachine`
1141    // on the controlling host, never by MeerkatMachine on a member runtime.
1142    BindHost,
1143    RebindHost,
1144    RevokeHost,
1145    MaterializeMember,
1146    ReleaseMember,
1147    InstallPeerTrust,
1148    RemovePeerTrust,
1149    HostStatus,
1150    MemberOperatorRequest,
1151}
1152
1153impl SupervisorBridgeCommandKind {
1154    pub const ALL: &'static [Self] = &[
1155        Self::BindMember,
1156        Self::AuthorizeSupervisor,
1157        Self::ObserveSupervisorRotation,
1158        Self::RevokeSupervisor,
1159        Self::DeliverMemberInput,
1160        Self::ObserveMember,
1161        Self::InterruptMember,
1162        Self::HardCancelMember,
1163        Self::CancelTrackedMemberInput,
1164        Self::RetireMember,
1165        Self::DestroyMember,
1166        Self::WireMember,
1167        Self::UnwireMember,
1168        Self::DeclareMemberOutboundTaint,
1169        Self::ReadMemberHistory,
1170        Self::PollMemberEvents,
1171        Self::OpenMemberLiveChannel,
1172        Self::CloseMemberLiveChannel,
1173        Self::MemberLiveChannelStatus,
1174        Self::ControlMemberLiveChannel,
1175        Self::BindHost,
1176        Self::RebindHost,
1177        Self::RevokeHost,
1178        Self::MaterializeMember,
1179        Self::ReleaseMember,
1180        Self::InstallPeerTrust,
1181        Self::RemovePeerTrust,
1182        Self::HostStatus,
1183        Self::MemberOperatorRequest,
1184    ];
1185
1186    /// Wire enum variant identifier this kind mirrors.
1187    #[must_use]
1188    pub const fn variant_name(self) -> &'static str {
1189        match self {
1190            Self::BindMember => "BindMember",
1191            Self::AuthorizeSupervisor => "AuthorizeSupervisor",
1192            Self::ObserveSupervisorRotation => "ObserveSupervisorRotation",
1193            Self::RevokeSupervisor => "RevokeSupervisor",
1194            Self::DeliverMemberInput => "DeliverMemberInput",
1195            Self::ObserveMember => "ObserveMember",
1196            Self::InterruptMember => "InterruptMember",
1197            Self::HardCancelMember => "HardCancelMember",
1198            Self::CancelTrackedMemberInput => "CancelTrackedMemberInput",
1199            Self::RetireMember => "RetireMember",
1200            Self::DestroyMember => "DestroyMember",
1201            Self::WireMember => "WireMember",
1202            Self::UnwireMember => "UnwireMember",
1203            Self::DeclareMemberOutboundTaint => "DeclareMemberOutboundTaint",
1204            Self::ReadMemberHistory => "ReadMemberHistory",
1205            Self::PollMemberEvents => "PollMemberEvents",
1206            Self::OpenMemberLiveChannel => "OpenMemberLiveChannel",
1207            Self::CloseMemberLiveChannel => "CloseMemberLiveChannel",
1208            Self::MemberLiveChannelStatus => "MemberLiveChannelStatus",
1209            Self::ControlMemberLiveChannel => "ControlMemberLiveChannel",
1210            Self::BindHost => "BindHost",
1211            Self::RebindHost => "RebindHost",
1212            Self::RevokeHost => "RevokeHost",
1213            Self::MaterializeMember => "MaterializeMember",
1214            Self::ReleaseMember => "ReleaseMember",
1215            Self::InstallPeerTrust => "InstallPeerTrust",
1216            Self::RemovePeerTrust => "RemovePeerTrust",
1217            Self::HostStatus => "HostStatus",
1218            Self::MemberOperatorRequest => "MemberOperatorRequest",
1219        }
1220    }
1221
1222    /// MeerkatMachine admission route for this command on a member runtime.
1223    #[must_use]
1224    pub const fn admission_route(self) -> SupervisorBridgeCommandAdmissionRoute {
1225        match self {
1226            Self::BindMember => SupervisorBridgeCommandAdmissionRoute::SupervisorBind,
1227            Self::AuthorizeSupervisor => SupervisorBridgeCommandAdmissionRoute::SupervisorAuthorize,
1228            Self::ObserveSupervisorRotation => {
1229                SupervisorBridgeCommandAdmissionRoute::SupervisorRotationObservation
1230            }
1231            Self::RevokeSupervisor
1232            | Self::DeliverMemberInput
1233            | Self::ObserveMember
1234            | Self::InterruptMember
1235            | Self::HardCancelMember
1236            | Self::CancelTrackedMemberInput
1237            | Self::RetireMember
1238            | Self::DestroyMember
1239            | Self::WireMember
1240            | Self::UnwireMember
1241            | Self::DeclareMemberOutboundTaint
1242            | Self::ReadMemberHistory
1243            | Self::PollMemberEvents
1244            | Self::OpenMemberLiveChannel
1245            | Self::CloseMemberLiveChannel
1246            | Self::MemberLiveChannelStatus
1247            | Self::ControlMemberLiveChannel => {
1248                SupervisorBridgeCommandAdmissionRoute::SupervisorBridgeCommand
1249            }
1250            Self::BindHost
1251            | Self::RebindHost
1252            | Self::RevokeHost
1253            | Self::MaterializeMember
1254            | Self::ReleaseMember
1255            | Self::InstallPeerTrust
1256            | Self::RemovePeerTrust
1257            | Self::HostStatus
1258            | Self::MemberOperatorRequest => {
1259                SupervisorBridgeCommandAdmissionRoute::NotMemberAddressed
1260            }
1261        }
1262    }
1263
1264    /// Current realization posture: member-runtime comms drain arm,
1265    /// off-drain responder, or fail-closed unsupported.
1266    #[must_use]
1267    pub const fn realization(self) -> SupervisorBridgeCommandRealization {
1268        match self {
1269            Self::BindMember
1270            | Self::AuthorizeSupervisor
1271            | Self::ObserveSupervisorRotation
1272            | Self::RevokeSupervisor
1273            | Self::DeliverMemberInput
1274            | Self::ObserveMember
1275            | Self::InterruptMember
1276            | Self::RetireMember
1277            | Self::DestroyMember
1278            | Self::WireMember
1279            | Self::UnwireMember
1280            | Self::DeclareMemberOutboundTaint
1281            // Phase 6 (§7.4): member-input cancellation + the observation
1282            // pair gained member-drain arms (DEC-P6E-1/3/6/7).
1283            | Self::HardCancelMember
1284            | Self::CancelTrackedMemberInput
1285            | Self::ReadMemberHistory
1286            | Self::PollMemberEvents
1287            // Phase 6b (§16): the live-channel family gained member-drain
1288            // arms served through the MemberLiveHost slot (ADJ-P6B-1); an
1289            // absent slot rejects typed LiveTransportUnavailable at the arm.
1290            | Self::OpenMemberLiveChannel
1291            | Self::CloseMemberLiveChannel
1292            | Self::MemberLiveChannelStatus
1293            | Self::ControlMemberLiveChannel => SupervisorBridgeCommandRealization::Realized,
1294            // Host-addressed bind/rebind (phase 2), materialize/release/
1295            // status (phase 3), and the peer-trust pair (phase 4) are served
1296            // by the mob host daemon's serving loop, never a member drain
1297            // arm (ADJ-13; admission is MobHostBindingAuthority-owned — the
1298            // trust mutation itself applies through the member session's
1299            // machine-gated direct-peer-endpoint seam).
1300            Self::BindHost
1301            | Self::RebindHost
1302            | Self::RevokeHost
1303            | Self::MaterializeMember
1304            | Self::ReleaseMember
1305            | Self::InstallPeerTrust
1306            | Self::RemovePeerTrust
1307            | Self::HostStatus => SupervisorBridgeCommandRealization::RealizedOffDrain {
1308                by: OffDrainResponder::HostDaemon,
1309            },
1310            // Member-originated operator requests are served by the
1311            // controlling host's supervisor-inbox responder (ADJ-13;
1312            // admission is MobMachine's ResolveMemberOperatorAdmission).
1313            Self::MemberOperatorRequest => SupervisorBridgeCommandRealization::RealizedOffDrain {
1314                by: OffDrainResponder::ControllingSupervisorInbox,
1315            },
1316        }
1317    }
1318}
1319
1320/// MeerkatMachine admission route for a supervisor bridge command arriving at
1321/// a member runtime.
1322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1323pub enum SupervisorBridgeCommandAdmissionRoute {
1324    /// Bind bootstrap ladder (`ResolveSupervisorBindAdmission` +
1325    /// `ResolveSupervisorBindMaterialAdmission`): establishes initial
1326    /// supervisor authority, so it cannot require a bound supervisor.
1327    SupervisorBind,
1328    /// Supervisor rotation ladder (`ResolveSupervisorAuthorizeAdmission`).
1329    SupervisorAuthorize,
1330    /// Typed observation of the machine-owned supervisor rotation operation.
1331    SupervisorRotationObservation,
1332    /// Bound-supervisor command ladder
1333    /// (`ResolveSupervisorBridgeCommandAdmission`).
1334    ///
1335    /// v1 posture (multi-host plan §6.4, deliberate and recorded): the ladder
1336    /// guards on bound supervisor identity + epoch + sender only. There is no
1337    /// member-side fence or topology-epoch validation on delivery — fences
1338    /// are validated supervisor-side on submit (`StaleFenceToken`).
1339    SupervisorBridgeCommand,
1340    /// Never admitted by MeerkatMachine: host-addressed commands are admitted
1341    /// by `MobHostBindingAuthority` on the host daemon; the member-originated
1342    /// operator request is admitted by `MobMachine` on the controlling host.
1343    NotMemberAddressed,
1344}
1345
1346impl SupervisorBridgeCommandAdmissionRoute {
1347    /// Generated MeerkatMachine admission inputs staged for this route.
1348    #[must_use]
1349    pub const fn admission_inputs(self) -> &'static [MeerkatMachineRuntimeInternalInput] {
1350        match self {
1351            Self::SupervisorBind => &[
1352                MeerkatMachineRuntimeInternalInput::ResolveSupervisorBindAdmission,
1353                MeerkatMachineRuntimeInternalInput::ResolveSupervisorBindMaterialAdmission,
1354            ],
1355            Self::SupervisorAuthorize => {
1356                &[MeerkatMachineRuntimeInternalInput::ResolveSupervisorAuthorizeAdmission]
1357            }
1358            Self::SupervisorRotationObservation => {
1359                &[MeerkatMachineRuntimeInternalInput::ObserveSupervisorRotation]
1360            }
1361            Self::SupervisorBridgeCommand => {
1362                &[MeerkatMachineRuntimeInternalInput::ResolveSupervisorBridgeCommandAdmission]
1363            }
1364            Self::NotMemberAddressed => &[],
1365        }
1366    }
1367}
1368
1369/// Realization posture of a supervisor bridge command in the member runtime's
1370/// comms drain (the `comms_drain.rs` bridge dispatch match) or an off-drain
1371/// responder.
1372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1373pub enum SupervisorBridgeCommandRealization {
1374    /// A dispatch arm stages admission and realizes the command today.
1375    Realized,
1376    /// Served OFF the member runtime's comms drain by a dedicated responder
1377    /// (ADJ-13): the drain keeps ZERO arms for these kinds — the drain-scan
1378    /// gate asserts `Realized` ⇔ drain arm, so an off-drain-served command
1379    /// is never recorded as a false `Realized` and never left as an
1380    /// under-describing `FailClosedUnsupported`.
1381    RealizedOffDrain { by: OffDrainResponder },
1382    /// No dispatch arm anywhere: the fail-closed `_ =>` arm replies
1383    /// `BridgeRejectionCause::Unsupported` (the `HardCancelMember` precedent).
1384    /// Admission classification is declared ahead of realization; when the
1385    /// execution arm lands it must flip this record to `Realized` (drain arm)
1386    /// or `RealizedOffDrain` (dedicated responder) in the same change-set
1387    /// (`supervisor_bridge_command_realization_matches_drain_dispatch`
1388    /// enforces the flip).
1389    FailClosedUnsupported,
1390}
1391
1392/// Which off-drain responder realizes a `RealizedOffDrain` command (ADJ-13).
1393#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1394pub enum OffDrainResponder {
1395    /// The mob host daemon's `MobHostActor` serving loop — host-addressed
1396    /// commands admitted by `MobHostBindingAuthority`.
1397    HostDaemon,
1398    /// The controlling host's supervisor-inbox responder — the
1399    /// member-originated operator family admitted by `MobMachine`
1400    /// (`ResolveMemberOperatorAdmission`).
1401    ControllingSupervisorInbox,
1402}
1403
1404/// Per-wire-command admission classification record.
1405#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1406pub struct SupervisorBridgeCommandClassificationRecord {
1407    pub kind: SupervisorBridgeCommandKind,
1408    pub route: SupervisorBridgeCommandAdmissionRoute,
1409    pub realization: SupervisorBridgeCommandRealization,
1410}
1411
1412#[doc(hidden)]
1413#[must_use]
1414pub fn canonical_supervisor_bridge_command_classifications()
1415-> Vec<SupervisorBridgeCommandClassificationRecord> {
1416    SupervisorBridgeCommandKind::ALL
1417        .iter()
1418        .copied()
1419        .map(|kind| SupervisorBridgeCommandClassificationRecord {
1420            kind,
1421            route: kind.admission_route(),
1422            realization: kind.realization(),
1423        })
1424        .collect()
1425}
1426
1427#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1428pub enum MeerkatMachineCommandClassification {
1429    CatalogInput(MeerkatMachineCatalogInput),
1430    CatalogInputs(&'static [MeerkatMachineCatalogInput]),
1431    ShellMechanic(MeerkatMachineShellMechanicReason),
1432}
1433
1434impl MeerkatMachineCommandClassification {
1435    #[must_use]
1436    pub fn catalog_inputs(self) -> Vec<MeerkatMachineCatalogInput> {
1437        match self {
1438            Self::CatalogInput(input) => vec![input],
1439            Self::CatalogInputs(inputs) => inputs.to_vec(),
1440            Self::ShellMechanic(_) => Vec::new(),
1441        }
1442    }
1443
1444    #[must_use]
1445    pub fn catalog_input_variants(self) -> Vec<MeerkatMachineInputVariant> {
1446        self.catalog_inputs()
1447            .into_iter()
1448            .map(MeerkatMachineCatalogInput::input_variant)
1449            .collect()
1450    }
1451}
1452
1453#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1454pub enum MeerkatMachineCatalogInput {
1455    RegisterSession,
1456    UnregisterSession,
1457    EnsureSessionWithExecutor,
1458    SetSilentIntents,
1459    CancelAfterBoundary,
1460    StopRuntimeExecutor,
1461    ServiceTurnCommitted,
1462    ContainsSession,
1463    SessionHasExecutor,
1464    SessionHasComms,
1465    OpsLifecycleRegistry,
1466    PrepareBindings,
1467    InputState,
1468    ListActiveInputs,
1469    ReconfigureSessionLlmIdentity,
1470    StagePersistentFilter,
1471    RequestDeferredTools,
1472    PublishCommittedVisibleSet,
1473    SetPeerIngressContext,
1474    NotifyDrainExited,
1475    AbortAll,
1476    Abort,
1477    Wait,
1478    Ingest,
1479    PublishEvent,
1480    Retire,
1481    Recycle,
1482    Reset,
1483    Recover,
1484    Destroy,
1485    RuntimeState,
1486    ModelRoutingStatus,
1487    SetModelRoutingBaseline,
1488    RequestFiniteSwitchTurn,
1489    RequestUntilChangedSwitchTurn,
1490    AdmitModelRoutingAssistantTurn,
1491    BeginImageOperation,
1492    DenyImageOperationPlan,
1493    ActivateImageOperationOverride,
1494    ClassifyImageOperationTerminal,
1495    CompleteImageOperation,
1496    RestoreImageOperationOverride,
1497    LoadBoundaryReceipt,
1498    AcceptWithCompletion,
1499    AcceptWithoutWake,
1500}
1501
1502impl MeerkatMachineCatalogInput {
1503    pub const ALL: &'static [Self] = &[
1504        Self::RegisterSession,
1505        Self::UnregisterSession,
1506        Self::EnsureSessionWithExecutor,
1507        Self::SetSilentIntents,
1508        Self::CancelAfterBoundary,
1509        Self::StopRuntimeExecutor,
1510        Self::ServiceTurnCommitted,
1511        Self::ContainsSession,
1512        Self::SessionHasExecutor,
1513        Self::SessionHasComms,
1514        Self::OpsLifecycleRegistry,
1515        Self::PrepareBindings,
1516        Self::InputState,
1517        Self::ListActiveInputs,
1518        Self::ReconfigureSessionLlmIdentity,
1519        Self::StagePersistentFilter,
1520        Self::RequestDeferredTools,
1521        Self::PublishCommittedVisibleSet,
1522        Self::SetPeerIngressContext,
1523        Self::NotifyDrainExited,
1524        Self::AbortAll,
1525        Self::Abort,
1526        Self::Wait,
1527        Self::Ingest,
1528        Self::PublishEvent,
1529        Self::Retire,
1530        Self::Recycle,
1531        Self::Reset,
1532        Self::Recover,
1533        Self::Destroy,
1534        Self::RuntimeState,
1535        Self::ModelRoutingStatus,
1536        Self::SetModelRoutingBaseline,
1537        Self::RequestFiniteSwitchTurn,
1538        Self::RequestUntilChangedSwitchTurn,
1539        Self::AdmitModelRoutingAssistantTurn,
1540        Self::BeginImageOperation,
1541        Self::DenyImageOperationPlan,
1542        Self::ActivateImageOperationOverride,
1543        Self::ClassifyImageOperationTerminal,
1544        Self::CompleteImageOperation,
1545        Self::RestoreImageOperationOverride,
1546        Self::LoadBoundaryReceipt,
1547        Self::AcceptWithCompletion,
1548        Self::AcceptWithoutWake,
1549    ];
1550
1551    #[must_use]
1552    pub const fn input_variant(self) -> MeerkatMachineInputVariant {
1553        match self {
1554            Self::RegisterSession => MeerkatMachineInputVariant::RegisterSession,
1555            Self::UnregisterSession => MeerkatMachineInputVariant::UnregisterSession,
1556            Self::EnsureSessionWithExecutor => {
1557                MeerkatMachineInputVariant::EnsureSessionWithExecutor
1558            }
1559            Self::SetSilentIntents => MeerkatMachineInputVariant::SetSilentIntents,
1560            Self::CancelAfterBoundary => MeerkatMachineInputVariant::CancelAfterBoundary,
1561            Self::StopRuntimeExecutor => MeerkatMachineInputVariant::StopRuntimeExecutor,
1562            Self::ServiceTurnCommitted => MeerkatMachineInputVariant::ServiceTurnCommitted,
1563            Self::ContainsSession => MeerkatMachineInputVariant::ContainsSession,
1564            Self::SessionHasExecutor => MeerkatMachineInputVariant::SessionHasExecutor,
1565            Self::SessionHasComms => MeerkatMachineInputVariant::SessionHasComms,
1566            Self::OpsLifecycleRegistry => MeerkatMachineInputVariant::OpsLifecycleRegistry,
1567            Self::PrepareBindings => MeerkatMachineInputVariant::PrepareBindings,
1568            Self::InputState => MeerkatMachineInputVariant::InputState,
1569            Self::ListActiveInputs => MeerkatMachineInputVariant::ListActiveInputs,
1570            Self::ReconfigureSessionLlmIdentity => {
1571                MeerkatMachineInputVariant::ReconfigureSessionLlmIdentity
1572            }
1573            Self::StagePersistentFilter => MeerkatMachineInputVariant::StagePersistentFilter,
1574            Self::RequestDeferredTools => MeerkatMachineInputVariant::RequestDeferredTools,
1575            Self::PublishCommittedVisibleSet => {
1576                MeerkatMachineInputVariant::PublishCommittedVisibleSet
1577            }
1578            Self::SetPeerIngressContext => MeerkatMachineInputVariant::SetPeerIngressContext,
1579            Self::NotifyDrainExited => MeerkatMachineInputVariant::NotifyDrainExited,
1580            Self::AbortAll => MeerkatMachineInputVariant::AbortAll,
1581            Self::Abort => MeerkatMachineInputVariant::Abort,
1582            Self::Wait => MeerkatMachineInputVariant::Wait,
1583            Self::Ingest => MeerkatMachineInputVariant::Ingest,
1584            Self::PublishEvent => MeerkatMachineInputVariant::PublishEvent,
1585            Self::Retire => MeerkatMachineInputVariant::Retire,
1586            Self::Recycle => MeerkatMachineInputVariant::Recycle,
1587            Self::Reset => MeerkatMachineInputVariant::Reset,
1588            Self::Recover => MeerkatMachineInputVariant::Recover,
1589            Self::Destroy => MeerkatMachineInputVariant::Destroy,
1590            Self::RuntimeState => MeerkatMachineInputVariant::RuntimeState,
1591            Self::ModelRoutingStatus => MeerkatMachineInputVariant::ModelRoutingStatus,
1592            Self::SetModelRoutingBaseline => MeerkatMachineInputVariant::SetModelRoutingBaseline,
1593            Self::RequestFiniteSwitchTurn => MeerkatMachineInputVariant::RequestFiniteSwitchTurn,
1594            Self::RequestUntilChangedSwitchTurn => {
1595                MeerkatMachineInputVariant::RequestUntilChangedSwitchTurn
1596            }
1597            Self::AdmitModelRoutingAssistantTurn => {
1598                MeerkatMachineInputVariant::AdmitModelRoutingAssistantTurn
1599            }
1600            Self::BeginImageOperation => MeerkatMachineInputVariant::BeginImageOperation,
1601            Self::DenyImageOperationPlan => MeerkatMachineInputVariant::DenyImageOperationPlan,
1602            Self::ActivateImageOperationOverride => {
1603                MeerkatMachineInputVariant::ActivateImageOperationOverride
1604            }
1605            Self::ClassifyImageOperationTerminal => {
1606                MeerkatMachineInputVariant::ClassifyImageOperationTerminal
1607            }
1608            Self::CompleteImageOperation => MeerkatMachineInputVariant::CompleteImageOperation,
1609            Self::RestoreImageOperationOverride => {
1610                MeerkatMachineInputVariant::RestoreImageOperationOverride
1611            }
1612            Self::LoadBoundaryReceipt => MeerkatMachineInputVariant::LoadBoundaryReceipt,
1613            Self::AcceptWithCompletion => MeerkatMachineInputVariant::AcceptWithCompletion,
1614            Self::AcceptWithoutWake => MeerkatMachineInputVariant::AcceptWithoutWake,
1615        }
1616    }
1617
1618    #[must_use]
1619    pub const fn as_str(self) -> &'static str {
1620        match self {
1621            Self::RegisterSession => "RegisterSession",
1622            Self::UnregisterSession => "UnregisterSession",
1623            Self::EnsureSessionWithExecutor => "EnsureSessionWithExecutor",
1624            Self::SetSilentIntents => "SetSilentIntents",
1625            Self::CancelAfterBoundary => "CancelAfterBoundary",
1626            Self::StopRuntimeExecutor => "StopRuntimeExecutor",
1627            Self::ServiceTurnCommitted => "ServiceTurnCommitted",
1628            Self::ContainsSession => "ContainsSession",
1629            Self::SessionHasExecutor => "SessionHasExecutor",
1630            Self::SessionHasComms => "SessionHasComms",
1631            Self::OpsLifecycleRegistry => "OpsLifecycleRegistry",
1632            Self::PrepareBindings => "PrepareBindings",
1633            Self::InputState => "InputState",
1634            Self::ListActiveInputs => "ListActiveInputs",
1635            Self::ReconfigureSessionLlmIdentity => "ReconfigureSessionLlmIdentity",
1636            Self::StagePersistentFilter => "StagePersistentFilter",
1637            Self::RequestDeferredTools => "RequestDeferredTools",
1638            Self::PublishCommittedVisibleSet => "PublishCommittedVisibleSet",
1639            Self::SetPeerIngressContext => "SetPeerIngressContext",
1640            Self::NotifyDrainExited => "NotifyDrainExited",
1641            Self::AbortAll => "AbortAll",
1642            Self::Abort => "Abort",
1643            Self::Wait => "Wait",
1644            Self::Ingest => "Ingest",
1645            Self::PublishEvent => "PublishEvent",
1646            Self::Retire => "Retire",
1647            Self::Recycle => "Recycle",
1648            Self::Reset => "Reset",
1649            Self::Recover => "Recover",
1650            Self::Destroy => "Destroy",
1651            Self::RuntimeState => "RuntimeState",
1652            Self::ModelRoutingStatus => "ModelRoutingStatus",
1653            Self::SetModelRoutingBaseline => "SetModelRoutingBaseline",
1654            Self::RequestFiniteSwitchTurn => "RequestFiniteSwitchTurn",
1655            Self::RequestUntilChangedSwitchTurn => "RequestUntilChangedSwitchTurn",
1656            Self::AdmitModelRoutingAssistantTurn => "AdmitModelRoutingAssistantTurn",
1657            Self::BeginImageOperation => "BeginImageOperation",
1658            Self::DenyImageOperationPlan => "DenyImageOperationPlan",
1659            Self::ActivateImageOperationOverride => "ActivateImageOperationOverride",
1660            Self::ClassifyImageOperationTerminal => "ClassifyImageOperationTerminal",
1661            Self::CompleteImageOperation => "CompleteImageOperation",
1662            Self::RestoreImageOperationOverride => "RestoreImageOperationOverride",
1663            Self::LoadBoundaryReceipt => "LoadBoundaryReceipt",
1664            Self::AcceptWithCompletion => "AcceptWithCompletion",
1665            Self::AcceptWithoutWake => "AcceptWithoutWake",
1666        }
1667    }
1668}
1669
1670impl MeerkatMachineCommandVariant {
1671    #[must_use]
1672    pub const fn catalog_input(self) -> Option<MeerkatMachineCatalogInput> {
1673        match self {
1674            Self::ConfigureModelRoutingBaseline
1675            | Self::RequestSwitchTurn
1676            | Self::ResolvedSessionLlmCapabilities
1677            | Self::SessionModelRoutingStatus
1678            | Self::PrepareLocalSessionBindings => None,
1679            Self::RegisterSession => Some(MeerkatMachineCatalogInput::RegisterSession),
1680            Self::UnregisterSession => Some(MeerkatMachineCatalogInput::UnregisterSession),
1681            Self::EnsureSessionWithExecutor => {
1682                Some(MeerkatMachineCatalogInput::EnsureSessionWithExecutor)
1683            }
1684            Self::SetSilentIntents => Some(MeerkatMachineCatalogInput::SetSilentIntents),
1685            Self::CancelAfterBoundary => Some(MeerkatMachineCatalogInput::CancelAfterBoundary),
1686            Self::StopRuntimeExecutor => Some(MeerkatMachineCatalogInput::StopRuntimeExecutor),
1687            Self::CommitServiceTurnTerminalReceipt => {
1688                Some(MeerkatMachineCatalogInput::ServiceTurnCommitted)
1689            }
1690            Self::ContainsSession => Some(MeerkatMachineCatalogInput::ContainsSession),
1691            Self::SessionHasExecutor => Some(MeerkatMachineCatalogInput::SessionHasExecutor),
1692            Self::SessionHasComms => Some(MeerkatMachineCatalogInput::SessionHasComms),
1693            Self::OpsLifecycleRegistry => Some(MeerkatMachineCatalogInput::OpsLifecycleRegistry),
1694            Self::PrepareBindings => Some(MeerkatMachineCatalogInput::PrepareBindings),
1695            Self::InputState => Some(MeerkatMachineCatalogInput::InputState),
1696            // Same machine-owned read route as `InputState` (the key
1697            // resolves through the generated admission map first).
1698            Self::InputStateByIdempotencyKey => Some(MeerkatMachineCatalogInput::InputState),
1699            // Terminal-status queries are the same machine-owned read route
1700            // as `InputState`: they evaluate the identical DSL-owned seed
1701            // facts (live snapshot or their durable store witnesses).
1702            Self::InteractionTerminalStatus => Some(MeerkatMachineCatalogInput::InputState),
1703            Self::RunTerminalStatus => Some(MeerkatMachineCatalogInput::InputState),
1704            Self::ListActiveInputs => Some(MeerkatMachineCatalogInput::ListActiveInputs),
1705            Self::ReconfigureSessionLlmIdentity => {
1706                Some(MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity)
1707            }
1708            Self::StagePersistentFilter => Some(MeerkatMachineCatalogInput::StagePersistentFilter),
1709            Self::RequestDeferredTools => Some(MeerkatMachineCatalogInput::RequestDeferredTools),
1710            Self::PublishCommittedVisibleSet => {
1711                Some(MeerkatMachineCatalogInput::PublishCommittedVisibleSet)
1712            }
1713            Self::SetPeerIngressContext => Some(MeerkatMachineCatalogInput::SetPeerIngressContext),
1714            Self::NotifyDrainExited => Some(MeerkatMachineCatalogInput::NotifyDrainExited),
1715            Self::AbortAll => Some(MeerkatMachineCatalogInput::AbortAll),
1716            Self::Abort => Some(MeerkatMachineCatalogInput::Abort),
1717            Self::Wait => Some(MeerkatMachineCatalogInput::Wait),
1718            Self::Ingest => Some(MeerkatMachineCatalogInput::Ingest),
1719            Self::PublishEvent => Some(MeerkatMachineCatalogInput::PublishEvent),
1720            Self::Retire => Some(MeerkatMachineCatalogInput::Retire),
1721            Self::Recycle => Some(MeerkatMachineCatalogInput::Recycle),
1722            Self::Reset => Some(MeerkatMachineCatalogInput::Reset),
1723            Self::Recover => Some(MeerkatMachineCatalogInput::Recover),
1724            Self::Destroy => Some(MeerkatMachineCatalogInput::Destroy),
1725            Self::RuntimeState => Some(MeerkatMachineCatalogInput::RuntimeState),
1726            Self::AdmitModelRoutingAssistantTurn => {
1727                Some(MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn)
1728            }
1729            Self::BeginImageOperation => Some(MeerkatMachineCatalogInput::BeginImageOperation),
1730            Self::DenyImageOperationPlan => {
1731                Some(MeerkatMachineCatalogInput::DenyImageOperationPlan)
1732            }
1733            Self::ActivateImageOperationOverride => {
1734                Some(MeerkatMachineCatalogInput::ActivateImageOperationOverride)
1735            }
1736            Self::ClassifyImageOperationTerminal => {
1737                Some(MeerkatMachineCatalogInput::ClassifyImageOperationTerminal)
1738            }
1739            Self::CompleteImageOperation => {
1740                Some(MeerkatMachineCatalogInput::CompleteImageOperation)
1741            }
1742            Self::RestoreImageOperationOverride => {
1743                Some(MeerkatMachineCatalogInput::RestoreImageOperationOverride)
1744            }
1745            Self::LoadBoundaryReceipt => Some(MeerkatMachineCatalogInput::LoadBoundaryReceipt),
1746            Self::AcceptWithCompletion => Some(MeerkatMachineCatalogInput::AcceptWithCompletion),
1747            Self::AcceptWithoutWake => Some(MeerkatMachineCatalogInput::AcceptWithoutWake),
1748        }
1749    }
1750}
1751
1752#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1753pub enum MeerkatMachineShellMechanicReason {
1754    ModelRoutingShellConfiguration,
1755    TurnControlOverlayRequest,
1756    RealtimeTransportObservation,
1757    SessionModelRoutingObservation,
1758    LocalSessionBindingBootstrap,
1759}
1760
1761#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1762pub struct MeerkatMachineCommandClassificationRecord {
1763    pub command: MeerkatMachineCommandVariant,
1764    pub classification: MeerkatMachineCommandClassification,
1765}
1766
1767#[doc(hidden)]
1768#[must_use]
1769pub fn canonical_meerkat_machine_command_classifications()
1770-> Vec<MeerkatMachineCommandClassificationRecord> {
1771    MeerkatMachineCommand::command_variant_manifest()
1772        .iter()
1773        .copied()
1774        .map(|variant| MeerkatMachineCommandClassificationRecord {
1775            command: variant,
1776            classification: meerkat_machine_command_classification(variant),
1777        })
1778        .collect()
1779}
1780
1781const fn meerkat_machine_command_classification(
1782    variant: MeerkatMachineCommandVariant,
1783) -> MeerkatMachineCommandClassification {
1784    match variant {
1785        MeerkatMachineCommandVariant::ConfigureModelRoutingBaseline => {
1786            MeerkatMachineCommandClassification::CatalogInput(
1787                MeerkatMachineCatalogInput::SetModelRoutingBaseline,
1788            )
1789        }
1790        MeerkatMachineCommandVariant::RequestSwitchTurn => {
1791            MeerkatMachineCommandClassification::CatalogInputs(&[
1792                MeerkatMachineCatalogInput::RequestFiniteSwitchTurn,
1793                MeerkatMachineCatalogInput::RequestUntilChangedSwitchTurn,
1794            ])
1795        }
1796        MeerkatMachineCommandVariant::ResolvedSessionLlmCapabilities => {
1797            MeerkatMachineCommandClassification::ShellMechanic(
1798                MeerkatMachineShellMechanicReason::SessionModelRoutingObservation,
1799            )
1800        }
1801        MeerkatMachineCommandVariant::SessionModelRoutingStatus => {
1802            MeerkatMachineCommandClassification::CatalogInput(
1803                MeerkatMachineCatalogInput::ModelRoutingStatus,
1804            )
1805        }
1806        MeerkatMachineCommandVariant::PrepareLocalSessionBindings => {
1807            MeerkatMachineCommandClassification::ShellMechanic(
1808                MeerkatMachineShellMechanicReason::LocalSessionBindingBootstrap,
1809            )
1810        }
1811        MeerkatMachineCommandVariant::RegisterSession => {
1812            MeerkatMachineCommandClassification::CatalogInput(
1813                MeerkatMachineCatalogInput::RegisterSession,
1814            )
1815        }
1816        MeerkatMachineCommandVariant::UnregisterSession => {
1817            MeerkatMachineCommandClassification::CatalogInput(
1818                MeerkatMachineCatalogInput::UnregisterSession,
1819            )
1820        }
1821        MeerkatMachineCommandVariant::EnsureSessionWithExecutor => {
1822            MeerkatMachineCommandClassification::CatalogInput(
1823                MeerkatMachineCatalogInput::EnsureSessionWithExecutor,
1824            )
1825        }
1826        MeerkatMachineCommandVariant::SetSilentIntents => {
1827            MeerkatMachineCommandClassification::CatalogInput(
1828                MeerkatMachineCatalogInput::SetSilentIntents,
1829            )
1830        }
1831        MeerkatMachineCommandVariant::CancelAfterBoundary => {
1832            MeerkatMachineCommandClassification::CatalogInput(
1833                MeerkatMachineCatalogInput::CancelAfterBoundary,
1834            )
1835        }
1836        MeerkatMachineCommandVariant::StopRuntimeExecutor => {
1837            MeerkatMachineCommandClassification::CatalogInput(
1838                MeerkatMachineCatalogInput::StopRuntimeExecutor,
1839            )
1840        }
1841        MeerkatMachineCommandVariant::CommitServiceTurnTerminalReceipt => {
1842            MeerkatMachineCommandClassification::CatalogInput(
1843                MeerkatMachineCatalogInput::ServiceTurnCommitted,
1844            )
1845        }
1846        MeerkatMachineCommandVariant::ContainsSession => {
1847            MeerkatMachineCommandClassification::CatalogInput(
1848                MeerkatMachineCatalogInput::ContainsSession,
1849            )
1850        }
1851        MeerkatMachineCommandVariant::SessionHasExecutor => {
1852            MeerkatMachineCommandClassification::CatalogInput(
1853                MeerkatMachineCatalogInput::SessionHasExecutor,
1854            )
1855        }
1856        MeerkatMachineCommandVariant::SessionHasComms => {
1857            MeerkatMachineCommandClassification::CatalogInput(
1858                MeerkatMachineCatalogInput::SessionHasComms,
1859            )
1860        }
1861        MeerkatMachineCommandVariant::OpsLifecycleRegistry => {
1862            MeerkatMachineCommandClassification::CatalogInput(
1863                MeerkatMachineCatalogInput::OpsLifecycleRegistry,
1864            )
1865        }
1866        MeerkatMachineCommandVariant::PrepareBindings => {
1867            MeerkatMachineCommandClassification::CatalogInput(
1868                MeerkatMachineCatalogInput::PrepareBindings,
1869            )
1870        }
1871        MeerkatMachineCommandVariant::InputState => {
1872            MeerkatMachineCommandClassification::CatalogInput(
1873                MeerkatMachineCatalogInput::InputState,
1874            )
1875        }
1876        // Same machine-owned read route as `InputState`: the idempotency
1877        // key resolves through the generated admission map, then the stored
1878        // input state is read identically.
1879        MeerkatMachineCommandVariant::InputStateByIdempotencyKey => {
1880            MeerkatMachineCommandClassification::CatalogInput(
1881                MeerkatMachineCatalogInput::InputState,
1882            )
1883        }
1884        // Terminal-status reads evaluate the same DSL-owned seed facts as
1885        // `InputState` (live snapshot for registered sessions, durable store
1886        // witnesses otherwise) — same machine-owned read route.
1887        MeerkatMachineCommandVariant::InteractionTerminalStatus
1888        | MeerkatMachineCommandVariant::RunTerminalStatus => {
1889            MeerkatMachineCommandClassification::CatalogInput(
1890                MeerkatMachineCatalogInput::InputState,
1891            )
1892        }
1893        MeerkatMachineCommandVariant::ListActiveInputs => {
1894            MeerkatMachineCommandClassification::CatalogInput(
1895                MeerkatMachineCatalogInput::ListActiveInputs,
1896            )
1897        }
1898        MeerkatMachineCommandVariant::ReconfigureSessionLlmIdentity => {
1899            MeerkatMachineCommandClassification::CatalogInput(
1900                MeerkatMachineCatalogInput::ReconfigureSessionLlmIdentity,
1901            )
1902        }
1903        MeerkatMachineCommandVariant::StagePersistentFilter => {
1904            MeerkatMachineCommandClassification::CatalogInput(
1905                MeerkatMachineCatalogInput::StagePersistentFilter,
1906            )
1907        }
1908        MeerkatMachineCommandVariant::RequestDeferredTools => {
1909            MeerkatMachineCommandClassification::CatalogInput(
1910                MeerkatMachineCatalogInput::RequestDeferredTools,
1911            )
1912        }
1913        MeerkatMachineCommandVariant::PublishCommittedVisibleSet => {
1914            MeerkatMachineCommandClassification::CatalogInput(
1915                MeerkatMachineCatalogInput::PublishCommittedVisibleSet,
1916            )
1917        }
1918        MeerkatMachineCommandVariant::SetPeerIngressContext => {
1919            MeerkatMachineCommandClassification::CatalogInput(
1920                MeerkatMachineCatalogInput::SetPeerIngressContext,
1921            )
1922        }
1923        MeerkatMachineCommandVariant::NotifyDrainExited => {
1924            MeerkatMachineCommandClassification::CatalogInput(
1925                MeerkatMachineCatalogInput::NotifyDrainExited,
1926            )
1927        }
1928        MeerkatMachineCommandVariant::AbortAll => {
1929            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::AbortAll)
1930        }
1931        MeerkatMachineCommandVariant::Abort => {
1932            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Abort)
1933        }
1934        MeerkatMachineCommandVariant::Wait => {
1935            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Wait)
1936        }
1937        MeerkatMachineCommandVariant::Ingest => {
1938            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Ingest)
1939        }
1940        MeerkatMachineCommandVariant::PublishEvent => {
1941            MeerkatMachineCommandClassification::CatalogInput(
1942                MeerkatMachineCatalogInput::PublishEvent,
1943            )
1944        }
1945        MeerkatMachineCommandVariant::Retire => {
1946            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Retire)
1947        }
1948        MeerkatMachineCommandVariant::Recycle => {
1949            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recycle)
1950        }
1951        MeerkatMachineCommandVariant::Reset => {
1952            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Reset)
1953        }
1954        MeerkatMachineCommandVariant::Recover => {
1955            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Recover)
1956        }
1957        MeerkatMachineCommandVariant::Destroy => {
1958            MeerkatMachineCommandClassification::CatalogInput(MeerkatMachineCatalogInput::Destroy)
1959        }
1960        MeerkatMachineCommandVariant::RuntimeState => {
1961            MeerkatMachineCommandClassification::CatalogInput(
1962                MeerkatMachineCatalogInput::RuntimeState,
1963            )
1964        }
1965        MeerkatMachineCommandVariant::AdmitModelRoutingAssistantTurn => {
1966            MeerkatMachineCommandClassification::CatalogInput(
1967                MeerkatMachineCatalogInput::AdmitModelRoutingAssistantTurn,
1968            )
1969        }
1970        MeerkatMachineCommandVariant::BeginImageOperation => {
1971            MeerkatMachineCommandClassification::CatalogInput(
1972                MeerkatMachineCatalogInput::BeginImageOperation,
1973            )
1974        }
1975        MeerkatMachineCommandVariant::DenyImageOperationPlan => {
1976            MeerkatMachineCommandClassification::CatalogInput(
1977                MeerkatMachineCatalogInput::DenyImageOperationPlan,
1978            )
1979        }
1980        MeerkatMachineCommandVariant::ActivateImageOperationOverride => {
1981            MeerkatMachineCommandClassification::CatalogInput(
1982                MeerkatMachineCatalogInput::ActivateImageOperationOverride,
1983            )
1984        }
1985        MeerkatMachineCommandVariant::ClassifyImageOperationTerminal => {
1986            MeerkatMachineCommandClassification::CatalogInput(
1987                MeerkatMachineCatalogInput::ClassifyImageOperationTerminal,
1988            )
1989        }
1990        MeerkatMachineCommandVariant::CompleteImageOperation => {
1991            MeerkatMachineCommandClassification::CatalogInput(
1992                MeerkatMachineCatalogInput::CompleteImageOperation,
1993            )
1994        }
1995        MeerkatMachineCommandVariant::RestoreImageOperationOverride => {
1996            MeerkatMachineCommandClassification::CatalogInput(
1997                MeerkatMachineCatalogInput::RestoreImageOperationOverride,
1998            )
1999        }
2000        MeerkatMachineCommandVariant::LoadBoundaryReceipt => {
2001            MeerkatMachineCommandClassification::CatalogInput(
2002                MeerkatMachineCatalogInput::LoadBoundaryReceipt,
2003            )
2004        }
2005        MeerkatMachineCommandVariant::AcceptWithCompletion => {
2006            MeerkatMachineCommandClassification::CatalogInput(
2007                MeerkatMachineCatalogInput::AcceptWithCompletion,
2008            )
2009        }
2010        MeerkatMachineCommandVariant::AcceptWithoutWake => {
2011            MeerkatMachineCommandClassification::CatalogInput(
2012                MeerkatMachineCatalogInput::AcceptWithoutWake,
2013            )
2014        }
2015    }
2016}
2017
2018/// Snapshot of completion waiters registered for one input.
2019///
2020/// This is a supporting-carrier view, not canonical semantic truth.
2021#[derive(Debug, Clone, PartialEq, Eq)]
2022pub struct MeerkatCompletionWaiterSnapshot {
2023    pub input_id: InputId,
2024    pub waiter_count: usize,
2025}
2026
2027/// Snapshot of the runtime completion waiter carrier.
2028///
2029/// Completion waiters are supporting carrier state rather than primary
2030/// authority, but they remain useful for diagnostics and recovery inspection.
2031#[derive(Debug, Clone, PartialEq, Eq)]
2032pub struct MeerkatCompletionWaitersSnapshot {
2033    pub input_count: usize,
2034    pub waiter_count: usize,
2035    pub waiting_inputs: Vec<MeerkatCompletionWaiterSnapshot>,
2036}
2037
2038/// Runtime driver flavor for a registered Meerkat session entry.
2039#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2040pub enum MeerkatDriverKind {
2041    Ephemeral,
2042    Persistent,
2043}
2044
2045/// Snapshot of the hidden runtime epoch cursor state.
2046#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2047pub struct MeerkatCursorSnapshot {
2048    pub agent_applied_cursor: u64,
2049    pub runtime_observed_seq: u64,
2050    pub runtime_last_injected_seq: u64,
2051}
2052
2053/// Snapshot of the hidden runtime binding for one Meerkat session.
2054#[derive(Debug, Clone)]
2055pub struct MeerkatBindingSnapshot {
2056    pub session_id: SessionId,
2057    pub runtime_id: LogicalRuntimeId,
2058    pub driver_kind: MeerkatDriverKind,
2059    pub driver_present: bool,
2060    pub completions_present: bool,
2061    pub ops_registry_present: bool,
2062    pub epoch_id: RuntimeEpochId,
2063    pub cursor_state: MeerkatCursorSnapshot,
2064}
2065
2066/// Snapshot of runtime control-plane truth for one session.
2067#[derive(Debug, Clone)]
2068pub struct MeerkatControlSnapshot {
2069    pub phase: RuntimeState,
2070    pub current_run_id: Option<RunId>,
2071    pub pre_run_phase: Option<RuntimeState>,
2072}
2073
2074/// Snapshot of one admitted runtime input.
2075#[derive(Debug, Clone, PartialEq, Eq)]
2076pub struct MeerkatAdmittedInputSnapshot {
2077    pub input_id: InputId,
2078    pub content_shape: Option<ContentShape>,
2079    pub request_id: Option<RequestId>,
2080    pub reservation_key: Option<ReservationKey>,
2081    pub handling_mode: Option<HandlingMode>,
2082    /// #338: machine-owned per-input live-interrupt verdict, carried from the
2083    /// admission `RuntimeInputSemantics`. The live-projection consumer reads
2084    /// this typed fact instead of re-scanning `handling_mode == Steer`.
2085    pub live_interrupt_required: bool,
2086    pub lifecycle: Option<InputLifecycleState>,
2087    pub terminal_outcome: Option<InputTerminalOutcome>,
2088    pub last_run_id: Option<RunId>,
2089    pub last_boundary_sequence: Option<u64>,
2090    pub is_prompt: bool,
2091}
2092
2093/// Snapshot of runtime ingress truth for one session.
2094#[derive(Debug, Clone, PartialEq, Eq)]
2095pub struct MeerkatInputsSnapshot {
2096    pub admission_order: Vec<MeerkatAdmittedInputSnapshot>,
2097    pub queue: Vec<InputId>,
2098    pub steer_queue: Vec<InputId>,
2099    pub current_run_id: Option<RunId>,
2100    pub current_run_contributors: Vec<InputId>,
2101    pub post_admission_signal: String,
2102    pub silent_intent_overrides: Vec<String>,
2103}
2104
2105/// Lightweight lifecycle snapshot used by archive/retire cleanup.
2106///
2107/// This intentionally avoids the heavier diagnostic regions that are not
2108/// needed for archive decisions. Control and input fields still come from the
2109/// generated runtime authority/driver projections.
2110#[derive(Debug, Clone)]
2111pub struct MeerkatArchiveSnapshot {
2112    pub control: MeerkatControlSnapshot,
2113    pub queue: Vec<InputId>,
2114    pub steer_queue: Vec<InputId>,
2115    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
2116}
2117
2118/// Snapshot of the canonical input-ledger carrier for one session.
2119///
2120/// These counts sit below the top-level Meerkat phase machine, but they drive
2121/// the exact values returned by control-plane reports such as
2122/// `DestroyReport.inputs_abandoned`.
2123#[derive(Debug, Clone)]
2124pub struct MeerkatLedgerSnapshot {
2125    pub input_count: usize,
2126    pub non_terminal_count: usize,
2127    pub accepted_count: usize,
2128    pub queued_count: usize,
2129    pub staged_count: usize,
2130    pub applied_count: usize,
2131    pub applied_pending_consumption_count: usize,
2132    pub consumed_count: usize,
2133    pub superseded_count: usize,
2134    pub coalesced_count: usize,
2135    pub abandoned_count: usize,
2136}
2137
2138/// Snapshot of runtime-owned async operation truth for one session.
2139#[derive(Debug, Clone)]
2140pub struct MeerkatOpsSnapshot {
2141    pub operation_count: usize,
2142    pub active_count: usize,
2143    pub wait_request_id: Option<WaitRequestId>,
2144    pub pending_wait_present: bool,
2145    pub pending_wait_request_id: Option<WaitRequestId>,
2146    pub wait_operation_ids: Vec<OperationId>,
2147    pub operations: Vec<OperationLifecycleSnapshot>,
2148}
2149
2150/// Diagnostic comms-drain projection for one session.
2151///
2152/// `phase` and `mode` are read from generated MeerkatMachine authority; the
2153/// handle flag is shell task mechanics only.
2154#[derive(Debug, Clone)]
2155pub struct MeerkatDrainSnapshot {
2156    pub slot_present: bool,
2157    pub phase: Option<CommsDrainPhase>,
2158    pub mode: Option<CommsDrainMode>,
2159    pub handle_present: bool,
2160}
2161
2162/// Schema-aligned projection of the Meerkat formal state derived from the
2163/// live runtime.
2164///
2165/// This stays diagnostic and intentionally stringifies field values so the
2166/// parity harness can compare full field vectors even where the runtime does
2167/// not expose a first-class Rust type for every formal field.
2168#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2169pub struct MeerkatFormalStateProjection {
2170    /// Formal fields that have a live runtime-backed projection today.
2171    pub available_fields: BTreeMap<String, String>,
2172    /// Formal fields that still have no canonical runtime source.
2173    pub unavailable_fields: Vec<String>,
2174}
2175
2176/// Diagnostic snapshot of the current Meerkat runtime spine.
2177///
2178/// This is an observational scaffold over the existing runtime-owned Meerkat
2179/// regions. It is intentionally not the final MeerkatMachine reducer.
2180#[derive(Debug, Clone)]
2181pub struct MeerkatMachineSpineSnapshot {
2182    pub binding: MeerkatBindingSnapshot,
2183    pub control: MeerkatControlSnapshot,
2184    pub inputs: MeerkatInputsSnapshot,
2185    pub ledger: MeerkatLedgerSnapshot,
2186    pub completion_waiters: MeerkatCompletionWaitersSnapshot,
2187    pub ops: MeerkatOpsSnapshot,
2188    pub drain: MeerkatDrainSnapshot,
2189    pub formal_state: MeerkatFormalStateProjection,
2190}
2191
2192impl MeerkatMachineSpineSnapshot {
2193    /// Validate TLA+ structural invariants against the current spine snapshot.
2194    ///
2195    /// Returns `Ok(())` if all invariants hold, or `Err(violations)` with a
2196    /// list of human-readable violation descriptions. This is release-mode
2197    /// validation — not `debug_assert`.
2198    pub fn validate_spine_invariants(&self) -> Result<(), Vec<String>> {
2199        let mut violations = Vec::new();
2200
2201        // --- Control/binding invariants ---
2202
2203        // RunningHasActiveRunInvariant: Running => HasActiveRun
2204        if self.control.phase == RuntimeState::Running && self.control.current_run_id.is_none() {
2205            violations
2206                .push("RunningHasActiveRunInvariant: phase is Running but no active run_id".into());
2207        }
2208
2209        // ActiveRunPhaseInvariant: HasActiveRun => phase in {Running, Retired}
2210        if self.control.current_run_id.is_some()
2211            && !matches!(
2212                self.control.phase,
2213                RuntimeState::Running | RuntimeState::Retired
2214            )
2215        {
2216            violations.push(format!(
2217                "ActiveRunPhaseInvariant: active run_id present but phase is {:?}",
2218                self.control.phase
2219            ));
2220        }
2221
2222        // DestroyedShapeInvariant: Destroyed => empty queues, no waiting inputs
2223        if self.control.phase == RuntimeState::Destroyed {
2224            if !self.inputs.queue.is_empty() {
2225                violations.push("DestroyedShapeInvariant: Destroyed but queue is non-empty".into());
2226            }
2227            if !self.inputs.steer_queue.is_empty() {
2228                violations
2229                    .push("DestroyedShapeInvariant: Destroyed but steer_queue is non-empty".into());
2230            }
2231            if self.completion_waiters.input_count > 0 {
2232                violations.push(
2233                    "DestroyedShapeInvariant: Destroyed but completion waiters remain".into(),
2234                );
2235            }
2236        }
2237
2238        // --- Input invariants ---
2239
2240        // QueueSteerDisjointInvariant
2241        let queue_set: std::collections::HashSet<_> = self.inputs.queue.iter().collect();
2242        let steer_set: std::collections::HashSet<_> = self.inputs.steer_queue.iter().collect();
2243        if !queue_set.is_disjoint(&steer_set) {
2244            violations
2245                .push("QueueSteerDisjointInvariant: queue and steer_queue share entries".into());
2246        }
2247
2248        // QueueHandlingInvariant: all queue entries must have handling_mode=Queue, lifecycle=Queued
2249        for qid in &self.inputs.queue {
2250            if let Some(snap) = self
2251                .inputs
2252                .admission_order
2253                .iter()
2254                .find(|a| &a.input_id == qid)
2255            {
2256                if snap.handling_mode != Some(HandlingMode::Queue) {
2257                    violations.push(format!(
2258                        "QueueHandlingInvariant: queue entry {qid} has handling_mode {:?}",
2259                        snap.handling_mode
2260                    ));
2261                }
2262                if snap.lifecycle != Some(InputLifecycleState::Queued) {
2263                    violations.push(format!(
2264                        "QueueHandlingInvariant: queue entry {qid} has lifecycle {:?}",
2265                        snap.lifecycle
2266                    ));
2267                }
2268            }
2269        }
2270
2271        // SteerHandlingInvariant: all steer_queue entries must have handling_mode=Steer, lifecycle=Queued
2272        for sid in &self.inputs.steer_queue {
2273            if let Some(snap) = self
2274                .inputs
2275                .admission_order
2276                .iter()
2277                .find(|a| &a.input_id == sid)
2278            {
2279                if snap.handling_mode != Some(HandlingMode::Steer) {
2280                    violations.push(format!(
2281                        "SteerHandlingInvariant: steer_queue entry {sid} has handling_mode {:?}",
2282                        snap.handling_mode
2283                    ));
2284                }
2285                if snap.lifecycle != Some(InputLifecycleState::Queued) {
2286                    violations.push(format!(
2287                        "SteerHandlingInvariant: steer_queue entry {sid} has lifecycle {:?}",
2288                        snap.lifecycle
2289                    ));
2290                }
2291            }
2292        }
2293
2294        // ContributorLifecycleInvariant: all current_run_contributors must
2295        // still belong to the active run lifecycle slice.
2296        for cid in &self.inputs.current_run_contributors {
2297            if let Some(snap) = self
2298                .inputs
2299                .admission_order
2300                .iter()
2301                .find(|a| &a.input_id == cid)
2302                && !matches!(
2303                    snap.lifecycle,
2304                    Some(
2305                        InputLifecycleState::Staged
2306                            | InputLifecycleState::Applied
2307                            | InputLifecycleState::AppliedPendingConsumption
2308                    )
2309                )
2310            {
2311                violations.push(format!(
2312                    "ContributorLifecycleInvariant: contributor {cid} has lifecycle {:?}",
2313                    snap.lifecycle
2314                ));
2315            }
2316        }
2317
2318        // TerminalInputsNotQueuedInvariant: terminal inputs must not be in queue or steer_queue
2319        for snap in &self.inputs.admission_order {
2320            if snap.terminal_outcome.is_some() {
2321                if queue_set.contains(&snap.input_id) {
2322                    violations.push(format!(
2323                        "TerminalInputsNotQueuedInvariant: terminal input {} in queue",
2324                        snap.input_id
2325                    ));
2326                }
2327                if steer_set.contains(&snap.input_id) {
2328                    violations.push(format!(
2329                        "TerminalInputsNotQueuedInvariant: terminal input {} in steer_queue",
2330                        snap.input_id
2331                    ));
2332                }
2333            }
2334        }
2335
2336        // CurrentRunContributorsInvariant: HasActiveRun => contributors non-empty
2337        if self.control.current_run_id.is_some() && self.inputs.current_run_contributors.is_empty()
2338        {
2339            violations
2340                .push("CurrentRunContributorsInvariant: active run but no contributors".into());
2341        }
2342
2343        // ContributorRunIdentityInvariant: when control owns an active run,
2344        // every current contributor must point at that same run in the
2345        // ingress bookkeeping.
2346        if let Some(control_run_id) = &self.control.current_run_id {
2347            for cid in &self.inputs.current_run_contributors {
2348                if let Some(snap) = self
2349                    .inputs
2350                    .admission_order
2351                    .iter()
2352                    .find(|a| &a.input_id == cid)
2353                    && snap.last_run_id.as_ref() != Some(control_run_id)
2354                {
2355                    violations.push(format!(
2356                        "ContributorRunIdentityInvariant: contributor {cid} has last_run_id {:?}, expected {:?}",
2357                        snap.last_run_id, control_run_id
2358                    ));
2359                }
2360            }
2361        }
2362
2363        // --- Ops invariants ---
2364
2365        // WaitAllAlignmentInvariant
2366        let wait_active = self.ops.wait_request_id.is_some();
2367        if wait_active && self.ops.wait_operation_ids.is_empty() {
2368            violations
2369                .push("WaitAllAlignmentInvariant: wait_active but no wait_operation_ids".into());
2370        }
2371        if !wait_active && !self.ops.wait_operation_ids.is_empty() {
2372            violations.push(
2373                "WaitAllAlignmentInvariant: wait_operation_ids present but no wait_request_id"
2374                    .into(),
2375            );
2376        }
2377
2378        // --- Drain invariants ---
2379
2380        // DrainModeInvariant: drain.phase != Inactive => mode is set and not Disabled
2381        if let Some(phase) = self.drain.phase
2382            && phase != CommsDrainPhase::Inactive
2383            && self.drain.mode.is_none()
2384        {
2385            violations.push("DrainModeInvariant: drain.phase is active but mode is None".into());
2386        }
2387
2388        if violations.is_empty() {
2389            Ok(())
2390        } else {
2391            Err(violations)
2392        }
2393    }
2394}