agentic_codebase/grounding/
truth.rs1use serde::{Deserialize, Serialize};
7
8use crate::graph::CodeGraph;
9
10use super::citation::{CitationEngine, GroundedClaim};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct MaintainedTruth {
17 pub claim: GroundedClaim,
19 pub established_at: u64,
21 pub status: TruthStatus,
23 pub invalidation: Option<TruthInvalidation>,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29pub enum TruthStatus {
30 Valid,
32 Stale,
34 Invalidated,
36 Deleted,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct TruthInvalidation {
43 pub invalidated_at: u64,
45 pub change: String,
47 pub new_truth: Option<String>,
49}
50
51pub struct TruthMaintainer<'g> {
55 citation_engine: CitationEngine<'g>,
56 truths: Vec<MaintainedTruth>,
57}
58
59impl<'g> TruthMaintainer<'g> {
60 pub fn new(graph: &'g CodeGraph) -> Self {
62 Self {
63 citation_engine: CitationEngine::new(graph),
64 truths: Vec::new(),
65 }
66 }
67
68 pub fn register_truth(&mut self, claim: &str) -> MaintainedTruth {
70 let grounded = self.citation_engine.ground_claim(claim);
71 let now = std::time::SystemTime::now()
72 .duration_since(std::time::UNIX_EPOCH)
73 .unwrap_or_default()
74 .as_secs();
75
76 let truth = MaintainedTruth {
77 claim: grounded,
78 established_at: now,
79 status: TruthStatus::Valid,
80 invalidation: None,
81 };
82
83 self.truths.push(truth.clone());
84 truth
85 }
86
87 pub fn check_truth(&self, claim: &str) -> TruthStatus {
89 let grounded = self.citation_engine.ground_claim(claim);
90 if grounded.fully_grounded {
91 TruthStatus::Valid
92 } else if grounded.citations.is_empty() {
93 TruthStatus::Deleted
94 } else {
95 TruthStatus::Stale
96 }
97 }
98
99 pub fn refresh_all(&mut self) -> Vec<MaintainedTruth> {
101 let now = std::time::SystemTime::now()
102 .duration_since(std::time::UNIX_EPOCH)
103 .unwrap_or_default()
104 .as_secs();
105
106 let mut results = Vec::new();
107
108 for truth in &mut self.truths {
109 let new_status = {
110 let grounded = self.citation_engine.ground_claim(&truth.claim.claim);
111 if grounded.fully_grounded {
112 TruthStatus::Valid
113 } else if grounded.citations.is_empty() {
114 TruthStatus::Deleted
115 } else {
116 TruthStatus::Stale
117 }
118 };
119
120 if new_status != TruthStatus::Valid && truth.status == TruthStatus::Valid {
121 truth.status = new_status;
122 truth.invalidation = Some(TruthInvalidation {
123 invalidated_at: now,
124 change: "Graph changed since truth was established".to_string(),
125 new_truth: None,
126 });
127 }
128
129 results.push(truth.clone());
130 }
131
132 results
133 }
134
135 pub fn truth_diff(&self) -> Vec<MaintainedTruth> {
138 self.truths
139 .iter()
140 .filter(|t| t.status != TruthStatus::Valid)
141 .cloned()
142 .collect()
143 }
144
145 pub fn truths(&self) -> &[MaintainedTruth] {
147 &self.truths
148 }
149}
150
151#[cfg(test)]
154mod tests {
155 use super::*;
156 use crate::types::{CodeUnit, CodeUnitType, Language, Span};
157 use std::path::PathBuf;
158
159 fn test_graph() -> CodeGraph {
160 let mut graph = CodeGraph::with_default_dimension();
161 graph.add_unit(CodeUnit::new(
162 CodeUnitType::Function,
163 Language::Python,
164 "process_payment".to_string(),
165 "payments.stripe.process_payment".to_string(),
166 PathBuf::from("src/payments/stripe.py"),
167 Span::new(10, 0, 30, 0),
168 ));
169 graph
170 }
171
172 #[test]
173 fn register_truth_is_valid() {
174 let graph = test_graph();
175 let mut maintainer = TruthMaintainer::new(&graph);
176 let truth = maintainer.register_truth("process_payment exists");
177 assert_eq!(truth.status, TruthStatus::Valid);
178 }
179
180 #[test]
181 fn check_truth_valid() {
182 let graph = test_graph();
183 let maintainer = TruthMaintainer::new(&graph);
184 let status = maintainer.check_truth("process_payment exists in codebase");
185 assert_eq!(status, TruthStatus::Valid);
186 }
187
188 #[test]
189 fn check_truth_deleted() {
190 let graph = test_graph();
191 let maintainer = TruthMaintainer::new(&graph);
192 let status = maintainer.check_truth("nonexistent_func does something");
193 assert_eq!(status, TruthStatus::Deleted);
194 }
195
196 #[test]
197 fn truth_diff_empty_when_valid() {
198 let graph = test_graph();
199 let mut maintainer = TruthMaintainer::new(&graph);
200 maintainer.register_truth("process_payment exists");
201 let diff = maintainer.truth_diff();
202 assert!(diff.is_empty());
203 }
204}