1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
use crate::{
AuditEntry, Decision, FactId, KnownFacts, Lattice, PreparedPolicy, Presence, evaluate,
};
/// Historical evidence cannot reconstruct the supplied policy's inputs.
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum ReplayError {
/// Policy identity, content hash or hash format differs.
#[error("historical policy anchor does not match")]
PolicyMismatch,
/// Selected evidence omitted required observations; a digest cannot recover them.
#[error("historical evidence is missing required observations")]
MissingObservations(Vec<FactId>),
}
impl<O: Lattice> PreparedPolicy<O> {
/// Reconstructs a historical decision from explicitly retained observations.
///
/// Requires the same policy anchor and every required fact. Does not query
/// sources, infer omitted negatives or refresh authority. This is historical
/// analysis, not permission to perform a new operation, nor proof of source
/// honesty or of the original event's integrity.
///
/// # Errors
/// Reports a mismatched anchor or all missing required observations.
pub fn replay(&self, entry: &AuditEntry) -> Result<Decision<O>, ReplayError> {
if self.anchor() != entry.anchor() {
return Err(ReplayError::PolicyMismatch);
}
let facts =
KnownFacts::from_known_entries(entry.fact_resolution().observations().iter().map(
|observation| {
let value = if observation.value() {
Presence::Present
} else {
Presence::Absent
};
(observation.fact().clone(), value)
},
));
let missing: Vec<_> = self
.required_facts()
.iter()
.filter(|fact| facts.observation(fact).is_none())
.cloned()
.collect();
if !missing.is_empty() {
return Err(ReplayError::MissingObservations(missing));
}
Ok(evaluate(self.policy(), &facts))
}
}