Skip to main content

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}