use rto_graph::{
AnchorState, DEFAULT_BASE_CONFIDENCE, DEFAULT_MEMORY_SCOPE, Decay, FactSet, MemoryKind,
MemoryWrite, Node, NodeKind, Recall, RecallOptions, Store, anchor_penalty,
};
fn seed_graph(store: &mut Store) {
let mut facts = FactSet::new();
let mut migrate = Node::new("sym:rust:src/migrate.rs#run", NodeKind::Fn, "run");
migrate.path = Some("src/migrate.rs".into());
migrate.blob_hash = Some("blob-migrate-v1".into());
let mut other = Node::new("sym:rust:src/lib.rs#main", NodeKind::Fn, "main");
other.path = Some("src/lib.rs".into());
other.blob_hash = Some("blob-main-v1".into());
facts.nodes = vec![migrate, other];
store.rebuild(&facts, Some("treeabc")).expect("rebuild");
}
fn drift_the_anchor(store: &mut Store) {
let mut facts = store.export_factset().expect("export");
for node in &mut facts.nodes {
if node.key == "sym:rust:src/migrate.rs#run" {
node.blob_hash = Some("blob-migrate-v2".into());
}
}
store.rebuild(&facts, Some("treedef")).expect("rebuild");
}
fn lesson(body: &str) -> MemoryWrite<'_> {
MemoryWrite {
scope: DEFAULT_MEMORY_SCOPE,
kind: MemoryKind::Lesson,
anchor: None,
body,
confidence: None,
supersedes: None,
}
}
fn confident<'a>(anchor: Option<&'a str>, body: &'a str) -> MemoryWrite<'a> {
MemoryWrite {
anchor,
confidence: Some(0.8),
..lesson(body)
}
}
fn recall(store: &Store) -> Recall {
store
.recall_memory(&RecallOptions::default())
.expect("recall")
}
fn bodies(recall: &Recall) -> Vec<&str> {
recall
.results
.iter()
.map(|r| r.record.body.as_str())
.collect()
}
#[test]
fn decay_none_recalls_byte_identically_across_runs() {
let path = std::env::temp_dir().join(format!(
"roteiro-recall-repro-{}-{:?}.db",
std::process::id(),
std::thread::current().id(),
));
std::fs::remove_file(&path).ok();
let first = {
let mut store = Store::open(&path).expect("open");
seed_graph(&mut store);
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/migrate.rs#run"),
confidence: Some(0.9),
..lesson("The retry loop double-counted partial batches.")
})
.expect("write");
store
.record_memory(&lesson("CI is Ubuntu-only."))
.expect("write");
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/gone.rs#dropped"),
..lesson("Removed because the batch cursor had no dedup key.")
})
.expect("write");
let once = serde_json::to_string(&recall(&store)).expect("serialize");
let twice = serde_json::to_string(&recall(&store)).expect("serialize");
assert_eq!(once, twice, "recall is not even stable within one session");
once
};
let second = {
let store = Store::open(&path).expect("reopen");
serde_json::to_string(&recall(&store)).expect("serialize")
};
assert_eq!(
first, second,
"decay = none must recall byte-identically across runs",
);
let store = Store::open(&path).expect("reopen");
let result = recall(&store);
assert!(result.reproducible, "decay = none reports itself so");
assert_eq!(result.decay, Decay::None, "and it is the default");
drop(store);
std::fs::remove_file(&path).expect("cleanup");
}
#[test]
fn recall_never_writes_to_the_store() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
for body in ["first lesson", "second lesson", "third lesson"] {
store.record_memory(&lesson(body)).expect("write");
}
let listing_before = serde_json::to_vec(
&store
.memory_listing(&rto_graph::MemoryFilter::default())
.expect("listing"),
)
.expect("serialize");
let counts_before = store.memory_counts().expect("counts");
for decay in [
Decay::None,
Decay::Linear { span: 2 },
Decay::Exponential { half_life: 1 },
] {
for _ in 0..3 {
store
.recall_memory(&RecallOptions {
decay,
..RecallOptions::default()
})
.expect("recall");
}
}
assert_eq!(
serde_json::to_vec(
&store
.memory_listing(&rto_graph::MemoryFilter::default())
.expect("listing")
)
.expect("serialize"),
listing_before,
"a read moved the store",
);
assert_eq!(store.memory_counts().expect("counts"), counts_before);
}
#[test]
fn a_superseded_memory_leaves_recall_immediately_regardless_of_age() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
let overruled = store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/migrate.rs#run"),
confidence: Some(1.0),
..lesson("Batch the writes: it is measurably faster.")
})
.expect("write");
store
.record_memory(&MemoryWrite {
supersedes: Some(overruled),
..lesson("Batching the writes lost the ordering guarantee. Do not.")
})
.expect("write");
for decay in [
Decay::None,
Decay::Linear { span: 1_000 },
Decay::Exponential { half_life: 1 },
] {
let result = store
.recall_memory(&RecallOptions {
decay,
..RecallOptions::default()
})
.expect("recall");
assert!(
!bodies(&result)
.iter()
.any(|b| b.contains("measurably faster")),
"the superseded record came back under {decay}",
);
assert_eq!(
bodies(&result),
vec!["Batching the writes lost the ordering guarantee. Do not."],
"the successor is what recall returns under {decay}",
);
assert_eq!(result.superseded, 1, "and the record is still stored");
}
let audited = store
.memory_records(&rto_graph::MemoryFilter {
include_superseded: true,
..rto_graph::MemoryFilter::default()
})
.expect("records");
assert_eq!(audited.len(), 2, "nothing was deleted, only dropped");
}
#[test]
fn forgetting_a_successor_returns_its_predecessor_to_recall() {
let mut store = Store::open_in_memory().expect("store");
let overruled = store.record_memory(&lesson("the old finding")).expect("w");
let successor = store
.record_memory(&MemoryWrite {
supersedes: Some(overruled),
..lesson("the new finding")
})
.expect("write");
assert_eq!(bodies(&recall(&store)), vec!["the new finding"]);
store.forget_memory(successor).expect("forget");
assert_eq!(
bodies(&recall(&store)),
vec!["the old finding"],
"the predecessor must return once its successor is gone",
);
}
#[test]
fn an_unanchored_memory_is_recalled_and_clearly_labelled() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&lesson("CI is Ubuntu-only."))
.expect("write");
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/never-existed.rs#nope"),
..lesson("A lesson about code that is not here.")
})
.expect("write");
let result = recall(&store);
assert_eq!(result.results.len(), 2, "both are retrievable");
let general = result
.results
.iter()
.find(|r| r.record.body.starts_with("CI is"))
.expect("the unanchored record is recalled");
assert_eq!(
general.record.anchor_state,
AnchorState::Unanchored,
"labelled as never anchored",
);
assert!(
general.record.applies,
"a general lesson applies everywhere"
);
assert!(general.record.anchor.is_none());
let failed = result
.results
.iter()
.find(|r| r.record.body.starts_with("A lesson about"))
.expect("the failed-anchor record is recalled too");
assert_eq!(
failed.record.anchor_state,
AnchorState::Vanished,
"an anchor that did not resolve is a different label entirely",
);
assert!(!failed.record.applies);
assert!(
general.score > failed.score,
"a repo-wide lesson must outrank one whose anchor did not resolve",
);
}
#[test]
fn anchor_drift_demotes_but_never_removes() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&confident(Some("sym:rust:src/migrate.rs#run"), "about run"))
.expect("write");
store
.record_memory(&confident(Some("sym:rust:src/lib.rs#main"), "about main"))
.expect("write");
store
.record_memory(&confident(Some("sym:rust:src/gone.rs#gone"), "about gone"))
.expect("write");
store
.record_memory(&confident(None, "about nothing"))
.expect("w");
drift_the_anchor(&mut store);
let result = recall(&store);
assert_eq!(result.results.len(), 4, "nothing was pruned by the rebuild");
let by_body = |body: &str| {
result
.results
.iter()
.find(|r| r.record.body == body)
.unwrap_or_else(|| panic!("{body} must still be recallable"))
.clone()
};
assert_eq!(
by_body("about run").record.anchor_state,
AnchorState::Drifted
);
assert_eq!(
by_body("about main").record.anchor_state,
AnchorState::Valid
);
assert_eq!(
by_body("about gone").record.anchor_state,
AnchorState::Vanished
);
assert_eq!(
by_body("about nothing").record.anchor_state,
AnchorState::Unanchored
);
assert_eq!(
bodies(&result),
vec!["about main", "about nothing", "about gone", "about run"],
"valid, then repo-wide, then vanished, then drifted",
);
for recalled in &result.results {
assert!(
recalled.score > 0.0,
"{} was silenced rather than demoted",
recalled.record.body,
);
}
}
#[test]
fn the_same_record_ranks_differently_on_a_tree_where_its_anchor_resolves() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/migrate.rs#run"),
..lesson("about run")
})
.expect("write");
let before = recall(&store).results[0].clone();
assert_eq!(before.record.anchor_state, AnchorState::Valid);
assert!(before.record.applies);
drift_the_anchor(&mut store);
let after = recall(&store).results[0].clone();
assert_eq!(after.record.anchor_state, AnchorState::Drifted);
assert!(!after.record.applies);
assert!(after.score < before.score, "drift must cost it something");
assert_eq!(
after.record.id, before.record.id,
"and it is the same record: nothing was rewritten",
);
}
#[test]
fn withholding_inapplicable_records_is_opt_in() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/gone.rs#gone"),
..lesson("about gone")
})
.expect("write");
store
.record_memory(&lesson("about nothing"))
.expect("write");
assert!(!RecallOptions::default().applicable_only);
assert_eq!(recall(&store).results.len(), 2);
let strict = store
.recall_memory(&RecallOptions {
applicable_only: true,
..RecallOptions::default()
})
.expect("recall");
assert_eq!(bodies(&strict), vec!["about nothing"]);
}
#[test]
fn the_score_is_exactly_the_product_of_the_terms_it_reports() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/migrate.rs#run"),
confidence: Some(0.75),
..lesson("stated confidence, valid anchor")
})
.expect("write");
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/gone.rs#gone"),
..lesson("no stated confidence, vanished anchor")
})
.expect("write");
for decay in [
Decay::None,
Decay::Linear { span: 4 },
Decay::Exponential { half_life: 2 },
] {
let result = store
.recall_memory(&RecallOptions {
decay,
..RecallOptions::default()
})
.expect("recall");
for recalled in &result.results {
let product =
recalled.base_confidence * recalled.anchor_penalty * recalled.decay_factor;
assert!(
(recalled.score - product).abs() < 1e-12,
"{decay}: {} != product of its terms",
recalled.record.body,
);
assert!((0.0..=1.0).contains(&recalled.score));
assert!(
(recalled.anchor_penalty - anchor_penalty(recalled.record.anchor_state)).abs()
< 1e-12,
"the reported penalty is the one the public function gives",
);
assert!(
(recalled.decay_factor - decay.factor(recalled.age)).abs() < 1e-12,
"the reported factor is the one the public decay gives",
);
}
}
let result = recall(&store);
let unstated = result
.results
.iter()
.find(|r| r.record.confidence.is_none())
.expect("present");
assert!((unstated.base_confidence - DEFAULT_BASE_CONFIDENCE).abs() < 1e-12);
}
#[test]
fn age_is_measured_in_generations_and_the_newest_record_is_age_zero() {
let mut store = Store::open_in_memory().expect("store");
let mut ids = Vec::new();
for body in ["oldest", "middle", "newest"] {
ids.push(store.record_memory(&lesson(body)).expect("write"));
}
let result = recall(&store);
assert_eq!(
result.generation,
*ids.last().expect("ids"),
"the generation recall ran at is the newest record's id",
);
for recalled in &result.results {
let expected = u64::try_from(result.generation - recalled.record.id).expect("age");
assert_eq!(recalled.age, expected, "{}", recalled.record.body);
}
let newest = result
.results
.iter()
.find(|r| r.record.body == "newest")
.expect("present");
assert_eq!(newest.age, 0);
}
#[test]
fn decay_reorders_otherwise_identical_records_by_generation() {
let mut store = Store::open_in_memory().expect("store");
for body in ["older", "newer"] {
store
.record_memory(&MemoryWrite {
confidence: Some(0.9),
..lesson(body)
})
.expect("write");
}
let flat = recall(&store);
assert!(
(flat.results[0].score - flat.results[1].score).abs() < f64::EPSILON,
"with no age term, identical records score identically",
);
assert_eq!(
bodies(&flat),
vec!["newer", "older"],
"and the tie breaks by newest generation, so the order is still total",
);
let decayed = store
.recall_memory(&RecallOptions {
decay: Decay::Exponential { half_life: 1 },
..RecallOptions::default()
})
.expect("recall");
assert!(
decayed.results[0].score > decayed.results[1].score,
"an age term must actually separate them",
);
assert_eq!(bodies(&decayed), vec!["newer", "older"]);
assert!(!decayed.reproducible, "and it says it is not reproducible");
}
#[test]
fn a_record_decayed_to_zero_is_ranked_last_and_still_returned() {
let mut store = Store::open_in_memory().expect("store");
for body in ["ancient", "recent"] {
store.record_memory(&lesson(body)).expect("write");
}
let result = store
.recall_memory(&RecallOptions {
decay: Decay::Linear { span: 1 },
..RecallOptions::default()
})
.expect("recall");
let ancient = result
.results
.iter()
.find(|r| r.record.body == "ancient")
.expect("a fully decayed record is still returned");
assert!(ancient.decay_factor.abs() < f64::EPSILON);
assert!(ancient.score.abs() < f64::EPSILON);
assert_eq!(
bodies(&result),
vec!["recent", "ancient"],
"ranked last, not withheld",
);
}
#[test]
fn a_query_narrows_the_set_without_touching_the_ranking() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/migrate.rs#run"),
..lesson("The retry loop double-counted partial batches.")
})
.expect("write");
store
.record_memory(&lesson("CI is Ubuntu-only."))
.expect("write");
let all = recall(&store);
let narrowed = store
.recall_memory(&RecallOptions {
query: Some("retry loop"),
..RecallOptions::default()
})
.expect("recall");
assert_eq!(narrowed.results.len(), 1);
let matched = &narrowed.results[0];
let same_record = all
.results
.iter()
.find(|r| r.record.id == matched.record.id)
.expect("present in both");
assert!(
(matched.score - same_record.score).abs() < f64::EPSILON,
"a query must not move a score",
);
assert_eq!(
store
.recall_memory(&RecallOptions {
query: Some("migrate.rs"),
..RecallOptions::default()
})
.expect("recall")
.results
.len(),
1,
);
assert!(
store
.recall_memory(&RecallOptions {
query: Some("retry loop pelican"),
..RecallOptions::default()
})
.expect("recall")
.results
.is_empty(),
"every token must appear",
);
}
#[test]
fn a_limit_returns_the_best_matches_not_the_newest() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/lib.rs#main"),
confidence: Some(1.0),
..lesson("the best record")
})
.expect("write");
for body in ["newer junk", "newest junk"] {
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/gone.rs#gone"),
confidence: Some(0.1),
..lesson(body)
})
.expect("write");
}
let top = store
.recall_memory(&RecallOptions {
limit: Some(1),
..RecallOptions::default()
})
.expect("recall");
assert_eq!(
bodies(&top),
vec!["the best record"],
"the oldest record wins on evidence, and a limit must not hide that",
);
assert_eq!(top.live, 3, "the counts still describe the whole store");
}
const CLI_DEFAULT_RECALL_LIMIT: usize = 10;
#[test]
fn a_zero_limit_recalls_every_record_not_none_of_them() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
let population = CLI_DEFAULT_RECALL_LIMIT * 3;
for i in 0..population {
store
.record_memory(&MemoryWrite {
anchor: Some("sym:rust:src/lib.rs#main"),
confidence: Some(0.5),
..lesson(&format!("record {i}"))
})
.expect("write");
}
let unlimited = store
.recall_memory(&RecallOptions {
limit: Some(0),
..RecallOptions::default()
})
.expect("recall");
assert_eq!(
unlimited.results.len(),
population,
"`limit = 0` is unlimited, the reading `window` fixes for every lens",
);
assert_eq!(
bodies(&unlimited),
bodies(&recall(&store)),
"`Some(0)` and `None` are one request, in the same order",
);
let huge = store
.recall_memory(&RecallOptions {
limit: Some(99_999),
..RecallOptions::default()
})
.expect("recall");
assert_eq!(bodies(&huge), bodies(&unlimited));
let paged = store
.recall_memory(&RecallOptions {
limit: Some(CLI_DEFAULT_RECALL_LIMIT),
..RecallOptions::default()
})
.expect("recall");
assert_eq!(paged.results.len(), CLI_DEFAULT_RECALL_LIMIT);
let live = u64::try_from(population).expect("population fits");
for report in [&unlimited, &paged] {
assert_eq!(report.live, live, "`live` is the pre-window population");
assert_eq!(report.superseded, 0);
}
assert_eq!(
u64::try_from(unlimited.results.len()).expect("fits"),
unlimited.live,
"at `limit = 0` the document reports every live record and returns them",
);
}
#[test]
fn a_zero_limit_lists_every_record_too() {
let mut store = Store::open_in_memory().expect("store");
for i in 0..5 {
store
.record_memory(&lesson(&format!("record {i}")))
.expect("write");
}
let unlimited = store
.memory_records(&rto_graph::MemoryFilter {
limit: Some(0),
..rto_graph::MemoryFilter::default()
})
.expect("records");
assert_eq!(unlimited.len(), 5, "SQL `LIMIT 0` must not reach the query");
assert_eq!(
unlimited.len(),
store
.memory_records(&rto_graph::MemoryFilter::default())
.expect("records")
.len(),
"`Some(0)` and `None` are one request here as well",
);
assert_eq!(
store
.memory_records(&rto_graph::MemoryFilter {
limit: Some(2),
..rto_graph::MemoryFilter::default()
})
.expect("records")
.len(),
2,
"and a limit that is a page still cuts",
);
}
#[test]
fn scope_narrows_recall_and_decides_nothing_else() {
let mut store = Store::open_in_memory().expect("store");
seed_graph(&mut store);
for scope in ["repo", "other-project"] {
store
.record_memory(&MemoryWrite {
scope,
anchor: Some("sym:rust:src/lib.rs#main"),
confidence: Some(0.6),
..lesson("same lesson, different namespace")
})
.expect("write");
}
let all = recall(&store);
assert_eq!(all.results.len(), 2);
assert!(
(all.results[0].score - all.results[1].score).abs() < f64::EPSILON,
"scope must not move a score",
);
for recalled in &all.results {
assert!(recalled.record.applies, "nor decide applicability");
}
let narrowed = store
.recall_memory(&RecallOptions {
scope: Some("other-project"),
..RecallOptions::default()
})
.expect("recall");
assert_eq!(narrowed.results.len(), 1);
assert_eq!(narrowed.results[0].record.scope, "other-project");
}
#[test]
fn an_empty_store_recalls_nothing_at_generation_zero() {
let store = Store::open_in_memory().expect("store");
let result = recall(&store);
assert!(result.results.is_empty());
assert_eq!(result.generation, 0);
assert_eq!((result.live, result.superseded), (0, 0));
assert_eq!(result.schema, rto_graph::RECALL_SCHEMA);
}