Skip to main content

meerkat_runtime/
identifiers.rs

1//! ยง6 Runtime-layer identifiers.
2//!
3//! These identifiers are used only by the runtime control-plane layer.
4//! Core-facing identifiers (RunId, InputId) live in `meerkat-core::lifecycle`.
5
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9/// Unique identifier for a runtime event.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct RuntimeEventId(pub Uuid);
12
13impl RuntimeEventId {
14    pub fn new() -> Self {
15        Self(Uuid::now_v7())
16    }
17}
18
19impl Default for RuntimeEventId {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl std::fmt::Display for RuntimeEventId {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        write!(f, "{}", self.0)
28    }
29}
30
31/// Logical identity of a runtime instance (survives retire/respawn).
32#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub struct LogicalRuntimeId(pub String);
34
35impl LogicalRuntimeId {
36    pub fn new(id: impl Into<String>) -> Self {
37        Self(id.into())
38    }
39}
40
41impl std::fmt::Display for LogicalRuntimeId {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(f, "{}", self.0)
44    }
45}
46
47/// Identifier for a conversation within a session.
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub struct ConversationId(pub Uuid);
50
51impl ConversationId {
52    pub fn new() -> Self {
53        Self(Uuid::now_v7())
54    }
55}
56
57impl Default for ConversationId {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl std::fmt::Display for ConversationId {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "{}", self.0)
66    }
67}
68
69/// Identifier linking an event to its cause.
70#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
71pub struct CausationId(pub Uuid);
72
73impl Default for CausationId {
74    fn default() -> Self {
75        Self::new()
76    }
77}
78
79impl CausationId {
80    pub fn new() -> Self {
81        Self(Uuid::now_v7())
82    }
83
84    pub fn from_uuid(uuid: Uuid) -> Self {
85        Self(uuid)
86    }
87}
88
89impl std::fmt::Display for CausationId {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        write!(f, "{}", self.0)
92    }
93}
94
95/// Correlation identifier for tracing related events across boundaries.
96#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
97pub struct CorrelationId(pub Uuid);
98
99impl Default for CorrelationId {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl CorrelationId {
106    pub fn new() -> Self {
107        Self(Uuid::now_v7())
108    }
109
110    pub fn from_uuid(uuid: Uuid) -> Self {
111        Self(uuid)
112    }
113}
114
115impl std::fmt::Display for CorrelationId {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        write!(f, "{}", self.0)
118    }
119}
120
121/// Client-provided key for idempotent input submission.
122#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
123pub struct IdempotencyKey(pub String);
124
125impl IdempotencyKey {
126    pub fn new(key: impl Into<String>) -> Self {
127        Self(key.into())
128    }
129}
130
131impl std::fmt::Display for IdempotencyKey {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        write!(f, "{}", self.0)
134    }
135}
136
137/// Key for supersession scoping (same key = same supersession window).
138#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
139pub struct SupersessionKey(pub String);
140
141impl SupersessionKey {
142    pub fn new(key: impl Into<String>) -> Self {
143        Self(key.into())
144    }
145}
146
147impl std::fmt::Display for SupersessionKey {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        write!(f, "{}", self.0)
150    }
151}
152
153/// Version of the policy table used for a decision.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
155pub struct PolicyVersion(pub u64);
156
157impl std::fmt::Display for PolicyVersion {
158    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159        write!(f, "{}", self.0)
160    }
161}
162
163/// Identifier for an input kind (e.g., "prompt", "peer_message").
164#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
165pub struct KindId(pub String);
166
167impl KindId {
168    pub fn new(id: impl Into<String>) -> Self {
169        Self(id.into())
170    }
171}
172
173impl std::fmt::Display for KindId {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        write!(f, "{}", self.0)
176    }
177}
178
179/// Identifier for a schema definition.
180#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
181pub struct SchemaId(pub String);
182
183impl SchemaId {
184    pub fn new(id: impl Into<String>) -> Self {
185        Self(id.into())
186    }
187}
188
189impl std::fmt::Display for SchemaId {
190    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191        write!(f, "{}", self.0)
192    }
193}
194
195/// Identifier for a projection rule.
196#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
197pub struct ProjectionRuleId(pub String);
198
199impl ProjectionRuleId {
200    pub fn new(id: impl Into<String>) -> Self {
201        Self(id.into())
202    }
203}
204
205impl std::fmt::Display for ProjectionRuleId {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        write!(f, "{}", self.0)
208    }
209}
210
211/// Stable event code for wire formats and SDK consumers.
212#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
213pub struct EventCodeId(pub String);
214
215impl EventCodeId {
216    pub fn new(id: impl Into<String>) -> Self {
217        Self(id.into())
218    }
219}
220
221impl std::fmt::Display for EventCodeId {
222    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223        write!(f, "{}", self.0)
224    }
225}
226
227#[cfg(test)]
228#[allow(clippy::unwrap_used)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn runtime_event_id_unique() {
234        let a = RuntimeEventId::new();
235        let b = RuntimeEventId::new();
236        assert_ne!(a, b);
237    }
238
239    #[test]
240    fn runtime_event_id_serde() {
241        let id = RuntimeEventId::new();
242        let json = serde_json::to_string(&id).unwrap();
243        let parsed: RuntimeEventId = serde_json::from_str(&json).unwrap();
244        assert_eq!(id, parsed);
245    }
246
247    #[test]
248    fn logical_runtime_id_serde() {
249        let id = LogicalRuntimeId::new("agent-1");
250        let json = serde_json::to_string(&id).unwrap();
251        let parsed: LogicalRuntimeId = serde_json::from_str(&json).unwrap();
252        assert_eq!(id, parsed);
253        assert_eq!(id.to_string(), "agent-1");
254    }
255
256    #[test]
257    fn conversation_id_unique() {
258        let a = ConversationId::new();
259        let b = ConversationId::new();
260        assert_ne!(a, b);
261    }
262
263    #[test]
264    fn idempotency_key_serde() {
265        let key = IdempotencyKey::new("req-abc-123");
266        let json = serde_json::to_string(&key).unwrap();
267        let parsed: IdempotencyKey = serde_json::from_str(&json).unwrap();
268        assert_eq!(key, parsed);
269    }
270
271    #[test]
272    fn supersession_key_serde() {
273        let key = SupersessionKey::new("peer-status");
274        let json = serde_json::to_string(&key).unwrap();
275        let parsed: SupersessionKey = serde_json::from_str(&json).unwrap();
276        assert_eq!(key, parsed);
277    }
278
279    #[test]
280    fn policy_version_serde() {
281        let v = PolicyVersion(42);
282        let json = serde_json::to_string(&v).unwrap();
283        let parsed: PolicyVersion = serde_json::from_str(&json).unwrap();
284        assert_eq!(v, parsed);
285    }
286
287    #[test]
288    fn kind_id_display() {
289        let id = KindId::new("prompt");
290        assert_eq!(id.to_string(), "prompt");
291    }
292}