use super::*;
#[cfg(feature = "bundled-embedder")]
#[test]
fn bundled_embedder_auto_attaches_on_default_dim() {
use crate::embedder::BUNDLED_EMBEDDER_DIM;
let db = YantrikDB::new(":memory:", BUNDLED_EMBEDDER_DIM).unwrap();
assert!(
db.has_embedder(),
"default-build YantrikDB::new with bundled dim must auto-attach BundledEmbedder"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn with_default_constructor_attaches_bundled_embedder() {
let db = YantrikDB::with_default(":memory:").unwrap();
assert!(
db.has_embedder(),
"with_default must auto-attach BundledEmbedder"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn bundled_embedder_does_not_attach_on_mismatched_dim() {
let db = YantrikDB::new(":memory:", 384).unwrap();
assert!(
!db.has_embedder(),
"dim mismatch should NOT auto-attach (avoids silent corruption)"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn bundled_embedder_record_text_round_trip() {
let db = YantrikDB::with_default(":memory:").unwrap();
let _rid = db
.record_text(
"Alice met Acme yesterday",
"episodic",
0.5,
0.0,
604800.0,
&empty_meta(),
"default",
0.8,
"general",
"user",
None,
)
.expect("record_text should work without explicit set_embedder");
let results = db.recall_text("Alice", 5).expect("recall_text should work");
assert!(!results.is_empty(), "recall finds the recorded memory");
assert!(
results[0].text.contains("Alice"),
"potion-2M finds the recorded memory; got: {:?}",
results[0].text
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn record_text_strips_leaked_tool_call_artifact_end_to_end() {
let db = YantrikDB::with_default(":memory:").unwrap();
let mangled = "Decision: adopt keyset cursors for list_records.</text>\n\
<parameter name=\"memory_type\">episodic";
let rid = db
.record_text(
mangled,
"episodic",
0.5,
0.0,
604800.0,
&empty_meta(),
"default",
0.8,
"general",
"user",
None,
)
.expect("record_text stores sanitized text");
let results = db.recall_text("keyset cursors list_records", 5).unwrap();
let hit = results
.iter()
.find(|r| r.rid == rid)
.expect("the recorded memory is retrievable");
assert!(
hit.text.contains("keyset cursors"),
"real content is preserved; got: {:?}",
hit.text
);
assert!(
!hit.text.contains("</text>"),
"the leaked closing tag must be stripped; got: {:?}",
hit.text
);
assert!(
!hit.text.contains("<parameter name="),
"the leaked parameter fragment must be stripped; got: {:?}",
hit.text
);
assert_eq!(
hit.text, "Decision: adopt keyset cursors for list_records.",
"stored text is exactly the cleaned content"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn repair_tool_call_artifacts_cleans_legacy_corpus() {
let db = YantrikDB::with_default(":memory:").unwrap();
let clean = "Postgres was chosen for the metadata store";
let rid = db
.record_text(
clean,
"semantic",
0.6,
0.0,
604800.0,
&empty_meta(),
"default",
0.8,
"general",
"user",
None,
)
.unwrap();
let dirty = "Postgres was chosen for the metadata store</text>\n\
<parameter name=\"memory_type\">semantic";
{
let conn = db.conn();
conn.execute(
"UPDATE memories SET text = ?1 WHERE rid = ?2",
rusqlite::params![dirty, rid],
)
.unwrap();
}
let dry = db.repair_tool_call_artifacts(true).unwrap();
assert!(dry.dry_run);
assert_eq!(dry.artifacts_found, 1);
assert_eq!(dry.repaired, 0);
assert!(dry.stripped_bytes > 0);
{
let conn = db.conn();
let t: String = conn
.query_row(
"SELECT text FROM memories WHERE rid = ?1",
rusqlite::params![rid],
|r| r.get(0),
)
.unwrap();
assert!(t.contains("</text>"), "dry run must NOT mutate");
}
let applied = db.repair_tool_call_artifacts(false).unwrap();
assert!(!applied.dry_run);
assert_eq!(applied.artifacts_found, 1);
assert_eq!(applied.repaired, 1);
assert_eq!(applied.skipped_concurrent_modification, 0);
assert!(applied.errors.is_empty(), "errors: {:?}", applied.errors);
{
let conn = db.conn();
let t: String = conn
.query_row(
"SELECT text FROM memories WHERE rid = ?1",
rusqlite::params![rid],
|r| r.get(0),
)
.unwrap();
assert_eq!(t, clean);
}
{
let conn = db.conn();
let orig: String = conn
.query_row(
"SELECT original_text FROM artifact_repair_audit WHERE rid = ?1",
rusqlite::params![rid],
|r| r.get(0),
)
.unwrap();
assert!(
orig.contains("</text>"),
"audit preserves the dirty original"
);
}
let again = db.repair_tool_call_artifacts(false).unwrap();
assert_eq!(again.artifacts_found, 0);
assert_eq!(again.repaired, 0);
let hits = db.recall_text("database for metadata", 5).unwrap();
assert!(
hits.iter().any(|h| h.rid == rid),
"repaired memory is still retrievable"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn importance_calibration_deflates_saturated_namespace() {
let db = YantrikDB::with_default(":memory:").unwrap();
let read_importance = |rid: &str| -> f64 {
let conn = db.conn();
conn.query_row(
"SELECT importance FROM memories WHERE rid = ?1",
rusqlite::params![rid],
|r| r.get(0),
)
.unwrap()
};
let rid0 = db
.record_text(
"first genuinely critical fact",
"semantic",
1.0,
0.0,
604800.0,
&empty_meta(),
"fresh",
0.8,
"general",
"user",
None,
)
.unwrap();
assert!(
(read_importance(&rid0) - 1.0).abs() < 1e-9,
"fresh namespace preserves importance exactly: {}",
read_importance(&rid0)
);
for i in 0..12 {
db.record_text(
&format!("everything here is marked critical {i}"),
"semantic",
1.0,
0.0,
604800.0,
&empty_meta(),
"saturated",
0.8,
"general",
"user",
None,
)
.unwrap();
}
let rid = db
.record_text(
"yet another self-declared critical fact",
"semantic",
1.0,
0.0,
604800.0,
&empty_meta(),
"saturated",
0.8,
"general",
"user",
None,
)
.unwrap();
let imp = read_importance(&rid);
assert!(imp < 1.0, "saturated namespace deflates importance: {imp}");
assert!(imp >= 0.70, "but keeps it in the high band: {imp}");
let hits = db.recall_text("self-declared critical fact", 5).unwrap();
assert!(hits.iter().any(|h| h.rid == rid));
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn recalibrate_unused_importance_reverts_stale_high_marks() {
let db = YantrikDB::with_default(":memory:").unwrap();
let read_imp = |rid: &str| -> f64 {
let conn = db.conn();
conn.query_row(
"SELECT importance FROM memories WHERE rid = ?1",
rusqlite::params![rid],
|r| r.get(0),
)
.unwrap()
};
let stale = db
.record_text(
"a once-critical fact nobody revisits",
"semantic",
1.0,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"general",
"user",
None,
)
.unwrap();
let fresh = db
.record_text(
"a fact that was just written",
"semantic",
1.0,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"general",
"user",
None,
)
.unwrap();
{
let conn = db.conn();
conn.execute(
"UPDATE memories SET last_access = 1000.0, access_count = 0 WHERE rid = ?1",
rusqlite::params![stale],
)
.unwrap();
}
let dry = db.recalibrate_unused_importance(true).unwrap();
assert!(dry.dry_run);
assert_eq!(dry.adjusted, 1);
assert!(
(read_imp(&stale) - 1.0).abs() < 1e-9,
"dry run must not mutate"
);
let applied = db.recalibrate_unused_importance(false).unwrap();
assert_eq!(applied.adjusted, 1);
assert!(applied.total_drift > 0.0);
let reverted = read_imp(&stale);
assert!(
reverted < 1.0,
"stale unused high mark reverted: {reverted}"
);
assert!(reverted >= 0.5, "but not below baseline: {reverted}");
assert!(
(read_imp(&fresh) - 1.0).abs() < 1e-9,
"a freshly-written memory is untouched"
);
let again = db.recalibrate_unused_importance(false).unwrap();
assert_eq!(
again.adjusted, 0,
"reversion does not compound across passes"
);
assert!((read_imp(&stale) - reverted).abs() < 1e-9);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn split_oversized_episodes_extracts_linked_atomic_facts() {
let db = YantrikDB::with_default(":memory:").unwrap();
let episode = "Session recap. Alice was promoted to engineering lead this week. \
The team chose Postgres for the metadata store after benchmarking. \
The production launch slipped to March 30 because of the migration. \
Bob will own the on-call rotation starting next sprint. \
We agreed to cap importance writes so the signal stays meaningful.";
let parent = db
.record_text(
episode,
"episodic",
1.0,
0.0,
604800.0,
&empty_meta(),
"recap",
0.9,
"work",
"user",
None,
)
.unwrap();
let dry = db.split_oversized_episodes(true, 120).unwrap();
assert_eq!(dry.episodes_scanned, 1);
assert_eq!(dry.episodes_split, 0);
assert!(dry.atomic_facts_created >= 2);
let applied = db.split_oversized_episodes(false, 120).unwrap();
assert_eq!(applied.episodes_split, 1);
assert!(applied.atomic_facts_created >= 2, "{applied:?}");
assert!(applied.errors.is_empty(), "errors: {:?}", applied.errors);
{
let conn = db.conn();
let status: String = conn
.query_row(
"SELECT consolidation_status FROM memories WHERE rid = ?1",
rusqlite::params![parent],
|r| r.get(0),
)
.unwrap();
assert_eq!(status, "consolidated", "parent episode demoted from recall");
}
let children = db
.linked_records(&parent, crate::types::LinkDirection::Inbound, None)
.unwrap();
assert!(
children.len() >= 2,
"parent has atomic-fact children linked back: {}",
children.len()
);
assert!(children.iter().all(|c| c.link_type == "derived_from"));
let hits = db.recall_text("who owns the on-call rotation", 5).unwrap();
assert!(!hits.is_empty());
assert_ne!(
hits[0].rid, parent,
"top hit is an atomic fact, not the dump"
);
assert!(
hits[0].text.chars().count() < episode.chars().count(),
"the returned fact is shorter than the original dump"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn conflict_stamping_and_auto_resolution() {
let db = YantrikDB::with_default(":memory:").unwrap();
let older = db
.record_text(
"The launch date is March 15",
"semantic",
0.7,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
{
let conn = db.conn();
conn.execute(
"UPDATE memories SET created_at = 1000.0 WHERE rid = ?1",
rusqlite::params![older],
)
.unwrap();
}
let newer = db
.record_text(
"The launch date is March 30",
"semantic",
0.7,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
{
let conn = db.conn();
conn.execute(
"INSERT INTO conflicts \
(conflict_id, conflict_type, priority, status, memory_a, memory_b, \
detected_at, detected_by, detection_reason, hlc, origin_actor) \
VALUES ('cf1', 'temporal', 'medium', 'open', ?1, ?2, 2000.0, 'test', \
'same attribute, different value', X'00', 'test')",
rusqlite::params![older, newer],
)
.unwrap();
}
let hits = db.recall_text("when is the launch date", 5).unwrap();
let flagged = hits.iter().any(|h| {
(h.rid == older || h.rid == newer)
&& h.why_retrieved
.iter()
.any(|w| w.contains("unresolved") && w.contains("conflict"))
});
assert!(flagged, "recall hits carry the conflict warning");
let dry = db.auto_resolve_conflicts(true).unwrap();
assert_eq!(dry.open_before, 1);
assert_eq!(dry.auto_resolved, 1);
assert_eq!(dry.routed_to_operator, 0);
let applied = db.auto_resolve_conflicts(false).unwrap();
assert_eq!(applied.auto_resolved, 1);
{
let conn = db.conn();
let status: String = conn
.query_row(
"SELECT status FROM conflicts WHERE conflict_id = 'cf1'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(status, "resolved");
let older_status: String = conn
.query_row(
"SELECT consolidation_status FROM memories WHERE rid = ?1",
rusqlite::params![older],
|r| r.get(0),
)
.unwrap();
assert_eq!(
older_status, "tombstoned",
"the older, superseded memory is tombstoned"
);
}
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn auto_resolve_routes_identity_conflicts_to_operator() {
let db = YantrikDB::with_default(":memory:").unwrap();
let a = db
.record_text(
"Pranab lives in Seattle",
"semantic",
0.9,
0.0,
604800.0,
&empty_meta(),
"ns",
0.9,
"people",
"user",
None,
)
.unwrap();
let b = db
.record_text(
"Pranab lives in Austin",
"semantic",
0.9,
0.0,
604800.0,
&empty_meta(),
"ns",
0.9,
"people",
"user",
None,
)
.unwrap();
{
let conn = db.conn();
conn.execute(
"INSERT INTO conflicts \
(conflict_id, conflict_type, priority, status, memory_a, memory_b, \
detected_at, detected_by, detection_reason, hlc, origin_actor) \
VALUES ('cf2', 'identity_fact', 'high', 'open', ?1, ?2, 1.0, 'test', \
'identity conflict', X'00', 'test')",
rusqlite::params![a, b],
)
.unwrap();
}
let report = db.auto_resolve_conflicts(false).unwrap();
assert_eq!(
report.auto_resolved, 0,
"identity/high conflicts are not auto-resolved"
);
assert_eq!(report.routed_to_operator, 1);
let conn = db.conn();
let status: String = conn
.query_row(
"SELECT status FROM conflicts WHERE conflict_id = 'cf2'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(status, "open", "left open for an operator");
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn trigger_prune_bounds_pending_backlog() {
let db = YantrikDB::with_default(":memory:").unwrap();
let insert = |id: &str, urgency: f64, expires_at: Option<f64>| {
let conn = db.conn();
conn.execute(
"INSERT INTO trigger_log \
(trigger_id, trigger_type, urgency, status, reason, suggested_action, \
source_rids, context, created_at, expires_at, hlc, origin_actor) \
VALUES (?1, 'decay_review', ?2, 'pending', 'r', 'a', '[]', '{}', 100.0, ?3, \
X'00', 'test')",
rusqlite::params![id, urgency, expires_at],
)
.unwrap();
};
insert("t_overdue1", 0.9, Some(1.0));
insert("t_overdue2", 0.9, Some(1.0));
insert("t_live_lo", 0.1, None);
insert("t_live_mid", 0.5, None);
insert("t_live_hi1", 0.8, None);
insert("t_live_hi2", 0.9, None);
insert("t_live_hi3", 0.95, None);
let count_pending = || -> i64 {
let conn = db.conn();
conn.query_row(
"SELECT COUNT(*) FROM trigger_log WHERE status = 'pending'",
[],
|r| r.get(0),
)
.unwrap()
};
let dry = db.prune_triggers(true, 3).unwrap();
assert_eq!(dry.pending_before, 7);
assert_eq!(dry.expired_overdue, 2);
assert_eq!(dry.expired_over_cap, 2);
assert_eq!(dry.pending_after, 3);
assert_eq!(count_pending(), 7, "dry run mutates nothing");
let applied = db.prune_triggers(false, 3).unwrap();
assert_eq!(applied.pending_after, 3);
assert_eq!(count_pending(), 3);
{
let conn = db.conn();
let lo: String = conn
.query_row(
"SELECT status FROM trigger_log WHERE trigger_id = 't_live_lo'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(lo, "expired", "lowest-urgency evicted");
let hi: String = conn
.query_row(
"SELECT status FROM trigger_log WHERE trigger_id = 't_live_hi3'",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(hi, "pending", "highest-urgency retained");
}
let again = db.prune_triggers(false, 3).unwrap();
assert_eq!(again.expired_overdue, 0);
assert_eq!(again.expired_over_cap, 0);
assert_eq!(again.pending_after, 3);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn skill_outcomes_are_recorded_durably() {
let db = YantrikDB::with_default(":memory:").unwrap();
assert_eq!(db.skill_outcome_count().unwrap(), 0);
let taught = db
.teach_skill(
"deploy the staging build".to_string(),
"k1".to_string(),
vec![],
crate::skills::SkillTrigger::default(),
)
.unwrap();
assert!(taught);
assert!(db.skill_succeeded("k1").unwrap());
assert!(db.skill_failed("k1").unwrap());
assert!(db.skill_accepted("k1").unwrap());
assert!(!db.skill_succeeded("does_not_exist").unwrap());
assert_eq!(
db.skill_outcome_count().unwrap(),
3,
"one durable event per real outcome, none for the missing skill"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn maintenance_cycle_runs_passes_and_records_last_run() {
let db = YantrikDB::with_default(":memory:").unwrap();
db.record_text(
"fact one about the project",
"semantic",
0.6,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
db.record_text(
"fact two about the project",
"semantic",
0.6,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
assert!(
db.last_maintenance_cycle().unwrap().is_none(),
"no cycle yet"
);
let report = db
.run_maintenance_cycle(&crate::MaintenanceCycleConfig::default())
.unwrap();
assert!(report.errors.is_empty(), "errors: {:?}", report.errors);
assert!(report.ran_at > 0.0);
assert!(report.think_consolidations.is_some());
assert!(report.entities_linked.is_some());
assert!(report.relations_upserted.is_some());
assert!(report.conflicts.is_some());
assert!(report.triggers.is_some());
assert!(report.importance.is_some());
assert!(report.split.is_none());
assert!(report.repair.is_none());
let last = db
.last_maintenance_cycle()
.unwrap()
.expect("last run recorded");
assert!(last.contains("ran_at"));
let again = db
.run_maintenance_cycle(&crate::MaintenanceCycleConfig::default())
.unwrap();
assert!(again.errors.is_empty());
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn recall_emits_structural_intent_hint() {
let db = YantrikDB::with_default(":memory:").unwrap();
db.record_text(
"entry one of the narrative",
"episodic",
0.5,
0.0,
604800.0,
&empty_meta(),
"chain",
0.8,
"self",
"user",
None,
)
.unwrap();
let emb = db.embed("the most recent entry in the chain").unwrap();
let response = db
.recall_with_response(
&emb,
5,
None,
None,
false,
true,
Some("what is the most recent entry in the chain"),
true,
None,
None,
None,
)
.unwrap();
assert!(
response
.hints
.iter()
.any(|h| h.hint_type == "structural" && h.suggestion.contains("chain_head")),
"a recency query yields a structural hint: {:?}",
response.hints
);
let emb2 = db.embed("tell me about the narrative").unwrap();
let plain = db
.recall_with_response(
&emb2,
5,
None,
None,
false,
true,
Some("tell me about the narrative content"),
true,
None,
None,
None,
)
.unwrap();
assert!(
!plain.hints.iter().any(|h| h.hint_type == "structural"),
"no structural hint for a non-structural query"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn draft_memories_from_summary_atomizes_and_flags_provisional() {
let db = YantrikDB::with_default(":memory:").unwrap();
let summary = "We decided to use keyset cursors for list_records. \
Alice will own the database migration next sprint. \
The production launch slipped to March 30 because of it.";
let rids = db
.draft_memories_from_summary(summary, "session", "work")
.unwrap();
assert!(
rids.len() >= 2,
"summary atomized into facts: {}",
rids.len()
);
for rid in &rids {
let conn = db.conn();
let meta: String = conn
.query_row(
"SELECT metadata FROM memories WHERE rid = ?1",
rusqlite::params![rid],
|r| r.get(0),
)
.unwrap();
assert!(
meta.contains("provisional"),
"drafted memory is flagged provisional"
);
}
let hits = db
.recall_text("who owns the database migration", 5)
.unwrap();
assert!(hits.iter().any(|h| h.text.contains("migration")));
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn recall_stamps_trust_metadata() {
let db = YantrikDB::with_default(":memory:").unwrap();
db.set_status_read_policy(false).unwrap();
let aged = db
.record_text(
"an old fact about the deployment process",
"semantic",
0.7,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
{
let conn = db.conn();
conn.execute(
"UPDATE memories SET created_at = ?1, access_count = 0 WHERE rid = ?2",
rusqlite::params![crate::time::now_secs() - 200.0 * 86_400.0, aged],
)
.unwrap();
}
let hits = db.recall_text("deployment process fact", 5).unwrap();
let h = hits
.iter()
.find(|h| h.rid == aged)
.expect("aged hit present");
assert!(
h.why_retrieved
.iter()
.any(|w| w.contains("old") && w.contains("verify")),
"aged-unconfirmed hedge present: {:?}",
h.why_retrieved
);
let old_v = db
.record_text(
"the API key rotates monthly",
"semantic",
0.6,
0.0,
604800.0,
&empty_meta(),
"ns2",
0.8,
"work",
"user",
None,
)
.unwrap();
let new_v = db
.record_text(
"the API key rotates weekly now",
"semantic",
0.6,
0.0,
604800.0,
&empty_meta(),
"ns2",
0.8,
"work",
"user",
None,
)
.unwrap();
db.link(
&new_v,
&crate::types::RecordLink {
target_rid: old_v.clone(),
link_type: crate::types::LinkType::Supersedes,
},
)
.unwrap();
let hits2 = db
.recall_text("how often does the API key rotate", 5)
.unwrap();
let ho = hits2
.iter()
.find(|h| h.rid == old_v)
.expect("superseded hit present");
assert!(
ho.why_retrieved.iter().any(|w| w.contains("superseded")),
"superseded hedge present: {:?}",
ho.why_retrieved
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn auto_relate_creates_cooccurrence_edges() {
let db = YantrikDB::with_default(":memory:").unwrap();
let r1 = db
.record_text(
"Alice and Acme launched the Falcon project",
"semantic",
0.7,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
let r2 = db
.record_text(
"Alice and Acme shipped Falcon version two",
"semantic",
0.7,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"work",
"user",
None,
)
.unwrap();
{
let conn = db.conn();
for (rid, ent) in [(&r1, "Alice"), (&r1, "Acme"), (&r2, "Alice"), (&r2, "Acme")] {
conn.execute(
"INSERT OR IGNORE INTO memory_entities (memory_rid, entity_name) VALUES (?1, ?2)",
rusqlite::params![rid, ent],
)
.unwrap();
}
}
let dry = db.auto_relate(true, 100).unwrap();
assert!(
dry.pairs_considered >= 1,
"co-occurring pairs: {}",
dry.pairs_considered
);
assert_eq!(dry.edges_upserted, 0, "dry run upserts nothing");
let applied = db.auto_relate(false, 100).unwrap();
assert!(
applied.edges_upserted >= 1,
"edges created: {}",
applied.edges_upserted
);
let again = db.auto_relate(false, 100).unwrap();
assert_eq!(again.pairs_considered, applied.pairs_considered);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn session_digest_assembles_boot_briefing() {
let db = YantrikDB::with_default(":memory:").unwrap();
let _n1 = db
.record_text(
"narrative entry one",
"episodic",
0.9,
0.0,
604800.0,
&empty_meta(),
"narr",
0.9,
"self",
"user",
None,
)
.unwrap();
let n2 = db
.record_text(
"narrative entry two, the latest self-state",
"episodic",
0.5,
0.0,
604800.0,
&empty_meta(),
"narr",
0.9,
"self",
"user",
None,
)
.unwrap();
db.record_text(
"decided to adopt keyset cursors for enumeration",
"semantic",
0.95,
0.0,
604800.0,
&empty_meta(),
"work",
0.9,
"work",
"user",
None,
)
.unwrap();
db.record_text(
"a trivial passing aside",
"semantic",
0.2,
0.0,
604800.0,
&empty_meta(),
"work",
0.5,
"work",
"user",
None,
)
.unwrap();
let cfg = crate::SessionDigestConfig {
narrative_namespace: Some("narr".to_string()),
..Default::default()
};
let digest = db.session_digest(&cfg).unwrap();
let head = digest.narrative_head.expect("narrative head present");
assert_eq!(head.rid, n2);
assert!(head.snippet.contains("latest self-state"));
assert!(digest
.top_decisions
.iter()
.any(|d| d.snippet.contains("keyset cursors")));
assert!(!digest
.top_decisions
.iter()
.any(|d| d.snippet.contains("trivial passing aside")));
assert_eq!(digest.open_conflict_count, 0);
assert_eq!(digest.pending_trigger_count, 0);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn chain_head_returns_exact_latest_entry() {
let db = YantrikDB::with_default(":memory:").unwrap();
assert!(
db.chain_head("chain").unwrap().is_none(),
"empty chain has no head"
);
let _e1 = db
.record_text(
"entry one of the narrative",
"episodic",
1.0,
0.0,
604800.0,
&empty_meta(),
"chain",
0.8,
"self",
"user",
None,
)
.unwrap();
let _e2 = db
.record_text(
"entry two of the narrative",
"episodic",
0.6,
0.0,
604800.0,
&empty_meta(),
"chain",
0.8,
"self",
"user",
None,
)
.unwrap();
let e3 = db
.record_text(
"entry three, the most recent",
"episodic",
0.3,
0.0,
604800.0,
&empty_meta(),
"chain",
0.8,
"self",
"user",
None,
)
.unwrap();
let head = db.chain_head("chain").unwrap().expect("head exists");
assert_eq!(
head.rid, e3,
"head is the latest write, not the highest-importance"
);
assert!(head.text.contains("most recent"));
assert!(db.chain_head("other").unwrap().is_none());
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn evict_protects_frequently_recalled_memories() {
let db = YantrikDB::with_default(":memory:").unwrap();
let mut rids = Vec::new();
for i in 0..5 {
rids.push(
db.record_text(
&format!("memory number {i} about assorted unrelated topics"),
"semantic",
0.5,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"general",
"user",
None,
)
.unwrap(),
);
}
let hot = rids[0].clone();
{
let conn = db.conn();
conn.execute(
"UPDATE memories SET created_at = 1000.0, last_access = 1000.0, access_count = 0",
[],
)
.unwrap();
conn.execute(
"UPDATE memories SET access_count = 50 WHERE rid = ?1",
rusqlite::params![hot],
)
.unwrap();
}
let evicted = db.evict(2).unwrap();
assert_eq!(evicted.len(), 3, "evicts down to max_active = 2");
assert!(
!evicted.contains(&hot),
"the frequently-recalled memory survives"
);
let tier: String = {
let conn = db.conn();
conn.query_row(
"SELECT storage_tier FROM memories WHERE rid = ?1",
rusqlite::params![hot],
|r| r.get(0),
)
.unwrap()
};
assert_eq!(tier, "hot", "the hot memory stays hot");
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn recall_logs_demand_and_surfaces_gaps() {
let db = YantrikDB::with_default(":memory:").unwrap();
db.record_text(
"the orchard wall was painted blue last spring",
"semantic",
0.5,
0.0,
604800.0,
&empty_meta(),
"ns",
0.8,
"general",
"user",
None,
)
.unwrap();
for _ in 0..4 {
let _ = db
.recall_text("how do I rotate the encryption keys", 5)
.unwrap();
}
let (count, avg_top) = db
.recall_demand_for(None, "how do I rotate the encryption keys")
.unwrap()
.expect("the query was logged as demand");
assert_eq!(count, 4, "asked four times");
let gaps = db.knowledge_gaps(None, 3, avg_top + 0.01, 10).unwrap();
assert!(
gaps.iter()
.any(|g| g.query.contains("rotate the encryption keys")),
"frequent poorly-answered query surfaces as a gap: {gaps:?}"
);
let emb = db.embed("a different internal probe query").unwrap();
let _ = db
.recall(
&emb,
5,
None,
None,
false,
true,
Some("a different internal probe query"),
true,
None,
None,
None,
None,
None,
false,
)
.unwrap();
assert!(
db.recall_demand_for(None, "a different internal probe query")
.unwrap()
.is_none(),
"internal (skip_reinforce) recalls are not logged as demand"
);
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn migration_v33_purges_unscopable_demand_rows() {
use tempfile::NamedTempFile;
let tmp = NamedTempFile::new().unwrap();
let path = tmp.path().to_str().unwrap();
{
let _db = YantrikDB::new(path, 8).unwrap();
}
{
let conn = rusqlite::Connection::open(path).unwrap();
conn.execute_batch(
"DROP TABLE recall_demand;
CREATE TABLE recall_demand (
query_norm TEXT PRIMARY KEY,
sample_text TEXT NOT NULL,
count INTEGER NOT NULL,
sum_top_score REAL NOT NULL,
sum_results INTEGER NOT NULL,
last_seen REAL NOT NULL
);
INSERT INTO recall_demand VALUES
('legacy unscopable query', 'Legacy Unscopable Query?', 7, 0.4, 3, 1.0);
INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', '32');",
)
.unwrap();
}
let db = YantrikDB::new(path, 8).expect("v33 migration must succeed on a populated v32 DB");
{
let conn = db.conn();
let rows: i64 = conn
.query_row("SELECT COUNT(*) FROM recall_demand", [], |r| r.get(0))
.unwrap();
assert_eq!(rows, 0, "unscopable legacy demand rows are purged");
conn.query_row("SELECT namespace FROM recall_demand LIMIT 1", [], |r| {
r.get::<_, String>(0)
})
.ok();
}
db.record_recall_demand(Some("ns-a"), "post migration question", 0, 0.0)
.unwrap();
assert!(db
.recall_demand_for(Some("ns-a"), "post migration question")
.unwrap()
.is_some());
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn session_digest_scopes_decisions_and_conflicts_to_namespace() {
let db = YantrikDB::with_default(":memory:").unwrap();
db.record_text(
"tenant A signed the enterprise contract",
"semantic",
0.95,
0.0,
604800.0,
&empty_meta(),
"tenant-a",
0.9,
"work",
"user",
None,
)
.unwrap();
db.record_text(
"tenant B is migrating to postgres",
"semantic",
0.95,
0.0,
604800.0,
&empty_meta(),
"tenant-b",
0.9,
"work",
"user",
None,
)
.unwrap();
let scoped = db
.session_digest(&crate::SessionDigestConfig {
namespace: Some("tenant-a".into()),
..Default::default()
})
.unwrap();
assert!(
!scoped.top_decisions.is_empty(),
"tenant-a's own decision is present"
);
assert!(
scoped
.top_decisions
.iter()
.all(|d| d.namespace == "tenant-a"),
"no cross-tenant decisions in a scoped digest: {:?}",
scoped.top_decisions
);
let global = db
.session_digest(&crate::SessionDigestConfig::default())
.unwrap();
let namespaces: std::collections::HashSet<_> = global
.top_decisions
.iter()
.map(|d| d.namespace.clone())
.collect();
assert!(namespaces.contains("tenant-a") && namespaces.contains("tenant-b"));
}
#[test]
fn digest_packet_is_status_led_and_reports_changes_since() {
let db = YantrikDB::new(":memory:", 8).unwrap();
let rec = |text: &str, seed: f32| {
db.record(
text,
"semantic",
0.9,
0.0,
604800.0,
&empty_meta(),
&vec_seed(seed, 8),
"t10",
0.8,
"general",
"user",
None,
)
.unwrap()
};
let a = rec("decision A: deploy to staging", 1.0);
let b = rec("decision B: deploy to production (corrects A)", 1.05);
let c = rec("open question C: which region", 2.0);
let d = rec("decision D: use postgres", 3.0);
let e = rec("decision E: use sqlite (rival of D)", 3.05);
db.link(
&b,
&crate::types::RecordLink {
target_rid: a.clone(),
link_type: crate::types::LinkType::Supersedes,
},
)
.unwrap();
crate::create_conflict(
&db,
&crate::types::ConflictType::Preference,
&d,
&e,
None,
None,
"T10 fixture: D vs E",
)
.unwrap();
{
let conn = db.conn();
conn.execute(
"UPDATE memories SET created_at = 1000.0 WHERE rid = ?1",
rusqlite::params![a],
)
.unwrap();
conn.execute(
"UPDATE memories SET created_at = 2000.0 WHERE rid IN (?1, ?2, ?3, ?4)",
rusqlite::params![b, c, d, e],
)
.unwrap();
}
let digest = db
.session_digest(&crate::SessionDigestConfig {
namespace: Some("t10".into()),
..Default::default()
})
.unwrap();
let rids: Vec<&str> = digest
.top_decisions
.iter()
.map(|x| x.rid.as_str())
.collect();
assert!(rids.contains(&b.as_str()), "head B in main view");
assert!(rids.contains(&c.as_str()), "open question C in main view");
assert!(
rids.contains(&d.as_str()),
"disputed D in main view (not dropped)"
);
assert!(
!rids.contains(&a.as_str()),
"superseded A absent from main view"
);
let d_entry = digest.top_decisions.iter().find(|x| x.rid == d).unwrap();
assert!(d_entry.disputed, "D carries the typed disputed flag");
let b_entry = digest.top_decisions.iter().find(|x| x.rid == b).unwrap();
assert!(!b_entry.disputed);
assert_eq!(b_entry.current_status, crate::types::RecordStatus::Active);
let expanded = db
.session_digest(&crate::SessionDigestConfig {
namespace: Some("t10".into()),
include_superseded: true,
..Default::default()
})
.unwrap();
let a_entry = expanded
.top_decisions
.iter()
.find(|x| x.rid == a)
.expect("A only behind include_superseded");
assert_eq!(
a_entry.current_status,
crate::types::RecordStatus::Superseded
);
assert_eq!(a_entry.superseded_by.as_deref(), Some(b.as_str()));
let changes = db.what_changed_since(1500.0, Some("t10"), 240).unwrap();
let new_rids: Vec<&str> = changes.new_records.iter().map(|x| x.rid.as_str()).collect();
for rid in [&b, &c, &d, &e] {
assert!(new_rids.contains(&rid.as_str()), "{rid} is new since T");
}
assert!(!new_rids.contains(&a.as_str()), "A predates T");
assert_eq!(
changes.status_transitions.len(),
1,
"exactly one transition"
);
let tr = &changes.status_transitions[0];
assert_eq!(tr.rid, a);
assert_eq!(tr.from, crate::types::RecordStatus::Active);
assert_eq!(tr.to, crate::types::RecordStatus::Superseded);
assert_eq!(tr.by_rid.as_deref(), Some(b.as_str()));
assert!(tr.at > 1500.0, "transition committed after T");
let quiet = db
.what_changed_since(crate::time::now_secs() + 10.0, Some("t10"), 240)
.unwrap();
assert!(quiet.new_records.is_empty());
assert!(quiet.status_transitions.is_empty());
}
#[cfg(feature = "bundled-embedder")]
#[test]
fn explicit_set_embedder_overrides_bundled() {
struct DummyEmbedder;
impl crate::types::Embedder for DummyEmbedder {
fn embed(
&self,
_t: &str,
) -> std::result::Result<Vec<f32>, Box<dyn std::error::Error + Send + Sync>> {
let mut v = vec![0.0; 64];
v[0] = 0.7777;
Ok(v)
}
fn dim(&self) -> usize {
64
}
}
let mut db = YantrikDB::with_default(":memory:").unwrap();
assert!(db.has_embedder(), "starts with bundled");
db.set_embedder(Box::new(DummyEmbedder)).unwrap();
let v = db.embed("anything").unwrap();
assert!(
(v[0] - 0.7777).abs() < 1e-6,
"DummyEmbedder's sentinel must be visible — set_embedder overrode bundled"
);
}