datasynth_eval/coherence/
mod.rs

1//! Semantic coherence evaluation module.
2//!
3//! Validates that generated data maintains accounting coherence including
4//! balance sheet equations, subledger reconciliation, and document chain integrity.
5
6mod balance;
7mod document_chain;
8mod intercompany;
9mod referential;
10mod subledger;
11
12pub use balance::{BalanceSheetEvaluation, BalanceSheetEvaluator};
13pub use document_chain::{DocumentChainEvaluation, DocumentChainEvaluator};
14pub use intercompany::{ICMatchingEvaluation, ICMatchingEvaluator};
15pub use referential::{ReferentialIntegrityEvaluation, ReferentialIntegrityEvaluator};
16pub use subledger::{SubledgerEvaluator, SubledgerReconciliationEvaluation};
17
18use serde::{Deserialize, Serialize};
19
20/// Combined coherence evaluation results.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct CoherenceEvaluation {
23    /// Balance sheet validation results.
24    pub balance: Option<BalanceSheetEvaluation>,
25    /// Subledger reconciliation results.
26    pub subledger: Option<SubledgerReconciliationEvaluation>,
27    /// Document chain completeness results.
28    pub document_chain: Option<DocumentChainEvaluation>,
29    /// Intercompany matching results.
30    pub intercompany: Option<ICMatchingEvaluation>,
31    /// Referential integrity results.
32    pub referential: Option<ReferentialIntegrityEvaluation>,
33    /// Overall pass/fail status.
34    pub passes: bool,
35    /// Summary of failed checks.
36    pub failures: Vec<String>,
37}
38
39impl CoherenceEvaluation {
40    /// Create a new empty evaluation.
41    pub fn new() -> Self {
42        Self {
43            balance: None,
44            subledger: None,
45            document_chain: None,
46            intercompany: None,
47            referential: None,
48            passes: true,
49            failures: Vec::new(),
50        }
51    }
52
53    /// Check all results against thresholds and update pass status.
54    pub fn check_thresholds(&mut self, thresholds: &crate::config::EvaluationThresholds) {
55        self.failures.clear();
56
57        if let Some(ref balance) = self.balance {
58            if !balance.equation_balanced {
59                self.failures.push(format!(
60                    "Balance sheet equation not balanced (max imbalance: {})",
61                    balance.max_imbalance
62                ));
63            }
64        }
65
66        if let Some(ref subledger) = self.subledger {
67            if subledger.completeness_score < thresholds.subledger_reconciliation_rate_min {
68                self.failures.push(format!(
69                    "Subledger reconciliation {} < {} (threshold)",
70                    subledger.completeness_score, thresholds.subledger_reconciliation_rate_min
71                ));
72            }
73        }
74
75        if let Some(ref doc_chain) = self.document_chain {
76            let min_rate = thresholds.document_chain_completion_min;
77            if doc_chain.p2p_completion_rate < min_rate {
78                self.failures.push(format!(
79                    "P2P chain completion {} < {} (threshold)",
80                    doc_chain.p2p_completion_rate, min_rate
81                ));
82            }
83            if doc_chain.o2c_completion_rate < min_rate {
84                self.failures.push(format!(
85                    "O2C chain completion {} < {} (threshold)",
86                    doc_chain.o2c_completion_rate, min_rate
87                ));
88            }
89        }
90
91        if let Some(ref ic) = self.intercompany {
92            if ic.match_rate < thresholds.ic_match_rate_min {
93                self.failures.push(format!(
94                    "IC match rate {} < {} (threshold)",
95                    ic.match_rate, thresholds.ic_match_rate_min
96                ));
97            }
98        }
99
100        if let Some(ref referential) = self.referential {
101            if referential.overall_integrity_score < thresholds.referential_integrity_min {
102                self.failures.push(format!(
103                    "Referential integrity {} < {} (threshold)",
104                    referential.overall_integrity_score, thresholds.referential_integrity_min
105                ));
106            }
107        }
108
109        self.passes = self.failures.is_empty();
110    }
111}
112
113impl Default for CoherenceEvaluation {
114    fn default() -> Self {
115        Self::new()
116    }
117}