Skip to main content

mempill_core/
config.rs

1//! EngineConfig — all tunable engine parameters.
2//!
3//! All tunables are named fields here and never hardcoded elsewhere.
4//! Default values are illustrative starting points; calibrate post-v0.1 based on
5//! your observed incident distribution.
6//! Every tunable field is tagged `// calibrate post-v0.1` in the source.
7
8use std::time::Duration;
9
10use mempill_types::Criticality;
11
12/// Configuration for the mempill engine.
13///
14/// All fields are runtime-tunable parameters, loadable from environment or config file.
15/// Defaults are illustrative v0.1 starting values; calibrate post-v0.1 based on your
16/// observed incident distribution.
17#[derive(Debug, Clone)]
18pub struct EngineConfig {
19    /// Minimum valid_time_confidence required to treat extracted valid-time as authoritative.
20    /// Below this threshold the engine treats valid-time as unknown and falls back to
21    /// transaction-time ordering for belief ranking.
22    // calibrate post-v0.1
23    pub valid_time_confidence_threshold: f32,
24
25    /// Minimum number of provenance-independent corroborating sources required for a currency boost.
26    /// Corroboration is a confidence modifier only — it does not by itself flip the routing decision.
27    // calibrate post-v0.1
28    pub corroboration_count_for_currency_boost: u32,
29
30    /// Daily fractional decay rate for currency decay.
31    /// Applied as: `current_currency = initial * (1 - rate) ^ days_since_refresh`.
32    // calibrate post-v0.1
33    pub currency_decay_rate_per_day: f32,
34
35    /// Minimum criticality at which oracle escalation is mandatory for belief-overturning operations.
36    // calibrate post-v0.1
37    pub criticality_overturn_floor: Criticality,
38
39    /// Maximum derivation depth eligible for currency boosts (provenance laundering cap).
40    /// Claims with a depth exceeding this cap cannot receive currency boosts from corroboration.
41    // calibrate post-v0.1
42    pub derivation_depth_cap_for_currency_boost: u32,
43
44    /// Maximum derivation depth eligible to overturn an incumbent belief (self-limiting cap).
45    /// Claims with a depth exceeding this cap cannot overturn; they route to PendingConflict instead.
46    // calibrate post-v0.1
47    pub derivation_depth_cap_for_overturning: u32,
48
49    /// Number of identical RecallReEntry candidates in a single write batch that triggers Quarantine.
50    /// This is the Amplification Guard burst detection threshold.
51    // calibrate post-v0.1
52    pub quarantine_burst_threshold: u32,
53
54    /// Days since last currency refresh before a belief enters the `AgingUnconfirmed` state.
55    // calibrate post-v0.1
56    pub aging_unconfirmed_threshold_days: u32,
57
58    /// Days since last currency refresh before a belief enters the `Decayed` state.
59    // calibrate post-v0.1
60    pub decayed_threshold_days: u32,
61
62    /// Default TTL for pending adjudications.
63    ///
64    /// When `Some(d)`, every pending row gets `expires_at = queued_at + d`. When `None`,
65    /// no TTL is set and pending rows never expire via the engine sweep.
66    ///
67    /// Per-request TTL override is deferred to a future wave (the `IngestClaimRequest` DTO
68    /// does not yet carry a TTL field); the config default is the v1 mechanism.
69    // OP-3: calibrate post-v0.1
70    pub default_adjudication_ttl: Option<Duration>,
71}
72
73impl Default for EngineConfig {
74    /// Illustrative defaults — calibrate post-v0.1 based on your production incident distribution.
75    fn default() -> Self {
76        Self {
77            valid_time_confidence_threshold: 0.7,
78            corroboration_count_for_currency_boost: 2,
79            currency_decay_rate_per_day: 0.05,
80            criticality_overturn_floor: Criticality::High,
81            derivation_depth_cap_for_currency_boost: 3,
82            derivation_depth_cap_for_overturning: 2,
83            quarantine_burst_threshold: 10,
84            aging_unconfirmed_threshold_days: 30,
85            decayed_threshold_days: 90,
86            default_adjudication_ttl: None,
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn default_valid_time_confidence_threshold() {
97        assert_eq!(EngineConfig::default().valid_time_confidence_threshold, 0.7);
98    }
99
100    #[test]
101    fn default_corroboration_count_for_currency_boost() {
102        assert_eq!(EngineConfig::default().corroboration_count_for_currency_boost, 2);
103    }
104
105    #[test]
106    fn default_currency_decay_rate_per_day() {
107        // Use approximate equality for f32
108        let delta = (EngineConfig::default().currency_decay_rate_per_day - 0.05).abs();
109        assert!(delta < f32::EPSILON * 10.0, "expected 0.05, got {}", EngineConfig::default().currency_decay_rate_per_day);
110    }
111
112    #[test]
113    fn default_criticality_overturn_floor() {
114        assert_eq!(EngineConfig::default().criticality_overturn_floor, Criticality::High);
115    }
116
117    #[test]
118    fn default_derivation_depth_cap_for_currency_boost() {
119        assert_eq!(EngineConfig::default().derivation_depth_cap_for_currency_boost, 3);
120    }
121
122    #[test]
123    fn default_derivation_depth_cap_for_overturning() {
124        assert_eq!(EngineConfig::default().derivation_depth_cap_for_overturning, 2);
125    }
126
127    #[test]
128    fn default_quarantine_burst_threshold() {
129        assert_eq!(EngineConfig::default().quarantine_burst_threshold, 10);
130    }
131
132    #[test]
133    fn default_aging_unconfirmed_threshold_days() {
134        assert_eq!(EngineConfig::default().aging_unconfirmed_threshold_days, 30);
135    }
136
137    #[test]
138    fn default_decayed_threshold_days() {
139        assert_eq!(EngineConfig::default().decayed_threshold_days, 90);
140    }
141
142    #[test]
143    fn engine_config_is_clone() {
144        let cfg = EngineConfig::default();
145        let cloned = cfg.clone();
146        assert_eq!(cloned.quarantine_burst_threshold, 10);
147    }
148
149    #[test]
150    fn engine_config_debug_format_contains_field_names() {
151        let s = format!("{:?}", EngineConfig::default());
152        assert!(s.contains("valid_time_confidence_threshold"));
153        assert!(s.contains("quarantine_burst_threshold"));
154    }
155}