Skip to main content

sochdb_memory/
provenance.rs

1use serde::{Deserialize, Serialize};
2
3/// Transparent trust score: weighted sum, not learned.
4#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
5pub struct TrustScore {
6    pub value: f32,
7    pub source_count: u32,
8    pub recency_factor: f32,
9    pub contradiction_penalty: f32,
10}
11
12#[derive(Debug, Clone)]
13pub struct TrustScoreConfig {
14    pub source_weight: f32,
15    pub recency_weight: f32,
16    pub contradiction_weight: f32,
17    pub half_life_secs: f64,
18}
19
20impl Default for TrustScoreConfig {
21    fn default() -> Self {
22        Self {
23            source_weight: 0.4,
24            recency_weight: 0.4,
25            contradiction_weight: 0.2,
26            half_life_secs: 86_400.0 * 30.0,
27        }
28    }
29}
30
31impl TrustScore {
32    pub fn compute(
33        cfg: &TrustScoreConfig,
34        source_count: u32,
35        t_created: u64,
36        contradiction_count: u32,
37    ) -> Self {
38        let now = std::time::SystemTime::now()
39            .duration_since(std::time::UNIX_EPOCH)
40            .map(|d| d.as_secs())
41            .unwrap_or(0);
42        let age_secs = now.saturating_sub(t_created / 1_000_000) as f64;
43        let recency = (-age_secs / cfg.half_life_secs).exp() as f32;
44        let source_factor = (source_count as f32).ln_1p().min(3.0) / 3.0;
45        let contradiction_penalty = (contradiction_count as f32 * 0.25).min(1.0);
46        let value = (cfg.source_weight * source_factor + cfg.recency_weight * recency
47            - cfg.contradiction_weight * contradiction_penalty)
48            .clamp(0.0, 1.0);
49        Self {
50            value,
51            source_count,
52            recency_factor: recency,
53            contradiction_penalty,
54        }
55    }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ProvenanceBundle {
60    pub episode_id: u64,
61    pub t_valid_from: u64,
62    pub t_valid_to: u64,
63    pub trust: TrustScore,
64}