Skip to main content

meerkat_runtime/meerkat_machine/
dsl.rs

1//! MeerkatMachine DSL definition with real bridging types.
2use meerkat_machine_dsl::machine;
3use meerkat_machine_schema::catalog::dsl::OptionValueExt;
4
5// ---------------------------------------------------------------------------
6// Bridging types
7// ---------------------------------------------------------------------------
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
10pub struct SessionId(pub String);
11
12impl<T: Into<String>> From<T> for SessionId {
13    fn from(s: T) -> Self {
14        Self(s.into())
15    }
16}
17
18impl SessionId {
19    pub fn from_domain(id: &meerkat_core::types::SessionId) -> Self {
20        Self(id.to_string())
21    }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
25pub struct AgentRuntimeId(pub String);
26
27impl<T: Into<String>> From<T> for AgentRuntimeId {
28    fn from(s: T) -> Self {
29        Self(s.into())
30    }
31}
32
33impl AgentRuntimeId {
34    pub fn from_domain(id: &crate::identifiers::LogicalRuntimeId) -> Self {
35        Self(id.to_string())
36    }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
40pub struct FenceToken(pub u64);
41
42impl From<u64> for FenceToken {
43    fn from(v: u64) -> Self {
44        Self(v)
45    }
46}
47
48impl FenceToken {
49    pub fn from_domain(value: u64) -> Self {
50        Self(value)
51    }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
55pub struct Generation(pub u64);
56
57impl From<u64> for Generation {
58    fn from(v: u64) -> Self {
59        Self(v)
60    }
61}
62
63impl Generation {
64    pub fn from_domain(value: u64) -> Self {
65        Self(value)
66    }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
70pub struct RunId(pub String);
71
72impl<T: Into<String>> From<T> for RunId {
73    fn from(s: T) -> Self {
74        Self(s.into())
75    }
76}
77
78impl RunId {
79    pub fn from_domain(id: &meerkat_core::lifecycle::RunId) -> Self {
80        Self(id.to_string())
81    }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
85pub struct InputId(pub String);
86
87impl<T: Into<String>> From<T> for InputId {
88    fn from(s: T) -> Self {
89        Self(s.into())
90    }
91}
92
93impl InputId {
94    pub fn from_domain(id: &meerkat_core::lifecycle::InputId) -> Self {
95        Self(id.to_string())
96    }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
100pub struct WorkId(pub String);
101
102impl<T: Into<String>> From<T> for WorkId {
103    fn from(s: T) -> Self {
104        Self(s.into())
105    }
106}
107
108impl WorkId {
109    pub fn from_domain(id: &meerkat_core::lifecycle::InputId) -> Self {
110        Self(id.to_string())
111    }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
115pub struct OperationId(pub String);
116
117impl<T: Into<String>> From<T> for OperationId {
118    fn from(s: T) -> Self {
119        Self(s.into())
120    }
121}
122
123impl OperationId {
124    pub fn from_domain(id: &meerkat_core::ops::OperationId) -> Self {
125        Self::from(serde_json::to_string(id).unwrap_or_else(|_| "\"unknown\"".to_string()))
126    }
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
130pub struct WaitRequestId(pub String);
131
132impl<T: Into<String>> From<T> for WaitRequestId {
133    fn from(s: T) -> Self {
134        Self(s.into())
135    }
136}
137
138impl WaitRequestId {
139    pub fn from_domain(id: &meerkat_core::lifecycle::WaitRequestId) -> Self {
140        Self(id.to_string())
141    }
142}
143
144/// Typed async-operation kind. Closed mirror of
145/// [`meerkat_core::ops_lifecycle::OperationKind`] — replaces the former
146/// newtype wrapper around an opaque JSON-encoded string. The DSL writes this
147/// variant directly on `RegisterOp` so guards on `PeerReadyOp`
148/// (`kind_is_mob_member_child`) can reason about the closed set without
149/// string parsing.
150#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
151pub enum OperationKind {
152    #[default]
153    MobMemberChild,
154    BackgroundToolOp,
155}
156
157impl From<meerkat_core::ops_lifecycle::OperationKind> for OperationKind {
158    fn from(kind: meerkat_core::ops_lifecycle::OperationKind) -> Self {
159        match kind {
160            meerkat_core::ops_lifecycle::OperationKind::MobMemberChild => Self::MobMemberChild,
161            meerkat_core::ops_lifecycle::OperationKind::BackgroundToolOp => Self::BackgroundToolOp,
162        }
163    }
164}
165
166impl From<OperationKind> for meerkat_core::ops_lifecycle::OperationKind {
167    fn from(kind: OperationKind) -> Self {
168        match kind {
169            OperationKind::MobMemberChild => Self::MobMemberChild,
170            OperationKind::BackgroundToolOp => Self::BackgroundToolOp,
171        }
172    }
173}
174
175impl OperationKind {
176    pub fn from_domain(kind: &meerkat_core::ops_lifecycle::OperationKind) -> Self {
177        Self::from(*kind)
178    }
179}
180
181/// Typed mirror of [`meerkat_core::Provider`] for use inside DSL bridging
182/// types. Closed 5-variant enum; the seam carries the discriminant directly
183/// rather than a JSON-encoded string.
184#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
185pub enum Provider {
186    #[default]
187    Anthropic,
188    OpenAI,
189    Gemini,
190    SelfHosted,
191    Other,
192}
193
194impl From<meerkat_core::provider::Provider> for Provider {
195    fn from(p: meerkat_core::provider::Provider) -> Self {
196        match p {
197            meerkat_core::provider::Provider::Anthropic => Self::Anthropic,
198            meerkat_core::provider::Provider::OpenAI => Self::OpenAI,
199            meerkat_core::provider::Provider::Gemini => Self::Gemini,
200            meerkat_core::provider::Provider::SelfHosted => Self::SelfHosted,
201            meerkat_core::provider::Provider::Other => Self::Other,
202        }
203    }
204}
205
206impl From<Provider> for meerkat_core::provider::Provider {
207    fn from(p: Provider) -> Self {
208        match p {
209            Provider::Anthropic => Self::Anthropic,
210            Provider::OpenAI => Self::OpenAI,
211            Provider::Gemini => Self::Gemini,
212            Provider::SelfHosted => Self::SelfHosted,
213            Provider::Other => Self::Other,
214        }
215    }
216}
217
218/// Typed mirror of [`meerkat_core::AuthBindingRef`] — structural string
219/// projection carrying the flat forms of `realm` / `binding` / `profile`
220/// with bidirectional `From`.
221///
222/// The DSL layer keeps string fields because this mirror is the
223/// DSL-layer identity carrier (used inside runtime-owned guards /
224/// transitions where slug validation has already happened at the
225/// boundary). Domain-side `AuthBindingRef` carries the typed atoms
226/// (`RealmId` / `BindingId` / `ProfileId`).
227#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
228pub struct AuthBindingRef {
229    pub realm_id: String,
230    pub binding_id: String,
231    pub profile_id: Option<String>,
232}
233
234impl From<&meerkat_core::AuthBindingRef> for AuthBindingRef {
235    fn from(r: &meerkat_core::AuthBindingRef) -> Self {
236        Self {
237            realm_id: r.realm.as_str().to_owned(),
238            binding_id: r.binding.as_str().to_owned(),
239            profile_id: r.profile.as_ref().map(|p| p.as_str().to_owned()),
240        }
241    }
242}
243
244/// Fallible conversion — DSL-layer flat strings may be slug-invalid
245/// (the DSL mirror intentionally accepts opaque strings to survive
246/// deserialization drift across schema versions), so lifting back to
247/// the typed-atom domain form may reject.
248impl TryFrom<AuthBindingRef> for meerkat_core::AuthBindingRef {
249    type Error = meerkat_core::IdentityError;
250
251    fn try_from(r: AuthBindingRef) -> Result<Self, Self::Error> {
252        Ok(Self {
253            realm: meerkat_core::RealmId::parse(&r.realm_id)?,
254            binding: meerkat_core::BindingId::parse(&r.binding_id)?,
255            profile: r
256                .profile_id
257                .as_deref()
258                .map(meerkat_core::ProfileId::parse)
259                .transpose()?,
260        })
261    }
262}
263
264/// Typed mirror of [`meerkat_core::SessionLlmIdentity`] — structural field
265/// projection with typed `Provider` and `AuthBindingRef` mirrors. The
266/// `provider_params` payload is a legitimately open-set `serde_json::Value`
267/// at the persistence boundary (arbitrary provider-specific options), so it
268/// rides on a stable JSON-serialization field inside the DSL — never parsed
269/// back as a discriminant inside any guard or transition.
270#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
271pub struct SessionLlmIdentity {
272    pub model: String,
273    pub provider: Provider,
274    pub self_hosted_server_id: Option<String>,
275    /// Stable JSON serialization of the open-set `provider_params` payload.
276    /// Carried as an opaque identity token; DSL guards never inspect its
277    /// content. Boundary-legitimate per the dogma round-4 brief's
278    /// "variable JSON payload" carve-out applied at field granularity.
279    pub provider_params_repr: Option<String>,
280    pub auth_binding: Option<AuthBindingRef>,
281}
282
283impl SessionLlmIdentity {
284    pub fn from_domain(id: &meerkat_core::SessionLlmIdentity) -> Self {
285        Self {
286            model: id.model.clone(),
287            provider: Provider::from(id.provider),
288            self_hosted_server_id: id.self_hosted_server_id.clone(),
289            provider_params_repr: id
290                .provider_params
291                .as_ref()
292                .map(|v| serde_json::to_string(v).unwrap_or_default()),
293            auth_binding: id.auth_binding.as_ref().map(AuthBindingRef::from),
294        }
295    }
296}
297
298/// Typed mirror of [`meerkat_core::SessionToolVisibilityState`] —
299/// structural projection using typed `ToolFilter` / `ToolVisibilityWitness`
300/// mirrors plus ordered name sets for deterministic Ord/Hash.
301#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
302pub struct SessionToolVisibilityState {
303    pub capability_base_filter: ToolFilter,
304    pub inherited_base_filter: ToolFilter,
305    pub active_filter: ToolFilter,
306    pub staged_filter: ToolFilter,
307    pub active_requested_deferred_names: std::collections::BTreeSet<String>,
308    pub staged_requested_deferred_names: std::collections::BTreeSet<String>,
309    pub active_revision: u64,
310    pub staged_revision: u64,
311    pub requested_witnesses: std::collections::BTreeMap<String, ToolVisibilityWitness>,
312    pub filter_witnesses: std::collections::BTreeMap<String, ToolVisibilityWitness>,
313}
314
315impl SessionToolVisibilityState {
316    pub fn from_domain(id: &meerkat_core::SessionToolVisibilityState) -> Self {
317        Self {
318            capability_base_filter: ToolFilter::from(&id.capability_base_filter),
319            inherited_base_filter: ToolFilter::from(&id.inherited_base_filter),
320            active_filter: ToolFilter::from(&id.active_filter),
321            staged_filter: ToolFilter::from(&id.staged_filter),
322            active_requested_deferred_names: id.active_requested_deferred_names.clone(),
323            staged_requested_deferred_names: id.staged_requested_deferred_names.clone(),
324            active_revision: id.active_revision,
325            staged_revision: id.staged_revision,
326            requested_witnesses: id
327                .requested_witnesses
328                .iter()
329                .map(|(k, w)| (k.clone(), ToolVisibilityWitness::from(w)))
330                .collect(),
331            filter_witnesses: id
332                .filter_witnesses
333                .iter()
334                .map(|(k, w)| (k.clone(), ToolVisibilityWitness::from(w)))
335                .collect(),
336        }
337    }
338}
339
340/// Typed mirror of
341/// [`crate::meerkat_machine_types::SessionLlmCapabilitySurface`] — structural
342/// projection of the boolean capability matrix plus optional call timeout.
343#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
344pub struct SessionLlmCapabilitySurface {
345    pub supports_temperature: bool,
346    pub supports_thinking: bool,
347    pub supports_reasoning: bool,
348    pub inline_video: bool,
349    pub vision: bool,
350    pub image_input: bool,
351    pub image_tool_results: bool,
352    pub supports_web_search: bool,
353    pub image_generation: bool,
354    pub realtime: bool,
355    pub call_timeout_secs: Option<u64>,
356}
357
358impl From<&crate::meerkat_machine_types::SessionLlmCapabilitySurface>
359    for SessionLlmCapabilitySurface
360{
361    fn from(s: &crate::meerkat_machine_types::SessionLlmCapabilitySurface) -> Self {
362        Self {
363            supports_temperature: s.supports_temperature,
364            supports_thinking: s.supports_thinking,
365            supports_reasoning: s.supports_reasoning,
366            inline_video: s.inline_video,
367            vision: s.vision,
368            image_input: s.image_input,
369            image_tool_results: s.image_tool_results,
370            supports_web_search: s.supports_web_search,
371            image_generation: s.image_generation,
372            realtime: s.realtime,
373            call_timeout_secs: s.call_timeout_secs,
374        }
375    }
376}
377
378impl SessionLlmCapabilitySurface {
379    pub fn from_domain(id: &crate::meerkat_machine_types::SessionLlmCapabilitySurface) -> Self {
380        Self::from(id)
381    }
382}
383
384/// Typed capability-surface resolution status. Closed mirror of
385/// [`crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus`] —
386/// replaces the former JSON-stringified wrapper the DSL used to carry the
387/// two-state discriminant across the seam.
388///
389/// The DSL stores the variant directly on `ReconfigureSessionLlmIdentity`
390/// flow state; the shell maps to/from the domain enum via the `From` impls
391/// below — no `serde_json::to_string`, no string compares.
392#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
393pub enum SessionLlmCapabilitySurfaceStatus {
394    Resolved,
395    #[default]
396    Unresolved,
397}
398
399impl From<crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus>
400    for SessionLlmCapabilitySurfaceStatus
401{
402    fn from(status: crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus) -> Self {
403        match status {
404            crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus::Resolved => {
405                Self::Resolved
406            }
407            crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus::Unresolved => {
408                Self::Unresolved
409            }
410        }
411    }
412}
413
414impl From<SessionLlmCapabilitySurfaceStatus>
415    for crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus
416{
417    fn from(status: SessionLlmCapabilitySurfaceStatus) -> Self {
418        match status {
419            SessionLlmCapabilitySurfaceStatus::Resolved => Self::Resolved,
420            SessionLlmCapabilitySurfaceStatus::Unresolved => Self::Unresolved,
421        }
422    }
423}
424
425impl SessionLlmCapabilitySurfaceStatus {
426    pub fn from_domain(
427        id: &crate::meerkat_machine_types::SessionLlmCapabilitySurfaceStatus,
428    ) -> Self {
429        Self::from(*id)
430    }
431}
432
433/// Typed mirror of
434/// [`crate::meerkat_machine_types::SessionToolVisibilityDelta`] — structural
435/// projection using typed `ToolFilter` mirrors plus the two boolean change
436/// flags. Replaces the former `format!("{id:?}")` Debug-stringified wrapper.
437#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
438pub struct SessionToolVisibilityDelta {
439    pub previous_capability_base_filter: ToolFilter,
440    pub current_capability_base_filter: ToolFilter,
441    pub committed_visible_set_changed: bool,
442    pub revision_bumped: bool,
443}
444
445impl SessionToolVisibilityDelta {
446    pub fn from_domain(id: &crate::meerkat_machine_types::SessionToolVisibilityDelta) -> Self {
447        Self {
448            previous_capability_base_filter: ToolFilter::from(&id.previous_capability_base_filter),
449            current_capability_base_filter: ToolFilter::from(&id.current_capability_base_filter),
450            committed_visible_set_changed: id.committed_visible_set_changed,
451            revision_bumped: id.revision_bumped,
452        }
453    }
454}
455
456/// Typed mirror of [`meerkat_core::ToolFilter`] — closed 3-variant
457/// discriminant with a `BTreeSet<String>` name payload for
458/// `Allow`/`Deny` so the value is `Ord + Hash` and deterministic across
459/// iteration, matching the R3 `InputAbandonReason::MaxAttemptsExhausted {
460/// attempts }` pattern of carrying the discriminant's companion data in a
461/// field with stable ordering.
462#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
463pub enum ToolFilter {
464    #[default]
465    All,
466    Allow(std::collections::BTreeSet<String>),
467    Deny(std::collections::BTreeSet<String>),
468}
469
470impl From<&meerkat_core::ToolFilter> for ToolFilter {
471    fn from(f: &meerkat_core::ToolFilter) -> Self {
472        match f {
473            meerkat_core::ToolFilter::All => Self::All,
474            meerkat_core::ToolFilter::Allow(names) => {
475                Self::Allow(names.iter().map(|name| name.as_str().to_string()).collect())
476            }
477            meerkat_core::ToolFilter::Deny(names) => {
478                Self::Deny(names.iter().map(|name| name.as_str().to_string()).collect())
479            }
480        }
481    }
482}
483
484impl From<ToolFilter> for meerkat_core::ToolFilter {
485    fn from(f: ToolFilter) -> Self {
486        match f {
487            ToolFilter::All => Self::All,
488            ToolFilter::Allow(names) => Self::Allow(names.into_iter().collect()),
489            ToolFilter::Deny(names) => Self::Deny(names.into_iter().collect()),
490        }
491    }
492}
493
494impl ToolFilter {
495    pub fn from_domain(id: &meerkat_core::ToolFilter) -> Self {
496        Self::from(id)
497    }
498}
499
500/// Typed mirror of [`meerkat_core::types::ToolSourceKind`] — closed
501/// Closed discriminant for tool provenance classification.
502#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
503pub enum ToolSourceKind {
504    #[default]
505    Builtin,
506    Shell,
507    Comms,
508    Memory,
509    Schedule,
510    WorkGraph,
511    Mob,
512    Callback,
513    Mcp,
514    RustBundle,
515}
516
517impl From<&meerkat_core::types::ToolSourceKind> for ToolSourceKind {
518    fn from(k: &meerkat_core::types::ToolSourceKind) -> Self {
519        match k {
520            meerkat_core::types::ToolSourceKind::Builtin => Self::Builtin,
521            meerkat_core::types::ToolSourceKind::Shell => Self::Shell,
522            meerkat_core::types::ToolSourceKind::Comms => Self::Comms,
523            meerkat_core::types::ToolSourceKind::Memory => Self::Memory,
524            meerkat_core::types::ToolSourceKind::Schedule => Self::Schedule,
525            meerkat_core::types::ToolSourceKind::WorkGraph => Self::WorkGraph,
526            meerkat_core::types::ToolSourceKind::Mob => Self::Mob,
527            meerkat_core::types::ToolSourceKind::Callback => Self::Callback,
528            meerkat_core::types::ToolSourceKind::Mcp => Self::Mcp,
529            meerkat_core::types::ToolSourceKind::RustBundle => Self::RustBundle,
530        }
531    }
532}
533
534/// Typed mirror of [`meerkat_core::types::ToolProvenance`] — structural
535/// projection carried inside [`ToolVisibilityWitness`], using the typed
536/// `ToolSourceKind` discriminant mirror.
537#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
538pub struct ToolProvenance {
539    pub kind: ToolSourceKind,
540    pub source_id: String,
541}
542
543impl From<&meerkat_core::types::ToolProvenance> for ToolProvenance {
544    fn from(p: &meerkat_core::types::ToolProvenance) -> Self {
545        Self {
546            kind: ToolSourceKind::from(&p.kind),
547            source_id: p.source_id.to_string(),
548        }
549    }
550}
551
552/// Typed mirror of [`meerkat_core::ToolVisibilityWitness`] — structural
553/// projection of the two optional witness fields.
554#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
555pub struct ToolVisibilityWitness {
556    pub stable_owner_key: Option<String>,
557    pub last_seen_provenance: Option<ToolProvenance>,
558}
559
560impl From<&meerkat_core::ToolVisibilityWitness> for ToolVisibilityWitness {
561    fn from(w: &meerkat_core::ToolVisibilityWitness) -> Self {
562        Self {
563            stable_owner_key: w.stable_owner_key.clone(),
564            last_seen_provenance: w.last_seen_provenance.as_ref().map(ToolProvenance::from),
565        }
566    }
567}
568
569impl ToolVisibilityWitness {
570    pub fn from_domain(id: &meerkat_core::ToolVisibilityWitness) -> Self {
571        Self::from(id)
572    }
573
574    fn len(&self) -> u64 {
575        u64::from(self.last_seen_provenance.is_some())
576    }
577}
578
579/// Bridging type for an MCP server identifier, matching the catalog type.
580/// Used as the key in `mcp_server_states` and carried on MCP lifecycle
581/// inputs and effects.
582#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
583pub struct McpServerId(pub String);
584
585impl<T: Into<String>> From<T> for McpServerId {
586    fn from(s: T) -> Self {
587        Self(s.into())
588    }
589}
590
591/// Bridging wrapper mapping [`meerkat_core::PeerCorrelationId`] into the DSL
592/// macro's type system. Keyed map values for `pending_peer_requests` and
593/// `inbound_peer_requests`; carried on every W1-A peer-lifecycle input and
594/// effect.
595#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
596pub struct PeerCorrelationId(pub String);
597
598impl From<meerkat_core::PeerCorrelationId> for PeerCorrelationId {
599    fn from(id: meerkat_core::PeerCorrelationId) -> Self {
600        Self(id.0.to_string())
601    }
602}
603
604impl From<uuid::Uuid> for PeerCorrelationId {
605    fn from(id: uuid::Uuid) -> Self {
606        Self(id.to_string())
607    }
608}
609
610impl From<String> for PeerCorrelationId {
611    fn from(s: String) -> Self {
612        Self(s)
613    }
614}
615
616impl From<&str> for PeerCorrelationId {
617    fn from(s: &str) -> Self {
618        Self(s.to_string())
619    }
620}
621
622/// Typed outbound peer-request state, mirroring
623/// [`meerkat_core::OutboundPeerRequestState`]. Unit variants only; failure
624/// reason travels on the `PeerResponseTerminalArrived` input's companion
625/// fields, not in the enum.
626#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
627pub enum OutboundPeerRequestState {
628    #[default]
629    Sent,
630    AcceptedProgress,
631    Completed,
632    Failed,
633    TimedOut,
634}
635
636impl From<meerkat_core::OutboundPeerRequestState> for OutboundPeerRequestState {
637    fn from(s: meerkat_core::OutboundPeerRequestState) -> Self {
638        match s {
639            meerkat_core::OutboundPeerRequestState::Sent => Self::Sent,
640            meerkat_core::OutboundPeerRequestState::AcceptedProgress => Self::AcceptedProgress,
641            meerkat_core::OutboundPeerRequestState::Completed => Self::Completed,
642            meerkat_core::OutboundPeerRequestState::Failed => Self::Failed,
643            meerkat_core::OutboundPeerRequestState::TimedOut => Self::TimedOut,
644            // core `#[non_exhaustive]` guard: new variants added there
645            // without a catalog mirror fall back to `Sent`. Detect at
646            // codegen time via the parity test, not at runtime.
647            _ => Self::Sent,
648        }
649    }
650}
651
652impl From<OutboundPeerRequestState> for meerkat_core::OutboundPeerRequestState {
653    fn from(s: OutboundPeerRequestState) -> Self {
654        match s {
655            OutboundPeerRequestState::Sent => Self::Sent,
656            OutboundPeerRequestState::AcceptedProgress => Self::AcceptedProgress,
657            OutboundPeerRequestState::Completed => Self::Completed,
658            OutboundPeerRequestState::Failed => Self::Failed,
659            OutboundPeerRequestState::TimedOut => Self::TimedOut,
660        }
661    }
662}
663
664/// Typed inbound peer-request state, mirroring
665/// [`meerkat_core::InboundPeerRequestState`].
666#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
667pub enum InboundPeerRequestState {
668    #[default]
669    Received,
670    Replied,
671}
672
673impl From<meerkat_core::InboundPeerRequestState> for InboundPeerRequestState {
674    fn from(s: meerkat_core::InboundPeerRequestState) -> Self {
675        match s {
676            meerkat_core::InboundPeerRequestState::Received => Self::Received,
677            meerkat_core::InboundPeerRequestState::Replied => Self::Replied,
678            _ => Self::Received,
679        }
680    }
681}
682
683impl From<InboundPeerRequestState> for meerkat_core::InboundPeerRequestState {
684    fn from(s: InboundPeerRequestState) -> Self {
685        match s {
686            InboundPeerRequestState::Received => Self::Received,
687            InboundPeerRequestState::Replied => Self::Replied,
688        }
689    }
690}
691
692/// Typed terminal disposition carried on `PeerResponseTerminalArrived`.
693/// Mirror of [`meerkat_core::handles::PeerTerminalDisposition`].
694#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
695pub enum PeerTerminalDisposition {
696    #[default]
697    Completed,
698    Failed,
699}
700
701impl From<meerkat_core::handles::PeerTerminalDisposition> for PeerTerminalDisposition {
702    fn from(d: meerkat_core::handles::PeerTerminalDisposition) -> Self {
703        match d {
704            meerkat_core::handles::PeerTerminalDisposition::Completed => Self::Completed,
705            meerkat_core::handles::PeerTerminalDisposition::Failed => Self::Failed,
706            _ => Self::Failed,
707        }
708    }
709}
710
711/// Typed lifecycle state of an interaction stream reservation (U6 / dogma #5).
712///
713/// Owns whether a reserved subscriber/stream channel is still claimable
714/// (`Reserved`), live with an attached consumer (`Attached`), or terminal
715/// (`Completed` after a terminal event won, `Expired` after the TTL elapsed
716/// without an attach, `ClosedEarly` after the consumer dropped the stream
717/// before terminal). Mirror of [`meerkat_core::InteractionStreamState`].
718#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
719pub enum InteractionStreamState {
720    #[default]
721    Reserved,
722    Attached,
723    Completed,
724    Expired,
725    ClosedEarly,
726}
727
728impl From<meerkat_core::InteractionStreamState> for InteractionStreamState {
729    fn from(s: meerkat_core::InteractionStreamState) -> Self {
730        match s {
731            meerkat_core::InteractionStreamState::Reserved => Self::Reserved,
732            meerkat_core::InteractionStreamState::Attached => Self::Attached,
733            meerkat_core::InteractionStreamState::Completed => Self::Completed,
734            meerkat_core::InteractionStreamState::Expired => Self::Expired,
735            meerkat_core::InteractionStreamState::ClosedEarly => Self::ClosedEarly,
736            _ => Self::Reserved,
737        }
738    }
739}
740
741impl From<InteractionStreamState> for meerkat_core::InteractionStreamState {
742    fn from(s: InteractionStreamState) -> Self {
743        match s {
744            InteractionStreamState::Reserved => Self::Reserved,
745            InteractionStreamState::Attached => Self::Attached,
746            InteractionStreamState::Completed => Self::Completed,
747            InteractionStreamState::Expired => Self::Expired,
748            InteractionStreamState::ClosedEarly => Self::ClosedEarly,
749        }
750    }
751}
752
753/// Per-server MCP connection lifecycle state. Matches the catalog copy;
754/// unit variants only so the DSL can reason about state via map inserts.
755/// Failure detail travels on the `McpServerFailed` input and
756/// `McpServerStateChanged` effect's companion fields, not on the enum.
757#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
758pub enum McpServerState {
759    #[default]
760    PendingConnect,
761    Connected,
762    Failed,
763    Disconnected,
764}
765
766/// Stable identity of a comms runtime instance (W2-G / issue #264).
767///
768/// The runtime derives this string from the `Arc<dyn CommsRuntime>` pointer
769/// address via `CommsRuntimeId::from_runtime()`. The DSL treats it as an
770/// opaque newtype; two distinct `Arc`s produce distinct ids so the owner
771/// invariant can catch silent transport swaps.
772#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
773pub struct CommsRuntimeId(pub String);
774
775impl<T: Into<String>> From<T> for CommsRuntimeId {
776    fn from(s: T) -> Self {
777        Self(s.into())
778    }
779}
780
781impl CommsRuntimeId {
782    /// Derive a stable id from an `Arc<dyn CommsRuntime>`'s pointer address.
783    ///
784    /// Two `Arc` instances with the same pointee produce the same id; two
785    /// distinct `Arc` instances produce distinct ids even if their contents
786    /// are equivalent. This is sufficient for detecting silent transport
787    /// swaps at the DSL boundary.
788    pub fn from_runtime(runtime: &std::sync::Arc<dyn meerkat_core::agent::CommsRuntime>) -> Self {
789        let ptr = std::sync::Arc::as_ptr(runtime).cast::<()>() as usize;
790        Self(format!("comms-runtime-0x{ptr:x}"))
791    }
792}
793
794/// Mob instance identifier for peer-ingress ownership (W2-G / issue #264).
795///
796/// Bridging newtype mirroring `meerkat_mob::ids::MobId`. The DSL layer keeps
797/// this opaque because `meerkat-runtime` does not depend on `meerkat-mob`;
798/// the shell stringifies the real `MobId` before firing `AttachMobIngress`.
799#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
800pub struct MobId(pub String);
801
802impl<T: Into<String>> From<T> for MobId {
803    fn from(s: T) -> Self {
804        Self(s.into())
805    }
806}
807
808/// Parsed transport envelope class for peer ingress.
809///
810/// This is the mechanical shape comms may derive from a wire envelope before
811/// semantic admission. The DSL consumes it to own the peer-input class,
812/// auth-exemption, lifecycle, silent-routing, and response-terminal facts.
813#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
814pub enum PeerIngressEnvelopeClass {
815    #[default]
816    Message,
817    Request,
818    Lifecycle,
819    Response,
820    Ack,
821}
822
823/// DSL-owned admitted ingress kind.
824#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
825pub enum PeerIngressAdmittedKind {
826    #[default]
827    Message,
828    Request,
829    Response,
830    Ack,
831    PlainEvent,
832}
833
834/// DSL-owned peer input class.
835#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
836pub enum PeerIngressInputClass {
837    #[default]
838    ActionableMessage,
839    ActionableRequest,
840    ResponseProgress,
841    ResponseTerminal,
842    PeerLifecycleAdded,
843    PeerLifecycleRetired,
844    PeerLifecycleUnwired,
845    SilentRequest,
846    Ack,
847    PlainEvent,
848}
849
850/// DSL-owned peer lifecycle classifier.
851#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
852pub enum PeerIngressLifecycleClass {
853    #[default]
854    PeerAdded,
855    PeerRetired,
856    PeerUnwired,
857}
858
859/// DSL-owned peer ingress auth classifier.
860#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
861pub enum PeerIngressAuthClass {
862    #[default]
863    Required,
864    SupervisorBridgeExempt,
865}
866
867/// Parsed response status for peer ingress.
868#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
869pub enum PeerIngressResponseStatus {
870    #[default]
871    Accepted,
872    Completed,
873    Failed,
874}
875
876/// DSL-owned response progress/terminal classifier.
877#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
878pub enum PeerIngressResponseTerminality {
879    #[default]
880    Progress,
881    TerminalCompleted,
882    TerminalFailed,
883}
884
885/// Peer-ingress transport capability ownership kind (W2-G / issue #264).
886///
887/// Paired with `peer_ingress_comms_runtime_id` and `peer_ingress_mob_id` in
888/// DSL state; `peer_ingress_owner_consistency` enforces pairing. Silent
889/// downgrade `MobOwned` → `SessionOwned` is structurally impossible:
890/// `AttachSessionIngress` requires `Unattached`; `AttachMobIngress` permits
891/// `Unattached` or `SessionOwned` but never `MobOwned` → `SessionOwned`.
892#[derive(
893    Debug,
894    Clone,
895    Copy,
896    PartialEq,
897    Eq,
898    PartialOrd,
899    Ord,
900    Hash,
901    Default,
902    serde::Serialize,
903    serde::Deserialize,
904)]
905pub enum PeerIngressOwnerKind {
906    #[default]
907    Unattached,
908    SessionOwned,
909    MobOwned,
910}
911
912/// Supervisor-bridge authorization kind (Wave 3 D Row 21).
913///
914/// Paired with `supervisor_bound_{name, peer_id, address, epoch}` in DSL
915/// state; `supervisor_binding_consistency` enforces pairing. Rotation is
916/// structural: `BindSupervisor` requires `Unbound`; `AuthorizeSupervisor`
917/// requires `Bound`; `RevokeSupervisor` requires `Bound` and returns to
918/// `Unbound`. Before Wave 3 D this fact lived as an `Option<AuthorizedSupervisorState>`
919/// on the comms drain task's stack — the identity and epoch of the
920/// authorized supervisor were helper-local while the corresponding trust
921/// edge was router-owned. Moving the authorization discriminant + epoch
922/// into DSL state collapses that split ownership.
923#[derive(
924    Debug,
925    Clone,
926    Copy,
927    PartialEq,
928    Eq,
929    PartialOrd,
930    Ord,
931    Hash,
932    Default,
933    serde::Serialize,
934    serde::Deserialize,
935)]
936pub enum SupervisorBindingKind {
937    #[default]
938    Unbound,
939    Bound,
940}
941
942/// Typed turn-execution phase, mirrored 1:1 by the closed set of literals the
943/// DSL transitions assign to `turn_phase`. Replaces the prior stringly-typed
944/// encoding so the ephemeral driver and runtime handles consume an exhaustive
945/// enum instead of parsing folklore.
946#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
947pub enum TurnPhase {
948    #[default]
949    Ready,
950    ApplyingPrimitive,
951    CallingLlm,
952    WaitingForOps,
953    DrainingBoundary,
954    Extracting,
955    ErrorRecovery,
956    Cancelling,
957    Completed,
958    Failed,
959    Cancelled,
960}
961
962impl TurnPhase {
963    pub const fn as_str(self) -> &'static str {
964        match self {
965            Self::Ready => "Ready",
966            Self::ApplyingPrimitive => "ApplyingPrimitive",
967            Self::CallingLlm => "CallingLlm",
968            Self::WaitingForOps => "WaitingForOps",
969            Self::DrainingBoundary => "DrainingBoundary",
970            Self::Extracting => "Extracting",
971            Self::ErrorRecovery => "ErrorRecovery",
972            Self::Cancelling => "Cancelling",
973            Self::Completed => "Completed",
974            Self::Failed => "Failed",
975            Self::Cancelled => "Cancelled",
976        }
977    }
978}
979
980/// Typed registration substate. Closed set of literals previously assigned to
981/// `registration_phase`.
982#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
983pub enum RegistrationPhase {
984    #[default]
985    Queuing,
986    Active,
987}
988
989impl RegistrationPhase {
990    pub const fn as_str(self) -> &'static str {
991        match self {
992            Self::Queuing => "Queuing",
993            Self::Active => "Active",
994        }
995    }
996}
997
998/// Typed comms drain substate. Mirrors the closed set of literals the DSL
999/// transitions assign to `drain_phase`.
1000#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1001pub enum DrainPhase {
1002    #[default]
1003    Inactive,
1004    Running,
1005    Stopped,
1006    ExitedRespawnable,
1007}
1008
1009impl DrainPhase {
1010    pub const fn as_str(self) -> &'static str {
1011        match self {
1012            Self::Inactive => "Inactive",
1013            Self::Running => "Running",
1014            Self::Stopped => "Stopped",
1015            Self::ExitedRespawnable => "ExitedRespawnable",
1016        }
1017    }
1018}
1019
1020/// Typed comms drain mode. Mirrors `crate::meerkat_machine::CommsDrainMode`
1021/// (which is the shell-side enum) so the DSL can hold a closed set of typed
1022/// variants instead of a `Debug`-formatted string.
1023#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1024pub enum DrainMode {
1025    #[default]
1026    Timed,
1027    AttachedSession,
1028    PersistentHost,
1029}
1030
1031impl DrainMode {
1032    pub const fn as_str(self) -> &'static str {
1033        match self {
1034            Self::Timed => "Timed",
1035            Self::AttachedSession => "AttachedSession",
1036            Self::PersistentHost => "PersistentHost",
1037        }
1038    }
1039}
1040
1041impl From<crate::meerkat_machine::CommsDrainMode> for DrainMode {
1042    fn from(mode: crate::meerkat_machine::CommsDrainMode) -> Self {
1043        match mode {
1044            crate::meerkat_machine::CommsDrainMode::Timed => Self::Timed,
1045            crate::meerkat_machine::CommsDrainMode::AttachedSession => Self::AttachedSession,
1046            crate::meerkat_machine::CommsDrainMode::PersistentHost => Self::PersistentHost,
1047        }
1048    }
1049}
1050
1051/// Typed external-tool surface global phase. Closed set of literals previously
1052/// assigned to `surface_phase`.
1053#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1054pub enum SurfacePhase {
1055    #[default]
1056    Operating,
1057    Shutdown,
1058}
1059
1060impl SurfacePhase {
1061    pub const fn as_str(self) -> &'static str {
1062        match self {
1063            Self::Operating => "Operating",
1064            Self::Shutdown => "Shutdown",
1065        }
1066    }
1067}
1068
1069/// Typed input-lifecycle phase, mirroring the closed set of literals the DSL
1070/// transitions assign to `input_phases`. The shell projects from this onto the
1071/// richer `crate::input_state::InputLifecycleState` (which keeps an `Accepted`
1072/// pre-DSL-admission variant the DSL itself never writes).
1073#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1074pub enum InputPhase {
1075    #[default]
1076    Queued,
1077    Staged,
1078    Applied,
1079    AppliedPendingConsumption,
1080    Consumed,
1081    Superseded,
1082    Coalesced,
1083    Abandoned,
1084}
1085
1086impl InputPhase {
1087    pub const fn as_str(self) -> &'static str {
1088        match self {
1089            Self::Queued => "Queued",
1090            Self::Staged => "Staged",
1091            Self::Applied => "Applied",
1092            Self::AppliedPendingConsumption => "AppliedPendingConsumption",
1093            Self::Consumed => "Consumed",
1094            Self::Superseded => "Superseded",
1095            Self::Coalesced => "Coalesced",
1096            Self::Abandoned => "Abandoned",
1097        }
1098    }
1099}
1100
1101/// Typed input terminal kind, mirroring the closed set of literals the DSL
1102/// transitions assign to `input_terminal_kind`. The companion fields
1103/// (`input_superseded_by`, `input_aggregate_id`, `input_abandon_reason`,
1104/// `input_abandon_attempt_count`) carry payload metadata for variants that
1105/// need it.
1106#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1107pub enum InputTerminalKind {
1108    #[default]
1109    Consumed,
1110    Superseded,
1111    Coalesced,
1112    Abandoned,
1113}
1114
1115impl InputTerminalKind {
1116    pub const fn as_str(self) -> &'static str {
1117        match self {
1118            Self::Consumed => "Consumed",
1119            Self::Superseded => "Superseded",
1120            Self::Coalesced => "Coalesced",
1121            Self::Abandoned => "Abandoned",
1122        }
1123    }
1124}
1125
1126/// Typed pending external-surface op. Closed set of literals previously
1127/// assigned to `surface_pending_op`.
1128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1129pub enum SurfacePendingOp {
1130    #[default]
1131    None,
1132    Add,
1133    Reload,
1134}
1135
1136impl SurfacePendingOp {
1137    pub const fn as_str(self) -> &'static str {
1138        match self {
1139            Self::None => "None",
1140            Self::Add => "Add",
1141            Self::Reload => "Reload",
1142        }
1143    }
1144}
1145
1146/// Typed staged external-surface op. Closed set of literals previously
1147/// assigned to `surface_staged_op`.
1148#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1149pub enum SurfaceStagedOp {
1150    #[default]
1151    None,
1152    Add,
1153    Remove,
1154    Reload,
1155}
1156
1157impl SurfaceStagedOp {
1158    pub const fn as_str(self) -> &'static str {
1159        match self {
1160            Self::None => "None",
1161            Self::Add => "Add",
1162            Self::Remove => "Remove",
1163            Self::Reload => "Reload",
1164        }
1165    }
1166}
1167
1168/// Typed turn primitive kind. Closed mirror of
1169/// [`meerkat_core::turn_execution_authority::TurnPrimitiveKind`] — replaces the
1170/// former literal-string `primitive_kind` field and `StartConversationRun`
1171/// input field.
1172#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1173pub enum TurnPrimitiveKind {
1174    #[default]
1175    None,
1176    ConversationTurn,
1177    ImmediateAppend,
1178    ImmediateContextAppend,
1179}
1180
1181impl From<meerkat_core::turn_execution_authority::TurnPrimitiveKind> for TurnPrimitiveKind {
1182    fn from(kind: meerkat_core::turn_execution_authority::TurnPrimitiveKind) -> Self {
1183        match kind {
1184            meerkat_core::turn_execution_authority::TurnPrimitiveKind::None => Self::None,
1185            meerkat_core::turn_execution_authority::TurnPrimitiveKind::ConversationTurn => {
1186                Self::ConversationTurn
1187            }
1188            meerkat_core::turn_execution_authority::TurnPrimitiveKind::ImmediateAppend => {
1189                Self::ImmediateAppend
1190            }
1191            meerkat_core::turn_execution_authority::TurnPrimitiveKind::ImmediateContextAppend => {
1192                Self::ImmediateContextAppend
1193            }
1194        }
1195    }
1196}
1197
1198impl From<TurnPrimitiveKind> for meerkat_core::turn_execution_authority::TurnPrimitiveKind {
1199    fn from(kind: TurnPrimitiveKind) -> Self {
1200        match kind {
1201            TurnPrimitiveKind::None => Self::None,
1202            TurnPrimitiveKind::ConversationTurn => Self::ConversationTurn,
1203            TurnPrimitiveKind::ImmediateAppend => Self::ImmediateAppend,
1204            TurnPrimitiveKind::ImmediateContextAppend => Self::ImmediateContextAppend,
1205        }
1206    }
1207}
1208
1209/// Typed turn primitive content shape. Closed mirror of
1210/// [`meerkat_core::turn_execution_authority::ContentShape`] so the runtime DSL
1211/// carries the same contract instead of local string labels.
1212#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1213pub enum ContentShape {
1214    #[default]
1215    Conversation,
1216    ConversationAndContext,
1217    Context,
1218    Empty,
1219    ImmediateAppend,
1220    ImmediateContext,
1221}
1222
1223impl ContentShape {
1224    pub const fn as_str(self) -> &'static str {
1225        match self {
1226            Self::Conversation => {
1227                meerkat_core::turn_execution_authority::ContentShape::Conversation.as_str()
1228            }
1229            Self::ConversationAndContext => {
1230                meerkat_core::turn_execution_authority::ContentShape::ConversationAndContext
1231                    .as_str()
1232            }
1233            Self::Context => meerkat_core::turn_execution_authority::ContentShape::Context.as_str(),
1234            Self::Empty => meerkat_core::turn_execution_authority::ContentShape::Empty.as_str(),
1235            Self::ImmediateAppend => {
1236                meerkat_core::turn_execution_authority::ContentShape::ImmediateAppend.as_str()
1237            }
1238            Self::ImmediateContext => {
1239                meerkat_core::turn_execution_authority::ContentShape::ImmediateContext.as_str()
1240            }
1241        }
1242    }
1243}
1244
1245impl std::fmt::Display for ContentShape {
1246    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1247        f.write_str(self.as_str())
1248    }
1249}
1250
1251impl From<meerkat_core::turn_execution_authority::ContentShape> for ContentShape {
1252    fn from(shape: meerkat_core::turn_execution_authority::ContentShape) -> Self {
1253        match shape {
1254            meerkat_core::turn_execution_authority::ContentShape::Conversation => {
1255                Self::Conversation
1256            }
1257            meerkat_core::turn_execution_authority::ContentShape::ConversationAndContext => {
1258                Self::ConversationAndContext
1259            }
1260            meerkat_core::turn_execution_authority::ContentShape::Context => Self::Context,
1261            meerkat_core::turn_execution_authority::ContentShape::Empty => Self::Empty,
1262            meerkat_core::turn_execution_authority::ContentShape::ImmediateAppend => {
1263                Self::ImmediateAppend
1264            }
1265            meerkat_core::turn_execution_authority::ContentShape::ImmediateContext => {
1266                Self::ImmediateContext
1267            }
1268        }
1269    }
1270}
1271
1272impl From<ContentShape> for meerkat_core::turn_execution_authority::ContentShape {
1273    fn from(shape: ContentShape) -> Self {
1274        match shape {
1275            ContentShape::Conversation => Self::Conversation,
1276            ContentShape::ConversationAndContext => Self::ConversationAndContext,
1277            ContentShape::Context => Self::Context,
1278            ContentShape::Empty => Self::Empty,
1279            ContentShape::ImmediateAppend => Self::ImmediateAppend,
1280            ContentShape::ImmediateContext => Self::ImmediateContext,
1281        }
1282    }
1283}
1284
1285/// Typed turn terminal outcome. Closed mirror of
1286/// [`meerkat_core::turn_execution_authority::TurnTerminalOutcome`] — replaces
1287/// the former literal-string `terminal_outcome` field.
1288#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1289pub enum TurnTerminalOutcome {
1290    #[default]
1291    None,
1292    Completed,
1293    Failed,
1294    Cancelled,
1295    BudgetExhausted,
1296    TimeBudgetExceeded,
1297    StructuredOutputValidationFailed,
1298}
1299
1300impl From<meerkat_core::turn_execution_authority::TurnTerminalOutcome> for TurnTerminalOutcome {
1301    fn from(outcome: meerkat_core::turn_execution_authority::TurnTerminalOutcome) -> Self {
1302        match outcome {
1303            meerkat_core::turn_execution_authority::TurnTerminalOutcome::None => Self::None,
1304            meerkat_core::turn_execution_authority::TurnTerminalOutcome::Completed => {
1305                Self::Completed
1306            }
1307            meerkat_core::turn_execution_authority::TurnTerminalOutcome::Failed => Self::Failed,
1308            meerkat_core::turn_execution_authority::TurnTerminalOutcome::Cancelled => {
1309                Self::Cancelled
1310            }
1311            meerkat_core::turn_execution_authority::TurnTerminalOutcome::BudgetExhausted => {
1312                Self::BudgetExhausted
1313            }
1314            meerkat_core::turn_execution_authority::TurnTerminalOutcome::TimeBudgetExceeded => {
1315                Self::TimeBudgetExceeded
1316            }
1317            meerkat_core::turn_execution_authority::TurnTerminalOutcome::StructuredOutputValidationFailed => {
1318                Self::StructuredOutputValidationFailed
1319            }
1320        }
1321    }
1322}
1323
1324impl From<TurnTerminalOutcome> for meerkat_core::turn_execution_authority::TurnTerminalOutcome {
1325    fn from(outcome: TurnTerminalOutcome) -> Self {
1326        match outcome {
1327            TurnTerminalOutcome::None => Self::None,
1328            TurnTerminalOutcome::Completed => Self::Completed,
1329            TurnTerminalOutcome::Failed => Self::Failed,
1330            TurnTerminalOutcome::Cancelled => Self::Cancelled,
1331            TurnTerminalOutcome::BudgetExhausted => Self::BudgetExhausted,
1332            TurnTerminalOutcome::TimeBudgetExceeded => Self::TimeBudgetExceeded,
1333            TurnTerminalOutcome::StructuredOutputValidationFailed => {
1334                Self::StructuredOutputValidationFailed
1335            }
1336        }
1337    }
1338}
1339
1340/// Typed turn terminal cause. Closed mirror of
1341/// [`meerkat_core::turn_execution_authority::TurnTerminalCauseKind`] carried by
1342/// MeerkatMachine terminal failure inputs/effects so display messages cannot
1343/// classify terminal failures.
1344#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1345pub enum TurnTerminalCauseKind {
1346    #[default]
1347    Unknown,
1348    HookDenied,
1349    HookFailure,
1350    LlmFailure,
1351    ToolFailure,
1352    StructuredOutputValidationFailed,
1353    BudgetExhausted,
1354    TimeBudgetExceeded,
1355    RetryExhausted,
1356    TurnLimitReached,
1357    RuntimeApplyFailure,
1358    FatalFailure,
1359}
1360
1361impl From<meerkat_core::turn_execution_authority::TurnTerminalCauseKind> for TurnTerminalCauseKind {
1362    fn from(kind: meerkat_core::turn_execution_authority::TurnTerminalCauseKind) -> Self {
1363        match kind {
1364            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::Unknown => {
1365                Self::Unknown
1366            }
1367            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::HookDenied => {
1368                Self::HookDenied
1369            }
1370            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::HookFailure => {
1371                Self::HookFailure
1372            }
1373            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::LlmFailure => {
1374                Self::LlmFailure
1375            }
1376            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::ToolFailure => {
1377                Self::ToolFailure
1378            }
1379            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::StructuredOutputValidationFailed => {
1380                Self::StructuredOutputValidationFailed
1381            }
1382            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::BudgetExhausted => {
1383                Self::BudgetExhausted
1384            }
1385            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::TimeBudgetExceeded => {
1386                Self::TimeBudgetExceeded
1387            }
1388            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::RetryExhausted => {
1389                Self::RetryExhausted
1390            }
1391            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::TurnLimitReached => {
1392                Self::TurnLimitReached
1393            }
1394            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::RuntimeApplyFailure => {
1395                Self::RuntimeApplyFailure
1396            }
1397            meerkat_core::turn_execution_authority::TurnTerminalCauseKind::FatalFailure => {
1398                Self::FatalFailure
1399            }
1400        }
1401    }
1402}
1403
1404impl From<TurnTerminalCauseKind> for meerkat_core::turn_execution_authority::TurnTerminalCauseKind {
1405    fn from(kind: TurnTerminalCauseKind) -> Self {
1406        match kind {
1407            TurnTerminalCauseKind::Unknown => Self::Unknown,
1408            TurnTerminalCauseKind::HookDenied => Self::HookDenied,
1409            TurnTerminalCauseKind::HookFailure => Self::HookFailure,
1410            TurnTerminalCauseKind::LlmFailure => Self::LlmFailure,
1411            TurnTerminalCauseKind::ToolFailure => Self::ToolFailure,
1412            TurnTerminalCauseKind::StructuredOutputValidationFailed => {
1413                Self::StructuredOutputValidationFailed
1414            }
1415            TurnTerminalCauseKind::BudgetExhausted => Self::BudgetExhausted,
1416            TurnTerminalCauseKind::TimeBudgetExceeded => Self::TimeBudgetExceeded,
1417            TurnTerminalCauseKind::RetryExhausted => Self::RetryExhausted,
1418            TurnTerminalCauseKind::TurnLimitReached => Self::TurnLimitReached,
1419            TurnTerminalCauseKind::RuntimeApplyFailure => Self::RuntimeApplyFailure,
1420            TurnTerminalCauseKind::FatalFailure => Self::FatalFailure,
1421        }
1422    }
1423}
1424
1425/// Typed classifier for failures surfaced by the runtime apply loop when a
1426/// `CoreExecutor::apply` call fails and terminalizes the runtime turn.
1427/// The companion `last_runtime_apply_failure_message` state field carries the
1428/// human-readable projection.
1429#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1430pub enum RuntimeApplyFailureCause {
1431    #[default]
1432    Unknown,
1433    PrimitiveRejected,
1434    RuntimeContextApply,
1435    RuntimeTurn,
1436    HookDenied,
1437    HookRuntimeFailure,
1438    ExecutorStopped,
1439    ExecutorControlFailed,
1440    ExecutorInternal,
1441}
1442
1443impl From<meerkat_core::lifecycle::CoreApplyFailureCauseKind> for RuntimeApplyFailureCause {
1444    fn from(kind: meerkat_core::lifecycle::CoreApplyFailureCauseKind) -> Self {
1445        match kind {
1446            meerkat_core::lifecycle::CoreApplyFailureCauseKind::PrimitiveRejected => {
1447                Self::PrimitiveRejected
1448            }
1449            meerkat_core::lifecycle::CoreApplyFailureCauseKind::RuntimeContextApply => {
1450                Self::RuntimeContextApply
1451            }
1452            meerkat_core::lifecycle::CoreApplyFailureCauseKind::RuntimeTurn => Self::RuntimeTurn,
1453            meerkat_core::lifecycle::CoreApplyFailureCauseKind::HookDenied => Self::HookDenied,
1454            meerkat_core::lifecycle::CoreApplyFailureCauseKind::HookRuntimeFailure => {
1455                Self::HookRuntimeFailure
1456            }
1457            meerkat_core::lifecycle::CoreApplyFailureCauseKind::ExecutorStopped => {
1458                Self::ExecutorStopped
1459            }
1460            meerkat_core::lifecycle::CoreApplyFailureCauseKind::ExecutorControlFailed => {
1461                Self::ExecutorControlFailed
1462            }
1463            meerkat_core::lifecycle::CoreApplyFailureCauseKind::ExecutorInternal => {
1464                Self::ExecutorInternal
1465            }
1466            meerkat_core::lifecycle::CoreApplyFailureCauseKind::Unknown => Self::Unknown,
1467            _ => Self::Unknown,
1468        }
1469    }
1470}
1471
1472impl From<&meerkat_core::lifecycle::CoreApplyFailureCause> for RuntimeApplyFailureCause {
1473    fn from(cause: &meerkat_core::lifecycle::CoreApplyFailureCause) -> Self {
1474        Self::from(cause.kind)
1475    }
1476}
1477
1478/// Typed pre-run phase marker. Closed set: `idle`, `attached`, `retired`.
1479/// Replaces the former literal-string `pre_run_phase` field.
1480#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1481pub enum PreRunPhase {
1482    #[default]
1483    Idle,
1484    Attached,
1485    Retired,
1486}
1487
1488/// Typed runtime notice classifier for the `RuntimeNotice` effect. Closed set
1489/// of per-transition runtime lifecycle markers (drain exited, runtime reset,
1490/// executor stopped/exited, runtime recovered) emitted by the runtime-control
1491/// plane. Replaces the former literal-string `kind` field on `RuntimeNotice`
1492/// so the shell dispatcher matches exhaustively on a typed discriminant
1493/// instead of comparing string literals. `detail` stays `String` — it's a
1494/// free-form diagnostic message that accompanies the kind.
1495#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1496pub enum RuntimeNoticeKind {
1497    #[default]
1498    Drain,
1499    Reset,
1500    Stop,
1501    Exit,
1502    Recover,
1503}
1504
1505/// Closed classifier for runtime-loop executor effects emitted as neutral DSL
1506/// facts before the runtime shell converts them to sealed executable effects.
1507#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1508pub enum RuntimeEffectKind {
1509    #[default]
1510    CancelAfterBoundary,
1511    StopRuntimeExecutor,
1512}
1513
1514/// Typed reason classifier for the `TurnRunCancelled` effect. Closed set of
1515/// cancellation-observation origins emitted when a turn's cancellation
1516/// request lands at an observable boundary. Replaces the former literal-
1517/// string `reason` field on `TurnRunCancelled`. Only one origin is emitted
1518/// today (`Observed`, fired by the `CancellationObserved` transition), but
1519/// this remains a closed classifier not a free-form message — future
1520/// cancellation origins extend the enum rather than reintroducing strings.
1521#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1522pub enum TurnCancellationReason {
1523    #[default]
1524    Observed,
1525}
1526
1527/// Typed recoverable LLM retry failure classifier. Closed mirror of
1528/// [`meerkat_core::retry::LlmRetryFailureKind`] so retry authority records the
1529/// retry cause as data, not as a parsed diagnostic string.
1530#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1531pub enum LlmRetryFailureKind {
1532    #[default]
1533    RateLimited,
1534    NetworkTimeout,
1535    CallTimeout,
1536    RetryableProviderError,
1537}
1538
1539impl From<meerkat_core::retry::LlmRetryFailureKind> for LlmRetryFailureKind {
1540    fn from(kind: meerkat_core::retry::LlmRetryFailureKind) -> Self {
1541        match kind {
1542            meerkat_core::retry::LlmRetryFailureKind::RateLimited => Self::RateLimited,
1543            meerkat_core::retry::LlmRetryFailureKind::NetworkTimeout => Self::NetworkTimeout,
1544            meerkat_core::retry::LlmRetryFailureKind::CallTimeout => Self::CallTimeout,
1545            meerkat_core::retry::LlmRetryFailureKind::RetryableProviderError => {
1546                Self::RetryableProviderError
1547            }
1548        }
1549    }
1550}
1551
1552/// Typed admission-signal classifier for the `PostAdmissionSignal` effect.
1553/// Closed set of post-admission wake/interrupt intents emitted by the
1554/// ingress authority so the shell dispatcher matches exhaustively on a
1555/// typed discriminant instead of comparing string literals. Mirrors the
1556/// shell-side `driver::ephemeral::PostAdmissionSignal` strength ordering
1557/// (WakeLoop < InterruptYielding < RequestImmediateProcessing); the
1558/// shell enum additionally carries a `None` bottom that the DSL never
1559/// emits, so only the three emitted variants appear here.
1560#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1561pub enum PostAdmissionSignalKind {
1562    #[default]
1563    WakeLoop,
1564    InterruptYielding,
1565    RequestImmediateProcessing,
1566}
1567
1568/// Typed base lifecycle state for an external tool surface. Closed mirror of
1569/// [`meerkat_core::tool_scope::ExternalToolSurfaceBaseState`] — replaces the
1570/// former literal-string values in `surface_base_state`.
1571#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1572pub enum ExternalToolSurfaceBaseState {
1573    #[default]
1574    Absent,
1575    Active,
1576    Removing,
1577    Removed,
1578}
1579
1580impl From<meerkat_core::tool_scope::ExternalToolSurfaceBaseState> for ExternalToolSurfaceBaseState {
1581    fn from(state: meerkat_core::tool_scope::ExternalToolSurfaceBaseState) -> Self {
1582        match state {
1583            meerkat_core::tool_scope::ExternalToolSurfaceBaseState::Absent => Self::Absent,
1584            meerkat_core::tool_scope::ExternalToolSurfaceBaseState::Active => Self::Active,
1585            meerkat_core::tool_scope::ExternalToolSurfaceBaseState::Removing => Self::Removing,
1586            meerkat_core::tool_scope::ExternalToolSurfaceBaseState::Removed => Self::Removed,
1587        }
1588    }
1589}
1590
1591impl From<ExternalToolSurfaceBaseState> for meerkat_core::tool_scope::ExternalToolSurfaceBaseState {
1592    fn from(state: ExternalToolSurfaceBaseState) -> Self {
1593        match state {
1594            ExternalToolSurfaceBaseState::Absent => Self::Absent,
1595            ExternalToolSurfaceBaseState::Active => Self::Active,
1596            ExternalToolSurfaceBaseState::Removing => Self::Removing,
1597            ExternalToolSurfaceBaseState::Removed => Self::Removed,
1598        }
1599    }
1600}
1601
1602/// Typed last-delta operation for an external tool surface. Closed mirror of
1603/// [`meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation`] — replaces
1604/// the former literal-string values in `surface_last_delta_operation`.
1605#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1606pub enum ExternalToolSurfaceDeltaOperation {
1607    #[default]
1608    None,
1609    Add,
1610    Remove,
1611    Reload,
1612}
1613
1614impl From<meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation>
1615    for ExternalToolSurfaceDeltaOperation
1616{
1617    fn from(op: meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation) -> Self {
1618        match op {
1619            meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation::None => Self::None,
1620            meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation::Add => Self::Add,
1621            meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation::Remove => Self::Remove,
1622            meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation::Reload => Self::Reload,
1623        }
1624    }
1625}
1626
1627impl From<ExternalToolSurfaceDeltaOperation>
1628    for meerkat_core::tool_scope::ExternalToolSurfaceDeltaOperation
1629{
1630    fn from(op: ExternalToolSurfaceDeltaOperation) -> Self {
1631        match op {
1632            ExternalToolSurfaceDeltaOperation::None => Self::None,
1633            ExternalToolSurfaceDeltaOperation::Add => Self::Add,
1634            ExternalToolSurfaceDeltaOperation::Remove => Self::Remove,
1635            ExternalToolSurfaceDeltaOperation::Reload => Self::Reload,
1636        }
1637    }
1638}
1639
1640/// Typed last-delta phase for an external tool surface. Closed mirror of
1641/// [`meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase`] — replaces the
1642/// former literal-string values in `surface_last_delta_phase`.
1643#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1644pub enum ExternalToolSurfaceDeltaPhase {
1645    #[default]
1646    None,
1647    Pending,
1648    Applied,
1649    Draining,
1650    Failed,
1651    Forced,
1652}
1653
1654impl From<meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase>
1655    for ExternalToolSurfaceDeltaPhase
1656{
1657    fn from(phase: meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase) -> Self {
1658        match phase {
1659            meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase::None => Self::None,
1660            meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase::Pending => Self::Pending,
1661            meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase::Applied => Self::Applied,
1662            meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase::Draining => Self::Draining,
1663            meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase::Failed => Self::Failed,
1664            meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase::Forced => Self::Forced,
1665        }
1666    }
1667}
1668
1669impl From<ExternalToolSurfaceDeltaPhase>
1670    for meerkat_core::tool_scope::ExternalToolSurfaceDeltaPhase
1671{
1672    fn from(phase: ExternalToolSurfaceDeltaPhase) -> Self {
1673        match phase {
1674            ExternalToolSurfaceDeltaPhase::None => Self::None,
1675            ExternalToolSurfaceDeltaPhase::Pending => Self::Pending,
1676            ExternalToolSurfaceDeltaPhase::Applied => Self::Applied,
1677            ExternalToolSurfaceDeltaPhase::Draining => Self::Draining,
1678            ExternalToolSurfaceDeltaPhase::Failed => Self::Failed,
1679            ExternalToolSurfaceDeltaPhase::Forced => Self::Forced,
1680        }
1681    }
1682}
1683
1684/// Typed failure cause for an external tool surface. Closed mirror of
1685/// [`meerkat_core::tool_scope::ExternalToolSurfaceFailureCause`] so pending
1686/// failure and call-rejection causes cross the DSL as data, not string codes.
1687#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1688pub enum ExternalToolSurfaceFailureCause {
1689    #[default]
1690    PendingFailed,
1691    SurfaceDraining,
1692    SurfaceUnavailable,
1693}
1694
1695impl From<meerkat_core::tool_scope::ExternalToolSurfaceFailureCause>
1696    for ExternalToolSurfaceFailureCause
1697{
1698    fn from(cause: meerkat_core::tool_scope::ExternalToolSurfaceFailureCause) -> Self {
1699        match cause {
1700            meerkat_core::tool_scope::ExternalToolSurfaceFailureCause::PendingFailed => {
1701                Self::PendingFailed
1702            }
1703            meerkat_core::tool_scope::ExternalToolSurfaceFailureCause::SurfaceDraining => {
1704                Self::SurfaceDraining
1705            }
1706            meerkat_core::tool_scope::ExternalToolSurfaceFailureCause::SurfaceUnavailable => {
1707                Self::SurfaceUnavailable
1708            }
1709        }
1710    }
1711}
1712
1713impl From<ExternalToolSurfaceFailureCause>
1714    for meerkat_core::tool_scope::ExternalToolSurfaceFailureCause
1715{
1716    fn from(cause: ExternalToolSurfaceFailureCause) -> Self {
1717        match cause {
1718            ExternalToolSurfaceFailureCause::PendingFailed => Self::PendingFailed,
1719            ExternalToolSurfaceFailureCause::SurfaceDraining => Self::SurfaceDraining,
1720            ExternalToolSurfaceFailureCause::SurfaceUnavailable => Self::SurfaceUnavailable,
1721        }
1722    }
1723}
1724
1725/// Typed drain-exit reason. Closed mirror of
1726/// [`meerkat_core::handles::DrainExitReason`] — replaces the former
1727/// literal-string `reason` field on `NotifyDrainExited`.
1728#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1729pub enum DrainExitReason {
1730    #[default]
1731    IdleTimeout,
1732    Dismissed,
1733    Failed,
1734    Aborted,
1735    SessionShutdown,
1736}
1737
1738impl From<meerkat_core::handles::DrainExitReason> for DrainExitReason {
1739    fn from(reason: meerkat_core::handles::DrainExitReason) -> Self {
1740        match reason {
1741            meerkat_core::handles::DrainExitReason::IdleTimeout => Self::IdleTimeout,
1742            meerkat_core::handles::DrainExitReason::Dismissed => Self::Dismissed,
1743            meerkat_core::handles::DrainExitReason::Failed => Self::Failed,
1744            meerkat_core::handles::DrainExitReason::Aborted => Self::Aborted,
1745            meerkat_core::handles::DrainExitReason::SessionShutdown => Self::SessionShutdown,
1746        }
1747    }
1748}
1749
1750impl From<DrainExitReason> for meerkat_core::handles::DrainExitReason {
1751    fn from(reason: DrainExitReason) -> Self {
1752        match reason {
1753            DrainExitReason::IdleTimeout => Self::IdleTimeout,
1754            DrainExitReason::Dismissed => Self::Dismissed,
1755            DrainExitReason::Failed => Self::Failed,
1756            DrainExitReason::Aborted => Self::Aborted,
1757            DrainExitReason::SessionShutdown => Self::SessionShutdown,
1758        }
1759    }
1760}
1761
1762/// Typed work-lane origin for [`MeerkatMachineInput::Ingest`]. Closed set of
1763/// the work-lane labels the DSL observes on the admission seam — replaces
1764/// the former literal-string `origin` field. Structurally mirrors the
1765/// `MobMachine.RequestRuntimeIngress.origin` seam so the cross-machine
1766/// composition binds on a single typed enum instead of parallel
1767/// string-typed slots. Transport sources ([`meerkat_core::comms::InputSource`])
1768/// arriving from the shell side collapse to `External`; the
1769/// runtime-control-plane `Ingest` dispatch uses the dedicated `Ingest`
1770/// variant; mob-bridged ingress carries `External`/`Internal` matching
1771/// `meerkat-mob::ids::WorkOrigin`.
1772#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1773pub enum WorkOrigin {
1774    #[default]
1775    External,
1776    Internal,
1777    /// Canonical admission entrypoint fired by the runtime control plane
1778    /// with no surface-level transport or work-lane label.
1779    Ingest,
1780}
1781
1782impl From<meerkat_core::comms::InputSource> for WorkOrigin {
1783    fn from(src: meerkat_core::comms::InputSource) -> Self {
1784        match src {
1785            // Transport-originated inputs are `External` work-lane: they
1786            // entered the runtime via a non-mob transport (TCP/UDS/stdin/
1787            // webhook/RPC). Mob-originated work fires the DSL directly
1788            // with `External`/`Internal` instead of going through the
1789            // session-admission handle.
1790            meerkat_core::comms::InputSource::Tcp
1791            | meerkat_core::comms::InputSource::Uds
1792            | meerkat_core::comms::InputSource::Stdin
1793            | meerkat_core::comms::InputSource::Webhook
1794            | meerkat_core::comms::InputSource::Rpc => Self::External,
1795        }
1796    }
1797}
1798
1799/// Typed async-operation lifecycle status. Closed mirror of
1800/// [`meerkat_core::ops_lifecycle::OperationStatus`] — replaces the former
1801/// literal-string values in the DSL's `op_statuses` map.
1802///
1803/// The DSL writes these variants directly on each ops lifecycle transition
1804/// (`RegisterOp`, `StartOp`, `CompleteOp`, `FailOp`, `CancelOp`, `AbortOp`,
1805/// `RetireRequestedOp`, `RetireCompletedOp`, `TerminateOp`). The shell's
1806/// `ShellState::status()` reads the typed value directly and maps to the
1807/// domain enum via the `From` impl below — no string compares, no string
1808/// parsing.
1809#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1810pub enum OperationStatus {
1811    #[default]
1812    Absent,
1813    Provisioning,
1814    Running,
1815    Retiring,
1816    Completed,
1817    Failed,
1818    Aborted,
1819    Cancelled,
1820    Retired,
1821    Terminated,
1822}
1823
1824impl From<meerkat_core::ops_lifecycle::OperationStatus> for OperationStatus {
1825    fn from(status: meerkat_core::ops_lifecycle::OperationStatus) -> Self {
1826        match status {
1827            meerkat_core::ops_lifecycle::OperationStatus::Absent => Self::Absent,
1828            meerkat_core::ops_lifecycle::OperationStatus::Provisioning => Self::Provisioning,
1829            meerkat_core::ops_lifecycle::OperationStatus::Running => Self::Running,
1830            meerkat_core::ops_lifecycle::OperationStatus::Retiring => Self::Retiring,
1831            meerkat_core::ops_lifecycle::OperationStatus::Completed => Self::Completed,
1832            meerkat_core::ops_lifecycle::OperationStatus::Failed => Self::Failed,
1833            meerkat_core::ops_lifecycle::OperationStatus::Aborted => Self::Aborted,
1834            meerkat_core::ops_lifecycle::OperationStatus::Cancelled => Self::Cancelled,
1835            meerkat_core::ops_lifecycle::OperationStatus::Retired => Self::Retired,
1836            meerkat_core::ops_lifecycle::OperationStatus::Terminated => Self::Terminated,
1837        }
1838    }
1839}
1840
1841impl From<OperationStatus> for meerkat_core::ops_lifecycle::OperationStatus {
1842    fn from(status: OperationStatus) -> Self {
1843        match status {
1844            OperationStatus::Absent => Self::Absent,
1845            OperationStatus::Provisioning => Self::Provisioning,
1846            OperationStatus::Running => Self::Running,
1847            OperationStatus::Retiring => Self::Retiring,
1848            OperationStatus::Completed => Self::Completed,
1849            OperationStatus::Failed => Self::Failed,
1850            OperationStatus::Aborted => Self::Aborted,
1851            OperationStatus::Cancelled => Self::Cancelled,
1852            OperationStatus::Retired => Self::Retired,
1853            OperationStatus::Terminated => Self::Terminated,
1854        }
1855    }
1856}
1857
1858/// Typed discriminant mirror of
1859/// [`meerkat_core::ops_lifecycle::OperationTerminalOutcome`] — replaces the
1860/// former opaque JSON string carried in the DSL's `op_terminal_outcomes`
1861/// map. Unit variants only; payload data (completion result, failure error,
1862/// cancellation reason, terminated reason) rides on the companion
1863/// `op_terminal_payload: Map<String, String>` field of the DSL state as
1864/// JSON keyed to the same operation id, and is reconstructed in the shell
1865/// by pairing the typed discriminant with the companion entry.
1866///
1867/// The DSL writes these variants directly on each terminal transition
1868/// (`CompleteOp`, `FailOp`, `CancelOp`, `AbortOp`, `RetireCompletedOp`,
1869/// `TerminateOp`); the shell reads them through the typed map and rebuilds
1870/// the domain enum in `ShellState::terminal_outcome`.
1871#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1872pub enum OperationTerminalOutcomeKind {
1873    #[default]
1874    Completed,
1875    Failed,
1876    Aborted,
1877    Cancelled,
1878    Retired,
1879    Terminated,
1880}
1881
1882/// Typed input-abandonment reason. Closed mirror of the discriminant set of
1883/// [`crate::input_state::InputAbandonReason`] — replaces the former
1884/// `format!("{reason:?}")` Debug round-trip in the DSL's
1885/// `input_abandon_reason` map.
1886///
1887/// The `MaxAttemptsExhausted` variant's `attempts` payload rides on the
1888/// companion `input_abandon_attempt_count: Map<String, u64>` field of the
1889/// DSL state; this enum only carries the discriminant. The domain
1890/// `InputAbandonReason::MaxAttemptsExhausted { attempts }` is reconstructed
1891/// in the driver by pairing the typed discriminant with that companion map.
1892#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1893pub enum InputAbandonReason {
1894    #[default]
1895    Retired,
1896    Reset,
1897    Stopped,
1898    Destroyed,
1899    Cancelled,
1900    MaxAttemptsExhausted,
1901}
1902
1903impl From<&crate::input_state::InputAbandonReason> for InputAbandonReason {
1904    fn from(reason: &crate::input_state::InputAbandonReason) -> Self {
1905        match reason {
1906            crate::input_state::InputAbandonReason::Retired => Self::Retired,
1907            crate::input_state::InputAbandonReason::Reset => Self::Reset,
1908            crate::input_state::InputAbandonReason::Stopped => Self::Stopped,
1909            crate::input_state::InputAbandonReason::Destroyed => Self::Destroyed,
1910            crate::input_state::InputAbandonReason::Cancelled => Self::Cancelled,
1911            crate::input_state::InputAbandonReason::MaxAttemptsExhausted { .. } => {
1912                Self::MaxAttemptsExhausted
1913            }
1914        }
1915    }
1916}
1917
1918impl InputAbandonReason {
1919    /// Stable lowercase label for event wire formats. Mirrors the
1920    /// snake-case serde representation of the domain enum for consistency
1921    /// with existing consumers.
1922    pub const fn as_str(self) -> &'static str {
1923        match self {
1924            Self::Retired => "retired",
1925            Self::Reset => "reset",
1926            Self::Stopped => "stopped",
1927            Self::Destroyed => "destroyed",
1928            Self::Cancelled => "cancelled",
1929            Self::MaxAttemptsExhausted => "max_attempts_exhausted",
1930        }
1931    }
1932}
1933
1934/// Typed work-lane assignment for admitted inputs. Replaces the former
1935/// parallel `queue_lane` / `steer_lane` sets with a single map
1936/// (`input_lane: Map<String, Enum<InputLane>>`) so mutual exclusion is
1937/// structural — an admitted input is in exactly one lane by construction.
1938///
1939/// DSL-side mirror of the shell's `meerkat_core::types::HandlingMode`; the
1940/// DSL owns the typed mirror so transitions can carry it without depending
1941/// on the shell's domain enum.
1942#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1943pub enum InputLane {
1944    #[default]
1945    Queue,
1946    Steer,
1947}
1948
1949impl From<crate::HandlingMode> for InputLane {
1950    fn from(mode: crate::HandlingMode) -> Self {
1951        match mode {
1952            crate::HandlingMode::Queue => Self::Queue,
1953            crate::HandlingMode::Steer => Self::Steer,
1954        }
1955    }
1956}
1957
1958#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1959pub enum RoutingSwitchTurnPhase {
1960    #[default]
1961    Requested,
1962    PendingForBoundary,
1963    ActiveFiniteOverride,
1964    ApplyingPersistentReconfigure,
1965    Terminal,
1966}
1967
1968#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1969pub enum RoutingSwitchTurnTerminal {
1970    #[default]
1971    Denied,
1972    ConsumedAndRestored,
1973    PersistentReconfigureApplied,
1974}
1975
1976#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1977pub enum RoutingDenialReason {
1978    #[default]
1979    CapabilityPolicy,
1980    ApprovalRequiredButUnavailable,
1981    DeniedDuringApproval,
1982    ScopedOverrideConflict,
1983    RealtimeTransportConflict,
1984}
1985
1986#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1987pub enum RoutingApprovalPhase {
1988    #[default]
1989    Pending,
1990    PresentedToUser,
1991    Approved,
1992    Denied,
1993    SurfaceDetached,
1994}
1995
1996#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1997pub enum RoutingApprovalParentKind {
1998    #[default]
1999    SwitchTurn,
2000    ImageOperation,
2001}
2002
2003#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2004pub enum RoutingImageOperationPhase {
2005    #[default]
2006    Requested,
2007    PlanResolved,
2008    ScopedOverrideActive,
2009    ProviderCallInFlight,
2010    ResultCommitted,
2011    RestoringScopedOverride,
2012    Terminal,
2013}
2014
2015#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2016pub enum RoutingImageTerminal {
2017    #[default]
2018    Generated,
2019    Denied,
2020    EmptyResult,
2021    RefusedByProvider,
2022    SafetyFiltered,
2023    Failed,
2024    Cancelled,
2025    Timeout,
2026    ScopedRestoreFailed,
2027}
2028
2029// Track-B (R5): declarative peer endpoint descriptor for the runtime
2030// DSL. Shape mirrors `meerkat_core::comms::TrustedPeerDescriptor`.
2031// The catalog DSL holds an identical type; the two are structurally
2032// equivalent so the schema validator sees consistent opaque struct
2033// shapes.
2034#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2035pub struct PeerEndpoint {
2036    pub name: PeerName,
2037    pub peer_id: PeerId,
2038    pub address: PeerAddress,
2039    pub signing_key: PeerSigningKey,
2040}
2041
2042impl PeerEndpoint {
2043    pub fn new(
2044        name: impl Into<PeerName>,
2045        peer_id: impl Into<PeerId>,
2046        address: impl Into<PeerAddress>,
2047        signing_key: impl Into<PeerSigningKey>,
2048    ) -> Self {
2049        Self {
2050            name: name.into(),
2051            peer_id: peer_id.into(),
2052            address: address.into(),
2053            signing_key: signing_key.into(),
2054        }
2055    }
2056}
2057
2058impl From<&meerkat_core::comms::TrustedPeerDescriptor> for PeerEndpoint {
2059    fn from(spec: &meerkat_core::comms::TrustedPeerDescriptor) -> Self {
2060        Self {
2061            name: PeerName(spec.name.as_str().to_owned()),
2062            peer_id: PeerId(spec.peer_id.to_string()),
2063            address: PeerAddress(spec.address.to_string()),
2064            signing_key: PeerSigningKey(spec.pubkey),
2065        }
2066    }
2067}
2068
2069/// DSL-local carrier for the Ed25519 public signing key associated with a
2070/// peer endpoint. The MeerkatMachine owns this projection alongside the
2071/// endpoint identity atoms so trust reconciliation can install the exact
2072/// key into the comms trust store without shell-side defaults.
2073#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2074pub struct PeerSigningKey(pub [u8; 32]);
2075
2076impl From<[u8; 32]> for PeerSigningKey {
2077    fn from(key: [u8; 32]) -> Self {
2078        Self(key)
2079    }
2080}
2081
2082/// DSL-local newtype for a peer display name. Wraps the slug string
2083/// so the schema validator sees a stable opaque shape; mirrors
2084/// `meerkat_core::comms::PeerName` but avoids dragging the core
2085/// comms types into the DSL grammar.
2086#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2087pub struct PeerName(pub String);
2088
2089impl<T: Into<String>> From<T> for PeerName {
2090    fn from(s: T) -> Self {
2091        Self(s.into())
2092    }
2093}
2094
2095impl PeerName {
2096    pub fn as_str(&self) -> &str {
2097        &self.0
2098    }
2099}
2100
2101/// DSL-local newtype for the canonical peer routing id.
2102#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2103pub struct PeerId(pub String);
2104
2105impl<T: Into<String>> From<T> for PeerId {
2106    fn from(s: T) -> Self {
2107        Self(s.into())
2108    }
2109}
2110
2111impl PeerId {
2112    pub fn as_str(&self) -> &str {
2113        &self.0
2114    }
2115}
2116
2117/// DSL-local newtype for a peer transport endpoint URL.
2118#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
2119pub struct PeerAddress(pub String);
2120
2121impl<T: Into<String>> From<T> for PeerAddress {
2122    fn from(s: T) -> Self {
2123        Self(s.into())
2124    }
2125}
2126
2127impl PeerAddress {
2128    pub fn as_str(&self) -> &str {
2129        &self.0
2130    }
2131}
2132
2133// Ensure we keep the exact generated schema DSL body from the catalog source.
2134
2135// MeerkatMachine production body is catalog-owned. Keep bridge/runtime mechanics
2136// outside this macro invocation; canonical semantics live in the catalog DSL.
2137meerkat_machine_schema::meerkat_catalog_machine_dsl!("meerkat-runtime", "meerkat_machine::dsl");
2138
2139// =====================================================================