use chrono::{DateTime, NaiveDate, NaiveDateTime};
use serde_json::Value;
use crate::generated::types::{BundleFact, FactId};
use super::kleene::{Inconclusive, Kleene};
pub const KNOWN_FACT_IDS: &[&str] = &[
"env_selectors",
"data_store_roots",
"classified_sources",
"approved_registries",
"approved_mcp_servers",
"approved_domains",
"approved_region_hosts",
"pricebook",
];
pub const KNOWN_FACT_KINDS: &[&str] = &["set", "map", "scalar"];
pub const KNOWN_FACT_OPS: &[&str] = &["equals", "in_set", "int_cmp"];
static NULL: Value = Value::Null;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FactStatus {
Present,
Absent,
Stale,
}
impl FactStatus {
pub fn inconclusive(self) -> Option<Inconclusive> {
match self {
FactStatus::Present => None,
FactStatus::Absent => Some(Inconclusive::Absent),
FactStatus::Stale => Some(Inconclusive::Stale),
}
}
pub fn is_present(self) -> bool {
matches!(self, FactStatus::Present)
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct FactSet {
pub facts: Vec<BundleFact>,
pub now_ms: i64,
}
impl FactSet {
pub fn new(facts: Vec<BundleFact>, now_ms: i64) -> FactSet {
FactSet { facts, now_ms }
}
pub fn declared_ids(&self) -> Vec<String> {
self.facts
.iter()
.filter_map(|f| f.fact_id.as_ref().map(|id| id.0.clone()))
.collect()
}
pub fn get(&self, fact_id: &str) -> Option<&BundleFact> {
self.facts
.iter()
.rev()
.find(|f| f.fact_id.as_ref().is_some_and(|id| id.0 == fact_id))
}
pub fn status_at(&self, fact_id: &str, now_ms: i64) -> FactStatus {
match self.get(fact_id) {
Some(fact) => status_of(fact, now_ms),
None => FactStatus::Absent,
}
}
pub fn status(&self, fact_id: &str) -> FactStatus {
self.status_at(fact_id, self.now_ms)
}
pub fn present(&self, fact_id: &str) -> Option<&BundleFact> {
self.get(fact_id)
.filter(|fact| status_of(fact, self.now_ms).is_present())
}
}
pub fn is_known_fact_id(fact_id: &str) -> bool {
KNOWN_FACT_IDS.contains(&fact_id)
}
pub fn resolve(bundle: &super::bundle::Bundle, now_ms: i64) -> FactSet {
let mut facts: Vec<BundleFact> =
Vec::with_capacity(bundle.facts.len() + bundle.fact_refs.len());
for reference in &bundle.fact_refs {
if let Some(fact_id) = reference.fact_id.as_ref() {
facts.push(BundleFact {
fact_id: Some(FactId(fact_id.0.clone())),
..BundleFact::default()
});
}
}
for fact in &bundle.facts {
if fact.fact_id.is_some() {
facts.push(fact.clone());
}
}
FactSet { facts, now_ms }
}
pub fn resolve_leaf(
facts: &FactSet,
fact_id: &str,
pred: &str,
value: Option<&Value>,
now_ms: i64,
) -> Kleene {
let Some(fact) = readable(facts, fact_id, now_ms) else {
return Kleene::Unknown(unreadable_reason(facts, fact_id, now_ms));
};
match pred {
"equals" => {
Kleene::from_bool(fact.value.as_ref().unwrap_or(&NULL) == value.unwrap_or(&NULL))
}
"in_set" => {
if fact.kind.as_deref() == Some("set") {
membership_in(fact, value.unwrap_or(&NULL))
} else {
let own = fact.value.as_ref().unwrap_or(&NULL);
let literal = as_list(value.unwrap_or(&NULL));
Kleene::from_bool(literal.contains(&own))
}
}
"int_cmp" => Kleene::from_bool(int_cmp(fact.value.as_ref(), value)),
_ => Kleene::UNKNOWN,
}
}
pub fn resolve_set(facts: &FactSet, fact_id: &str, now_ms: i64) -> Option<Vec<String>> {
let fact = readable(facts, fact_id, now_ms)?;
Some(
set_members(fact)
.into_iter()
.map(|member| match member {
Value::String(text) => text,
other => other.to_string(),
})
.collect(),
)
}
pub fn membership(facts: &FactSet, fact_id: &str, candidate: &Value, now_ms: i64) -> Kleene {
match readable(facts, fact_id, now_ms) {
Some(fact) => membership_in(fact, candidate),
None => Kleene::Unknown(unreadable_reason(facts, fact_id, now_ms)),
}
}
pub fn split_field(field: &str) -> Option<(&str, &str)> {
let rest = field.strip_prefix("fact.")?;
Some(match rest.split_once('.') {
Some((fact_id, path)) => (fact_id, path),
None => (rest, ""),
})
}
pub fn field_values<'a>(
facts: &'a FactSet,
field: &str,
now_ms: i64,
) -> Result<Vec<&'a Value>, Inconclusive> {
let Some((fact_id, path)) = split_field(field) else {
return Err(Inconclusive::Absent);
};
let Some(fact) = readable(facts, fact_id, now_ms) else {
return Err(unreadable_reason(facts, fact_id, now_ms));
};
let mut value = fact.value.as_ref().unwrap_or(&NULL);
if !path.is_empty() {
for segment in path.split('.') {
value = match value.as_object().and_then(|map| map.get(segment)) {
Some(next) => next,
None => return Err(Inconclusive::Absent),
};
}
}
Ok(match value {
Value::Array(members) => members.iter().collect(),
single => vec![single],
})
}
fn readable<'a>(facts: &'a FactSet, fact_id: &str, now_ms: i64) -> Option<&'a BundleFact> {
facts
.get(fact_id)
.filter(|fact| status_of(fact, now_ms).is_present())
}
fn unreadable_reason(facts: &FactSet, fact_id: &str, now_ms: i64) -> Inconclusive {
match facts.status_at(fact_id, now_ms).inconclusive() {
Some(reason) => reason,
None => Inconclusive::Absent,
}
}
fn status_of(fact: &BundleFact, now_ms: i64) -> FactStatus {
let Some(kind) = fact.kind.as_deref() else {
return FactStatus::Absent;
};
if !KNOWN_FACT_KINDS.contains(&kind) {
return FactStatus::Absent;
}
let Some(observed_at) = rfc3339_ms(fact.observed_at.as_deref()) else {
return FactStatus::Absent;
};
let max_age_s = fact.max_age_s.unwrap_or(0);
if max_age_s != 0 {
let budget_ms = max_age_s
.saturating_add(fact.leeway_s.unwrap_or(0))
.saturating_mul(1_000);
if now_ms.saturating_sub(observed_at) > budget_ms {
return FactStatus::Stale;
}
}
if rfc3339_ms(fact.valid_from.as_deref()).is_some_and(|from| now_ms < from) {
return FactStatus::Stale;
}
if rfc3339_ms(fact.valid_until.as_deref()).is_some_and(|until| now_ms > until) {
return FactStatus::Stale;
}
FactStatus::Present
}
fn rfc3339_ms(text: Option<&str>) -> Option<i64> {
let text = text?;
if let Ok(parsed) = DateTime::parse_from_rfc3339(text) {
return Some(parsed.timestamp_millis());
}
for format in ["%Y-%m-%dT%H:%M:%S%.f", "%Y-%m-%d %H:%M:%S%.f"] {
if let Ok(naive) = NaiveDateTime::parse_from_str(text, format) {
return Some(naive.and_utc().timestamp_millis());
}
}
if let Ok(date) = NaiveDate::parse_from_str(text, "%Y-%m-%d") {
return Some(date.and_hms_opt(0, 0, 0)?.and_utc().timestamp_millis());
}
None
}
fn set_members(fact: &BundleFact) -> Vec<Value> {
match fact.value.as_ref() {
Some(Value::Array(members)) => members.clone(),
Some(Value::Object(map)) => map.keys().map(|key| Value::String(key.clone())).collect(),
Some(other) => vec![other.clone()],
None => vec![Value::Null],
}
}
fn membership_in(fact: &BundleFact, candidate: &Value) -> Kleene {
if set_members(fact).iter().any(|member| member == candidate) {
return Kleene::True;
}
if fact.closed_world == Some(true) {
Kleene::False
} else {
Kleene::Unknown(Inconclusive::OpenWorldMiss)
}
}
fn as_list(value: &Value) -> Vec<&Value> {
match value {
Value::Array(members) => members.iter().collect(),
single => vec![single],
}
}
fn int_cmp(left: Option<&Value>, spec: Option<&Value>) -> bool {
let (Some(left), Some(spec)) = (left.and_then(Value::as_i64), spec) else {
return false;
};
let Some(n) = spec.get("n").and_then(Value::as_i64) else {
return false;
};
match spec.get("op").and_then(Value::as_str) {
Some("lt") => left < n,
Some("le") => left <= n,
Some("gt") => left > n,
Some("ge") => left >= n,
Some("eq") => left == n,
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
const NOW: i64 = 1_756_742_400_000;
fn fact(value: serde_json::Value) -> BundleFact {
serde_json::from_value(value).expect("the fixture is a BundleFact")
}
fn one(value: serde_json::Value) -> FactSet {
FactSet::new(vec![fact(value)], NOW)
}
fn fresh_ticket() -> serde_json::Value {
json!({
"fact_id": "change_ticket",
"kind": "scalar",
"revision": 1,
"source": "platform",
"observed_at": "2025-09-01T15:59:00+00:00",
"max_age_s": 86400,
"leeway_s": 300,
"closed_world": false,
"value": true,
})
}
fn domains(closed_world: bool) -> serde_json::Value {
json!({
"fact_id": "approved_domains",
"kind": "set",
"observed_at": "2025-09-01T15:59:00+00:00",
"max_age_s": 86400,
"leeway_s": 300,
"closed_world": closed_world,
"value": ["api.example.com"],
})
}
#[test]
fn a_readable_fact_is_present() {
assert_eq!(
one(fresh_ticket()).status("change_ticket"),
FactStatus::Present
);
}
#[test]
fn a_fact_nobody_declared_is_absent() {
let facts = one(fresh_ticket());
assert_eq!(facts.status("change_ticket_typo"), FactStatus::Absent);
assert_eq!(
facts.status_at("change_ticket_typo", NOW).inconclusive(),
Some(Inconclusive::Absent)
);
}
#[test]
fn a_kind_this_engine_does_not_know_is_absent_not_stale() {
let mut raw = fresh_ticket();
raw["kind"] = json!("graph");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Absent);
}
#[test]
fn a_fact_with_no_kind_at_all_is_absent() {
let raw = json!({"fact_id": "change_ticket", "value": true});
assert_eq!(one(raw).status("change_ticket"), FactStatus::Absent);
}
#[test]
fn an_unparseable_observed_at_is_absent_never_now() {
let mut raw = fresh_ticket();
raw["observed_at"] = json!("not-a-date");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Absent);
}
#[test]
fn a_missing_observed_at_is_absent() {
let mut raw = fresh_ticket();
raw.as_object_mut().expect("object").remove("observed_at");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Absent);
}
#[test]
fn a_fact_older_than_max_age_plus_leeway_is_stale() {
let mut raw = fresh_ticket();
raw["observed_at"] = json!("2025-09-01T14:53:20+00:00"); raw["max_age_s"] = json!(3600);
assert_eq!(one(raw).status("change_ticket"), FactStatus::Stale);
}
#[test]
fn the_leeway_is_added_to_max_age_not_subtracted() {
let mut raw = fresh_ticket();
raw["observed_at"] = json!("2025-09-01T14:58:20+00:00");
raw["max_age_s"] = json!(3600);
assert_eq!(one(raw).status("change_ticket"), FactStatus::Present);
}
#[test]
fn the_staleness_boundary_is_exclusive_to_the_millisecond() {
let mut raw = fresh_ticket();
raw["max_age_s"] = json!(3600);
raw["leeway_s"] = json!(300);
raw["observed_at"] = json!("2025-09-01T14:55:00+00:00");
assert_eq!(
one(raw.clone()).status("change_ticket"),
FactStatus::Present
);
raw["observed_at"] = json!("2025-09-01T14:54:59.999+00:00");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Stale);
}
#[test]
fn max_age_zero_or_absent_never_ages_a_fact_out() {
let mut raw = fresh_ticket();
raw["observed_at"] = json!("2025-09-01T04:53:20+00:00"); raw["max_age_s"] = json!(0);
assert_eq!(
one(raw.clone()).status("change_ticket"),
FactStatus::Present
);
raw.as_object_mut().expect("object").remove("max_age_s");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Present);
}
#[test]
fn an_absurd_max_age_saturates_instead_of_wrapping() {
let mut raw = fresh_ticket();
raw["max_age_s"] = json!(i64::MAX);
raw["leeway_s"] = json!(i64::MAX);
assert_eq!(one(raw).status("change_ticket"), FactStatus::Present);
}
#[test]
fn before_valid_from_is_stale() {
let mut raw = fresh_ticket();
raw["valid_from"] = json!("2025-09-02T16:00:00+00:00");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Stale);
}
#[test]
fn after_valid_until_is_stale() {
let mut raw = fresh_ticket();
raw["valid_until"] = json!("2025-08-31T16:00:00+00:00");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Stale);
}
#[test]
fn inside_the_validity_window_is_present() {
let mut raw = fresh_ticket();
raw["valid_from"] = json!("2025-08-31T16:00:00+00:00");
raw["valid_until"] = json!("2025-09-02T16:00:00+00:00");
assert_eq!(one(raw).status("change_ticket"), FactStatus::Present);
}
#[test]
fn an_offsetless_observed_at_is_read_as_utc_never_as_local_time() {
let mut raw = fresh_ticket();
raw["observed_at"] = json!("2025-09-01T15:59:00");
raw["max_age_s"] = json!(3600);
assert_eq!(one(raw).status("change_ticket"), FactStatus::Present);
assert_eq!(
rfc3339_ms(Some("2025-09-01T15:59:00")),
rfc3339_ms(Some("2025-09-01T15:59:00+00:00")),
"the two spellings are the same instant"
);
}
#[test]
fn a_z_suffix_and_a_non_utc_offset_both_parse() {
assert_eq!(
rfc3339_ms(Some("2025-09-01T16:00:00Z")),
rfc3339_ms(Some("2025-09-01T18:00:00+02:00"))
);
assert_eq!(rfc3339_ms(Some("2025-09-01T16:00:00Z")), Some(NOW));
}
#[test]
fn an_unparseable_timestamp_is_none_and_never_a_substitute_instant() {
for text in ["not-a-date", "", "2025-13-45T99:99:99Z", "16:00:00"] {
assert_eq!(rfc3339_ms(Some(text)), None, "{text:?} must not parse");
}
assert_eq!(rfc3339_ms(None), None);
}
#[test]
fn a_membership_hit_is_true_under_either_world() {
for closed in [true, false] {
let facts = one(domains(closed));
assert_eq!(
membership(&facts, "approved_domains", &json!("api.example.com"), NOW),
Kleene::True
);
}
}
#[test]
fn a_closed_world_miss_is_false_and_not_an_unknown() {
let facts = one(domains(true));
assert_eq!(
membership(&facts, "approved_domains", &json!("evil.example.net"), NOW),
Kleene::False,
"closed_world means 'not in the set' is a real negative"
);
}
#[test]
fn an_open_world_miss_is_unknown_and_names_no_fact() {
let facts = one(domains(false));
let answer = membership(&facts, "approved_domains", &json!("evil.example.net"), NOW);
assert_eq!(answer, Kleene::Unknown(Inconclusive::OpenWorldMiss));
assert!(
!answer.reason().expect("⊥ carries a reason").names_a_fact(),
"the fact was read — it does not belong in inconclusive_facts[]"
);
}
#[test]
fn an_absent_or_stale_set_is_unknown_before_membership_is_even_asked() {
let facts = FactSet::new(vec![], NOW);
assert_eq!(
membership(&facts, "approved_domains", &json!("api.example.com"), NOW),
Kleene::Unknown(Inconclusive::Absent)
);
let mut raw = domains(true);
raw["observed_at"] = json!("2025-09-01T14:53:20+00:00");
raw["max_age_s"] = json!(3600);
let facts = one(raw);
assert_eq!(
membership(&facts, "approved_domains", &json!("api.example.com"), NOW),
Kleene::Unknown(Inconclusive::Stale),
"a stale set does not get to answer, hit or miss"
);
}
#[test]
fn a_map_fact_supplies_its_keys_as_the_set() {
let facts = one(json!({
"fact_id": "pricebook",
"kind": "set",
"observed_at": "2025-09-01T15:59:00+00:00",
"closed_world": true,
"value": {"claude-opus-5": {"input_micro_usd": 15}},
}));
assert_eq!(
membership(&facts, "pricebook", &json!("claude-opus-5"), NOW),
Kleene::True
);
assert_eq!(
resolve_set(&facts, "pricebook", NOW),
Some(vec!["claude-opus-5".to_string()])
);
}
#[test]
fn resolve_set_is_none_for_anything_that_is_not_present() {
let facts = FactSet::new(vec![], NOW);
assert_eq!(resolve_set(&facts, "approved_domains", NOW), None);
assert_eq!(
resolve_set(&one(domains(false)), "approved_domains", NOW),
Some(vec!["api.example.com".to_string()])
);
}
#[test]
fn equals_compares_the_facts_own_value() {
let facts = one(fresh_ticket());
assert_eq!(
resolve_leaf(&facts, "change_ticket", "equals", Some(&json!(true)), NOW),
Kleene::True
);
assert_eq!(
resolve_leaf(&facts, "change_ticket", "equals", Some(&json!(false)), NOW),
Kleene::False,
"present-and-not-matching is a decided FALSE, never a ⊥"
);
}
#[test]
fn in_set_on_a_non_set_kind_tests_the_value_against_a_literal_list() {
let facts = one(json!({
"fact_id": "pricebook",
"kind": "map",
"observed_at": "2025-09-01T15:59:00+00:00",
"value": {"claude-opus-5": {"input_micro_usd": 15}},
}));
assert_eq!(
resolve_leaf(
&facts,
"pricebook",
"in_set",
Some(&json!("claude-opus-5")),
NOW
),
Kleene::False,
"the MAP is not a member of [\"claude-opus-5\"] — no ⊥ anywhere"
);
let mut raw = fresh_ticket();
raw["kind"] = json!("scalar");
raw["value"] = json!("green");
let facts = one(raw);
assert_eq!(
resolve_leaf(
&facts,
"change_ticket",
"in_set",
Some(&json!(["green", "amber"])),
NOW
),
Kleene::True
);
}
#[test]
fn int_cmp_reads_the_facts_integer_and_a_non_integer_is_a_plain_false() {
let mut raw = fresh_ticket();
raw["value"] = json!(7);
let facts = one(raw);
for (op, expected) in [
("ge", Kleene::True),
("gt", Kleene::False),
("le", Kleene::True),
("lt", Kleene::False),
("eq", Kleene::True),
] {
assert_eq!(
resolve_leaf(
&facts,
"change_ticket",
"int_cmp",
Some(&json!({"op": op, "n": 7})),
NOW
),
expected,
"int_cmp {op} 7 against 7"
);
}
let facts = one(fresh_ticket());
assert_eq!(
resolve_leaf(
&facts,
"change_ticket",
"int_cmp",
Some(&json!({"op": "ge", "n": 1})),
NOW
),
Kleene::False
);
}
#[test]
fn an_op_the_loader_should_have_rejected_is_bottom_and_not_a_panic() {
let facts = one(fresh_ticket());
assert!(!resolve_leaf(&facts, "change_ticket", "is_approved", None, NOW).is_known());
}
#[test]
fn every_unreadable_cause_reaches_the_leaf_as_its_own_reason() {
let mut stale = fresh_ticket();
stale["observed_at"] = json!("2025-09-01T14:53:20+00:00");
stale["max_age_s"] = json!(3600);
let cases: [(FactSet, Inconclusive); 3] = [
(FactSet::new(vec![], NOW), Inconclusive::Absent),
(one(stale), Inconclusive::Stale),
(one(domains(false)), Inconclusive::OpenWorldMiss),
];
let (absent, stale, miss) = (&cases[0], &cases[1], &cases[2]);
assert_eq!(
resolve_leaf(
&absent.0,
"change_ticket",
"equals",
Some(&json!(true)),
NOW
),
Kleene::Unknown(absent.1)
);
assert_eq!(
resolve_leaf(&stale.0, "change_ticket", "equals", Some(&json!(true)), NOW),
Kleene::Unknown(stale.1)
);
assert_eq!(
resolve_leaf(
&miss.0,
"approved_domains",
"in_set",
Some(&json!("evil.example.net")),
NOW
),
Kleene::Unknown(miss.1)
);
}
#[test]
fn a_dotted_field_splits_on_the_first_segment_only() {
assert_eq!(
split_field("fact.change_ticket"),
Some(("change_ticket", ""))
);
assert_eq!(
split_field("fact.change_ticket.approved"),
Some(("change_ticket", "approved"))
);
assert_eq!(
split_field("fact.a.b.c"),
Some(("a", "b.c")),
"everything after the first dot is the PATH"
);
assert_eq!(split_field("url.host"), None);
}
#[test]
fn a_fact_literally_named_with_a_dot_is_not_a_second_reading() {
let facts = one(json!({
"fact_id": "change_ticket.approved",
"kind": "scalar",
"observed_at": "2025-09-01T15:59:00+00:00",
"value": true,
}));
assert_eq!(
field_values(&facts, "fact.change_ticket.approved", NOW),
Err(Inconclusive::Absent)
);
}
#[test]
fn a_dotted_path_reads_into_a_map() {
let facts = one(json!({
"fact_id": "change_ticket",
"kind": "map",
"observed_at": "2025-09-01T15:59:00+00:00",
"value": {"approved": true, "reviewers": ["ana", "bo"]},
}));
assert_eq!(
field_values(&facts, "fact.change_ticket.approved", NOW),
Ok(vec![&json!(true)])
);
assert_eq!(
field_values(&facts, "fact.change_ticket.reviewers", NOW),
Ok(vec![&json!("ana"), &json!("bo")]),
"a list value spreads, so any member can match"
);
assert!(
field_values(&facts, "fact.change_ticket", NOW)
.expect("the whole value is readable")
.len()
== 1,
"no path reads the value whole"
);
}
#[test]
fn a_path_that_misses_is_bottom_and_names_the_fact() {
let facts = one(json!({
"fact_id": "change_ticket",
"kind": "map",
"observed_at": "2025-09-01T15:59:00+00:00",
"value": {"approved": true},
}));
let miss = field_values(&facts, "fact.change_ticket.signed", NOW);
assert_eq!(miss, Err(Inconclusive::Absent));
assert!(miss.expect_err("⊥").names_a_fact());
assert_eq!(
field_values(&one(fresh_ticket()), "fact.change_ticket.approved", NOW),
Err(Inconclusive::Absent)
);
}
#[test]
fn a_field_on_a_stale_fact_is_stale_not_absent() {
let mut raw = fresh_ticket();
raw["kind"] = json!("map");
raw["value"] = json!({"approved": true});
raw["observed_at"] = json!("2025-09-01T14:53:20+00:00");
raw["max_age_s"] = json!(3600);
assert_eq!(
field_values(&one(raw), "fact.change_ticket.approved", NOW),
Err(Inconclusive::Stale)
);
}
fn bundle_with(extra: serde_json::Value) -> super::super::bundle::Bundle {
let mut doc = json!({
"schema_version": 2,
"organization_id": "org",
"revision": 1,
"built_at": "2026-09-01T00:00:00Z",
"enforcement_enabled": true,
"signature": null,
"artifacts": [],
});
for (key, value) in extra.as_object().expect("object") {
doc[key] = value.clone();
}
super::super::bundle::load(doc).expect("the envelope parses")
}
fn a_ref() -> serde_json::Value {
json!([{
"fact_id": "change_ticket",
"sha256": "0000000000000000000000000000000000000000000000000000000000000000",
"size_bytes": 4096,
"url": "https://platform.example.com/facts/change_ticket",
"observed_at": "2025-09-01T15:59:00+00:00",
"max_age_s": 86400,
"revision": 1,
}])
}
#[test]
fn a_ref_the_poller_never_inlined_is_absent_not_a_default() {
let bundle = bundle_with(json!({"fact_refs": a_ref()}));
let facts = resolve(&bundle, NOW);
assert_eq!(facts.status("change_ticket"), FactStatus::Absent);
assert_eq!(
resolve_leaf(&facts, "change_ticket", "equals", Some(&json!(true)), NOW),
Kleene::Unknown(Inconclusive::Absent),
"unfetchable, oversize and hash-mismatching all land here — there is \
nothing for a fetch failure to be INSTEAD of"
);
assert!(
facts.declared_ids().contains(&"change_ticket".to_string()),
"a ref is still a declaration, so a typo in one is still warned about"
);
}
#[test]
fn an_inlined_ref_resolves_as_the_inline_fact() {
let bundle = bundle_with(json!({
"facts": [fresh_ticket()],
"fact_refs": a_ref(),
}));
let facts = resolve(&bundle, NOW);
assert_eq!(facts.status("change_ticket"), FactStatus::Present);
assert_eq!(
resolve_leaf(&facts, "change_ticket", "equals", Some(&json!(true)), NOW),
Kleene::True,
"the inline entry shadows the placeholder, whatever their order"
);
}
#[test]
fn a_plain_inline_fact_needs_no_ref() {
let bundle = bundle_with(json!({"facts": [fresh_ticket()]}));
let facts = resolve(&bundle, NOW);
assert_eq!(
facts.present("change_ticket").map(|f| f.value.clone()),
Some(Some(json!(true)))
);
}
#[test]
fn a_later_declaration_of_the_same_id_wins() {
let mut second = fresh_ticket();
second["value"] = json!(false);
let bundle = bundle_with(json!({"facts": [fresh_ticket(), second]}));
let facts = resolve(&bundle, NOW);
assert_eq!(
resolve_leaf(&facts, "change_ticket", "equals", Some(&json!(true)), NOW),
Kleene::False
);
}
#[test]
fn a_fact_with_no_id_is_carried_by_nobody() {
let bundle = bundle_with(json!({"facts": [{"kind": "scalar", "value": true}]}));
assert!(resolve(&bundle, NOW).declared_ids().is_empty());
}
#[test]
fn the_registry_warns_and_never_rejects() {
assert!(is_known_fact_id("approved_domains"));
assert!(!is_known_fact_id("aproved_domains"));
assert!(
!is_known_fact_id("change_ticket"),
"the corpus's own probe fact is outside the registry — which is why \
every 04 row expects the warning, and why none of them expects a \
rejection"
);
let facts = one(fresh_ticket());
assert_eq!(
resolve_leaf(&facts, "change_ticket", "equals", Some(&json!(true)), NOW),
Kleene::True
);
}
#[test]
fn present_is_the_guard_a_caller_without_a_now_ms_uses() {
let mut raw = domains(true);
raw["observed_at"] = json!("2025-09-01T14:53:20+00:00");
raw["max_age_s"] = json!(3600);
let facts = one(raw);
assert!(facts.get("approved_domains").is_some(), "still declared");
assert!(
facts.present("approved_domains").is_none(),
"but not readable"
);
}
struct Walked {
value: Kleene,
noted: Vec<String>,
}
fn walk(
node: &crate::generated::types::T1Node,
facts: &FactSet,
event: &serde_json::Value,
now_ms: i64,
noted: &mut Vec<String>,
) -> Option<Kleene> {
match node.op.as_deref() {
Some("and") | Some("or") => {
let mut children = Vec::with_capacity(node.children.len());
for child in &node.children {
children.push(walk(child, facts, event, now_ms, noted)?);
}
Some(if node.op.as_deref() == Some("and") {
Kleene::all(&children)
} else {
Kleene::any(&children)
})
}
Some("not") => {
let child = node.children.first()?;
Some(!walk(child, facts, event, now_ms, noted)?)
}
Some("leaf") => walk_leaf(node.leaf.as_ref()?, facts, event, now_ms, noted),
_ => None,
}
}
fn walk_leaf(
leaf: &crate::generated::types::T1Leaf,
facts: &FactSet,
event: &serde_json::Value,
now_ms: i64,
noted: &mut Vec<String>,
) -> Option<Kleene> {
let pred = leaf.pred.as_deref().unwrap_or_default();
if pred == "fact" {
let spec = leaf.fact.as_ref()?;
let fact_id = spec.fact_id.as_ref().map(|id| id.0.as_str()).unwrap_or("");
let value = resolve_leaf(
facts,
fact_id,
spec.op.as_deref().unwrap_or(""),
spec.value.as_ref(),
now_ms,
);
note(noted, fact_id, value.reason());
return Some(value);
}
let field = leaf.field.as_deref().unwrap_or_default();
if let Some((fact_id, _)) = split_field(field) {
return Some(match field_values(facts, field, now_ms) {
Ok(values) => {
if pred == "exists" {
Kleene::from_bool(!values.is_empty())
} else {
Kleene::from_bool(values.iter().any(|v| Some(*v) == leaf.value.as_ref()))
}
}
Err(reason) => {
note(noted, fact_id, Some(reason));
Kleene::Unknown(reason)
}
});
}
if field == "tool.name" && pred == "equals" {
return Some(Kleene::from_bool(
event.get("tool_name") == leaf.value.as_ref(),
));
}
None
}
fn note(noted: &mut Vec<String>, fact_id: &str, reason: Option<Inconclusive>) {
if reason.is_some_and(|reason| reason.names_a_fact())
&& !noted.iter().any(|seen| seen == fact_id)
{
noted.push(fact_id.to_string());
}
}
fn on_inconclusive_verdict(artifact: &serde_json::Value) -> &'static str {
let declared = if artifact["mode"].as_str() == Some("monitor") {
"allow_and_flag"
} else {
artifact["on_inconclusive"]
.as_str()
.unwrap_or("allow_and_flag")
};
match declared {
"ask" => "ask",
"block" => "block",
_ => "allow",
}
}
fn corpus_rows() -> Vec<(
String,
serde_json::Value,
super::super::bundle::Bundle,
serde_json::Value,
)> {
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("schemas/conformance");
let text = std::fs::read_to_string(dir.join("04-facts-kleene.jsonl"))
.expect("the corpus file is readable");
let mut rows = Vec::new();
for line in text.lines().filter(|line| !line.trim().is_empty()) {
let row: serde_json::Value =
serde_json::from_str(line).expect("every corpus row is valid JSON");
let bundle_ref = row["bundle_ref"]
.as_str()
.expect("every row names a bundle");
let raw: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(dir.join(bundle_ref)).expect("the bundle is readable"),
)
.expect("the bundle is valid JSON");
let bundle = super::super::bundle::load(raw.clone()).expect("the fixture bundle loads");
let id = row["id"].as_str().unwrap_or_default().to_string();
rows.push((id, row, bundle, raw));
}
assert_eq!(rows.len(), 46, "the corpus was found and read in full");
rows
}
fn walk_row(
row: &serde_json::Value,
bundle: &super::super::bundle::Bundle,
) -> Option<Vec<Walked>> {
let now_ms = row["now_ms"].as_i64().expect("every row carries now_ms");
let facts = resolve(bundle, now_ms);
let mut out = Vec::new();
for artifact in &bundle.artifacts {
let super::super::bundle::ArtifactBody::T1(body) = &artifact.body else {
return None; };
let node = body.node.as_ref()?;
let mut noted = Vec::new();
let value = walk(node, &facts, &row["event"], now_ms, &mut noted)?;
out.push(Walked { value, noted });
}
Some(out)
}
#[test]
fn the_corpus_pins_which_facts_reach_inconclusive_facts() {
let mut checked = 0usize;
let mut disagreements: Vec<String> = Vec::new();
for (id, row, bundle, _raw) in corpus_rows() {
let Some(walked) = walk_row(&row, &bundle) else {
continue; };
let mut got: Vec<String> = walked
.iter()
.filter(|artifact| !artifact.value.is_known())
.flat_map(|artifact| artifact.noted.clone())
.collect();
got.sort();
got.dedup();
let expected: Vec<String> = row["expected"]["decision"]["inconclusive_facts"]
.as_array()
.expect("every row carries inconclusive_facts")
.iter()
.map(|value| value.as_str().unwrap_or_default().to_string())
.collect();
checked += 1;
if got != expected {
disagreements.push(format!("{id}: expected {expected:?}, got {got:?}"));
}
}
assert_eq!(checked, 42, "every walkable corpus row was read");
assert!(
disagreements.is_empty(),
"{} of {checked} corpus rows disagree with fact resolution — the \
corpus is oracle-authored, so the engine is wrong until the spec \
says the row was:\n {}",
disagreements.len(),
disagreements.join("\n ")
);
}
#[test]
fn the_corpus_pins_every_fact_leaf_as_a_truth_value() {
let mut checked = 0usize;
let mut skipped: Vec<String> = Vec::new();
let mut disagreements: Vec<String> = Vec::new();
for (id, row, bundle, raw) in corpus_rows() {
let Some(walked) = walk_row(&row, &bundle) else {
skipped.push(id);
continue;
};
let envelopes = raw["artifacts"].as_array().cloned().unwrap_or_default();
let ([single], [artifact]) = (&walked[..], &envelopes[..]) else {
skipped.push(id);
continue;
};
let decision = &row["expected"]["decision"];
let verdict = decision["verdict"].as_str().unwrap_or_default();
let declared = artifact["body"]["verdict"].as_str().unwrap_or_default();
let on_inconclusive = on_inconclusive_verdict(artifact);
let named_a_fact = !decision["inconclusive_facts"]
.as_array()
.map(|list| list.is_empty())
.unwrap_or(true);
#[allow(clippy::if_same_then_else)]
let expected = if decision["undecided"].as_bool() == Some(true) {
Kleene::False
} else if verdict == declared && verdict != on_inconclusive {
Kleene::True
} else if verdict == on_inconclusive && verdict != declared {
Kleene::UNKNOWN
} else if verdict == on_inconclusive && named_a_fact {
Kleene::UNKNOWN
} else {
panic!(
"{id}: verdict {verdict:?} is both the artifact's own and its \
on_inconclusive, and it names no fact — the row cannot be \
read as a truth value and this decoder must grow a rule"
);
};
checked += 1;
let agrees = match (expected, single.value) {
(Kleene::Unknown(_), Kleene::Unknown(_)) => true,
(left, right) => left == right,
};
if !agrees {
disagreements.push(format!(
"{id}: expected {expected:?}, got {:?}",
single.value
));
}
}
assert_eq!(checked, 41, "every single-artifact corpus row was read");
assert_eq!(
skipped.len(),
5,
"and every skipped row was skipped for a named reason"
);
assert!(
disagreements.is_empty(),
"{} of {checked} corpus leaves disagree with fact resolution (skipped \
{}, which read the effect classifier):\n {}",
disagreements.len(),
skipped.len(),
disagreements.join("\n ")
);
}
#[test]
fn no_unresolvable_fact_ever_yields_a_decided_value() {
let mut unknown_kind = fresh_ticket();
unknown_kind["kind"] = json!("graph");
let mut bad_stamp = fresh_ticket();
bad_stamp["observed_at"] = json!("not-a-date");
let mut aged_out = fresh_ticket();
aged_out["observed_at"] = json!("2025-09-01T14:53:20+00:00");
aged_out["max_age_s"] = json!(3600);
let mut too_early = fresh_ticket();
too_early["valid_from"] = json!("2025-09-02T16:00:00+00:00");
let mut too_late = fresh_ticket();
too_late["valid_until"] = json!("2025-08-31T16:00:00+00:00");
let unresolvable = [
("never declared", FactSet::new(vec![], NOW)),
("unknown kind", one(unknown_kind)),
("unparseable observed_at", one(bad_stamp)),
("aged out", one(aged_out)),
("before valid_from", one(too_early)),
("after valid_until", one(too_late)),
(
"a ref nobody inlined",
resolve(&bundle_with(json!({"fact_refs": a_ref()})), NOW),
),
];
let literals = [
json!(true),
json!(false),
json!(null),
json!({"op": "ge", "n": 0}),
json!(["anything"]),
];
for (cause, facts) in &unresolvable {
for op in KNOWN_FACT_OPS {
for literal in &literals {
let answer = resolve_leaf(facts, "change_ticket", op, Some(literal), NOW);
assert!(
!answer.is_known(),
"{cause}: `{op}` against {literal} decided {answer:?} — a \
fact-level default is not expressible in this engine"
);
let reason = answer.reason().expect("⊥ carries a reason");
assert!(
reason.names_a_fact(),
"{cause}: an UNREADABLE fact must name itself in \
inconclusive_facts[], and {reason:?} does not"
);
}
assert!(!resolve_leaf(facts, "change_ticket", op, None, NOW).is_known());
}
assert!(
field_values(facts, "fact.change_ticket.approved", NOW).is_err(),
"{cause}: the field route has no default either"
);
assert!(
!membership(facts, "change_ticket", &json!("anything"), NOW).is_known(),
"{cause}: membership has no default either"
);
assert!(facts.present("change_ticket").is_none(), "{cause}");
}
}
#[test]
fn this_module_expresses_no_truth_value_fallback() {
let source = include_str!("facts.rs");
let shipped = source
.split_once("#[cfg(test)]")
.map_or(source, |(before, _)| before);
let code: String = shipped
.lines()
.filter(|line| !line.trim_start().starts_with("//"))
.collect::<Vec<_>>()
.join("\n");
for forbidden in [
"unwrap_or(false)",
"unwrap_or(true)",
"unwrap_or(Kleene",
"unwrap_or_else(|| Kleene",
"unwrap_or_default()",
"Kleene::from_bool(fact.closed_world",
] {
assert!(
!code.contains(forbidden),
"`{forbidden}` is a fact-level default; the resolution order has \
no branch that produces one"
);
}
}
}