use urge_core::{
decision::{Confidence, CrossValidation, EntryOutcome, LogicTrace, Stage, TraceEntry, Verdict},
engine::Paradigm,
};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
pub struct CrossValidator;
impl CrossValidator {
#[cfg(feature = "alloc")]
pub fn validate(
verdicts: &[Result<Verdict, urge_core::engine::EngineError>],
trace: &mut LogicTrace,
) -> (bool, Confidence, CrossValidation) {
let successful: Vec<&Verdict> = verdicts.iter().filter_map(|v| v.as_ref().ok()).collect();
if successful.is_empty() {
return (
false,
Confidence::NONE,
CrossValidation {
consistent: false,
conflicts_detected: 1,
conflict_detail: Some("no engines succeeded"),
},
);
}
let mut conflicts: u8 = 0;
let mut conflict_detail = None;
let modal_verdict = find_by_paradigm(&successful, Paradigm::Modal);
let boolean_verdict = find_by_paradigm(&successful, Paradigm::Boolean);
if let (Some(modal), Some(bool_v)) = (modal_verdict, boolean_verdict) {
if modal.valid && !bool_v.valid {
conflicts += 1;
conflict_detail = Some("modal necessity vs boolean contradiction");
trace.push(TraceEntry {
stage: Stage::CrossValidation,
paradigm: None,
description: "CONFLICT: □φ=true but Boolean φ=false",
outcome: EntryOutcome::Conflict,
});
}
}
let temporal_verdict = find_by_paradigm(&successful, Paradigm::Temporal);
let deontic_verdict = find_by_paradigm(&successful, Paradigm::Deontic);
if let (Some(temporal), Some(deontic)) = (temporal_verdict, deontic_verdict) {
if !temporal.valid && deontic.valid {
conflicts += 1;
conflict_detail =
conflict_detail.or(Some("temporal deadline exceeded: obligation violated"));
trace.push(TraceEntry {
stage: Stage::CrossValidation,
paradigm: None,
description: "CONFLICT: temporal constraint violated while obligation active",
outcome: EntryOutcome::Conflict,
});
}
}
if let Some(para) = find_by_paradigm(&successful, Paradigm::Paraconsistent) {
if !para.cross_validation.consistent {
conflicts += 1;
conflict_detail =
conflict_detail.or(Some("paraconsistent scenario: see engine trace"));
}
}
let majority_valid =
{
let (yes, no) = successful.iter().fold((0u8, 0u8), |(y, n), v| {
if v.valid {
(y + 1, n)
} else {
(y, n + 1)
}
});
yes >= no
};
let agreement_count = successful
.iter()
.filter(|v| v.valid == majority_valid)
.count() as u8;
let total = successful.len() as u8;
let confidence = Confidence::from_agreement(agreement_count, total);
trace.push(TraceEntry {
stage: Stage::CrossValidation,
paradigm: None,
description: if conflicts == 0 {
"cross-validation: consistent"
} else {
"cross-validation: conflicts detected"
},
outcome: if conflicts == 0 {
EntryOutcome::Evaluated
} else {
EntryOutcome::Conflict
},
});
let final_valid = if let Some(deontic) = deontic_verdict {
if !deontic.valid {
false
} else {
majority_valid
}
} else {
majority_valid
};
(
final_valid && conflicts == 0,
confidence,
CrossValidation {
consistent: conflicts == 0,
conflicts_detected: conflicts,
conflict_detail,
},
)
}
pub fn validate_single(verdict: &Verdict, _trace: &mut LogicTrace) -> CrossValidation {
verdict.cross_validation.clone()
}
}
#[cfg(feature = "alloc")]
fn find_by_paradigm<'a>(verdicts: &[&'a Verdict], paradigm: Paradigm) -> Option<&'a Verdict> {
verdicts
.iter()
.find(|v| v.paradigms_evaluated.contains(paradigm))
.copied()
}