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}