mimir_core/memory_kind.rs
1//! `MemoryKind` — the four canonical memory types from
2//! `docs/concepts/memory-type-taxonomy.md` § 5.
3//!
4//! Each variant is a distinct struct with distinct fields, lifecycle,
5//! and decay profile. Variants are frozen at the spec's graduation;
6//! adding a fifth type is a breaking change per `PRINCIPLES.md` § 10.
7
8use crate::clock::ClockTime;
9use crate::confidence::Confidence;
10use crate::symbol::SymbolId;
11use crate::value::Value;
12
13/// A Semantic memory — a general fact about the world.
14///
15/// Canonical examples: entity attributes, relationships, category
16/// memberships. See `memory-type-taxonomy.md` § 3.1.
17#[derive(Clone, Debug, PartialEq)]
18pub struct Semantic {
19 /// Subject.
20 pub s: SymbolId,
21 /// Predicate.
22 pub p: SymbolId,
23 /// Object — may be a symbol, literal value, or timestamp.
24 pub o: Value,
25 /// Grounding source.
26 pub source: SymbolId,
27 /// Stored confidence at write time.
28 pub confidence: Confidence,
29 /// When the fact became true in the world.
30 pub valid_at: ClockTime,
31}
32
33/// An Episodic memory — an event at a point in time.
34///
35/// See `memory-type-taxonomy.md` § 3.2. `participants` is ontic (actors
36/// in the event); `source` is epistemic (who witnessed or reported).
37#[derive(Clone, Debug, PartialEq)]
38pub struct Episodic {
39 /// Stable memory ID for this event.
40 pub event_id: SymbolId,
41 /// Event-type tag (e.g. `@rename`, `@commit`, `@discussion`).
42 pub kind: SymbolId,
43 /// Actors in the event (ontic).
44 pub participants: Vec<SymbolId>,
45 /// Where the event occurred.
46 pub location: SymbolId,
47 /// When the event occurred.
48 pub at_time: ClockTime,
49 /// When the recording agent observed the event — distinct from
50 /// `at_time`.
51 pub observed_at: ClockTime,
52 /// Witness / reporter (epistemic).
53 pub source: SymbolId,
54 /// Stored confidence at write time.
55 pub confidence: Confidence,
56}
57
58/// A Procedural memory — a trigger-action rule that directs future
59/// behavior.
60///
61/// See `memory-type-taxonomy.md` § 3.3.
62#[derive(Clone, Debug, PartialEq)]
63pub struct Procedural {
64 /// Stable memory ID for this rule.
65 pub rule_id: SymbolId,
66 /// Trigger condition — typically a string describing the match.
67 pub trigger: Value,
68 /// Action to take on match.
69 pub action: Value,
70 /// Optional additional gating on the trigger.
71 pub precondition: Option<Value>,
72 /// Scope in which this rule applies (e.g. `@mimir_repo`).
73 pub scope: SymbolId,
74 /// Grounding source.
75 pub source: SymbolId,
76 /// Stored confidence at write time.
77 pub confidence: Confidence,
78}
79
80/// An Inferential memory — a fact derived from other memories rather
81/// than from an external source.
82///
83/// See `memory-type-taxonomy.md` § 3.4. `derived_from` is non-empty;
84/// `method` resolves to a registered inference-method symbol.
85#[derive(Clone, Debug, PartialEq)]
86pub struct Inferential {
87 /// Subject.
88 pub s: SymbolId,
89 /// Predicate.
90 pub p: SymbolId,
91 /// Object.
92 pub o: Value,
93 /// Parent memory IDs that this derivation depends on. Must be
94 /// non-empty.
95 pub derived_from: Vec<SymbolId>,
96 /// Registered inference method (resolves to a Symbol of kind
97 /// `InferenceMethod`).
98 pub method: SymbolId,
99 /// Stored confidence at derivation time.
100 pub confidence: Confidence,
101 /// When the derived claim is taken to hold true.
102 pub valid_at: ClockTime,
103}
104
105/// A canonical memory kind.
106///
107/// Four variants, frozen per `memory-type-taxonomy.md` § 5. The enum is
108/// **not** `#[non_exhaustive]`; adding a fifth type is a breaking
109/// change under semver (`PRINCIPLES.md` § 10).
110#[derive(Clone, Debug, PartialEq)]
111pub enum MemoryKind {
112 /// General facts about the world.
113 Semantic(Semantic),
114 /// Events at a point in time.
115 Episodic(Episodic),
116 /// Rules that direct future behavior.
117 Procedural(Procedural),
118 /// Facts derived from other memories.
119 Inferential(Inferential),
120}
121
122/// A compact tag for the memory kind without the variant body.
123///
124/// Used in admission-check APIs like [`crate::SourceKind::admits`]
125/// where only the tag is needed, not the full record.
126#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
127pub enum MemoryKindTag {
128 /// Tag for [`Semantic`].
129 Semantic,
130 /// Tag for [`Episodic`].
131 Episodic,
132 /// Tag for [`Procedural`].
133 Procedural,
134 /// Tag for [`Inferential`].
135 Inferential,
136}
137
138impl MemoryKind {
139 /// Compact tag for this memory kind.
140 #[must_use]
141 pub const fn tag(&self) -> MemoryKindTag {
142 match self {
143 Self::Semantic(_) => MemoryKindTag::Semantic,
144 Self::Episodic(_) => MemoryKindTag::Episodic,
145 Self::Procedural(_) => MemoryKindTag::Procedural,
146 Self::Inferential(_) => MemoryKindTag::Inferential,
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 fn clock(millis: u64) -> ClockTime {
156 ClockTime::try_from_millis(millis).expect("non-sentinel")
157 }
158
159 fn conf(raw: u16) -> Confidence {
160 Confidence::from_u16(raw)
161 }
162
163 #[test]
164 fn tag_reflects_variant() {
165 let sem = MemoryKind::Semantic(Semantic {
166 s: SymbolId::new(1),
167 p: SymbolId::new(2),
168 o: Value::String("x".into()),
169 source: SymbolId::new(3),
170 confidence: conf(62_258),
171 valid_at: clock(1_000),
172 });
173 assert_eq!(sem.tag(), MemoryKindTag::Semantic);
174
175 let epi = MemoryKind::Episodic(Episodic {
176 event_id: SymbolId::new(10),
177 kind: SymbolId::new(11),
178 participants: vec![SymbolId::new(12)],
179 location: SymbolId::new(13),
180 at_time: clock(1_000),
181 observed_at: clock(1_000),
182 source: SymbolId::new(14),
183 confidence: conf(u16::MAX),
184 });
185 assert_eq!(epi.tag(), MemoryKindTag::Episodic);
186
187 let pro = MemoryKind::Procedural(Procedural {
188 rule_id: SymbolId::new(20),
189 trigger: Value::String("agent about to write".into()),
190 action: Value::String("route via librarian".into()),
191 precondition: None,
192 scope: SymbolId::new(21),
193 source: SymbolId::new(22),
194 confidence: conf(u16::MAX),
195 });
196 assert_eq!(pro.tag(), MemoryKindTag::Procedural);
197
198 let inf = MemoryKind::Inferential(Inferential {
199 s: SymbolId::new(30),
200 p: SymbolId::new(31),
201 o: Value::Boolean(true),
202 derived_from: vec![SymbolId::new(32), SymbolId::new(33)],
203 method: SymbolId::new(34),
204 confidence: conf(50_000),
205 valid_at: clock(2_000),
206 });
207 assert_eq!(inf.tag(), MemoryKindTag::Inferential);
208 }
209}