#[cfg(any(test, feature = "test-support"))]
use chrono::Utc;
#[cfg(any(test, feature = "test-support"))]
use uuid::Uuid;
#[cfg(any(test, feature = "test-support"))]
use mempill_types::{
claim::{Cardinality, Claim, Confidence, Criticality, Fact},
disposition::Disposition,
edge::{ClaimEdge, EdgeKind},
identity::{AgentId, ClaimRef},
ledger::{LedgerEntry, LedgerEventKind},
provenance::{ExternalAnchor, ExternalKind, ProvenanceLabel},
time::{TransactionTime, ValidTime},
validity::{AssertionKind, ValidityAssertion},
};
#[cfg(any(test, feature = "test-support"))]
use crate::ports::persistence::PersistencePort;
#[cfg(any(test, feature = "test-support"))]
fn make_claim(agent_id: &AgentId, subject: &str, predicate: &str) -> Claim {
Claim::new(
ClaimRef::new_random(),
agent_id.clone(),
Fact {
subject: subject.to_owned(),
predicate: predicate.to_owned(),
value: serde_json::json!("test-value"),
},
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(Utc::now()),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
)
}
#[cfg(any(test, feature = "test-support"))]
fn make_ledger_entry(agent_id: &AgentId, claim_ref: &ClaimRef) -> LedgerEntry {
LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent_id.clone(),
claim_ref: claim_ref.clone(),
event_kind: LedgerEventKind::ClaimCommitted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(Utc::now()),
}
}
#[cfg(any(test, feature = "test-support"))]
fn make_validity_assertion(agent_id: &AgentId, claim_ref: &ClaimRef) -> ValidityAssertion {
ValidityAssertion {
assertion_ref: Uuid::new_v4(),
agent_id: agent_id.clone(),
target_claim: claim_ref.clone(),
kind: AssertionKind::Bound { bound_at: Utc::now() },
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
confidence: Confidence { value_confidence: 0.9, valid_time_confidence: 0.9 },
asserted_at: TransactionTime(Utc::now()),
}
}
#[cfg(any(test, feature = "test-support"))]
fn make_edge(agent_id: &AgentId, from: ClaimRef, to: ClaimRef, kind: EdgeKind) -> ClaimEdge {
ClaimEdge {
edge_id: Uuid::new_v4(),
agent_id: agent_id.clone(),
from_claim: from,
to_claim: to,
kind,
created_at: TransactionTime(Utc::now()),
}
}
#[cfg(any(test, feature = "test-support"))]
pub fn run_persistence_conformance<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
test_begin_commit_roundtrip(store);
test_append_all_four_tables(store);
test_rollback_leaves_zero_rows(store);
test_load_subject_line_ordering(store);
test_load_lineage_multi_hop(store);
test_load_edges_for_ordering(store);
test_edge_uniqueness_constraint(store);
test_load_validity_assertions_ordering(store);
test_load_ledger_with_from(store);
test_load_injected_claims(store);
test_load_claim_missing(store);
test_load_edges_for_empty(store);
test_load_ledger_for_claims_scoped(store);
}
#[cfg(any(test, feature = "test-support"))]
pub fn run_disposition_scope_conformance<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
test_superseded_claim_excluded_despite_large_agent_ledger(store);
}
#[cfg(any(test, feature = "test-support"))]
fn test_begin_commit_roundtrip<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t1".into());
let claim = make_claim(&agent, "user", "favourite_colour");
let claim_ref = claim.claim_ref().clone();
let mut txn = store
.begin_atomic(&agent)
.expect("conformance[t1]: begin_atomic must succeed");
store
.append_claim(&mut txn, &claim)
.expect("conformance[t1]: append_claim must succeed");
store.commit(txn).expect("conformance[t1]: commit must succeed");
let loaded = store
.load_claim(&agent, &claim_ref)
.expect("conformance[t1]: load_claim must not error");
let loaded = loaded.expect("conformance[t1]: load_claim must return Some after commit");
assert_eq!(
loaded.claim_ref(),
&claim_ref,
"conformance[t1]: claim_ref must round-trip"
);
assert_eq!(
loaded.fact().subject,
"user",
"conformance[t1]: subject must be preserved"
);
assert_eq!(
loaded.fact().predicate,
"favourite_colour",
"conformance[t1]: predicate must be preserved"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_append_all_four_tables<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t2".into());
let claim = make_claim(&agent, "user", "language");
let claim_ref = claim.claim_ref().clone();
let validity = make_validity_assertion(&agent, &claim_ref);
let ledger = make_ledger_entry(&agent, &claim_ref);
let claim2 = make_claim(&agent, "user", "location");
let claim2_ref = claim2.claim_ref().clone();
let edge = make_edge(&agent, claim_ref.clone(), claim2_ref.clone(), EdgeKind::DependsOn);
let mut txn = store
.begin_atomic(&agent)
.expect("conformance[t2]: begin_atomic must succeed");
store
.append_claim(&mut txn, &claim)
.expect("conformance[t2]: append_claim must succeed");
store
.append_claim(&mut txn, &claim2)
.expect("conformance[t2]: append_claim2 must succeed");
store
.append_validity_assertion(&mut txn, &validity)
.expect("conformance[t2]: append_validity_assertion must succeed");
store
.append_ledger_entry(&mut txn, &ledger)
.expect("conformance[t2]: append_ledger_entry must succeed");
store
.append_claim_edge(&mut txn, &edge)
.expect("conformance[t2]: append_claim_edge must succeed");
store.commit(txn).expect("conformance[t2]: commit must succeed");
let loaded_claim = store
.load_claim(&agent, &claim_ref)
.expect("conformance[t2]: load_claim must not error")
.expect("conformance[t2]: load_claim must return Some");
assert_eq!(loaded_claim.claim_ref(), &claim_ref, "conformance[t2]: claim_ref must match");
let assertions = store
.load_validity_assertions_for(&agent, &claim_ref)
.expect("conformance[t2]: load_validity_assertions_for must not error");
assert_eq!(
assertions.len(),
1,
"conformance[t2]: must have 1 validity assertion"
);
assert_eq!(
assertions[0].assertion_ref, validity.assertion_ref,
"conformance[t2]: assertion_ref must match"
);
let entries = store
.load_ledger(&agent, None, 100)
.expect("conformance[t2]: load_ledger must not error");
assert_eq!(entries.len(), 1, "conformance[t2]: must have 1 ledger entry");
assert_eq!(
entries[0].entry_id, ledger.entry_id,
"conformance[t2]: entry_id must match"
);
let edges = store
.load_edges_for(&agent, &claim_ref)
.expect("conformance[t2]: load_edges_for must not error");
assert_eq!(edges.len(), 1, "conformance[t2]: must have 1 edge");
assert_eq!(edges[0].edge_id, edge.edge_id, "conformance[t2]: edge_id must match");
}
#[cfg(any(test, feature = "test-support"))]
fn test_rollback_leaves_zero_rows<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t3".into());
let claim = make_claim(&agent, "subject-rb", "predicate-rb");
let claim_ref = claim.claim_ref().clone();
let validity = make_validity_assertion(&agent, &claim_ref);
let ledger = make_ledger_entry(&agent, &claim_ref);
let mut txn = store
.begin_atomic(&agent)
.expect("conformance[t3]: begin_atomic must succeed");
store
.append_claim(&mut txn, &claim)
.expect("conformance[t3]: append_claim must succeed");
store
.append_validity_assertion(&mut txn, &validity)
.expect("conformance[t3]: append_validity_assertion must succeed");
store
.append_ledger_entry(&mut txn, &ledger)
.expect("conformance[t3]: append_ledger_entry must succeed");
store.rollback(txn).expect("conformance[t3]: rollback must succeed");
let loaded_claim = store
.load_claim(&agent, &claim_ref)
.expect("conformance[t3]: load_claim must not error after rollback");
assert!(
loaded_claim.is_none(),
"conformance[t3]: claim must be absent after rollback"
);
let assertions = store
.load_validity_assertions_for(&agent, &claim_ref)
.expect("conformance[t3]: load_validity_assertions_for must not error");
assert!(
assertions.is_empty(),
"conformance[t3]: validity assertions must be absent after rollback"
);
let ledger_entries = store
.load_ledger(&agent, None, 100)
.expect("conformance[t3]: load_ledger must not error");
assert!(
ledger_entries.is_empty(),
"conformance[t3]: ledger entries must be absent after rollback"
);
let edges = store
.load_edges_for(&agent, &claim_ref)
.expect("conformance[t3]: load_edges_for must not error");
assert!(
edges.is_empty(),
"conformance[t3]: edges must be absent after rollback"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_subject_line_ordering<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t4".into());
let t1 = chrono::DateTime::<chrono::Utc>::from_timestamp(1_000_000, 0).unwrap();
let t2 = chrono::DateTime::<chrono::Utc>::from_timestamp(1_000_001, 0).unwrap();
let claim1 = Claim::new(
ClaimRef::new_random(),
agent.clone(),
Fact { subject: "user".into(), predicate: "job".into(), value: serde_json::json!("engineer") },
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(t1),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let claim2 = Claim::new(
ClaimRef::new_random(),
agent.clone(),
Fact { subject: "user".into(), predicate: "job".into(), value: serde_json::json!("architect") },
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(t2),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let ref1 = claim1.claim_ref().clone();
let ref2 = claim2.claim_ref().clone();
let mut txn = store
.begin_atomic(&agent)
.expect("conformance[t4]: begin_atomic must succeed");
store.append_claim(&mut txn, &claim1).expect("conformance[t4]: append claim1");
store.append_claim(&mut txn, &claim2).expect("conformance[t4]: append claim2");
store.commit(txn).expect("conformance[t4]: commit");
let line = store
.load_subject_line(&agent, "user", "job")
.expect("conformance[t4]: load_subject_line must not error");
assert_eq!(line.len(), 2, "conformance[t4]: must have 2 claims on subject line");
assert_eq!(
line[0].claim_ref(),
&ref1,
"conformance[t4]: first claim must have earliest tx_time (ASC order)"
);
assert_eq!(
line[1].claim_ref(),
&ref2,
"conformance[t4]: second claim must have latest tx_time"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_lineage_multi_hop<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t5".into());
let claim_a = make_claim(&agent, "topic", "summary");
let claim_b = make_claim(&agent, "topic", "detail");
let claim_c = make_claim(&agent, "topic", "inference");
let ref_a = claim_a.claim_ref().clone();
let ref_b = claim_b.claim_ref().clone();
let ref_c = claim_c.claim_ref().clone();
let t_base = chrono::DateTime::<chrono::Utc>::from_timestamp(2_000_000, 0).unwrap();
let edge_ab = ClaimEdge {
edge_id: Uuid::new_v4(),
agent_id: agent.clone(),
from_claim: ref_a.clone(),
to_claim: ref_b.clone(),
kind: EdgeKind::DerivedFrom,
created_at: TransactionTime(t_base),
};
let edge_bc = ClaimEdge {
edge_id: Uuid::new_v4(),
agent_id: agent.clone(),
from_claim: ref_b.clone(),
to_claim: ref_c.clone(),
kind: EdgeKind::DerivedFrom,
created_at: TransactionTime(t_base + chrono::Duration::seconds(1)),
};
let mut txn = store.begin_atomic(&agent).expect("conformance[t5]: begin_atomic");
store.append_claim(&mut txn, &claim_a).expect("conformance[t5]: append A");
store.append_claim(&mut txn, &claim_b).expect("conformance[t5]: append B");
store.append_claim(&mut txn, &claim_c).expect("conformance[t5]: append C");
store.append_claim_edge(&mut txn, &edge_ab).expect("conformance[t5]: append edge A→B");
store.append_claim_edge(&mut txn, &edge_bc).expect("conformance[t5]: append edge B→C");
store.commit(txn).expect("conformance[t5]: commit");
let lineage = store
.load_lineage(&agent, &ref_a)
.expect("conformance[t5]: load_lineage must not error");
assert_eq!(
lineage.len(),
2,
"conformance[t5]: lineage from A must have 2 edges (A→B at depth 1, B→C at depth 2)"
);
assert_eq!(
lineage[0].from_claim, ref_a,
"conformance[t5]: first edge must start from A (depth 1)"
);
assert_eq!(
lineage[0].to_claim, ref_b,
"conformance[t5]: first edge must point to B"
);
assert_eq!(
lineage[1].from_claim, ref_b,
"conformance[t5]: second edge must start from B (depth 2)"
);
assert_eq!(
lineage[1].to_claim, ref_c,
"conformance[t5]: second edge must point to C"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_edges_for_ordering<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t6".into());
let claim_hub = make_claim(&agent, "hub", "central");
let claim_x = make_claim(&agent, "spoke", "x");
let claim_y = make_claim(&agent, "spoke", "y");
let hub_ref = claim_hub.claim_ref().clone();
let x_ref = claim_x.claim_ref().clone();
let y_ref = claim_y.claim_ref().clone();
let t1 = chrono::DateTime::<chrono::Utc>::from_timestamp(3_000_000, 0).unwrap();
let t2 = chrono::DateTime::<chrono::Utc>::from_timestamp(3_000_001, 0).unwrap();
let edge1 = ClaimEdge {
edge_id: Uuid::new_v4(),
agent_id: agent.clone(),
from_claim: hub_ref.clone(),
to_claim: x_ref.clone(),
kind: EdgeKind::DependsOn,
created_at: TransactionTime(t1),
};
let edge2 = ClaimEdge {
edge_id: Uuid::new_v4(),
agent_id: agent.clone(),
from_claim: hub_ref.clone(),
to_claim: y_ref.clone(),
kind: EdgeKind::DependsOn,
created_at: TransactionTime(t2),
};
let mut txn = store.begin_atomic(&agent).expect("conformance[t6]: begin_atomic");
store.append_claim(&mut txn, &claim_hub).expect("conformance[t6]: append hub");
store.append_claim(&mut txn, &claim_x).expect("conformance[t6]: append x");
store.append_claim(&mut txn, &claim_y).expect("conformance[t6]: append y");
store.append_claim_edge(&mut txn, &edge1).expect("conformance[t6]: append edge1");
store.append_claim_edge(&mut txn, &edge2).expect("conformance[t6]: append edge2");
store.commit(txn).expect("conformance[t6]: commit");
let edges = store
.load_edges_for(&agent, &hub_ref)
.expect("conformance[t6]: load_edges_for must not error");
assert_eq!(edges.len(), 2, "conformance[t6]: hub must have 2 edges");
assert_eq!(
edges[0].to_claim, x_ref,
"conformance[t6]: first edge (ASC created_at) must point to x"
);
assert_eq!(
edges[1].to_claim, y_ref,
"conformance[t6]: second edge must point to y"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_edge_uniqueness_constraint<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t7".into());
let claim_a = make_claim(&agent, "dup-from", "p");
let claim_b = make_claim(&agent, "dup-to", "p");
let ref_a = claim_a.claim_ref().clone();
let ref_b = claim_b.claim_ref().clone();
let edge1 = ClaimEdge {
edge_id: Uuid::new_v4(),
agent_id: agent.clone(),
from_claim: ref_a.clone(),
to_claim: ref_b.clone(),
kind: EdgeKind::DependsOn,
created_at: TransactionTime(Utc::now()),
};
let mut txn = store.begin_atomic(&agent).expect("conformance[t7]: begin_atomic");
store.append_claim(&mut txn, &claim_a).expect("conformance[t7]: append A");
store.append_claim(&mut txn, &claim_b).expect("conformance[t7]: append B");
store.append_claim_edge(&mut txn, &edge1).expect("conformance[t7]: append first edge");
store.commit(txn).expect("conformance[t7]: first commit");
let edge_dup = ClaimEdge {
edge_id: Uuid::new_v4(), agent_id: agent.clone(),
from_claim: ref_a.clone(),
to_claim: ref_b.clone(),
kind: EdgeKind::DependsOn,
created_at: TransactionTime(Utc::now()),
};
let mut txn2 = store.begin_atomic(&agent).expect("conformance[t7]: begin_atomic txn2");
let result = store.append_claim_edge(&mut txn2, &edge_dup);
let _ = store.rollback(txn2);
assert!(
result.is_err(),
"conformance[t7]: duplicate edge insert must return Err (UNIQUE constraint)"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_validity_assertions_ordering<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t8".into());
let claim = make_claim(&agent, "food", "allergy");
let claim_ref = claim.claim_ref().clone();
let t1 = chrono::DateTime::<chrono::Utc>::from_timestamp(4_000_000, 0).unwrap();
let t2 = chrono::DateTime::<chrono::Utc>::from_timestamp(4_000_001, 0).unwrap();
let va1 = ValidityAssertion {
assertion_ref: Uuid::new_v4(),
agent_id: agent.clone(),
target_claim: claim_ref.clone(),
kind: AssertionKind::Bound { bound_at: t1 },
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
confidence: Confidence { value_confidence: 0.9, valid_time_confidence: 0.9 },
asserted_at: TransactionTime(t1),
};
let va2 = ValidityAssertion {
assertion_ref: Uuid::new_v4(),
agent_id: agent.clone(),
target_claim: claim_ref.clone(),
kind: AssertionKind::Reopen { reopen_at: t2 },
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
confidence: Confidence { value_confidence: 0.8, valid_time_confidence: 0.8 },
asserted_at: TransactionTime(t2),
};
let ref1 = va1.assertion_ref;
let ref2 = va2.assertion_ref;
let mut txn = store.begin_atomic(&agent).expect("conformance[t8]: begin_atomic");
store.append_claim(&mut txn, &claim).expect("conformance[t8]: append claim");
store.append_validity_assertion(&mut txn, &va2).expect("conformance[t8]: append va2 first");
store.append_validity_assertion(&mut txn, &va1).expect("conformance[t8]: append va1 second");
store.commit(txn).expect("conformance[t8]: commit");
let assertions = store
.load_validity_assertions_for(&agent, &claim_ref)
.expect("conformance[t8]: load_validity_assertions_for must not error");
assert_eq!(assertions.len(), 2, "conformance[t8]: must have 2 assertions");
assert_eq!(
assertions[0].assertion_ref, ref1,
"conformance[t8]: first assertion must be the earliest asserted_at (ASC)"
);
assert_eq!(
assertions[1].assertion_ref, ref2,
"conformance[t8]: second assertion must be the later asserted_at"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_ledger_with_from<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t9".into());
let t_early = chrono::DateTime::<chrono::Utc>::from_timestamp(5_000_000, 0).unwrap();
let t_late = chrono::DateTime::<chrono::Utc>::from_timestamp(5_000_002, 0).unwrap();
let claim_early = make_claim(&agent, "ledger-sub", "early");
let ref_early = claim_early.claim_ref().clone();
let claim_late = make_claim(&agent, "ledger-sub", "late");
let ref_late = claim_late.claim_ref().clone();
let entry_early = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref_early.clone(),
event_kind: LedgerEventKind::ClaimCommitted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(t_early),
};
let entry_late = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref_late.clone(),
event_kind: LedgerEventKind::ClaimCommitted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(t_late),
};
let late_id = entry_late.entry_id;
let mut txn = store.begin_atomic(&agent).expect("conformance[t9]: begin_atomic");
store.append_claim(&mut txn, &claim_early).expect("conformance[t9]: append claim_early");
store.append_claim(&mut txn, &claim_late).expect("conformance[t9]: append claim_late");
store.append_ledger_entry(&mut txn, &entry_early).expect("conformance[t9]: append early entry");
store.append_ledger_entry(&mut txn, &entry_late).expect("conformance[t9]: append late entry");
store.commit(txn).expect("conformance[t9]: commit");
let from_time = TransactionTime(t_late);
let entries = store
.load_ledger(&agent, Some(&from_time), 100)
.expect("conformance[t9]: load_ledger with from must not error");
assert_eq!(
entries.len(),
1,
"conformance[t9]: load_ledger with from=t_late must return 1 entry (not the earlier one)"
);
assert_eq!(
entries[0].entry_id, late_id,
"conformance[t9]: the returned entry must be the late one"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_injected_claims<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t10".into());
let claim1 = make_claim(&agent, "injected-sub", "p1");
let ref1 = claim1.claim_ref().clone();
let claim2 = make_claim(&agent, "injected-sub", "p2");
let ref2 = claim2.claim_ref().clone();
let entry_injected = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref1.clone(),
event_kind: LedgerEventKind::ServedAsInjected,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(Utc::now()),
};
let entry_committed = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref2.clone(),
event_kind: LedgerEventKind::ClaimCommitted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(Utc::now()),
};
let mut txn = store.begin_atomic(&agent).expect("conformance[t10]: begin_atomic");
store.append_claim(&mut txn, &claim1).expect("conformance[t10]: append claim1");
store.append_claim(&mut txn, &claim2).expect("conformance[t10]: append claim2");
store.append_ledger_entry(&mut txn, &entry_injected).expect("conformance[t10]: append injected entry");
store.append_ledger_entry(&mut txn, &entry_committed).expect("conformance[t10]: append committed entry");
store.commit(txn).expect("conformance[t10]: commit");
let injected = store
.load_injected_claims(&agent)
.expect("conformance[t10]: load_injected_claims must not error");
assert_eq!(
injected.len(),
1,
"conformance[t10]: must return exactly 1 injected claim (ServedAsInjected only)"
);
assert_eq!(
injected[0], ref1,
"conformance[t10]: injected claim ref must be ref1"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_claim_missing<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t11".into());
let nonexistent_ref = ClaimRef::new_random();
let result = store
.load_claim(&agent, &nonexistent_ref)
.expect("conformance[t11]: load_claim for missing ref must not error");
assert!(
result.is_none(),
"conformance[t11]: load_claim for nonexistent ClaimRef must return None"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_edges_for_empty<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-t12".into());
let claim = make_claim(&agent, "isolated-sub", "p");
let claim_ref = claim.claim_ref().clone();
let mut txn = store.begin_atomic(&agent).expect("conformance[t12]: begin_atomic");
store.append_claim(&mut txn, &claim).expect("conformance[t12]: append claim");
store.commit(txn).expect("conformance[t12]: commit");
let edges = store
.load_edges_for(&agent, &claim_ref)
.expect("conformance[t12]: load_edges_for must not error");
assert!(
edges.is_empty(),
"conformance[t12]: load_edges_for must return empty vec when no edges exist"
);
}
#[cfg(any(test, feature = "test-support"))]
pub fn run_history_conformance<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
hist_empty_line(store);
hist_single_claim(store);
hist_succession_ordering(store);
hist_current_agrees_with_fold(store);
}
#[cfg(any(test, feature = "test-support"))]
fn hist_empty_line<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-hist-t1".into());
let claims = store
.load_subject_line(&agent, "hist-nobody", "hist-nothing")
.expect("conformance[hist-t1]: load_subject_line must not error");
assert!(
claims.is_empty(),
"conformance[hist-t1]: unknown subject-line must return empty vec"
);
}
#[cfg(any(test, feature = "test-support"))]
fn hist_single_claim<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
use crate::application::query_history::compute_effective_windows;
use crate::config::EngineConfig;
let agent = AgentId("conformance-hist-t2".into());
let tx = chrono::DateTime::<chrono::Utc>::from_timestamp(10_000_000, 0).unwrap();
let claim = Claim::new(
ClaimRef::new_random(),
agent.clone(),
Fact { subject: "hist-acme".to_owned(), predicate: "ceo".to_owned(), value: serde_json::json!("Alice") },
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(tx),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let mut txn = store.begin_atomic(&agent).expect("conformance[hist-t2]: begin_atomic");
store.append_claim(&mut txn, &claim).expect("conformance[hist-t2]: append_claim");
store.commit(txn).expect("conformance[hist-t2]: commit");
let claims = store
.load_subject_line(&agent, "hist-acme", "ceo")
.expect("conformance[hist-t2]: load_subject_line must not error");
assert_eq!(claims.len(), 1, "conformance[hist-t2]: must have 1 claim");
let config = EngineConfig::default();
let refs: Vec<&Claim> = claims.iter().collect();
let windows = compute_effective_windows(&refs, &config);
assert_eq!(windows.len(), 1, "conformance[hist-t2]: 1 window");
assert_eq!(windows[0], None, "conformance[hist-t2]: single claim has open-ended window");
}
#[cfg(any(test, feature = "test-support"))]
fn hist_succession_ordering<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
use crate::application::query_history::compute_effective_windows;
use crate::config::EngineConfig;
let agent = AgentId("conformance-hist-t3".into());
let t_alice = chrono::DateTime::<chrono::Utc>::from_timestamp(11_000_000, 0).unwrap();
let t_john = chrono::DateTime::<chrono::Utc>::from_timestamp(11_000_001, 0).unwrap();
let t_bob = chrono::DateTime::<chrono::Utc>::from_timestamp(11_000_002, 0).unwrap();
let make_c = |val: &str, tx: chrono::DateTime<chrono::Utc>| -> Claim {
Claim::new(
ClaimRef::new_random(),
agent.clone(),
Fact { subject: "hist-corp".to_owned(), predicate: "ceo".to_owned(), value: serde_json::json!(val) },
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(tx),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
)
};
let c_alice = make_c("Alice", t_alice);
let c_john = make_c("John", t_john);
let c_bob = make_c("Bob", t_bob);
let mut txn = store.begin_atomic(&agent).expect("conformance[hist-t3]: begin_atomic");
store.append_claim(&mut txn, &c_alice).expect("conformance[hist-t3]: append Alice");
store.append_claim(&mut txn, &c_john).expect("conformance[hist-t3]: append John");
store.append_claim(&mut txn, &c_bob).expect("conformance[hist-t3]: append Bob");
store.commit(txn).expect("conformance[hist-t3]: commit");
let mut claims = store
.load_subject_line(&agent, "hist-corp", "ceo")
.expect("conformance[hist-t3]: load_subject_line must not error");
assert_eq!(claims.len(), 3, "conformance[hist-t3]: must have 3 claims (Alice, John, Bob)");
let config = EngineConfig::default();
claims.sort_by(|a, b| {
a.transaction_time().0.cmp(&b.transaction_time().0)
.then(a.claim_ref().0.as_u128().cmp(&b.claim_ref().0.as_u128()))
});
let refs: Vec<&Claim> = claims.iter().collect();
let windows = compute_effective_windows(&refs, &config);
assert_eq!(windows[0], Some(t_john), "conformance[hist-t3]: Alice's valid_until = John's ordering key");
assert_eq!(windows[1], Some(t_bob), "conformance[hist-t3]: John's valid_until = Bob's ordering key");
assert_eq!(windows[2], None, "conformance[hist-t3]: Bob is open-ended (current)");
assert_eq!(claims[0].fact().value, serde_json::json!("Alice"), "conformance[hist-t3]: oldest is Alice");
assert_eq!(claims[1].fact().value, serde_json::json!("John"), "conformance[hist-t3]: middle is John");
assert_eq!(claims[2].fact().value, serde_json::json!("Bob"), "conformance[hist-t3]: newest is Bob");
}
#[cfg(any(test, feature = "test-support"))]
fn hist_current_agrees_with_fold<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
use crate::config::EngineConfig;
use crate::engine::truth_engine;
use std::collections::HashMap;
use mempill_types::disposition::Disposition;
let agent = AgentId("conformance-hist-t4".into());
let t1 = chrono::DateTime::<chrono::Utc>::from_timestamp(12_000_000, 0).unwrap();
let t2 = chrono::DateTime::<chrono::Utc>::from_timestamp(12_000_001, 0).unwrap();
let c = Claim::new(
ClaimRef::new_random(),
agent.clone(),
Fact { subject: "hist-org".to_owned(), predicate: "lead".to_owned(), value: serde_json::json!("Leader-A") },
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(t1),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let mut txn = store.begin_atomic(&agent).expect("conformance[hist-t4]: begin_atomic");
store.append_claim(&mut txn, &c).expect("conformance[hist-t4]: append");
store.commit(txn).expect("conformance[hist-t4]: commit");
let claims = store
.load_subject_line(&agent, "hist-org", "lead")
.expect("conformance[hist-t4]: load_subject_line");
assert_eq!(claims.len(), 1, "conformance[hist-t4]: one claim loaded");
let config = EngineConfig::default();
let latest_disposition: HashMap<_, Disposition> = HashMap::new();
let now = t2;
let fold = truth_engine::fold(
claims.clone(),
|_| vec![],
now,
&config,
&latest_disposition,
);
assert_eq!(fold.live_claims.len(), 1, "conformance[hist-t4]: one live claim in fold");
assert_eq!(
fold.live_claims[0].claim.fact().value,
serde_json::json!("Leader-A"),
"conformance[hist-t4]: fold's live claim must match the single committed claim"
);
}
#[cfg(any(test, feature = "test-support"))]
fn test_load_ledger_for_claims_scoped<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-lfc-t1".into());
let claim_a = make_claim(&agent, "scope-subj", "scope-pred");
let claim_b = make_claim(&agent, "scope-subj", "scope-pred-b");
let ref_a = claim_a.claim_ref().clone();
let ref_b = claim_b.claim_ref().clone();
let ledger_a = make_ledger_entry(&agent, &ref_a);
let ledger_b = make_ledger_entry(&agent, &ref_b);
let mut txn = store.begin_atomic(&agent).expect("conformance[lfc-t1]: begin_atomic");
store.append_claim(&mut txn, &claim_a).expect("conformance[lfc-t1]: append_claim_a");
store.append_claim(&mut txn, &claim_b).expect("conformance[lfc-t1]: append_claim_b");
store.append_ledger_entry(&mut txn, &ledger_a).expect("conformance[lfc-t1]: append_ledger_a");
store.append_ledger_entry(&mut txn, &ledger_b).expect("conformance[lfc-t1]: append_ledger_b");
store.commit(txn).expect("conformance[lfc-t1]: commit");
let result = store
.load_ledger_for_claims(&agent, &[ref_a.clone()])
.expect("conformance[lfc-t1]: load_ledger_for_claims must not error");
assert_eq!(result.len(), 1, "conformance[lfc-t1]: exactly one entry for claim_a");
assert_eq!(result[0].claim_ref, ref_a, "conformance[lfc-t1]: entry must be for claim_a");
let empty = store
.load_ledger_for_claims(&agent, &[])
.expect("conformance[lfc-t1]: empty input must not error");
assert!(empty.is_empty(), "conformance[lfc-t1]: empty input must return empty vec");
}
#[cfg(any(test, feature = "test-support"))]
fn test_superseded_claim_excluded_despite_large_agent_ledger<P>(store: &P)
where
P: PersistencePort,
P::Error: std::fmt::Debug,
{
let agent = AgentId("conformance-dscope-t1".into());
for i in 0..50u32 {
let noise_claim = Claim::new(
ClaimRef::new_random(),
agent.clone(),
mempill_types::claim::Fact {
subject: format!("noise-subject-{i}"),
predicate: "noise-predicate".to_owned(),
value: serde_json::json!(i),
},
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(Utc::now()),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let noise_ref = noise_claim.claim_ref().clone();
let noise_ledger1 = make_ledger_entry(&agent, &noise_ref);
let noise_ledger2 = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: noise_ref.clone(),
event_kind: LedgerEventKind::ValidityAsserted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(Utc::now()),
};
let mut txn = store.begin_atomic(&agent).expect("dscope[t1]: noise begin_atomic");
store.append_claim(&mut txn, &noise_claim).expect("dscope[t1]: noise append_claim");
store.append_ledger_entry(&mut txn, &noise_ledger1).expect("dscope[t1]: noise ledger1");
store.append_ledger_entry(&mut txn, &noise_ledger2).expect("dscope[t1]: noise ledger2");
store.commit(txn).expect("dscope[t1]: noise commit");
}
let t_a = chrono::DateTime::<Utc>::from_timestamp(1_000_000, 0).unwrap();
let claim_a = Claim::new(
ClaimRef::new_random(),
agent.clone(),
mempill_types::claim::Fact {
subject: "dscope-org".to_owned(),
predicate: "ceo".to_owned(),
value: serde_json::json!("Alice"),
},
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(t_a),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let ref_a = claim_a.claim_ref().clone();
let ledger_a_committed = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref_a.clone(),
event_kind: LedgerEventKind::ClaimCommitted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(t_a),
};
let mut txn = store.begin_atomic(&agent).expect("dscope[t1]: claim_a begin");
store.append_claim(&mut txn, &claim_a).expect("dscope[t1]: claim_a append");
store.append_ledger_entry(&mut txn, &ledger_a_committed).expect("dscope[t1]: claim_a ledger");
store.commit(txn).expect("dscope[t1]: claim_a commit");
let t_b = chrono::DateTime::<Utc>::from_timestamp(2_000_000, 0).unwrap();
let claim_b = Claim::new(
ClaimRef::new_random(),
agent.clone(),
mempill_types::claim::Fact {
subject: "dscope-org".to_owned(),
predicate: "ceo".to_owned(),
value: serde_json::json!("Bob"),
},
Cardinality::Functional,
ProvenanceLabel::External(ExternalKind::UserAsserted),
ExternalAnchor { nearest_external_anchor: None, derivation_depth: 0 },
TransactionTime(t_b),
ValidTime { start: None, end: None, valid_time_confidence: 0.0 },
Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
Criticality::Low,
vec![],
None,
None,
);
let ref_b = claim_b.claim_ref().clone();
let ledger_a_superseded = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref_a.clone(),
event_kind: LedgerEventKind::ValidityAsserted,
disposition: Disposition::Superseded,
rationale: None,
recorded_at: TransactionTime(t_b),
};
let ledger_b_committed = LedgerEntry {
entry_id: Uuid::new_v4(),
agent_id: agent.clone(),
claim_ref: ref_b.clone(),
event_kind: LedgerEventKind::ClaimCommitted,
disposition: Disposition::CommittedCheap,
rationale: None,
recorded_at: TransactionTime(t_b),
};
let mut txn = store.begin_atomic(&agent).expect("dscope[t1]: claim_b begin");
store.append_claim(&mut txn, &claim_b).expect("dscope[t1]: claim_b append");
store.append_ledger_entry(&mut txn, &ledger_a_superseded).expect("dscope[t1]: ledger_a_superseded");
store.append_ledger_entry(&mut txn, &ledger_b_committed).expect("dscope[t1]: ledger_b_committed");
store.commit(txn).expect("dscope[t1]: claim_b commit");
let scoped = store
.load_ledger_for_claims(&agent, &[ref_a.clone(), ref_b.clone()])
.expect("dscope[t1]: load_ledger_for_claims must not error");
assert_eq!(
scoped.len(), 3,
"dscope[t1]: load_ledger_for_claims must return all 3 entries for the 2 subject-line claims"
);
use crate::application::ingest_claim::build_latest_disposition_map;
use crate::config::EngineConfig;
use crate::engine::truth_engine;
let subject_claims = store
.load_subject_line(&agent, "dscope-org", "ceo")
.expect("dscope[t1]: load_subject_line must not error");
assert_eq!(subject_claims.len(), 2, "dscope[t1]: must have 2 claims on subject-line");
let subject_refs: Vec<ClaimRef> = subject_claims.iter().map(|c| c.claim_ref().clone()).collect();
let scoped_ledger = store
.load_ledger_for_claims(&agent, &subject_refs)
.expect("dscope[t1]: load_ledger_for_claims must not error after B committed");
let latest_disposition = build_latest_disposition_map(&scoped_ledger);
let now = chrono::DateTime::<Utc>::from_timestamp(3_000_000, 0).unwrap();
let config = EngineConfig::default();
let fold = truth_engine::fold(
subject_claims,
|_cref| vec![],
now,
&config,
&latest_disposition,
);
assert_eq!(
fold.live_claims.len(), 1,
"dscope[t1]: exactly one live claim (Bob/B); got {:?}",
fold.live_claims.iter().map(|cs| &cs.claim.fact().value).collect::<Vec<_>>()
);
assert_eq!(
fold.live_claims[0].claim.fact().value,
serde_json::json!("Bob"),
"dscope[t1]: the live claim must be Bob (B), not Alice (A — superseded)"
);
}