Skip to main content

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}