Skip to main content

meerkat_core/lifecycle/
identifiers.rs

1//! Core lifecycle identifiers
2//!
3//! Only identifiers that core directly operates on during run execution.
4//! Runtime-only identifiers (RuntimeEventId, LogicalRuntimeId, etc.) live in `meerkat-runtime`.
5
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9/// Unique identifier for a run (a single execution of the agent loop).
10///
11/// Core emits this in `RunEvent` and tracks it across the run lifecycle.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct RunId(pub Uuid);
14
15impl RunId {
16    /// Create a new run ID using UUID v7 (time-ordered).
17    pub fn new() -> Self {
18        Self(crate::time_compat::new_uuid_v7())
19    }
20
21    /// Create from an existing UUID.
22    pub fn from_uuid(uuid: Uuid) -> Self {
23        Self(uuid)
24    }
25}
26
27impl Default for RunId {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl std::fmt::Display for RunId {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(f, "{}", self.0)
36    }
37}
38
39/// Opaque identifier for an authority-owned async wait request.
40///
41/// Runtime-owned barrier waits use this to distinguish the wait lifecycle from
42/// the turn `RunId` they eventually feed back into.
43#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
44pub struct WaitRequestId(pub Uuid);
45
46impl WaitRequestId {
47    /// Create a new wait request ID using UUID v7 (time-ordered).
48    pub fn new() -> Self {
49        Self(crate::time_compat::new_uuid_v7())
50    }
51
52    /// Create from an existing UUID.
53    pub fn from_uuid(uuid: Uuid) -> Self {
54        Self(uuid)
55    }
56}
57
58impl Default for WaitRequestId {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64impl std::fmt::Display for WaitRequestId {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(f, "{}", self.0)
67    }
68}
69
70/// Opaque identifier for an input accepted by the runtime layer.
71///
72/// Core passes this through in `contributing_input_ids` on receipts and events
73/// but NEVER interprets it. The runtime layer creates and manages these.
74#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
75pub struct InputId(pub Uuid);
76
77impl InputId {
78    /// Create a new input ID using UUID v7 (time-ordered).
79    pub fn new() -> Self {
80        Self(crate::time_compat::new_uuid_v7())
81    }
82
83    /// Create from an existing UUID.
84    pub fn from_uuid(uuid: Uuid) -> Self {
85        Self(uuid)
86    }
87}
88
89impl Default for InputId {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95impl std::fmt::Display for InputId {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        write!(f, "{}", self.0)
98    }
99}
100
101#[cfg(test)]
102#[allow(clippy::unwrap_used)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn run_id_new_is_unique() {
108        let a = RunId::new();
109        let b = RunId::new();
110        assert_ne!(a, b);
111    }
112
113    #[test]
114    fn run_id_from_uuid_roundtrip() {
115        let uuid = Uuid::now_v7();
116        let id = RunId::from_uuid(uuid);
117        assert_eq!(id.0, uuid);
118    }
119
120    #[test]
121    fn run_id_serde_roundtrip() {
122        let id = RunId::new();
123        let json = serde_json::to_string(&id).unwrap();
124        let parsed: RunId = serde_json::from_str(&json).unwrap();
125        assert_eq!(id, parsed);
126    }
127
128    #[test]
129    fn run_id_display() {
130        let uuid = Uuid::nil();
131        let id = RunId::from_uuid(uuid);
132        assert_eq!(id.to_string(), "00000000-0000-0000-0000-000000000000");
133    }
134
135    #[test]
136    fn input_id_new_is_unique() {
137        let a = InputId::new();
138        let b = InputId::new();
139        assert_ne!(a, b);
140    }
141
142    #[test]
143    fn wait_request_id_new_is_unique() {
144        let a = WaitRequestId::new();
145        let b = WaitRequestId::new();
146        assert_ne!(a, b);
147    }
148
149    #[test]
150    fn wait_request_id_serde_roundtrip() {
151        let id = WaitRequestId::new();
152        let json = serde_json::to_string(&id).unwrap();
153        let parsed: WaitRequestId = serde_json::from_str(&json).unwrap();
154        assert_eq!(id, parsed);
155    }
156
157    #[test]
158    fn wait_request_id_display() {
159        let uuid = Uuid::nil();
160        let id = WaitRequestId::from_uuid(uuid);
161        assert_eq!(id.to_string(), "00000000-0000-0000-0000-000000000000");
162    }
163
164    #[test]
165    fn input_id_serde_roundtrip() {
166        let id = InputId::new();
167        let json = serde_json::to_string(&id).unwrap();
168        let parsed: InputId = serde_json::from_str(&json).unwrap();
169        assert_eq!(id, parsed);
170    }
171
172    #[test]
173    fn input_id_display() {
174        let uuid = Uuid::nil();
175        let id = InputId::from_uuid(uuid);
176        assert_eq!(id.to_string(), "00000000-0000-0000-0000-000000000000");
177    }
178
179    #[test]
180    fn run_id_and_input_id_are_distinct_types() {
181        // Compile-time type safety: these are different types
182        let run_id = RunId::new();
183        let wait_request_id = WaitRequestId::new();
184        let input_id = InputId::new();
185        // They cannot be compared directly (different types)
186        let _ = run_id;
187        let _ = wait_request_id;
188        let _ = input_id;
189    }
190}