Skip to main content

mempill_types/
provenance.rs

1//! Provenance types: the typed, immutable channel assigned to every write.
2//!
3//! Provenance is set at injection time and never rewritten. It determines routing
4//! through the adjudication gate and whether a claim is eligible for the cheap
5//! (non-conflicting) commit path.
6
7/// The provenance channel assigned to every write — required, typed, and immutable.
8///
9/// Provenance is set at injection time and cannot be changed after the claim is committed.
10/// It determines how the engine routes the claim through the adjudication gate.
11/// Model-emitted content must use `ModelDerived` — the ingestion gateway enforces this
12/// and callers cannot override it by supplying a more prestigious label.
13#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
14#[serde(tag = "type", content = "kind")]
15#[non_exhaustive]
16pub enum ProvenanceLabel {
17    /// First-hand external evidence — the ONLY cheap-path-eligible channel.
18    External(ExternalKind),
19    /// Content the engine itself previously served, re-entering the write path.
20    /// Caught by the Amplification Guard (firewall). Corroborates by identity; never becomes ground truth.
21    RecallReEntry,
22    /// Model-emitted / inferred content. The mandatory default for model output.
23    /// Committed down-weighted (Inferred disposition); ineligible to overturn until anchored.
24    ModelDerived,
25}
26
27/// Sub-channel for External provenance.
28#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
29#[non_exhaustive]
30pub enum ExternalKind {
31    /// A first-hand human assertion (user as oracle).
32    UserAsserted,
33    /// First-hand external evidence (tool result, system-of-record, sensor).
34    ExternalFirstHand,
35}
36
37impl ProvenanceLabel {
38    /// Returns true iff this label is eligible for the cheap (non-conflicting commit) path.
39    /// Only External(*) qualifies. RecallReEntry and ModelDerived never qualify.
40    pub fn is_cheap_path_eligible(&self) -> bool {
41        matches!(self, Self::External(_))
42    }
43
44    /// Returns true iff this is a RecallReEntry — must be caught by the Amplification Guard.
45    pub fn is_recall_reentry(&self) -> bool {
46        matches!(self, Self::RecallReEntry)
47    }
48}
49
50/// Distance from the nearest first-hand external anchor in the provenance lineage.
51/// Derivation depth 0 = the claim is itself a first-hand external claim.
52/// Claims with a depth exceeding the configured cap are ineligible for currency boosts or
53/// overturning an incumbent belief.
54#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
55pub struct ExternalAnchor {
56    /// ClaimRef of the nearest first-hand external claim in the lineage, if known.
57    pub nearest_external_anchor: Option<crate::identity::ClaimRef>,
58    /// Number of inference hops from that anchor. 0 = this claim is first-hand external.
59    pub derivation_depth: u32,
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn external_user_asserted_is_cheap_path_eligible() {
68        let p = ProvenanceLabel::External(ExternalKind::UserAsserted);
69        assert!(p.is_cheap_path_eligible());
70        assert!(!p.is_recall_reentry());
71    }
72
73    #[test]
74    fn external_first_hand_is_cheap_path_eligible() {
75        let p = ProvenanceLabel::External(ExternalKind::ExternalFirstHand);
76        assert!(p.is_cheap_path_eligible());
77        assert!(!p.is_recall_reentry());
78    }
79
80    #[test]
81    fn recall_reentry_is_not_cheap_path_eligible() {
82        let p = ProvenanceLabel::RecallReEntry;
83        assert!(!p.is_cheap_path_eligible());
84        assert!(p.is_recall_reentry());
85    }
86
87    #[test]
88    fn model_derived_is_neither() {
89        let p = ProvenanceLabel::ModelDerived;
90        assert!(!p.is_cheap_path_eligible());
91        assert!(!p.is_recall_reentry());
92    }
93
94    #[test]
95    fn provenance_label_round_trip_serde() {
96        let labels = [
97            ProvenanceLabel::External(ExternalKind::UserAsserted),
98            ProvenanceLabel::External(ExternalKind::ExternalFirstHand),
99            ProvenanceLabel::RecallReEntry,
100            ProvenanceLabel::ModelDerived,
101        ];
102        for label in &labels {
103            let json = serde_json::to_string(label).unwrap();
104            let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
105            assert_eq!(label, &back);
106        }
107    }
108
109    #[test]
110    fn provenance_label_python_friendly_json_shapes() {
111        // External(UserAsserted) → adjacently-tagged: {"type":"External","kind":"UserAsserted"}
112        let ext_ua = ProvenanceLabel::External(ExternalKind::UserAsserted);
113        let json = serde_json::to_string(&ext_ua).unwrap();
114        assert_eq!(json, r#"{"type":"External","kind":"UserAsserted"}"#);
115        let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
116        assert_eq!(ext_ua, back);
117
118        // External(ExternalFirstHand) → {"type":"External","kind":"ExternalFirstHand"}
119        let ext_fh = ProvenanceLabel::External(ExternalKind::ExternalFirstHand);
120        let json = serde_json::to_string(&ext_fh).unwrap();
121        assert_eq!(json, r#"{"type":"External","kind":"ExternalFirstHand"}"#);
122        let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
123        assert_eq!(ext_fh, back);
124
125        // RecallReEntry → {"type":"RecallReEntry"} (unit variant — no "kind" key)
126        let rre = ProvenanceLabel::RecallReEntry;
127        let json = serde_json::to_string(&rre).unwrap();
128        assert_eq!(json, r#"{"type":"RecallReEntry"}"#);
129        let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
130        assert_eq!(rre, back);
131
132        // ModelDerived → {"type":"ModelDerived"}
133        let md = ProvenanceLabel::ModelDerived;
134        let json = serde_json::to_string(&md).unwrap();
135        assert_eq!(json, r#"{"type":"ModelDerived"}"#);
136        let back: ProvenanceLabel = serde_json::from_str(&json).unwrap();
137        assert_eq!(md, back);
138    }
139
140    #[test]
141    fn external_anchor_round_trip_serde() {
142        let anchor = ExternalAnchor {
143            nearest_external_anchor: Some(crate::identity::ClaimRef::new_random()),
144            derivation_depth: 2,
145        };
146        let json = serde_json::to_string(&anchor).unwrap();
147        let back: ExternalAnchor = serde_json::from_str(&json).unwrap();
148        assert_eq!(anchor, back);
149    }
150}