use super::*;
use crate::memory_core::analytics::RecallLog;
use crate::memory_core::palace::{Drawer, DrawerType, Palace, PalaceId, RoomType};
use crate::memory_core::store::{kg::KnowledgeGraph, vector::UsearchStore, vector::VectorStore};
use std::collections::HashMap;
use std::sync::Arc;
use tempfile::tempdir;
use uuid::Uuid;
use super::layers::{L1_NO_SIMILARITY_PENALTY, uuid_prefix_eq};
use super::types::L1_CAP;
fn init_embedder() {
seed_shared_embedder_with_mock();
}
fn make_handle(dir: &std::path::Path) -> PalaceHandle {
let vs = UsearchStore::new(dir.join("idx.usearch"), 384).unwrap();
let kg = KnowledgeGraph::open(&dir.join("kg.db")).unwrap();
PalaceHandle::new(PalaceId::new("test"), "Test palace".to_string(), vs, kg)
}
#[test]
fn l0_l1_always_present() {
let dir = tempdir().unwrap();
let mut handle = make_handle(dir.path());
let room_id = uuid::Uuid::new_v4();
let mut d = Drawer::new(room_id, "important fact");
d.importance = 0.9;
handle.add_drawer(d);
handle.refresh_l1();
let results = retrieve_l0_l1(&handle);
assert!(results.iter().any(|r| r.layer == 0), "L0 identity missing");
assert!(results.iter().any(|r| r.layer == 1), "L1 drawer missing");
}
#[tokio::test]
async fn l2_returns_relevant_drawer() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let room_id = uuid::Uuid::new_v4();
let drawer = Drawer::new(room_id, "Rust is a systems programming language");
let drawer_id = drawer.id;
let vecs = embedder
.embed_batch(std::slice::from_ref(&drawer.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(drawer_id, vecs[0].clone())
.await
.unwrap();
handle.add_drawer(drawer);
let results = retrieve_l2(
&handle,
embedder.as_ref(),
"systems programming Rust",
None,
5,
)
.await
.unwrap();
assert!(!results.is_empty(), "L2 should return results");
assert!(
uuid_prefix_eq(results[0].drawer.id, drawer_id),
"Top L2 result should match the upserted drawer (got {:?}, want {:?})",
results[0].drawer.id,
drawer_id
);
assert_eq!(results[0].layer, 2);
}
#[tokio::test]
async fn l2_room_filter_excludes_other_rooms() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let backend_room_id = crate::memory_core::room_identity::room_to_uuid(&RoomType::Backend);
let frontend_room_id = crate::memory_core::room_identity::room_to_uuid(&RoomType::Frontend);
let backend_drawer = Drawer::new(backend_room_id, "Rust is a systems programming language");
let backend_id = backend_drawer.id;
let frontend_drawer = Drawer::new(frontend_room_id, "Rust is a systems programming toolkit");
let frontend_id = frontend_drawer.id;
for d in [&backend_drawer, &frontend_drawer] {
let vecs = embedder
.embed_batch(std::slice::from_ref(&d.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(d.id, vecs[0].clone())
.await
.unwrap();
}
handle.add_drawer(backend_drawer);
handle.add_drawer(frontend_drawer);
let results = retrieve_l2(
&handle,
embedder.as_ref(),
"systems programming Rust",
Some(RoomType::Backend),
5,
)
.await
.unwrap();
assert!(
!results.is_empty(),
"L2 should return the matching-room drawer"
);
assert!(
results
.iter()
.all(|r| uuid_prefix_eq(r.drawer.id, backend_id)),
"room_filter must exclude drawers from other rooms, got: {:?}",
results.iter().map(|r| r.drawer.id).collect::<Vec<_>>()
);
assert!(
!results
.iter()
.any(|r| uuid_prefix_eq(r.drawer.id, frontend_id)),
"Frontend-room drawer leaked through a Backend room_filter"
);
}
#[tokio::test]
async fn cli_remember_and_recall() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("test"),
name: "Test".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
let _id = handle
.remember(
"Rust async runtime is tokio".into(),
RoomType::Backend,
vec!["rust".into()],
0.7,
)
.await
.unwrap();
handle
.remember(
"React uses a virtual DOM".into(),
RoomType::Frontend,
vec![],
0.5,
)
.await
.unwrap();
let results = recall_with_default_embedder(&handle, "tokio rust async", 5)
.await
.unwrap();
assert!(
results.iter().any(|r| r.drawer.content().contains("tokio")),
"expected to recall the tokio drawer; got {results:?}"
);
}
#[tokio::test]
async fn cli_forget_removes_drawer() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("forget-test"),
name: "Forget".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("forget-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
let id = handle
.remember(
"ephemeral fact about Quokkas".into(),
RoomType::General,
vec![],
0.5,
)
.await
.unwrap();
assert_eq!(handle.forget(id).await.unwrap(), ForgetOutcome::Deleted);
let results = recall_with_default_embedder(&handle, "Quokkas ephemeral", 5)
.await
.unwrap();
assert!(
!results.iter().any(|r| r.drawer.id == id),
"forgotten drawer should not appear in recall results"
);
}
fn open_disk_palace(dir: &std::path::Path, id: &str) -> (Palace, Arc<PalaceHandle>) {
let palace = Palace {
id: PalaceId::new(id),
name: id.to_string(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.join(id),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
(palace, handle)
}
#[tokio::test]
async fn forget_reports_not_found_for_an_unknown_drawer() {
init_embedder();
let dir = tempdir().unwrap();
let (_palace, handle) = open_disk_palace(dir.path(), "forget-miss");
let kept = handle
.remember(
"Numbats eat about twenty thousand termites a day".into(),
RoomType::General,
vec![],
0.5,
)
.await
.unwrap();
let outcome = handle.forget(Uuid::new_v4()).await.unwrap();
assert_eq!(outcome, ForgetOutcome::NotFound);
assert!(!outcome.is_deleted());
assert!(
handle.drawers.read().iter().any(|d| d.id == kept),
"a no-op forget must not disturb the drawers that do exist"
);
}
#[tokio::test]
async fn forget_reports_deleted_and_the_drawer_stays_gone_after_reopen() {
init_embedder();
let dir = tempdir().unwrap();
let (palace, handle) = open_disk_palace(dir.path(), "forget-durable");
let id = handle
.remember(
"Pangolin scales are made of keratin, like fingernails".into(),
RoomType::General,
vec![],
0.5,
)
.await
.unwrap();
let outcome = handle.forget(id).await.unwrap();
assert_eq!(outcome, ForgetOutcome::Deleted);
assert!(!handle.drawers.read().iter().any(|d| d.id == id));
handle.flush().unwrap();
drop(handle);
let reopened = PalaceHandle::open(&palace).unwrap();
assert!(
!reopened.drawers.read().iter().any(|d| d.id == id),
"a drawer reported deleted must not come back on reopen"
);
assert_eq!(
reopened.forget(id).await.unwrap(),
ForgetOutcome::NotFound,
"second forget of the same id must report not_found"
);
}
#[tokio::test]
async fn remember_concurrent_does_not_lose_writes() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("concurrent-test"),
name: "Concurrent".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("concurrent-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
let mut tasks = Vec::with_capacity(32);
for i in 0..32u32 {
let h = handle.clone();
tasks.push(tokio::spawn(async move {
h.remember(
format!(
"concurrent write test payload number {i} with enough \
tokens to satisfy the default token filter check"
),
RoomType::General,
vec!["concurrent".into(), format!("idx-{i}")],
0.5,
)
.await
}));
}
let mut ok = 0usize;
let mut errs = Vec::new();
for t in tasks {
match t.await.expect("task panicked") {
Ok(_id) => ok += 1,
Err(e) => errs.push(format!("{e:#}")),
}
}
assert_eq!(
ok, 32,
"expected all 32 concurrent remembers to succeed; failures: {errs:?}"
);
let drawer_count = handle.drawers.read().len();
assert_eq!(
drawer_count, 32,
"expected 32 drawers after concurrent burst, got {drawer_count}"
);
let leaked: Vec<_> = std::fs::read_dir(&palace.data_dir)
.unwrap()
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().into_owned())
.filter(|n| n.starts_with("l1_cache.json") && n.contains(".tmp."))
.collect();
assert!(
leaked.is_empty(),
"expected no .tmp.* orphans after concurrent saves; found {leaked:?}"
);
}
#[tokio::test]
async fn cli_list_filters_by_room() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("list-test"),
name: "List".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("list-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
handle
.remember(
"backend fact about the test fixture".into(),
RoomType::Backend,
vec![],
0.5,
)
.await
.unwrap();
handle
.remember(
"frontend fact about the test fixture".into(),
RoomType::Frontend,
vec![],
0.5,
)
.await
.unwrap();
handle
.remember(
"docs fact about the test fixture".into(),
RoomType::Documentation,
vec![],
0.5,
)
.await
.unwrap();
let backend_only = handle.list_drawers(Some(RoomType::Backend), None, 10);
assert_eq!(
backend_only.len(),
1,
"expected exactly 1 backend drawer, got {backend_only:?}"
);
assert!(backend_only[0].content().contains("backend"));
}
#[tokio::test]
async fn recall_logs_events_when_log_present() {
init_embedder();
let dir = tempdir().unwrap();
let log = Arc::new(RecallLog::open(&dir.path().join("recall.db")).unwrap());
let mut handle = make_handle(dir.path()).with_recall_log(log.clone());
let embedder = shared_embedder().await.unwrap();
let room_id = uuid::Uuid::new_v4();
let drawer = Drawer::new(room_id, "Rust is a systems programming language");
let drawer_id = drawer.id;
let vecs = embedder
.embed_batch(std::slice::from_ref(&drawer.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(drawer_id, vecs[0].clone())
.await
.unwrap();
handle.add_drawer(drawer);
handle.refresh_l1();
let _ = recall(&handle, embedder.as_ref(), "systems programming Rust", 5)
.await
.unwrap();
let mut hits = 0u64;
for _ in 0..20 {
hits = log.hit_count(drawer_id).await.unwrap();
if hits >= 1 {
break;
}
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
}
assert!(hits >= 1, "expected at least one logged hit, got {hits}");
}
#[tokio::test]
async fn open_attaches_recall_log_automatically() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("analytics-auto"),
name: "AnalyticsAuto".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("analytics-auto"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
assert!(
handle.recall_log.is_some(),
"PalaceHandle::open must auto-attach a RecallLog (issue #53)"
);
assert!(
palace.data_dir.join("recall.redb").exists(),
"recall.redb must exist on disk after open"
);
let drawer_id = handle
.remember(
"the platypus is a monotreme native to eastern Australia".into(),
RoomType::Research,
vec!["wildlife".into()],
0.7,
)
.await
.unwrap();
let embedder = shared_embedder().await.unwrap();
let _ = recall(&handle, embedder.as_ref(), "platypus monotreme", 5)
.await
.unwrap();
let log = handle.recall_log.as_ref().unwrap().clone();
let mut hits = 0u64;
for _ in 0..20 {
hits = log.hit_count(drawer_id).await.unwrap();
if hits >= 1 {
break;
}
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
}
assert!(
hits >= 1,
"auto-attached recall log must record events; got {hits}"
);
}
#[tokio::test]
async fn closet_updated_after_remember() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("closet-test"),
name: "Closet".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("closet-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
let id = handle
.remember(
"Quokkas are happy marsupials".into(),
RoomType::General,
vec![],
0.5,
)
.await
.unwrap();
let closets = handle.closets.read();
let entry = closets
.get("quokkas")
.expect("expected `quokkas` keyword in closet index");
assert!(
entry.contains(&id),
"closet entry for `quokkas` should contain the new drawer id"
);
}
#[test]
fn expand_query_adds_synonyms() {
let out = expand_query("how fast is vector search?");
assert!(out.contains("HNSW"), "expected HNSW synonym, got: {out}");
assert!(
out.contains("latency"),
"expected latency synonym, got: {out}"
);
}
#[test]
fn expand_query_noop_for_unmatched() {
let out = expand_query("what is a borrow checker?");
assert!(
out.contains("borrow checker"),
"expected original query preserved, got: {out}"
);
assert!(
out.contains("ownership") || out.contains("lifetime"),
"expected ownership/lifetime synonyms, got: {out}"
);
let untouched = expand_query("what colour is the sky on Tuesday");
assert_eq!(
untouched, "what colour is the sky on Tuesday",
"queries with no triggers must pass through unchanged"
);
}
#[ignore = "requires real ONNX embedder (issue #850)"]
#[tokio::test]
async fn cold_restart_recalls_beyond_l1_snapshot() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("cold-restart"),
name: "Cold".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("cold-restart"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let needle_id = {
let handle = PalaceHandle::open(&palace).unwrap();
for i in 0..19 {
handle
.remember(
format!("filler drawer number {i} about generic topics"),
RoomType::General,
vec![],
0.9,
)
.await
.unwrap();
}
handle
.remember(
"the pangolin is a scaly nocturnal mammal".into(),
RoomType::Research,
vec![],
0.1,
)
.await
.unwrap()
};
let handle2 = PalaceHandle::open(&palace).unwrap();
let count = handle2.drawers.read().len();
assert!(
count >= 20,
"expected >=20 drawers after cold reopen, got {count}"
);
let results = recall_with_default_embedder(&handle2, "pangolin scaly mammal", 10)
.await
.unwrap();
assert!(
results.iter().any(|r| r.drawer.id == needle_id),
"low-importance drawer beyond L1 must still be recallable after cold restart; got {results:?}"
);
}
#[tokio::test]
async fn shared_embedder_is_singleton() {
init_embedder();
let a = shared_embedder().await.unwrap();
let b = shared_embedder().await.unwrap();
assert!(
Arc::ptr_eq(&a, &b),
"shared_embedder must return the same Arc on every call"
);
}
#[test]
fn shared_embedder_initialized_flips_after_seeding() {
seed_shared_embedder_with_mock();
assert!(
shared_embedder_initialized(),
"the shared embedder must report initialised once seeded"
);
}
#[tokio::test]
async fn retrieve_l2_tag_boost_raises_rank() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("boost-test"),
name: "Boost".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("boost-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
let id_tagged = handle
.remember(
"Vector search performance benchmarks show low latency".into(),
RoomType::Backend,
vec!["vector-search".into()],
0.5,
)
.await
.unwrap();
let _id_other = handle
.remember(
"React components render through a virtual DOM".into(),
RoomType::Frontend,
vec![],
0.5,
)
.await
.unwrap();
let embedder = shared_embedder().await.unwrap();
let results = retrieve_l2(
&handle,
embedder.as_ref(),
"vector search performance",
None,
5,
)
.await
.unwrap();
assert!(!results.is_empty(), "L2 should return results");
assert!(
uuid_prefix_eq(results[0].drawer.id, id_tagged),
"tagged drawer should rank first; got {:?}",
results[0].drawer.content()
);
}
#[tokio::test]
async fn recall_across_palaces_merges_results() {
init_embedder();
let dir = tempdir().unwrap();
let palace_a = Palace {
id: PalaceId::new("alpha"),
name: "Alpha".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("alpha"),
};
std::fs::create_dir_all(&palace_a.data_dir).unwrap();
let handle_a = PalaceHandle::open(&palace_a).unwrap();
handle_a
.remember(
"the pangolin is a scaly nocturnal mammal".into(),
RoomType::Research,
vec![],
0.6,
)
.await
.unwrap();
let palace_b = Palace {
id: PalaceId::new("beta"),
name: "Beta".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("beta"),
};
std::fs::create_dir_all(&palace_b.data_dir).unwrap();
let handle_b = PalaceHandle::open(&palace_b).unwrap();
handle_b
.remember(
"the platypus is a venomous monotreme".into(),
RoomType::Research,
vec![],
0.6,
)
.await
.unwrap();
let handles = vec![handle_a, handle_b];
let results = recall_across_palaces_with_default_embedder(
&handles,
"pangolin platypus mammal",
10,
false,
)
.await
.unwrap();
assert!(!results.is_empty(), "expected merged results, got none");
assert!(
results.iter().any(|r| r.palace_id == "alpha"),
"expected at least one alpha result; got {:?}",
results.iter().map(|r| &r.palace_id).collect::<Vec<_>>()
);
assert!(
results.iter().any(|r| r.palace_id == "beta"),
"expected at least one beta result; got {:?}",
results.iter().map(|r| &r.palace_id).collect::<Vec<_>>()
);
for w in results.windows(2) {
assert!(
w[0].result.score >= w[1].result.score,
"results not sorted: {} < {}",
w[0].result.score,
w[1].result.score
);
}
}
#[tokio::test]
async fn remember_rejects_short_content() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let err = handle
.remember("too short".to_string(), RoomType::General, vec![], 0.5)
.await
.expect_err("should reject");
let msg = format!("{err:#}");
assert!(
msg.to_lowercase().contains("too short")
|| msg.contains("memory_note")
|| msg.contains("tokens"),
"expected actionable error, got: {msg}"
);
}
#[tokio::test]
async fn remember_rejects_known_noise_patterns() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let cases = [
"Tool use: search_files with query parameter very_long_string_here",
"feat(memory): add filter for noise patterns to reduce drawer clutter",
"Running cargo test --workspace --all-features for the entire monorepo...",
];
for c in cases {
let err = handle
.remember(c.to_string(), RoomType::General, vec![], 0.5)
.await
.expect_err("should reject");
assert!(
format!("{err:#}").to_lowercase().contains("noise")
|| format!("{err:#}").to_lowercase().contains("low-signal"),
"expected noise-pattern reject for: {c}",
);
}
}
#[tokio::test]
async fn remember_force_bypasses_filter() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let id = handle
.remember_with_options(
"x".to_string(),
RoomType::General,
vec![],
0.5,
RememberOptions::forced(),
)
.await
.expect("force should bypass filter");
assert_ne!(id, uuid::Uuid::nil());
}
#[tokio::test]
async fn remember_force_still_blocks_secret() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let err = handle
.remember_with_options(
"sk-abcdefghijklmnopqrstuvwxyz01234567890123".to_string(), RoomType::General,
vec![],
0.5,
RememberOptions::forced(),
)
.await
.expect_err("force=true must still reject secret-shaped content");
assert!(
format!("{err:#}").to_lowercase().contains("secret"),
"expected a secret-gate rejection, got: {err:#}"
);
}
#[tokio::test]
async fn remember_force_and_allow_secret_like_stores_secret_shaped_content() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let mut opts = RememberOptions::forced();
opts.allow_secret_like = true;
let id = handle
.remember_with_options(
"sk-abcdefghijklmnopqrstuvwxyz01234567890123".to_string(), RoomType::General,
vec![],
0.5,
opts,
)
.await
.expect("force + allow_secret_like must bypass the secret gate too");
assert_ne!(id, uuid::Uuid::nil());
}
#[tokio::test]
async fn note_options_skip_token_check_but_keep_noise_filter() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let id = handle
.remember_with_options(
"User prefers snake_case".to_string(),
RoomType::General,
vec![],
1.0,
RememberOptions::note(),
)
.await
.expect("note should accept short curated fact");
let stored_type = {
let drawers = handle.drawers.read();
let stored = drawers.iter().find(|d| d.id == id).expect("present");
stored.drawer_type
};
assert_eq!(stored_type, DrawerType::UserFact);
let err = handle
.remember_with_options(
"Tool use: x".to_string(),
RoomType::General,
vec![],
1.0,
RememberOptions::note(),
)
.await
.expect_err("note must still reject noise patterns");
assert!(format!("{err:#}").to_lowercase().contains("noise"));
}
#[tokio::test]
async fn remember_classifies_commit_messages() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let id = handle
.remember_with_options(
"feat(scope): non-empty long enough message body here please".to_string(),
RoomType::General,
vec![],
0.5,
RememberOptions::forced(),
)
.await
.expect("forced commit message");
let drawers = handle.drawers.read();
let stored = drawers.iter().find(|d| d.id == id).expect("present");
assert_eq!(stored.drawer_type, DrawerType::Commit);
}
#[tokio::test]
async fn purge_expired_drops_only_past_ttl() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let room_id = uuid::Uuid::new_v4();
let mut expired = Drawer::new(room_id, "expired");
expired.expires_at = Some(chrono::Utc::now() - chrono::Duration::days(1));
let expired_id = expired.id;
let mut future = Drawer::new(room_id, "future");
future.expires_at = Some(chrono::Utc::now() + chrono::Duration::days(7));
let future_id = future.id;
let permanent = Drawer::new(room_id, "permanent");
let permanent_id = permanent.id;
handle.add_drawer(expired);
handle.add_drawer(future);
handle.add_drawer(permanent);
let pruned = handle.purge_expired().await.expect("purge");
assert_eq!(pruned, 1, "exactly one drawer should be pruned");
let remaining: Vec<uuid::Uuid> = handle.drawers.read().iter().map(|d| d.id).collect();
assert!(!remaining.contains(&expired_id));
assert!(remaining.contains(&future_id));
assert!(remaining.contains(&permanent_id));
}
#[test]
fn expired_l1_drawer_is_excluded_without_reopen() {
let dir = tempdir().unwrap();
let mut handle = make_handle(dir.path());
let room_id = uuid::Uuid::new_v4();
let mut expired = Drawer::new(room_id, "PR #4818 is in flight at head d3963848");
expired.importance = 0.95;
expired.expires_at = Some(chrono::Utc::now() - chrono::Duration::minutes(1));
let expired_id = expired.id;
let mut live = Drawer::new(room_id, "write plainly");
live.importance = 0.9;
let live_id = live.id;
handle.add_drawer(expired);
handle.add_drawer(live);
handle.refresh_l1();
assert!(
handle.l1_drawers.iter().any(|d| d.id == expired_id),
"precondition: the expired drawer is cached in L1"
);
let results = retrieve_l0_l1(&handle);
let returned: Vec<uuid::Uuid> = results.iter().map(|r| r.drawer.id).collect();
assert!(
!returned.contains(&expired_id),
"an expired drawer must not be served by L0/L1"
);
assert!(
returned.contains(&live_id),
"an unexpired drawer must still be served"
);
}
#[tokio::test]
async fn expired_drawer_is_excluded_from_l2() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let room_id = uuid::Uuid::new_v4();
let mut expired = Drawer::new(room_id, "Rust is a systems programming language");
expired.expires_at = Some(chrono::Utc::now() - chrono::Duration::minutes(1));
let expired_id = expired.id;
let vecs = embedder
.embed_batch(std::slice::from_ref(&expired.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(expired_id, vecs[0].clone())
.await
.unwrap();
handle.add_drawer(expired);
let results = retrieve_l2(
&handle,
embedder.as_ref(),
"systems programming Rust",
None,
5,
)
.await
.unwrap();
assert!(
!results.iter().any(|r| r.drawer.id == expired_id),
"L2 must not return a drawer past its TTL, even on a strong vector hit"
);
}
#[test]
fn read_time_expiry_filters_without_deleting() {
let dir = tempdir().unwrap();
let mut handle = make_handle(dir.path());
let room_id = uuid::Uuid::new_v4();
let mut expired = Drawer::new(room_id, "stale point-in-time fact");
expired.expires_at = Some(chrono::Utc::now() - chrono::Duration::minutes(1));
let expired_id = expired.id;
handle.add_drawer(expired);
handle.refresh_l1();
let results = retrieve_l0_l1(&handle);
assert!(!results.iter().any(|r| r.drawer.id == expired_id));
assert!(
handle.drawers.read().iter().any(|d| d.id == expired_id),
"the read path must leave the drawer in place for the sweep to reclaim"
);
}
#[ignore = "requires real ONNX embedder (issue #850)"]
#[tokio::test]
async fn recall_ranks_by_similarity_over_importance() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("similarity-ranking-test"),
name: "SimilarityRank".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("similarity-ranking-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
let _high_imp_id = handle
.remember(
"concurrent write regression test fixture number one payload here".into(),
RoomType::General,
vec![],
1.0,
)
.await
.unwrap();
let on_topic_id = handle
.remember(
"the pangolin is a scaly nocturnal mammal with protective keratin scales".into(),
RoomType::Research,
vec![],
0.1,
)
.await
.unwrap();
let embedder = shared_embedder().await.unwrap();
let results = recall(
&handle,
embedder.as_ref(),
"pangolin scaly nocturnal mammal",
10,
)
.await
.unwrap();
assert!(
!results.is_empty(),
"recall must return at least one result"
);
let on_topic_rank = results
.iter()
.enumerate()
.find(|(_, r)| r.drawer.id == on_topic_id)
.map(|(i, _)| i);
assert!(
on_topic_rank.is_some(),
"on-topic drawer must appear in recall results"
);
let rank = on_topic_rank.unwrap();
assert!(
rank <= 2,
"on-topic drawer (importance=0.1) should rank in top-3 for a semantically \
matching query, but ranked at position {rank}. \
Results: {:?}",
results
.iter()
.map(|r| format!(
"[layer={} imp={:.2} score={:.3}] {}",
r.layer,
r.drawer.importance,
r.score,
&r.drawer.content()[..r.drawer.content().len().min(40)]
))
.collect::<Vec<_>>()
);
for w in results.windows(2) {
assert!(
w[0].score >= w[1].score,
"results must be sorted by score descending: {} >= {} failed",
w[0].score,
w[1].score
);
}
}
#[test]
fn rescore_l1_by_similarity_patches_scores() {
let room_id = uuid::Uuid::new_v4();
let mut identity_drawer = Drawer::new(Uuid::nil(), "identity");
identity_drawer.id = Uuid::nil();
identity_drawer.importance = 1.0;
identity_drawer.drawer_type = crate::memory_core::palace::DrawerType::UserFact;
let mut matched = Drawer::new(room_id, "matched drawer");
matched.importance = 0.9;
let matched_id = matched.id;
let mut unmatched = Drawer::new(room_id, "unmatched drawer");
unmatched.importance = 1.0;
let mut results = vec![
RecallResult {
drawer: identity_drawer,
score: 1.0,
layer: 0,
},
RecallResult {
drawer: matched.clone(),
score: matched.importance, layer: 1,
},
RecallResult {
drawer: unmatched.clone(),
score: unmatched.importance, layer: 1,
},
];
let mut sim_scores = HashMap::new();
sim_scores.insert(matched_id, 0.75_f32);
rescore_l1_by_similarity(&mut results, &sim_scores);
assert!(
(results[0].score - 1.0).abs() < 1e-6,
"L0 identity score must not change"
);
assert!(
(results[1].score - 0.75).abs() < 1e-6,
"matched L1 entry must get similarity score 0.75, got {}",
results[1].score
);
let expected_penalty = 1.0_f32 * L1_NO_SIMILARITY_PENALTY;
assert!(
(results[2].score - expected_penalty).abs() < 1e-6,
"unmatched L1 entry must get importance * L1_NO_SIMILARITY_PENALTY = {expected_penalty}, got {}",
results[2].score
);
}
#[tokio::test]
async fn recall_top_k_caps_result_count() {
init_embedder();
let dir = tempdir().unwrap();
let palace = Palace {
id: PalaceId::new("topk-cap-test"),
name: "TopKCap".into(),
description: None,
created_at: chrono::Utc::now(),
data_dir: dir.path().join("topk-cap-test"),
};
std::fs::create_dir_all(&palace.data_dir).unwrap();
let handle = PalaceHandle::open(&palace).unwrap();
for i in 0..20u32 {
handle
.remember(
format!(
"test fact number {i} about the recall top-k cap regression \
with enough tokens to pass the default filter check"
),
RoomType::General,
vec![],
(i as f32) * 0.04 + 0.1,
)
.await
.unwrap();
}
let embedder = shared_embedder().await.unwrap();
let top_k = 5_usize;
let shallow = recall(&handle, embedder.as_ref(), "test fact recall", top_k)
.await
.unwrap();
assert!(
shallow.len() <= top_k,
"shallow recall: expected at most {top_k} results, got {}",
shallow.len()
);
let deep = recall_deep(&handle, embedder.as_ref(), "test fact recall", top_k)
.await
.unwrap();
assert!(
deep.len() <= top_k,
"deep recall: expected at most {top_k} results, got {}",
deep.len()
);
}
async fn seed_two_rooms(
handle: &PalaceHandle,
embedder: &dyn crate::memory_core::embed::Embedder,
) -> (Uuid, Uuid) {
let backend = Drawer::new(
crate::memory_core::room_identity::room_to_uuid(&RoomType::Backend),
"Rust is a systems programming language",
);
let frontend = Drawer::new(
crate::memory_core::room_identity::room_to_uuid(&RoomType::Frontend),
"Rust is a systems programming toolkit",
);
let (backend_id, frontend_id) = (backend.id, frontend.id);
for d in [&backend, &frontend] {
let vecs = embedder
.embed_batch(std::slice::from_ref(&d.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(d.id, vecs[0].clone())
.await
.unwrap();
}
handle.add_drawer(backend);
handle.add_drawer(frontend);
(backend_id, frontend_id)
}
#[tokio::test]
async fn l3_room_filter_excludes_other_rooms() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let (backend_id, frontend_id) = seed_two_rooms(&handle, embedder.as_ref()).await;
let results = retrieve_l3(
&handle,
embedder.as_ref(),
"systems programming Rust",
Some(RoomType::Backend),
5,
)
.await
.unwrap();
assert!(!results.is_empty(), "L3 should return the in-room drawer");
assert!(
results
.iter()
.all(|r| uuid_prefix_eq(r.drawer.id, backend_id)),
"room_filter must exclude drawers from other rooms, got: {:?}",
results.iter().map(|r| r.drawer.id).collect::<Vec<_>>()
);
assert!(
!results
.iter()
.any(|r| uuid_prefix_eq(r.drawer.id, frontend_id)),
"Frontend-room drawer leaked through a Backend room_filter"
);
}
#[tokio::test]
async fn l3_without_a_room_filter_returns_every_room() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let drawer = Drawer::new(
crate::memory_core::room_identity::room_to_uuid(&RoomType::Frontend),
"React uses a virtual DOM for rendering",
);
let drawer_id = drawer.id;
let vecs = embedder
.embed_batch(std::slice::from_ref(&drawer.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(drawer_id, vecs[0].clone())
.await
.unwrap();
handle.add_drawer(drawer);
let unfiltered = retrieve_l3(&handle, embedder.as_ref(), "virtual DOM React", None, 5)
.await
.unwrap();
assert!(
unfiltered
.iter()
.any(|r| uuid_prefix_eq(r.drawer.id, drawer_id)),
"an unfiltered L3 must return the drawer regardless of its room"
);
let other_room = retrieve_l3(
&handle,
embedder.as_ref(),
"virtual DOM React",
Some(RoomType::Backend),
5,
)
.await
.unwrap();
assert!(
other_room.is_empty(),
"a Backend filter must not return a Frontend drawer, got {:?}",
other_room.iter().map(|r| r.drawer.id).collect::<Vec<_>>()
);
}
#[tokio::test]
async fn recall_in_room_scopes_l2_hits() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let (_backend_id, frontend_id) = seed_two_rooms(&handle, embedder.as_ref()).await;
let results = recall_in_room(
&handle,
embedder.as_ref(),
"systems programming Rust",
Some(RoomType::Backend),
10,
)
.await
.unwrap();
assert!(
!results
.iter()
.any(|r| r.layer == 2 && uuid_prefix_eq(r.drawer.id, frontend_id)),
"an out-of-room drawer reached a room-scoped recall"
);
}
#[tokio::test]
async fn recall_deep_in_room_scopes_l3_hits() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let (_backend_id, frontend_id) = seed_two_rooms(&handle, embedder.as_ref()).await;
let results = recall_deep_in_room(
&handle,
embedder.as_ref(),
"systems programming Rust",
Some(RoomType::Backend),
10,
)
.await
.unwrap();
assert!(
!results
.iter()
.any(|r| r.layer == 3 && uuid_prefix_eq(r.drawer.id, frontend_id)),
"an out-of-room drawer reached a room-scoped deep recall"
);
}
use crate::memory_core::room_identity::DEFAULT_WING_ID;
use crate::memory_core::store::rooms::resolve_or_create_room_in_wing_sync;
use crate::memory_core::store::wings::{ensure_default_wing, resolve_or_create_wing_sync};
fn wing_with_room(handle: &PalaceHandle, wing: &str, label: &str) -> (Uuid, Uuid) {
ensure_default_wing(&handle.kg).expect("seed default wing");
let store = handle.kg.store();
let (wing_id, _) = resolve_or_create_wing_sync(&store, wing).expect("create wing");
let room = RoomType::parse(label);
let room_id = resolve_or_create_room_in_wing_sync(&store, &room, wing_id).expect("create room");
(wing_id, room_id)
}
#[test]
fn scope_all_matches_everything() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
assert!(
RecallScope::All.allowed_room_ids(&handle.kg).is_none(),
"All must be 'no filter', not 'empty filter'"
);
assert!(scope_admits(&None, Uuid::new_v4()));
}
#[test]
fn wing_scope_is_fail_closed() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
ensure_default_wing(&handle.kg).expect("seed");
let allowed = RecallScope::Wing(Uuid::from_u128(999)).allowed_room_ids(&handle.kg);
assert_eq!(allowed, Some(std::collections::HashSet::new()));
assert!(!scope_admits(&allowed, Uuid::new_v4()));
}
#[test]
fn wing_scope_returns_only_that_wings_drawers() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let (engineer, eng_room) = wing_with_room(&handle, "engineer", "Planning");
let (pm, pm_room) = wing_with_room(&handle, "pm", "Planning");
let eng_drawer = Drawer::new(eng_room, "engineer planning note");
let eng_id = eng_drawer.id;
let pm_drawer = Drawer::new(pm_room, "pm planning note");
let pm_id = pm_drawer.id;
handle.add_drawer(eng_drawer);
handle.add_drawer(pm_drawer);
let eng_hits = list_drawers_in_wing(&handle, engineer, None, 50);
assert_eq!(eng_hits.len(), 1, "engineer wing sees only its own drawer");
assert_eq!(eng_hits[0].id, eng_id);
let pm_hits = list_drawers_in_wing(&handle, pm, None, 50);
assert_eq!(pm_hits.len(), 1);
assert_eq!(pm_hits[0].id, pm_id);
assert!(list_drawers_in_wing(&handle, DEFAULT_WING_ID, None, 50).is_empty());
}
fn seed_importance_tie(handle: &PalaceHandle, room_id: Uuid, tag: &str, count: u128) -> Uuid {
let now = chrono::Utc::now();
let mut newest = Uuid::nil();
for i in 0..count {
let mut drawer = Drawer::new(room_id, format!("tied drawer {i}"));
drawer.id = Uuid::from_u128(i + 1);
drawer.importance = 0.5;
drawer.created_at = now - chrono::Duration::minutes((count - 1 - i) as i64);
drawer.tags = vec![tag.to_string()];
newest = drawer.id;
handle.add_drawer(drawer);
}
newest
}
#[test]
fn list_drawers_keeps_the_newest_drawer_within_an_importance_tie() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let newest = seed_importance_tie(&handle, Uuid::nil(), "pre-authorized", 20);
let listed = handle.list_drawers(None, Some("pre-authorized".to_string()), 5);
assert_eq!(listed.len(), 5, "the tag matches 20 drawers, limit is 5");
assert_eq!(
listed[0].id,
newest,
"an importance tie must be broken by recency, so the newest drawer leads; got {:?}",
listed.iter().map(|d| d.content()).collect::<Vec<_>>()
);
}
#[test]
fn list_drawers_in_wing_keeps_the_newest_drawer_within_an_importance_tie() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let (wing, room) = wing_with_room(&handle, "engineer", "Planning");
let newest = seed_importance_tie(&handle, room, "pre-authorized", 20);
let listed = list_drawers_in_wing(&handle, wing, Some("pre-authorized".to_string()), 5);
assert_eq!(listed.len(), 5);
assert_eq!(
listed[0].id, newest,
"the wing lister must order ties by recency too"
);
}
#[test]
fn list_drawers_ranks_importance_above_recency() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
seed_importance_tie(&handle, Uuid::nil(), "pre-authorized", 5);
let mut curated = Drawer::new(Uuid::nil(), "curated essential".to_string());
curated.id = Uuid::from_u128(9_999);
curated.importance = 1.0;
curated.created_at = chrono::Utc::now() - chrono::Duration::days(30);
curated.tags = vec!["pre-authorized".to_string()];
let curated_id = curated.id;
handle.add_drawer(curated);
let listed = handle.list_drawers(None, Some("pre-authorized".to_string()), 3);
assert_eq!(
listed[0].id, curated_id,
"importance still outranks recency; recency only breaks ties"
);
}
#[test]
fn refresh_l1_keeps_the_newest_drawers_within_an_importance_tie() {
let dir = tempdir().unwrap();
let mut handle = make_handle(dir.path());
let newest = seed_importance_tie(&handle, Uuid::nil(), "pre-authorized", 20);
handle.refresh_l1();
assert_eq!(handle.l1_drawers.len(), L1_CAP, "L1 is capped at L1_CAP");
assert_eq!(
handle.l1_drawers[0].id,
newest,
"an importance tie must be broken by recency, so the newest drawer leads L1; got {:?}",
handle
.l1_drawers
.iter()
.map(|d| d.content())
.collect::<Vec<_>>()
);
assert!(
handle.l1_drawers.iter().all(|d| d.id >= Uuid::from_u128(6)),
"L1 must hold the newest 15 of the tie, not the oldest 15"
);
}
#[test]
fn same_named_rooms_in_two_wings_stay_distinct() {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let (_e, eng_room) = wing_with_room(&handle, "engineer", "Planning");
let (_p, pm_room) = wing_with_room(&handle, "pm", "Planning");
assert_ne!(
eng_room, pm_room,
"one label in two wings must be two rooms"
);
}
#[tokio::test]
async fn wing_scoped_recall_returns_only_that_wing() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let (engineer, eng_room) = wing_with_room(&handle, "engineer", "Planning");
let (_pm, pm_room) = wing_with_room(&handle, "pm", "Planning");
let eng = Drawer::new(eng_room, "Rust is a systems programming language");
let eng_id = eng.id;
let pm = Drawer::new(pm_room, "the launch review happens every quarter");
let pm_id = pm.id;
for d in [&eng, &pm] {
let vecs = embedder
.embed_batch(std::slice::from_ref(&d.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(d.id, vecs[0].clone())
.await
.unwrap();
}
handle.add_drawer(eng);
handle.add_drawer(pm);
let results = retrieve_l2_scoped(
&handle,
embedder.as_ref(),
"systems programming Rust",
&RecallScope::Wing(engineer),
5,
)
.await
.unwrap();
assert!(
!results.is_empty(),
"the engineer wing has a matching drawer"
);
assert!(
results.iter().all(|r| uuid_prefix_eq(r.drawer.id, eng_id)),
"wing scope must exclude the other wing, got: {:?}",
results.iter().map(|r| r.drawer.id).collect::<Vec<_>>()
);
assert!(
!results.iter().any(|r| uuid_prefix_eq(r.drawer.id, pm_id)),
"a pm-wing drawer leaked through an engineer-wing scope"
);
}
#[tokio::test]
async fn unscoped_recall_is_unchanged_by_wings() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let (_e, eng_room) = wing_with_room(&handle, "engineer", "Planning");
let (_p, pm_room) = wing_with_room(&handle, "pm", "Planning");
let eng = Drawer::new(eng_room, "Rust is a systems programming language");
let pm = Drawer::new(pm_room, "the launch review happens every quarter");
let ids = [eng.id, pm.id];
let pm_id = pm.id;
for d in [&eng, &pm] {
let vecs = embedder
.embed_batch(std::slice::from_ref(&d.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(d.id, vecs[0].clone())
.await
.unwrap();
}
handle.add_drawer(eng);
handle.add_drawer(pm);
let listed = handle.list_drawers(None, None, 50);
for id in ids {
assert!(
listed.iter().any(|d| d.id == id),
"an unscoped list must still see every wing's drawers"
);
}
let results = retrieve_l2(
&handle,
embedder.as_ref(),
"the launch review happens every quarter",
None,
5,
)
.await
.unwrap();
assert!(
results.iter().any(|r| uuid_prefix_eq(r.drawer.id, pm_id)),
"an unscoped recall must not filter out a non-default wing's drawer"
);
}
#[tokio::test]
async fn unscoped_write_still_lands_in_the_default_wing() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
ensure_default_wing(&handle.kg).expect("seed");
let id = handle
.remember(
"a perfectly ordinary curated fact about the project".to_string(),
RoomType::Planning,
vec![],
0.5,
)
.await
.expect("remember");
let drawer = handle
.drawers
.read()
.iter()
.find(|d| d.id == id)
.cloned()
.expect("stored");
let scoped = crate::memory_core::store::wings::rooms_in_wing(&handle.kg, DEFAULT_WING_ID)
.expect("scope");
assert!(
scoped.contains(&drawer.room_id),
"a wing-less write must land in the default wing"
);
assert!(
list_drawers_in_wing(&handle, DEFAULT_WING_ID, None, 50)
.iter()
.any(|d| d.id == id)
);
}
#[tokio::test]
async fn wing_scoped_deep_recall_returns_only_that_wing() {
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let (engineer, eng_room) = wing_with_room(&handle, "engineer", "Planning");
let (_pm, pm_room) = wing_with_room(&handle, "pm", "Planning");
let eng = Drawer::new(eng_room, "Rust is a systems programming language");
let eng_id = eng.id;
let pm = Drawer::new(pm_room, "the launch review happens every quarter");
let pm_id = pm.id;
for d in [&eng, &pm] {
let vecs = embedder
.embed_batch(std::slice::from_ref(&d.content().to_string()))
.await
.unwrap();
handle
.vector_store
.upsert(d.id, vecs[0].clone())
.await
.unwrap();
}
handle.add_drawer(eng);
handle.add_drawer(pm);
let results = retrieve_l3_scoped(
&handle,
embedder.as_ref(),
"Rust systems programming",
&RecallScope::Wing(engineer),
5,
)
.await
.unwrap();
assert!(
results.iter().any(|r| uuid_prefix_eq(r.drawer.id, eng_id)),
"engineer-wing deep recall returned no matching drawer"
);
assert!(
!results.iter().any(|r| uuid_prefix_eq(r.drawer.id, pm_id)),
"a pm-wing drawer leaked through an engineer-wing deep recall"
);
}
fn vec_at_cosine(reference: &[f32], cos: f32, seed: usize) -> Vec<f32> {
let norm = |v: &[f32]| v.iter().map(|x| x * x).sum::<f32>().sqrt();
let rn = norm(reference);
let r: Vec<f32> = reference.iter().map(|x| x / rn).collect();
let base: Vec<f32> = (0..r.len())
.map(|i| {
if (i / (seed + 1)).is_multiple_of(2) {
1.0
} else {
-1.0
}
})
.collect();
let dot: f32 = base.iter().zip(&r).map(|(s, x)| s * x).sum();
let mut o: Vec<f32> = base.iter().zip(&r).map(|(s, x)| s - dot * x).collect();
let on = norm(&o);
for x in o.iter_mut() {
*x /= on;
}
let sin = (1.0 - cos * cos).max(0.0).sqrt();
r.iter().zip(&o).map(|(a, b)| cos * a + sin * b).collect()
}
async fn pad_index(handle: &PalaceHandle, qv: &[f32], n: usize) {
let room_id = Uuid::new_v4();
for i in 0..n {
let mut d = Drawer::new(room_id, format!("unrelated filler drawer {i}"));
d.importance = 0.5;
handle
.vector_store
.upsert(d.id, vec_at_cosine(qv, 0.60 - 0.02 * i as f32, i + 2))
.await
.unwrap();
handle.add_drawer(d);
}
}
#[test]
fn rank_score_keeps_importance_a_tiebreaker() {
use super::layers::rank_score;
let more_similar_less_important = rank_score(0.80, 0.05, 0.0);
let less_similar_most_important = rank_score(0.75, 1.00, 0.0);
assert!(
more_similar_less_important > less_similar_most_important,
"importance overrode a 0.05 similarity gap: {more_similar_less_important} vs \
{less_similar_most_important}"
);
assert!(
rank_score(0.70, 1.00, 0.0) > rank_score(0.70, 0.05, 0.0),
"importance stopped breaking ties between equally-similar candidates"
);
assert!(rank_score(0.70, 0.5, 0.15) > rank_score(0.70, 0.5, 0.0));
assert_eq!(rank_score(1.0, 1.0, 0.15), 1.0);
}
#[tokio::test]
async fn l2_ranks_similar_low_importance_above_less_similar_high_importance() {
init_embedder();
let embedder = shared_embedder().await.unwrap();
let query = "context budget startup memory migration duplicate deletion";
let qv = embedder
.embed_batch(&[query.to_string()])
.await
.unwrap()
.remove(0);
let (_dir, handle, on_topic_id, loud_id) = ranking_fixture_with_both_candidates(&qv).await;
let results = retrieve_l2(&handle, embedder.as_ref(), query, None, 5)
.await
.unwrap();
assert_eq!(results.len(), 5, "top_k=5 over a padded index");
assert!(
uuid_prefix_eq(results[0].drawer.id, on_topic_id),
"the more similar drawer must rank first; importance won instead \
(top={:?} scores={:?})",
results[0].drawer.id,
results.iter().map(|r| r.score).collect::<Vec<_>>()
);
assert!(
uuid_prefix_eq(results[1].drawer.id, loud_id),
"expected the high-importance drawer second, got {:?} \
(order={:?} scores={:?})",
results[1].drawer.id,
results.iter().map(|r| r.drawer.id).collect::<Vec<_>>(),
results.iter().map(|r| r.score).collect::<Vec<_>>()
);
}
async fn ranking_fixture_with_both_candidates(
qv: &[f32],
) -> (tempfile::TempDir, PalaceHandle, Uuid, Uuid) {
const MAX_ATTEMPTS: usize = 12;
const POOL: usize = 15;
for _ in 0..MAX_ATTEMPTS {
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let room_id = Uuid::new_v4();
let mut on_topic = Drawer::new(room_id, "the fact the user was actually asking for");
on_topic.importance = 0.05;
let on_topic_id = on_topic.id;
let mut loud = Drawer::new(room_id, "an unrelated but maximally important drawer");
loud.importance = 1.0;
let loud_id = loud.id;
handle
.vector_store
.upsert(on_topic_id, vec_at_cosine(qv, 0.80, 0))
.await
.unwrap();
handle
.vector_store
.upsert(loud_id, vec_at_cosine(qv, 0.75, 1))
.await
.unwrap();
handle.add_drawer(on_topic);
handle.add_drawer(loud);
pad_index(&handle, qv, 24).await;
let pool = handle.vector_store.search(qv, POOL).await.unwrap();
let has_both = [on_topic_id, loud_id]
.iter()
.all(|id| pool.iter().any(|h| uuid_prefix_eq(h.drawer_id, *id)));
if has_both && pool.len() == POOL {
return (dir, handle, on_topic_id, loud_id);
}
}
panic!(
"{MAX_ATTEMPTS} index builds in a row failed to surface both ranking \
drawers in a {POOL}-candidate pool; at the measured 0.42% miss rate \
that is not chance — the vector store or the fixture is broken"
);
}
#[tokio::test]
async fn l2_rank_trace_emits_one_event_per_candidate() {
use std::sync::Mutex;
use tracing::subscriber::with_default;
init_embedder();
let dir = tempdir().unwrap();
let handle = make_handle(dir.path());
let embedder = shared_embedder().await.unwrap();
let query = "trace every candidate before truncation";
let qv = embedder
.embed_batch(&[query.to_string()])
.await
.unwrap()
.remove(0);
pad_index(&handle, &qv, 24).await;
let pool = handle.vector_store.search(&qv, 3).await.unwrap();
assert_eq!(
pool.len(),
3,
"expected a 3-wide over-fetched candidate pool"
);
#[derive(Clone, Default)]
struct Counter(Arc<Mutex<usize>>);
impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for Counter {
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
if event.metadata().target() == super::layers::RANK_TRACE_TARGET {
*self.0.lock().unwrap() += 1;
}
}
}
let counter = Counter::default();
let seen = counter.0.clone();
{
use tracing_subscriber::layer::SubscriberExt as _;
let subscriber = tracing_subscriber::registry()
.with(tracing_subscriber::filter::LevelFilter::DEBUG)
.with(counter);
let fut = retrieve_l2(&handle, embedder.as_ref(), query, None, 1);
let results = with_default(subscriber, || futures::executor::block_on(fut)).unwrap();
assert_eq!(results.len(), 1, "top_k=1 must truncate to one result");
}
assert_eq!(
*seen.lock().unwrap(),
pool.len(),
"every candidate must be traced, including the ones truncation drops"
);
}