use mempill_core::application::{AuditQueryRequest, IngestClaimRequest, QueryMemoryRequest};
use mempill_sqlite::open_default_in_memory;
use mempill_types::{
AgentId, Cardinality, ClaimRef, Confidence, Criticality, Disposition,
ExternalKind, LedgerEventKind, ProvenanceLabel,
};
const AMPLIFICATION_COUNT: usize = 808;
#[tokio::test]
async fn acid_amplification_808_recall_reentries_collapse_to_one_claim() {
let engine = open_default_in_memory().expect("in-memory engine must open");
let agent = AgentId("acid-amplification-agent".into());
let original_req = IngestClaimRequest {
agent_id: agent.clone(),
subject: "patient".into(),
predicate: "recall_test".into(),
value: serde_json::json!("canonical-content-808"),
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.95, valid_time_confidence: 0.0 },
criticality: Criticality::High,
derived_from: vec![],
};
let original_resp = engine
.ingest_claim(original_req)
.await
.expect("original ingest must succeed");
assert!(
!original_resp.claim_ref.0.is_nil(),
"original claim_ref must be a valid UUID"
);
assert_eq!(
original_resp.disposition,
Disposition::CommittedCheap,
"first External claim must be CommittedCheap"
);
let original_claim_ref: ClaimRef = original_resp.claim_ref.clone();
let mut corroborate_count = 0usize;
let mut unexpected_new_refs = 0usize;
for i in 0..AMPLIFICATION_COUNT {
let re_req = IngestClaimRequest {
agent_id: agent.clone(),
subject: "patient".into(),
predicate: "recall_test".into(),
value: serde_json::json!("canonical-content-808"),
provenance: ProvenanceLabel::RecallReEntry,
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.95, valid_time_confidence: 0.0 },
criticality: Criticality::High,
derived_from: vec![original_claim_ref.clone()],
};
let re_resp = engine
.ingest_claim(re_req)
.await
.unwrap_or_else(|e| panic!("re-ingest {i} must succeed: {e}"));
if re_resp.claim_ref == original_claim_ref {
corroborate_count += 1;
} else {
unexpected_new_refs += 1;
}
}
let query_resp = engine
.query_memory(QueryMemoryRequest {
agent_id: agent.clone(),
subject: "patient".into(),
predicate: "recall_test".into(),
as_of_tx_time: None,
})
.await
.expect("query must succeed");
let primary = query_resp
.belief
.primary
.as_ref()
.expect("primary belief must be present after original ingest");
assert_eq!(
primary.claim_ref,
original_claim_ref,
"ACID I6: the sole live belief MUST be the original claim — not a re-entry clone"
);
assert_eq!(
primary.fact.value,
serde_json::json!("canonical-content-808"),
"original content must be preserved unchanged"
);
assert_eq!(
corroborate_count, AMPLIFICATION_COUNT,
"ACID I6 mem0 #4573: all {AMPLIFICATION_COUNT} RecallReEntry re-ingestions must return the EXISTING \
claim_ref (CorroborateByIdentity). Unexpected new refs: {unexpected_new_refs}"
);
assert_eq!(
unexpected_new_refs, 0,
"ACID I6: zero new claim rows must be created by {AMPLIFICATION_COUNT} RecallReEntry re-ingestions"
);
let audit_resp = engine
.query_audit(AuditQueryRequest {
agent_id: agent.clone(),
claim_ref: Some(original_claim_ref.clone()),
from_tx_time: None,
limit: 2000,
})
.await
.expect("audit query must succeed");
let committed_entries: Vec<_> = audit_resp
.entries
.iter()
.filter(|e| {
matches!(
e.event_kind,
LedgerEventKind::ClaimCommitted
) && e.claim_ref == original_claim_ref
})
.collect();
assert_eq!(
committed_entries.len(),
1,
"ACID I6: EXACTLY ONE ClaimCommitted ledger entry for the original claim. \
808 RecallReEntry re-ingestions must NOT generate additional ledger rows. \
Found: {}",
committed_entries.len()
);
println!(
"ACID AMPLIFICATION PASS: {AMPLIFICATION_COUNT} RecallReEntry re-ingestions → 1 claim row, \
{corroborate_count} corroborations, {unexpected_new_refs} unexpected new refs"
);
}
#[tokio::test]
async fn acid_amplification_genuine_new_claim_on_fresh_subject_line_is_admitted() {
let engine = open_default_in_memory().expect("in-memory engine must open");
let agent = AgentId("acid-amplification-distinct-agent".into());
let req_a = IngestClaimRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "city".into(),
value: serde_json::json!("Berlin"),
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
criticality: Criticality::Medium,
derived_from: vec![],
};
let resp_a = engine
.ingest_claim(req_a)
.await
.expect("first ingest must succeed");
assert_eq!(resp_a.disposition, Disposition::CommittedCheap);
let req_b = IngestClaimRequest {
agent_id: agent.clone(),
subject: "user".into(),
predicate: "country".into(), value: serde_json::json!("Germany"),
provenance: ProvenanceLabel::External(ExternalKind::UserAsserted),
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.9, valid_time_confidence: 0.0 },
criticality: Criticality::Medium,
derived_from: vec![],
};
let resp_b = engine
.ingest_claim(req_b)
.await
.expect("second ingest on different predicate must succeed");
assert_ne!(
resp_b.claim_ref,
resp_a.claim_ref,
"second claim on different predicate must get a NEW claim_ref"
);
assert_eq!(
resp_b.disposition,
Disposition::CommittedCheap,
"second claim on different predicate must be CommittedCheap (no conflict)"
);
println!(
"AMPLIFICATION SMOKE PASS: distinct subject-line admitted. \
city_ref={}, country_ref={}",
resp_a.claim_ref.0,
resp_b.claim_ref.0
);
}
#[tokio::test]
async fn acid_amplification_recall_reentry_no_ref_degrades_to_admit() {
let engine = open_default_in_memory().expect("in-memory engine must open");
let agent = AgentId("acid-amplification-noref-agent".into());
let req = IngestClaimRequest {
agent_id: agent.clone(),
subject: "orphan".into(),
predicate: "data".into(),
value: serde_json::json!("no-prior-ref"),
provenance: ProvenanceLabel::RecallReEntry,
cardinality: Cardinality::Functional,
valid_time: None,
confidence: Confidence { value_confidence: 0.8, valid_time_confidence: 0.0 },
criticality: Criticality::Low,
derived_from: vec![], };
let resp = engine
.ingest_claim(req)
.await
.expect("RecallReEntry with no ref must succeed (degrades to Admit)");
assert!(
!resp.claim_ref.0.is_nil(),
"orphaned RecallReEntry must produce a valid claim_ref (graceful Admit)"
);
println!(
"AMPLIFICATION NOREF PASS: RecallReEntry with no derived_from degrades to Admit. \
claim_ref={}",
resp.claim_ref.0
);
}