mimir_core/source_kind.rs
1//! `SourceKind` — the eleven-kind source taxonomy from
2//! `docs/concepts/grounding-model.md` § 3.
3//!
4//! `confidence_bound()` and `admits()` are `const fn` so the taxonomy
5//! is compile-time-resolved (per grounding-model.md § 4).
6
7use crate::confidence::Confidence;
8use crate::memory_kind::MemoryKindTag;
9
10/// Kind of grounding source attached to a memory.
11///
12/// Eleven kinds, matching `grounding-model.md` § 3. `#[non_exhaustive]`
13/// so additions do not break semver.
14///
15/// # Examples
16///
17/// ```
18/// use mimir_core::{Confidence, MemoryKindTag, SourceKind};
19///
20/// let bound = SourceKind::Profile.confidence_bound();
21/// assert!(bound < Confidence::ONE);
22/// assert!(SourceKind::Profile.admits(MemoryKindTag::Semantic));
23/// assert!(!SourceKind::Profile.admits(MemoryKindTag::Procedural));
24/// ```
25#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
26#[non_exhaustive]
27pub enum SourceKind {
28 /// User / entity-provided identity or attribute data.
29 Profile,
30 /// Directly witnessed by an agent.
31 Observation,
32 /// Subject reported the fact about themselves.
33 SelfReport,
34 /// A participant in an event reported it.
35 ParticipantReport,
36 /// A cited document, URL, paper, or canonical spec.
37 Document,
38 /// An authoritative registry (package manifest, DNS, filesystem).
39 Registry,
40 /// A deliberate act of policy-making by a rule-maker.
41 Policy,
42 /// An instruction from the agent's operator / owner.
43 AgentInstruction,
44 /// A trusted third-party service or API (not a static document).
45 ExternalAuthority,
46 /// A transitional marker: the claim has not been primary-source
47 /// verified yet.
48 PendingVerification,
49 /// A fact the librarian itself emitted (timestamps, symbol IDs).
50 LibrarianAssignment,
51}
52
53impl SourceKind {
54 /// Default confidence upper bound for this source kind.
55 ///
56 /// User-overridable via `mimir.toml` per `confidence-decay.md` § 4.
57 /// This method returns the shipped default only.
58 #[must_use]
59 pub const fn confidence_bound(self) -> Confidence {
60 // Fixed-point `u16` values derived per grounding-model.md § 3 table.
61 // 1.0 = u16::MAX (65535); 0.95 ≈ 62258; 0.9 ≈ 58982; 0.85 ≈ 55705;
62 // 0.6 ≈ 39321.
63 match self {
64 Self::Observation | Self::Policy | Self::LibrarianAssignment => Confidence::ONE,
65 Self::Profile | Self::Registry | Self::AgentInstruction => Confidence::from_u16(62_258),
66 Self::SelfReport | Self::Document | Self::ExternalAuthority => {
67 Confidence::from_u16(58_982)
68 }
69 Self::ParticipantReport => Confidence::from_u16(55_705),
70 Self::PendingVerification => Confidence::from_u16(39_321),
71 }
72 }
73
74 /// Whether this source kind is admissible for a given memory kind.
75 ///
76 /// Matches the `Admits` column in `grounding-model.md` § 3.1 table.
77 /// Inferential memories do not use the `source` field (they use
78 /// `derived_from` + `method`), so `SourceKind::admits(Inferential)`
79 /// is never `true`.
80 #[must_use]
81 pub const fn admits(self, kind: MemoryKindTag) -> bool {
82 // Inferential memories never carry a `source` field.
83 if matches!(kind, MemoryKindTag::Inferential) {
84 return false;
85 }
86 match self {
87 // Admit Semantic only: Profile, Document, Registry,
88 // ExternalAuthority, LibrarianAssignment.
89 Self::Profile
90 | Self::Document
91 | Self::Registry
92 | Self::ExternalAuthority
93 | Self::LibrarianAssignment => matches!(kind, MemoryKindTag::Semantic),
94 Self::Observation | Self::SelfReport => {
95 matches!(kind, MemoryKindTag::Semantic | MemoryKindTag::Episodic)
96 }
97 Self::ParticipantReport => matches!(kind, MemoryKindTag::Episodic),
98 Self::Policy => matches!(kind, MemoryKindTag::Procedural),
99 Self::AgentInstruction => {
100 matches!(kind, MemoryKindTag::Procedural | MemoryKindTag::Semantic)
101 }
102 Self::PendingVerification => {
103 matches!(
104 kind,
105 MemoryKindTag::Semantic | MemoryKindTag::Episodic | MemoryKindTag::Procedural
106 )
107 }
108 }
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn inferential_admits_none() {
118 for kind in [
119 SourceKind::Profile,
120 SourceKind::Observation,
121 SourceKind::SelfReport,
122 SourceKind::ParticipantReport,
123 SourceKind::Document,
124 SourceKind::Registry,
125 SourceKind::Policy,
126 SourceKind::AgentInstruction,
127 SourceKind::ExternalAuthority,
128 SourceKind::PendingVerification,
129 SourceKind::LibrarianAssignment,
130 ] {
131 assert!(
132 !kind.admits(MemoryKindTag::Inferential),
133 "{kind:?} must not admit Inferential"
134 );
135 }
136 }
137
138 #[test]
139 fn policy_admits_only_procedural() {
140 assert!(SourceKind::Policy.admits(MemoryKindTag::Procedural));
141 assert!(!SourceKind::Policy.admits(MemoryKindTag::Semantic));
142 assert!(!SourceKind::Policy.admits(MemoryKindTag::Episodic));
143 }
144
145 #[test]
146 fn pending_verification_bounded_at_point_six() {
147 let c = SourceKind::PendingVerification.confidence_bound();
148 assert!((c.as_f32() - 0.6).abs() < 1e-3);
149 }
150
151 #[test]
152 fn observation_is_fully_confident() {
153 assert_eq!(SourceKind::Observation.confidence_bound(), Confidence::ONE);
154 }
155}