use std::cmp::Reverse;
use std::collections::BTreeSet;
use serde::Serialize;
use crate::lexical::{Bm25Ranker, LexDoc, LexicalCorpus, LexicalRanker};
use crate::memory::{Memory, MemoryType, Status, normalize_key};
const THREAD_FRESH_DAYS: i64 = 14;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Dimension {
Paths,
Globs,
Commands,
Tags,
}
impl Dimension {
pub(crate) fn specificity(self) -> u8 {
match self {
Self::Paths => 3,
Self::Globs => 2,
Self::Commands => 1,
Self::Tags => 0,
}
}
pub(crate) fn label(self) -> &'static str {
match self {
Self::Paths => "paths",
Self::Globs => "globs",
Self::Commands => "commands",
Self::Tags => "tags",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ScopeMatch {
pub(crate) specificity: u8,
pub(crate) dim: Dimension,
}
impl ScopeMatch {
fn of(dim: Dimension) -> Self {
Self {
specificity: dim.specificity(),
dim,
}
}
}
#[derive(Debug, Clone, Default)]
pub(crate) struct QueryContext {
pub(crate) paths: Vec<String>,
pub(crate) globs: Vec<String>,
pub(crate) commands: Vec<String>,
pub(crate) tags: Vec<String>,
pub(crate) query: Option<String>,
}
impl QueryContext {
pub(crate) fn has_scope_constraints(&self) -> bool {
!self.paths.is_empty()
|| !self.globs.is_empty()
|| !self.commands.is_empty()
|| !self.tags.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct QueryPartition {
pub(crate) workspace: String,
pub(crate) repo: Option<String>,
}
fn path_components(p: &str) -> Vec<&str> {
p.split('/').filter(|s| !s.is_empty()).collect()
}
fn path_admits(scope: &str, query: &str) -> bool {
let s = path_components(scope);
let q = path_components(query);
!s.is_empty() && s.len() <= q.len() && s.iter().zip(&q).all(|(a, b)| a == b)
}
fn glob_admits(pattern: &str, query: &str) -> bool {
glob::Pattern::new(pattern).is_ok_and(|p| p.matches(query))
}
fn command_admits(scope: &str, query: &str) -> bool {
let s: Vec<&str> = scope.split_whitespace().collect();
let q: Vec<&str> = query.split_whitespace().collect();
!s.is_empty() && s.len() <= q.len() && s.iter().zip(&q).all(|(a, b)| a == b)
}
pub(crate) fn match_scope(m: &Memory, q: &QueryContext) -> Option<ScopeMatch> {
let locations = || q.paths.iter().chain(q.globs.iter());
if locations().any(|l| m.scope.paths.iter().any(|sp| path_admits(sp, l))) {
return Some(ScopeMatch::of(Dimension::Paths));
}
if locations().any(|l| m.scope.globs.iter().any(|g| glob_admits(g, l))) {
return Some(ScopeMatch::of(Dimension::Globs));
}
if q.commands
.iter()
.any(|c| m.scope.commands.iter().any(|sc| command_admits(sc, c)))
{
return Some(ScopeMatch::of(Dimension::Commands));
}
let scope_tags: BTreeSet<&str> = m.scope.tags.iter().map(String::as_str).collect();
if q.tags.iter().any(|t| scope_tags.contains(t.as_str())) {
return Some(ScopeMatch::of(Dimension::Tags));
}
None
}
pub(crate) fn base_filter(m: &Memory, part: &QueryPartition, include_draft: bool) -> bool {
if m.scope.workspace != part.workspace {
return false;
}
if !m.scope.repo.is_empty() && part.repo.as_deref() != Some(m.scope.repo.as_str()) {
return false;
}
match m.status {
Status::Active => true,
Status::Draft => include_draft,
Status::Superseded | Status::Archived | Status::Retracted | Status::Quarantined => false,
}
}
pub(crate) fn thread_expiry(m: &Memory, _matched: ScopeMatch, today: &str) -> bool {
if m.kind != MemoryType::Thread {
return true;
}
if m.verification_state != "verified" {
return false;
}
match days_between(&m.reviewed, today) {
Some(d) => (0..=THREAD_FRESH_DAYS).contains(&d),
None => false,
}
}
fn parse_ymd(s: &str) -> Option<time::Date> {
let mut it = s.split('-');
let year: i32 = it.next()?.parse().ok()?;
let month: u8 = it.next()?.parse().ok()?;
let day: u8 = it.next()?.parse().ok()?;
if it.next().is_some() {
return None;
}
let month = time::Month::try_from(month).ok()?;
time::Date::from_calendar_date(year, month, day).ok()
}
pub(crate) fn days_between(a: &str, b: &str) -> Option<i64> {
let from = parse_ymd(a)?;
let to = parse_ymd(b)?;
Some((to - from).whole_days())
}
fn lex_doc(m: &Memory) -> LexDoc {
let tags = m.scope.tags.join(" ");
let text = [
m.title.as_str(),
m.summary.as_str(),
tags.as_str(),
m.key.as_deref().unwrap_or_default(),
]
.join(" ");
LexDoc {
id: m.uid.clone(),
text,
}
}
fn exact_key_match(m: &Memory, q: &QueryContext) -> bool {
match (&m.key, q.query.as_deref()) {
(Some(k), Some(query)) => normalize_key(query).ok().as_deref() == Some(k.as_str()),
_ => false,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Staleness {
Fresh,
Stale,
Unknown,
Unanchored,
Reference,
}
impl Staleness {
pub(crate) fn label(self) -> &'static str {
match self {
Self::Fresh => "fresh",
Self::Stale => "stale",
Self::Unknown => "unknown",
Self::Unanchored => "unanchored",
Self::Reference => "reference",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) struct GitFacts {
pub(crate) commits_since: Option<u32>,
}
const FRESH_DAYS: i64 = 30;
fn is_global_reference(m: &Memory) -> bool {
let scoped =
!m.scope.paths.is_empty() || !m.scope.globs.is_empty() || !m.scope.commands.is_empty();
scoped
&& m.scope.repo.is_empty()
&& m.anchor.kind == crate::git::AnchorKind::None
&& m.anchor.verified_sha.is_empty()
}
fn staleness(m: &Memory, facts: GitFacts, today: &str) -> Staleness {
if !m.scope.paths.is_empty() && !m.anchor.verified_sha.is_empty() {
return match facts.commits_since {
Some(0) => Staleness::Fresh,
Some(_) => Staleness::Stale,
None => Staleness::Unknown,
};
}
if is_global_reference(m) {
return Staleness::Reference;
}
if let Some(days) = days_between(&m.reviewed, today) {
return if days <= FRESH_DAYS {
Staleness::Fresh
} else {
Staleness::Stale
};
}
if m.anchor.kind == crate::git::AnchorKind::None {
Staleness::Unanchored
} else {
Staleness::Unknown
}
}
fn verification_rank(s: &str) -> u8 {
match s {
"verified" => 0,
"unverified" => 1,
"stale" => 2,
"disputed" => 3,
_ => 4,
}
}
fn trust_rank(s: &str) -> u8 {
match s {
"high" => 0,
"medium" => 1,
"low" => 2,
_ => 3,
}
}
fn severity_rank(s: &str) -> u8 {
match s {
"critical" => 0,
"high" => 1,
"medium" => 2,
"low" => 3,
"none" => 4,
_ => 5,
}
}
pub(crate) struct Candidate<'a> {
pub(crate) memory: &'a Memory,
pub(crate) scope_match: Option<ScopeMatch>,
pub(crate) staleness: Staleness,
pub(crate) lexical: u32,
pub(crate) exact_key: bool,
}
impl<'a> Candidate<'a> {
pub(crate) fn new(
m: &'a Memory,
scope_match: Option<ScopeMatch>,
q: &QueryContext,
facts: GitFacts,
today: &str,
lexical: u32,
) -> Self {
Self {
memory: m,
scope_match,
staleness: staleness(m, facts, today),
lexical,
exact_key: exact_key_match(m, q),
}
}
}
type SortKey<'a> = (
bool, Reverse<u32>, Reverse<u8>, u8, u8, u8, Reverse<i64>, i64, &'a str, &'a str, );
fn sort_key<'a>(c: &Candidate<'a>, today: &str) -> SortKey<'a> {
let m = c.memory;
(
!c.exact_key,
Reverse(c.lexical),
Reverse(c.scope_match.map_or(0, |s| s.specificity)),
verification_rank(&m.verification_state),
trust_rank(&m.trust_level),
severity_rank(&m.severity),
Reverse(m.weight),
days_between(&m.reviewed, today).unwrap_or(i64::MAX),
m.uid.as_str(),
m.key.as_deref().unwrap_or(""),
)
}
pub(crate) fn rank<'a>(mut cands: Vec<Candidate<'a>>, today: &str) -> Vec<Candidate<'a>> {
cands.sort_by(|a, b| sort_key(a, today).cmp(&sort_key(b, today)));
cands
}
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use anyhow::Result;
pub(crate) struct Snapshot {
pub(crate) today: String,
pub(crate) target: Option<String>,
pub(crate) part: QueryPartition,
}
pub(crate) fn freeze(root: &Path) -> Snapshot {
let frame = crate::git::capture(root).ok();
let target = frame
.as_ref()
.map(|f| f.base_commit.clone())
.filter(|s| !s.is_empty());
let repo = frame
.as_ref()
.map(|f| f.repo.repo_id.clone())
.filter(|s| !s.is_empty());
Snapshot {
today: crate::clock::today(),
target,
part: QueryPartition {
workspace: crate::memory::WORKSPACE.to_owned(),
repo,
},
}
}
fn git_facts(root: &Path, m: &Memory, snap: &Snapshot) -> GitFacts {
if m.scope.paths.is_empty() || m.anchor.verified_sha.is_empty() {
return GitFacts::default();
}
let Some(target) = snap.target.as_deref() else {
return GitFacts::default();
};
GitFacts {
commits_since: crate::git::commits_touching(
root,
&m.scope.paths,
&m.anchor.verified_sha,
target,
),
}
}
pub(crate) fn query<'a>(
mems: &'a [Memory],
q: &QueryContext,
snap: &Snapshot,
include_draft: bool,
root: &Path,
ranker: &dyn LexicalRanker,
) -> Vec<Candidate<'a>> {
let scoped = q.has_scope_constraints();
let active: Vec<&'a Memory> = mems
.iter()
.filter(|m| base_filter(m, &snap.part, include_draft))
.collect();
let docs: Vec<LexDoc> = active.iter().map(|m| lex_doc(m)).collect();
let corpus = LexicalCorpus::Raw(&docs);
let survivors: Vec<(&'a Memory, Option<ScopeMatch>)> = active
.iter()
.filter_map(|&m| {
let scope_match = match_scope(m, q);
if scoped && scope_match.is_none() {
return None;
}
let probe = scope_match.unwrap_or_else(|| ScopeMatch::of(Dimension::Tags));
if !thread_expiry(m, probe, &snap.today) {
return None;
}
Some((m, scope_match))
})
.collect();
let targets: Vec<&str> = survivors.iter().map(|(m, _)| m.uid.as_str()).collect();
let scores = ranker.score(q.query.as_deref(), &corpus, &targets);
let cands: Vec<Candidate<'a>> = survivors
.iter()
.zip(scores.iter())
.map(|(&(m, scope_match), &(_, lexical))| {
let facts = git_facts(root, m, snap);
Candidate::new(m, scope_match, q, facts, &snap.today, lexical)
})
.collect();
rank(cands, &snap.today)
}
fn format_find_table(cands: &[&Candidate<'_>]) -> String {
let scrub = |s: &str| crate::memory::scrub_line(s);
let rows: Vec<[String; 8]> = cands
.iter()
.map(|c| {
let m = c.memory;
[
m.uid.clone(),
m.kind.as_str().to_owned(),
m.status.as_str().to_owned(),
c.staleness.label().to_owned(),
scrub(&m.trust_level),
scrub(&m.severity),
c.scope_match.map_or("-", |s| s.dim.label()).to_owned(),
scrub(&m.title),
]
})
.collect();
let mut widths = [0usize; 7];
for r in &rows {
for (w, cell) in widths.iter_mut().zip(r.iter()) {
*w = (*w).max(cell.len());
}
}
let lines: Vec<String> = rows
.iter()
.map(|r| {
let mut parts: Vec<String> = r
.iter()
.take(widths.len())
.zip(widths.iter())
.map(|(cell, w)| format!("{cell:<w$}", w = *w))
.collect();
if let Some(title) = r.last() {
parts.push(title.clone());
}
parts.join(" ")
})
.collect();
if lines.is_empty() {
String::new()
} else {
lines.join("\n") + "\n"
}
}
#[derive(Serialize)]
struct MemoryFindRow {
uid: String,
#[serde(rename = "type")]
kind: String,
status: String,
staleness: String,
trust: String,
severity: String,
spec: String,
title: String,
}
impl From<&&Candidate<'_>> for MemoryFindRow {
fn from(c: &&Candidate<'_>) -> Self {
let m = c.memory;
MemoryFindRow {
uid: m.uid.clone(),
kind: m.kind.as_str().to_owned(),
status: m.status.as_str().to_owned(),
staleness: c.staleness.label().to_owned(),
trust: crate::memory::scrub_line(&m.trust_level),
severity: crate::memory::scrub_line(&m.severity),
spec: c.scope_match.map_or("-", |s| s.dim.label()).to_owned(),
title: crate::memory::scrub_line(&m.title),
}
}
}
fn format_find_json(cands: &[&Candidate<'_>]) -> Result<String> {
let rows: Vec<MemoryFindRow> = cands.iter().map(MemoryFindRow::from).collect();
crate::listing::json_envelope("memory_find", &rows)
}
struct Loaded {
root: PathBuf,
mems: Vec<Memory>,
q: QueryContext,
snap: Snapshot,
}
#[expect(clippy::too_many_arguments, reason = "CLI surface fans flags 1:1")]
fn load_query(
path: Option<PathBuf>,
paths: Vec<String>,
globs: Vec<String>,
commands: Vec<String>,
tags: Vec<String>,
free_query: Option<String>,
type_f: Option<MemoryType>,
status_f: Option<Status>,
) -> Result<Loaded> {
let root = crate::root::find(path, &crate::root::default_markers())?;
let mems =
crate::memory::select_rows(crate::memory::collect_all(&root)?, type_f, status_f, None);
let q = QueryContext {
paths,
globs,
commands,
tags,
query: free_query,
};
let snap = freeze(&root);
Ok(Loaded {
root,
mems,
q,
snap,
})
}
#[expect(clippy::too_many_arguments, reason = "CLI surface fans flags 1:1")]
pub(crate) fn run_find(
path: Option<PathBuf>,
paths: Vec<String>,
globs: Vec<String>,
commands: Vec<String>,
tags: Vec<String>,
free_query: Option<String>,
type_f: Option<MemoryType>,
status_f: Option<Status>,
include_draft: bool,
format: crate::listing::Format,
offset: usize,
limit: Option<usize>,
) -> Result<()> {
let Loaded {
root,
mems,
q,
snap,
..
} = load_query(
path, paths, globs, commands, tags, free_query, type_f, status_f,
)?;
let ranker = Bm25Ranker;
let ranked = query(&mems, &q, &snap, include_draft, &root, &ranker);
let total = ranked.len();
let visible: Vec<&Candidate<'_>> = ranked
.iter()
.skip(offset)
.take(limit.unwrap_or(usize::MAX))
.collect();
let shown = visible.len();
let mut parts: Vec<String> = Vec::new();
let body = match format {
crate::listing::Format::Table => format_find_table(&visible),
crate::listing::Format::Json => format_find_json(&visible)?,
};
parts.push(body);
if format == crate::listing::Format::Table && shown < total {
let page_size = limit.unwrap_or(RETRIEVE_LIMIT_DEFAULT);
parts.push(format_truncation_notice(shown, total, offset, page_size));
}
let output = parts.concat();
write!(io::stdout(), "{output}")?;
Ok(())
}
pub(crate) const RETRIEVE_LIMIT_DEFAULT: usize = 5;
pub(crate) const RETRIEVE_LIMIT_MAX: usize = 20;
pub(crate) fn parse_min_trust(s: &str) -> std::result::Result<String, String> {
match s {
"high" | "medium" | "low" => Ok(s.to_owned()),
other => Err(format!(
"invalid trust level {other:?} (expected high|medium|low)"
)),
}
}
fn holdback_floor(min_trust: Option<&str>) -> u8 {
let default = trust_rank("medium");
min_trust.map_or(default, |l| trust_rank(l).min(default))
}
fn held_back(m: &Memory, floor: u8) -> bool {
severity_rank(&m.severity) <= severity_rank("high") && trust_rank(&m.trust_level) > floor
}
fn format_truncation_notice(shown: usize, total: usize, offset: usize, page_size: usize) -> String {
if total == 0 {
return String::new();
}
if offset >= total {
return format!(
"{shown} of {total}; no results at this offset; reduce --offset or --page\n"
);
}
#[expect(
clippy::integer_division,
reason = "floor division for 1-based page calc"
)]
let next_page = (offset / page_size) + 2; format!("{shown} of {total}; use --page {next_page} for next or specify a higher --limit\n")
}
#[derive(Serialize)]
struct MemoryRetrieveRow {
uid: String,
#[serde(rename = "type")]
kind: String,
status: String,
staleness: String,
trust: String,
severity: String,
title: String,
}
impl From<&Candidate<'_>> for MemoryRetrieveRow {
fn from(c: &Candidate<'_>) -> Self {
let m = c.memory;
MemoryRetrieveRow {
uid: m.uid.clone(),
kind: m.kind.as_str().to_owned(),
status: m.status.as_str().to_owned(),
staleness: c.staleness.label().to_owned(),
trust: crate::memory::scrub_line(&m.trust_level),
severity: crate::memory::scrub_line(&m.severity),
title: crate::memory::scrub_line(&m.title),
}
}
}
fn format_retrieve_json(cands: &[&Candidate<'_>]) -> Result<String> {
let rows: Vec<MemoryRetrieveRow> = cands.iter().map(|c| MemoryRetrieveRow::from(*c)).collect();
crate::listing::json_envelope("memory_retrieve", &rows)
}
#[expect(clippy::too_many_arguments, reason = "CLI surface fans flags 1:1")]
pub(crate) fn run_retrieve(
path: Option<PathBuf>,
paths: Vec<String>,
globs: Vec<String>,
commands: Vec<String>,
tags: Vec<String>,
free_query: Option<String>,
type_f: Option<MemoryType>,
status_f: Option<Status>,
include_draft: bool,
limit: usize,
min_trust: Option<&str>,
offset: usize,
format: crate::listing::Format,
) -> Result<()> {
let Loaded {
root,
mems,
q,
snap,
} = load_query(
path, paths, globs, commands, tags, free_query, type_f, status_f,
)?;
let ranker = Bm25Ranker;
let ranked = query(&mems, &q, &snap, include_draft, &root, &ranker);
let floor = holdback_floor(min_trust);
let eligible: Vec<&Candidate<'_>> = ranked
.iter()
.filter(|c| !held_back(c.memory, floor))
.collect();
let total = eligible.len();
let visible: Vec<&Candidate<'_>> = eligible.iter().skip(offset).take(limit).copied().collect();
let shown = visible.len();
let mut parts: Vec<String> = Vec::new();
match format {
crate::listing::Format::Table => {
for c in &visible {
let body = crate::memory::read_body(&root, &c.memory.uid);
let nonce = uuid::Uuid::new_v4().simple().to_string();
parts.push(crate::memory::render_show(
c.memory,
&body,
&nonce,
Some(c.staleness.label()),
));
}
if shown < total {
parts.push(format_truncation_notice(shown, total, offset, limit));
}
}
crate::listing::Format::Json => {
parts.push(format_retrieve_json(&visible)?);
}
}
let output = parts.concat();
write!(io::stdout(), "{output}")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::Memory;
const UID: &str = "mem_018f3a1b2c3d4e5f60718293a4b5c6d7";
struct Fixture {
uid: &'static str,
key: &'static str,
kind: &'static str,
status: &'static str,
title: &'static str,
summary: &'static str,
workspace: &'static str,
repo: &'static str,
paths: &'static [&'static str],
globs: &'static [&'static str],
commands: &'static [&'static str],
tags: &'static [&'static str],
anchor_kind: &'static str,
verified_sha: &'static str,
verification_state: &'static str,
reviewed: &'static str,
trust_level: &'static str,
severity: &'static str,
weight: i64,
}
impl Default for Fixture {
fn default() -> Self {
Self {
uid: UID,
key: "",
kind: "fact",
status: "active",
title: "t",
summary: "s",
workspace: "default",
repo: "",
paths: &[],
globs: &[],
commands: &[],
tags: &[],
anchor_kind: "",
verified_sha: "",
verification_state: "unverified",
reviewed: "2026-06-01",
trust_level: "medium",
severity: "none",
weight: 0,
}
}
}
fn toml_list(items: &[&str]) -> String {
let inner = items
.iter()
.map(|s| format!("{s:?}"))
.collect::<Vec<_>>()
.join(", ");
format!("[{inner}]")
}
fn memory(f: &Fixture) -> Memory {
let key_line = if f.key.is_empty() {
String::new()
} else {
format!("memory_key = {:?}\n", f.key)
};
let text = format!(
r#"
memory_uid = "{uid}"
{key_line}schema_version = 1
memory_type = "{kind}"
status = "{status}"
title = "{title}"
summary = "{summary}"
created = "2026-06-01"
updated = "2026-06-01"
[scope]
workspace = "{workspace}"
repo = "{repo}"
paths = {paths}
globs = {globs}
commands = {commands}
tags = {tags}
[git]
anchor_kind = "{anchor_kind}"
verified_sha = "{verified_sha}"
[review]
verification_state = "{vs}"
reviewed = "{reviewed}"
review_by = ""
[trust]
trust_level = "{trust_level}"
[ranking]
severity = "{severity}"
weight = {weight}
"#,
uid = f.uid,
kind = f.kind,
status = f.status,
title = f.title,
summary = f.summary,
workspace = f.workspace,
repo = f.repo,
paths = toml_list(f.paths),
globs = toml_list(f.globs),
commands = toml_list(f.commands),
tags = toml_list(f.tags),
anchor_kind = f.anchor_kind,
verified_sha = f.verified_sha,
vs = f.verification_state,
reviewed = f.reviewed,
trust_level = f.trust_level,
severity = f.severity,
weight = f.weight,
);
Memory::parse(&text).unwrap()
}
fn q(paths: &[&str]) -> QueryContext {
QueryContext {
paths: paths.iter().map(|s| (*s).to_owned()).collect(),
..Default::default()
}
}
#[test]
fn has_scope_constraints_query_alone_is_not_scope_bearing() {
let mut c = QueryContext {
query: Some("auth bug".to_owned()),
..Default::default()
};
assert!(!c.has_scope_constraints(), "free-text query is not scope");
c.tags.push("rust".to_owned());
assert!(c.has_scope_constraints(), "a tag is a scope constraint");
}
#[test]
fn has_scope_constraints_any_dimension_counts() {
for c in [
q(&["src"]),
QueryContext {
globs: vec!["**/*.rs".to_owned()],
..Default::default()
},
QueryContext {
commands: vec!["cargo".to_owned()],
..Default::default()
},
QueryContext {
tags: vec!["x".to_owned()],
..Default::default()
},
] {
assert!(c.has_scope_constraints());
}
assert!(!QueryContext::default().has_scope_constraints());
}
#[test]
fn paths_match_is_exact_or_component_prefix() {
let m = memory(&Fixture {
paths: &["src"],
..Default::default()
});
assert!(
match_scope(
&memory(&Fixture {
paths: &["src/memory.rs"],
..Default::default()
}),
&q(&["src/memory.rs"])
)
.is_some()
);
let hit = match_scope(&m, &q(&["src/memory.rs"])).unwrap();
assert_eq!(hit.dim, Dimension::Paths);
assert_eq!(hit.specificity, 3);
assert!(match_scope(&m, &q(&["srcfoo/x.rs"])).is_none());
}
#[test]
fn globs_match_is_star_star_aware() {
let m = memory(&Fixture {
globs: &["src/**/*.rs"],
..Default::default()
});
let hit = match_scope(&m, &q(&["src/a/b/c.rs"])).unwrap();
assert_eq!(hit.dim, Dimension::Globs);
assert_eq!(hit.specificity, 2);
assert!(match_scope(&m, &q(&["src/a/b/c.txt"])).is_none());
}
#[test]
fn commands_match_is_token_prefix() {
let m = memory(&Fixture {
commands: &["cargo test"],
..Default::default()
});
let probe = QueryContext {
commands: vec!["cargo test --release".to_owned()],
..Default::default()
};
let hit = match_scope(&m, &probe).unwrap();
assert_eq!(hit.dim, Dimension::Commands);
assert_eq!(hit.specificity, 1);
let miss = QueryContext {
commands: vec!["cargo build".to_owned()],
..Default::default()
};
assert!(match_scope(&m, &miss).is_none());
}
#[test]
fn tags_match_is_set_intersection() {
let m = memory(&Fixture {
tags: &["rust", "cli"],
..Default::default()
});
let hit = match_scope(
&m,
&QueryContext {
tags: vec!["cli".to_owned()],
..Default::default()
},
)
.unwrap();
assert_eq!(hit.dim, Dimension::Tags);
assert_eq!(hit.specificity, 0);
let miss = QueryContext {
tags: vec!["python".to_owned()],
..Default::default()
};
assert!(match_scope(&m, &miss).is_none());
}
#[test]
fn highest_specificity_dimension_wins() {
let m = memory(&Fixture {
paths: &["src"],
tags: &["rust"],
..Default::default()
});
let probe = QueryContext {
paths: vec!["src/memory.rs".to_owned()],
tags: vec!["rust".to_owned()],
..Default::default()
};
assert_eq!(match_scope(&m, &probe).unwrap().dim, Dimension::Paths);
}
#[test]
fn no_scope_query_path_probes_memory_globs() {
let m = memory(&Fixture {
globs: &["src/**"],
..Default::default()
});
assert_eq!(
match_scope(&m, &q(&["src/memory.rs"])).unwrap().dim,
Dimension::Globs
);
}
#[test]
fn unscoped_memory_never_matches_a_scope_query() {
let m = memory(&Fixture::default()); assert!(match_scope(&m, &q(&["src/memory.rs"])).is_none());
}
fn part(repo: Option<&str>) -> QueryPartition {
QueryPartition {
workspace: "default".to_owned(),
repo: repo.map(str::to_owned),
}
}
#[test]
fn base_filter_drops_cross_workspace() {
let m = memory(&Fixture {
workspace: "other",
..Default::default()
});
assert!(!base_filter(&m, &part(None), false));
}
#[test]
fn base_filter_repo_partition() {
let repo_scoped = memory(&Fixture {
repo: "repo:abc",
..Default::default()
});
assert!(base_filter(&repo_scoped, &part(Some("repo:abc")), false));
assert!(!base_filter(&repo_scoped, &part(Some("repo:xyz")), false));
assert!(!base_filter(&repo_scoped, &part(None), false));
let global = memory(&Fixture::default());
assert!(base_filter(&global, &part(None), false));
assert!(base_filter(&global, &part(Some("repo:abc")), false));
}
#[test]
fn global_class_admitted_in_any_partition_and_surfaces_only_on_scope_hit() {
let global = memory(&Fixture {
repo: "",
anchor_kind: "none",
paths: &["install/manifest.toml"],
..Default::default()
});
assert!(base_filter(
&global,
&part(Some("repo:some-other-client")),
false
));
assert!(base_filter(&global, &part(None), false));
assert_eq!(
match_scope(&global, &q(&["install/manifest.toml"])),
Some(ScopeMatch::of(Dimension::Paths))
);
assert_eq!(match_scope(&global, &q(&["src/main.rs"])), None);
}
#[test]
fn base_filter_lifecycle() {
let active = memory(&Fixture::default());
assert!(base_filter(&active, &part(None), false));
for bad in ["superseded", "archived", "retracted", "quarantined"] {
let m = memory(&Fixture {
status: bad,
..Default::default()
});
assert!(!base_filter(&m, &part(None), false), "{bad} must drop");
assert!(
!base_filter(&m, &part(None), true),
"{bad} must drop even with include_draft"
);
}
}
#[test]
fn base_filter_draft_is_gated() {
let m = memory(&Fixture {
status: "draft",
..Default::default()
});
assert!(!base_filter(&m, &part(None), false));
assert!(base_filter(&m, &part(None), true));
}
fn matched() -> ScopeMatch {
ScopeMatch::of(Dimension::Paths)
}
#[test]
fn thread_expiry_non_thread_always_passes() {
let m = memory(&Fixture {
kind: "fact",
verification_state: "unverified",
..Default::default()
});
assert!(thread_expiry(&m, matched(), "2030-01-01"));
}
#[test]
fn thread_expiry_requires_verified() {
let m = memory(&Fixture {
kind: "thread",
verification_state: "unverified",
reviewed: "2026-06-05",
..Default::default()
});
assert!(!thread_expiry(&m, matched(), "2026-06-05"));
}
#[test]
fn thread_expiry_window_is_14_days() {
let fresh = memory(&Fixture {
kind: "thread",
verification_state: "verified",
reviewed: "2026-06-01",
..Default::default()
});
assert!(thread_expiry(&fresh, matched(), "2026-06-15")); assert!(!thread_expiry(&fresh, matched(), "2026-06-16")); }
#[test]
fn thread_expiry_unparseable_review_drops() {
let m = memory(&Fixture {
kind: "thread",
verification_state: "verified",
reviewed: "not-a-date",
..Default::default()
});
assert!(!thread_expiry(&m, matched(), "2026-06-05"));
}
#[test]
fn days_between_basics() {
assert_eq!(days_between("2026-06-01", "2026-06-15"), Some(14));
assert_eq!(days_between("2026-06-15", "2026-06-01"), Some(-14));
assert_eq!(days_between("2026-06-01", "2026-06-01"), Some(0));
assert_eq!(days_between("bad", "2026-06-01"), None);
assert_eq!(days_between("2026-06-01", ""), None);
assert_eq!(days_between("2026-13-01", "2026-06-01"), None); }
fn with_query(q: &str) -> QueryContext {
QueryContext {
query: Some(q.to_owned()),
..Default::default()
}
}
#[test]
fn overlap_ranker_preserves_retired_overlap() {
use crate::lexical::{LexicalCorpus, LexicalRanker, OverlapRanker};
let cases = [
(
Fixture {
key: "mem.auth.flow",
title: "Token expiry",
summary: "middleware check",
tags: &["rust"],
..Default::default()
},
[
("token middleware rust auth", 4), ("token token token", 1), ("python django", 0), ("src memory rs lint clippy", 0), ("", 0), ],
),
(
Fixture {
key: "mem.pattern.lint",
title: "src/memory.rs clippy",
summary: "expiry token",
tags: &["lint", "rust"],
..Default::default()
},
[
("token middleware rust auth", 2), ("token token token", 1),
("python django", 0),
("src memory rs lint clippy", 5), ("", 0),
],
),
];
for (f, expected) in &cases {
let m = memory(f);
let docs = vec![lex_doc(&m)]; let corpus = LexicalCorpus::Raw(&docs);
let uid = m.uid.as_str();
for (q, want) in expected {
let got = OverlapRanker.score(Some(q), &corpus, &[uid]);
assert_eq!(got.len(), 1, "positional: one entry per target");
assert_eq!(
got[0],
(m.uid.clone(), *want),
"OverlapRanker diverged from frozen overlap for query {q:?}"
);
}
assert_eq!(
OverlapRanker.score(None, &corpus, &[uid]),
vec![(m.uid.clone(), 0)]
);
}
}
#[test]
fn exact_key_match_full_key_only() {
let m = memory(&Fixture {
key: "mem.auth.flow",
..Default::default()
});
assert!(exact_key_match(&m, &with_query("mem.auth.flow")));
assert!(exact_key_match(&m, &with_query("auth.flow")));
assert!(!exact_key_match(&m, &with_query("auth")));
assert!(!exact_key_match(&m, &with_query("not a key!")));
assert!(!exact_key_match(&m, &QueryContext::default()));
let keyless = memory(&Fixture::default());
assert!(!exact_key_match(&keyless, &with_query("mem.auth.flow")));
}
fn facts(commits_since: Option<u32>) -> GitFacts {
GitFacts { commits_since }
}
#[test]
fn staleness_branch1_commit_mode_when_scoped_and_attested() {
let base = Fixture {
paths: &["src"],
anchor_kind: "commit",
verified_sha: "deadbeef",
..Default::default()
};
let m = memory(&base);
assert_eq!(
staleness(&m, facts(Some(0)), "2026-06-05"),
Staleness::Fresh
);
assert_eq!(
staleness(&m, facts(Some(3)), "2026-06-05"),
Staleness::Stale
);
assert_eq!(staleness(&m, facts(None), "2026-06-05"), Staleness::Unknown);
}
#[test]
fn staleness_paths_empty_but_verified_falls_to_time_branch() {
let m = memory(&Fixture {
paths: &[],
verified_sha: "deadbeef",
reviewed: "2026-06-01",
..Default::default()
});
assert_eq!(
staleness(&m, facts(Some(9)), "2026-06-05"),
Staleness::Fresh
);
}
#[test]
fn staleness_time_branch_boundary_is_inclusive_30() {
let m = memory(&Fixture {
reviewed: "2026-05-01",
..Default::default()
});
assert_eq!(staleness(&m, facts(None), "2026-05-31"), Staleness::Fresh);
assert_eq!(staleness(&m, facts(None), "2026-06-01"), Staleness::Stale);
}
#[test]
fn staleness_anchored_unattested_no_date_is_unknown() {
let m = memory(&Fixture {
anchor_kind: "commit",
verified_sha: "",
reviewed: "not-a-date",
..Default::default()
});
assert_eq!(staleness(&m, facts(None), "2026-06-05"), Staleness::Unknown);
}
#[test]
fn staleness_no_anchor_no_date_is_unanchored() {
let m = memory(&Fixture {
anchor_kind: "",
verified_sha: "",
reviewed: "",
..Default::default()
});
assert_eq!(
staleness(&m, facts(None), "2026-06-05"),
Staleness::Unanchored
);
}
#[test]
fn staleness_dirty_then_verified_uses_verified_sha() {
let m = memory(&Fixture {
paths: &["src"],
anchor_kind: "checkout_state",
verified_sha: "cleansha",
..Default::default()
});
assert_eq!(
staleness(&m, facts(Some(0)), "2026-06-05"),
Staleness::Fresh
);
}
fn global_class_fixture() -> Fixture {
Fixture {
repo: "",
anchor_kind: "none",
verified_sha: "",
paths: &["install/manifest.toml"],
..Default::default()
}
}
#[test]
fn global_class_renders_reference_never_decaying() {
let m = memory(&global_class_fixture());
let aeons_later = "2099-01-01";
assert_eq!(
staleness(&m, facts(None), aeons_later),
Staleness::Reference,
"the global class is exempt from days-since-reviewed decay"
);
}
#[test]
fn an_attested_global_scoped_memory_uses_commit_mode_not_reference() {
let m = memory(&Fixture {
verified_sha: "deadbeef",
..global_class_fixture()
});
assert_eq!(
staleness(&m, facts(Some(0)), "2026-06-05"),
Staleness::Fresh
);
assert_eq!(
staleness(&m, facts(Some(2)), "2026-06-05"),
Staleness::Stale
);
}
#[test]
fn a_scopeless_repo_empty_memory_is_not_reference() {
let dated = memory(&Fixture {
reviewed: "2026-06-01",
..Default::default()
});
assert_eq!(
staleness(&dated, facts(None), "2026-06-05"),
Staleness::Fresh
);
let undated = memory(&Fixture {
reviewed: "",
..Default::default()
});
assert_eq!(
staleness(&undated, facts(None), "2026-06-05"),
Staleness::Unanchored
);
}
#[test]
fn rank_ordinals_polarity_and_unknown_worst() {
assert!(verification_rank("verified") < verification_rank("unverified"));
assert!(verification_rank("unverified") < verification_rank("stale"));
assert!(verification_rank("stale") < verification_rank("disputed"));
assert!(verification_rank("disputed") < verification_rank("???"));
assert!(trust_rank("high") < trust_rank("medium"));
assert!(trust_rank("medium") < trust_rank("low"));
assert!(trust_rank("low") < trust_rank("???"));
assert!(severity_rank("critical") < severity_rank("high"));
assert!(severity_rank("high") < severity_rank("medium"));
assert!(severity_rank("medium") < severity_rank("low"));
assert!(severity_rank("low") < severity_rank("none"));
assert!(severity_rank("none") < severity_rank("???"));
}
fn cand<'a>(
m: &'a Memory,
lexical: u32,
exact_key: bool,
dim: Option<Dimension>,
) -> Candidate<'a> {
Candidate {
memory: m,
scope_match: dim.map(ScopeMatch::of),
staleness: Staleness::Unknown,
lexical,
exact_key,
}
}
const TODAY: &str = "2026-06-05";
fn uids() -> [&'static str; 3] {
[
"mem_018f3a1b2c3d4e5f60718293a4b5c601",
"mem_018f3a1b2c3d4e5f60718293a4b5c602",
"mem_018f3a1b2c3d4e5f60718293a4b5c603",
]
}
#[test]
fn rank_is_deterministic_under_shuffle() {
let [u0, u1, u2] = uids();
let m0 = memory(&Fixture {
uid: u0,
..Default::default()
});
let m1 = memory(&Fixture {
uid: u1,
..Default::default()
});
let m2 = memory(&Fixture {
uid: u2,
..Default::default()
});
let order_of = |cs: Vec<Candidate<'_>>| {
rank(cs, TODAY)
.iter()
.map(|c| c.memory.uid.clone())
.collect::<Vec<_>>()
};
let baseline = order_of(vec![
cand(&m0, 1, false, None),
cand(&m1, 1, false, None),
cand(&m2, 1, false, None),
]);
assert_eq!(baseline, vec![u0.to_owned(), u1.to_owned(), u2.to_owned()]);
for perm in [[2usize, 0, 1], [1, 2, 0], [2, 1, 0], [0, 2, 1]] {
let ms = [&m0, &m1, &m2];
let cs = perm.iter().map(|&i| cand(ms[i], 1, false, None)).collect();
assert_eq!(order_of(cs), baseline, "perm {perm:?} must match");
}
}
fn assert_ranks_first(win: Candidate<'_>, lose: Candidate<'_>) {
let win_uid = win.memory.uid.clone();
let ranked = rank(vec![lose, win], TODAY);
assert_eq!(ranked[0].memory.uid, win_uid);
}
#[test]
fn rank_key1_exact_key_beats_everything() {
let [u0, u1, _] = uids();
let exact = memory(&Fixture {
uid: u0,
key: "mem.k",
..Default::default()
});
let lexy = memory(&Fixture {
uid: u1,
..Default::default()
});
assert_ranks_first(
cand(&exact, 0, true, None),
cand(&lexy, 99, false, Some(Dimension::Paths)),
);
}
#[test]
fn rank_key2_higher_lexical_first() {
let [u0, u1, _] = uids();
let hi = memory(&Fixture {
uid: u0,
..Default::default()
});
let lo = memory(&Fixture {
uid: u1,
..Default::default()
});
assert_ranks_first(cand(&hi, 5, false, None), cand(&lo, 1, false, None));
}
#[test]
fn rank_key3_higher_specificity_first() {
let [u0, u1, _] = uids();
let paths = memory(&Fixture {
uid: u0,
..Default::default()
});
let tags = memory(&Fixture {
uid: u1,
..Default::default()
});
assert_ranks_first(
cand(&paths, 1, false, Some(Dimension::Paths)), cand(&tags, 1, false, Some(Dimension::Tags)), );
}
#[test]
fn rank_key4_verification_better_first() {
let [u0, u1, _] = uids();
let verified = memory(&Fixture {
uid: u0,
verification_state: "verified",
..Default::default()
});
let disputed = memory(&Fixture {
uid: u1,
verification_state: "disputed",
..Default::default()
});
assert_ranks_first(
cand(&verified, 1, false, None),
cand(&disputed, 1, false, None),
);
}
#[test]
fn rank_key5_higher_trust_first() {
let [u0, u1, _] = uids();
let high = memory(&Fixture {
uid: u0,
trust_level: "high",
..Default::default()
});
let low = memory(&Fixture {
uid: u1,
trust_level: "low",
..Default::default()
});
assert_ranks_first(cand(&high, 1, false, None), cand(&low, 1, false, None));
}
#[test]
fn rank_key6_higher_severity_first() {
let [u0, u1, _] = uids();
let crit = memory(&Fixture {
uid: u0,
severity: "critical",
..Default::default()
});
let none = memory(&Fixture {
uid: u1,
severity: "none",
..Default::default()
});
assert_ranks_first(cand(&crit, 1, false, None), cand(&none, 1, false, None));
}
#[test]
fn rank_key7_higher_weight_first() {
let [u0, u1, _] = uids();
let heavy = memory(&Fixture {
uid: u0,
weight: 9,
..Default::default()
});
let light = memory(&Fixture {
uid: u1,
weight: 0,
..Default::default()
});
assert_ranks_first(cand(&heavy, 1, false, None), cand(&light, 1, false, None));
}
#[test]
fn rank_key8_more_recent_first_missing_last() {
let [u0, u1, u2] = uids();
let recent = memory(&Fixture {
uid: u0,
reviewed: "2026-06-04",
..Default::default()
});
let old = memory(&Fixture {
uid: u1,
reviewed: "2026-01-01",
..Default::default()
});
assert_ranks_first(cand(&recent, 1, false, None), cand(&old, 1, false, None));
let dated = memory(&Fixture {
uid: u0,
reviewed: "2026-01-01",
..Default::default()
});
let undated = memory(&Fixture {
uid: u2,
reviewed: "garbage",
..Default::default()
});
assert_ranks_first(cand(&dated, 1, false, None), cand(&undated, 1, false, None));
}
#[test]
fn rank_verification_stale_not_double_penalised_by_staleness() {
let [u0, u1, _] = uids();
let a = memory(&Fixture {
uid: u0,
..Default::default()
});
let b = memory(&Fixture {
uid: u1,
..Default::default()
});
let mut ca = cand(&a, 1, false, None);
ca.staleness = Staleness::Stale;
let mut cb = cand(&b, 1, false, None);
cb.staleness = Staleness::Fresh;
let ranked = rank(vec![cb, ca], TODAY);
assert_eq!(ranked[0].memory.uid, u0);
}
fn snap(target: Option<&str>, repo: Option<&str>) -> Snapshot {
Snapshot {
today: TODAY.to_owned(),
target: target.map(str::to_owned),
part: part(repo),
}
}
#[test]
fn staleness_and_dimension_labels() {
assert_eq!(Staleness::Fresh.label(), "fresh");
assert_eq!(Staleness::Stale.label(), "stale");
assert_eq!(Staleness::Unknown.label(), "unknown");
assert_eq!(Staleness::Unanchored.label(), "unanchored");
assert_eq!(Staleness::Reference.label(), "reference");
assert_eq!(Dimension::Paths.label(), "paths");
assert_eq!(Dimension::Globs.label(), "globs");
assert_eq!(Dimension::Commands.label(), "commands");
assert_eq!(Dimension::Tags.label(), "tags");
}
#[test]
fn freeze_outside_a_repo_degrades_to_no_target_no_repo() {
let dir = tempfile::tempdir().expect("tempdir");
let s = freeze(dir.path());
assert_eq!(s.target, None);
assert_eq!(s.part.repo, None);
assert_eq!(s.part.workspace, "default");
assert!(!s.today.is_empty());
}
#[test]
fn git_facts_gate_skips_without_spawning() {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
let unscoped = memory(&Fixture {
verified_sha: "abc",
..Default::default()
});
assert_eq!(
git_facts(root, &unscoped, &snap(Some("dead"), None)).commits_since,
None
);
let unattested = memory(&Fixture {
paths: &["src/x.rs"],
..Default::default()
});
assert_eq!(
git_facts(root, &unattested, &snap(Some("dead"), None)).commits_since,
None
);
let no_target = memory(&Fixture {
paths: &["src/x.rs"],
verified_sha: "abc",
..Default::default()
});
assert_eq!(
git_facts(root, &no_target, &snap(None, None)).commits_since,
None
);
}
#[test]
fn query_scope_bearing_drops_nonmatching() {
let [u0, u1, _] = uids();
let hit = memory(&Fixture {
uid: u0,
paths: &["src/main.rs"],
..Default::default()
});
let miss = memory(&Fixture {
uid: u1,
paths: &["docs/guide.md"],
..Default::default()
});
let mems = vec![hit, miss];
let ranked = query(
&mems,
&q(&["src/main.rs"]),
&snap(None, None),
false,
Path::new("."),
&Bm25Ranker,
);
assert_eq!(ranked.len(), 1);
assert_eq!(ranked[0].memory.uid, u0);
}
#[test]
fn query_bare_query_keeps_all_active_ranked_lexically() {
let [u0, u1, _] = uids();
let matchy = memory(&Fixture {
uid: u0,
title: "auth token",
..Default::default()
});
let other = memory(&Fixture {
uid: u1,
title: "unrelated",
..Default::default()
});
let mems = vec![matchy, other];
let ranked = query(
&mems,
&with_query("auth"),
&snap(None, None),
false,
Path::new("."),
&crate::lexical::OverlapRanker,
);
assert_eq!(ranked.len(), 2, "bare --query keeps all active");
assert_eq!(ranked[0].memory.uid, u0, "lexical hit ranks first");
}
#[test]
fn query_base_filter_excludes_non_active() {
let retracted = memory(&Fixture {
status: "retracted",
paths: &["src/main.rs"],
..Default::default()
});
let mems = vec![retracted];
let ranked = query(
&mems,
&q(&["src/main.rs"]),
&snap(None, None),
false,
Path::new("."),
&Bm25Ranker,
);
assert!(ranked.is_empty(), "retracted is dropped by base_filter");
}
#[test]
fn query_exact_key_dominates_higher_bm25() {
let [u0, u1, _] = uids();
let keyhit = memory(&Fixture {
uid: u0,
key: "mem.zzz",
title: "zzz",
summary: "alpha beta gamma delta epsilon zeta eta theta iota kappa",
..Default::default()
});
let lexhit = memory(&Fixture {
uid: u1,
title: "mem zzz",
summary: "",
..Default::default()
});
let mems = vec![keyhit, lexhit];
let ranked = query(
&mems,
&with_query("mem.zzz"),
&snap(None, None),
false,
Path::new("."),
&Bm25Ranker,
);
assert_eq!(ranked.len(), 2);
assert!(ranked[0].exact_key, "exact-key memory ranks first");
assert_eq!(ranked[0].memory.uid, u0);
assert!(
ranked[0].lexical < ranked[1].lexical,
"exact-key wins DESPITE a lower BM25 (Key-1 over Key-2): {} vs {}",
ranked[0].lexical,
ranked[1].lexical
);
}
#[test]
fn query_is_shuffle_invariant_under_bm25() {
let [u0, u1, u2] = uids();
let mk = |uid, title| {
memory(&Fixture {
uid,
title,
..Default::default()
})
};
let a = mk(u0, "rare token");
let b = mk(u1, "common token");
let c = mk(u2, "common other");
let forward = vec![a.clone(), b.clone(), c.clone()];
let reversed = vec![c, b, a];
let run = |mems: &[Memory]| -> Vec<(String, u32)> {
query(
mems,
&with_query("rare token"),
&snap(None, None),
false,
Path::new("."),
&Bm25Ranker,
)
.iter()
.map(|c| (c.memory.uid.clone(), c.lexical))
.collect()
};
assert_eq!(
run(&forward),
run(&reversed),
"permuted store ⇒ identical ranked (uid, lexical)"
);
}
#[test]
fn query_bm25_and_overlap_order_oppositely() {
let uids5 = [
"mem_018f3a1b2c3d4e5f60718293a4b5c601",
"mem_018f3a1b2c3d4e5f60718293a4b5c602",
"mem_018f3a1b2c3d4e5f60718293a4b5c6f3",
"mem_018f3a1b2c3d4e5f60718293a4b5c6f4",
"mem_018f3a1b2c3d4e5f60718293a4b5c6f5",
];
let mk = |uid, title| {
memory(&Fixture {
uid,
title,
..Default::default()
})
};
let a = mk(uids5[0], "common ubiq"); let b = mk(uids5[1], "rare"); let f1 = mk(uids5[2], "common ubiq");
let f2 = mk(uids5[3], "common ubiq");
let f3 = mk(uids5[4], "common ubiq");
let mems = vec![a, b, f1, f2, f3];
let qctx = with_query("common ubiq rare");
let order = |ranker: &dyn LexicalRanker| -> Vec<String> {
query(
&mems,
&qctx,
&snap(None, None),
false,
Path::new("."),
ranker,
)
.iter()
.map(|c| c.memory.uid.clone())
.collect()
};
let bm25 = order(&Bm25Ranker);
let overlap = order(&crate::lexical::OverlapRanker);
assert_eq!(
bm25[0], uids5[1],
"BM25 lifts the rare-term match: {bm25:?}"
);
assert_eq!(
overlap[0], uids5[0],
"overlap ranks the higher raw-count first: {overlap:?}"
);
assert_ne!(
bm25[0], overlap[0],
"the two rankers disagree — the intended quality change"
);
}
#[test]
fn query_bm25_score_is_derived_not_persisted_on_memory() {
let m = memory(&Fixture {
title: "rare token",
..Default::default()
});
let before = format!("{m:?}");
assert!(
!before.contains("lexical"),
"Memory carries no lexical field"
);
let mems = vec![m];
let ranked = query(
&mems,
&with_query("rare token"),
&snap(None, None),
false,
Path::new("."),
&Bm25Ranker,
);
assert!(ranked[0].lexical > 0, "BM25 scored the survivor nonzero");
assert_eq!(
format!("{:?}", mems[0]),
before,
"Memory representation unchanged by scoring (immutable borrow)"
);
}
#[test]
fn format_find_row_carries_full_uid_and_required_columns() {
let m = memory(&Fixture {
paths: &["src/main.rs"],
trust_level: "low",
severity: "high",
title: "be careful",
..Default::default()
});
let mut c = cand(&m, 0, false, Some(Dimension::Paths));
c.staleness = Staleness::Unknown;
let out = format_find_table(&[&c]);
assert!(out.contains(UID), "full uid printed");
assert!(out.contains("paths"), "spec column = matched dim");
assert!(out.contains("low"), "trust visible");
assert!(out.contains("high"), "severity visible");
assert!(out.contains("unknown"), "staleness column");
assert!(out.contains("be careful"), "title");
assert!(out.ends_with('\n'));
}
#[test]
fn format_find_scrubs_a_newline_title() {
let m = memory(&Fixture {
title: "real",
..Default::default()
});
let mut m2 = m.clone();
m2.title = "row1\nforged-row2".to_owned();
let c = cand(&m2, 0, false, None);
let out = format_find_table(&[&c]);
assert!(
!out.contains("\nforged-row2"),
"newline must not forge a row"
);
assert!(out.contains("\\nforged-row2"), "newline rendered as escape");
}
#[test]
fn format_find_empty_is_empty_string() {
let empty: [&Candidate<'_>; 0] = [];
assert_eq!(format_find_table(&empty), "");
}
#[test]
fn parse_min_trust_accepts_only_the_three_tiers() {
for ok in ["high", "medium", "low"] {
assert_eq!(parse_min_trust(ok).as_deref(), Ok(ok));
}
assert!(parse_min_trust("banana").is_err());
assert!(parse_min_trust("").is_err());
assert!(parse_min_trust("High").is_err()); }
#[test]
fn holdback_floor_defaults_medium_and_only_raises() {
let medium = trust_rank("medium");
assert_eq!(holdback_floor(None), medium, "default floor is medium");
assert_eq!(
holdback_floor(Some("high")),
trust_rank("high"),
"high raises"
);
assert_eq!(
holdback_floor(Some("medium")),
medium,
"medium is the default"
);
assert_eq!(holdback_floor(Some("low")), medium, "low is clamped up");
}
fn risky(uid: &'static str, trust: &'static str, severity: &'static str) -> Memory {
memory(&Fixture {
uid,
trust_level: trust,
severity,
..Default::default()
})
}
#[test]
fn held_back_is_low_trust_and_high_severity_at_default_floor() {
let floor = holdback_floor(None);
assert!(held_back(&risky(UID, "low", "critical"), floor));
assert!(held_back(&risky(UID, "low", "high"), floor));
assert!(!held_back(&risky(UID, "low", "medium"), floor));
assert!(!held_back(&risky(UID, "low", "none"), floor));
assert!(!held_back(&risky(UID, "medium", "high"), floor));
assert!(!held_back(&risky(UID, "high", "critical"), floor));
}
#[test]
fn min_trust_high_raises_the_floor_over_medium() {
let floor = holdback_floor(Some("high"));
assert!(
held_back(&risky(UID, "medium", "high"), floor),
"medium now held"
);
assert!(
held_back(&risky(UID, "low", "high"), floor),
"low still held"
);
assert!(
!held_back(&risky(UID, "high", "critical"), floor),
"high passes"
);
assert!(!held_back(&risky(UID, "low", "none"), floor));
}
fn ranked_cand(m: &Memory) -> Candidate<'_> {
cand(m, 1, false, None)
}
#[test]
fn holdback_suppresses_low_trust_critical_pre_offset_limit() {
let [u0, u1, _] = uids();
let clean = risky(u0, "medium", "high"); let held = risky(u1, "low", "critical"); let ranked = vec![ranked_cand(&clean), ranked_cand(&held)];
let floor = holdback_floor(None);
let eligible: Vec<&Candidate<'_>> = ranked
.iter()
.filter(|c| !held_back(c.memory, floor))
.collect();
let uids: Vec<&str> = eligible.iter().map(|c| c.memory.uid.as_str()).collect();
assert_eq!(
uids,
vec![u0],
"held-back memory absent from the eligible set"
);
}
#[test]
fn holdback_does_not_consume_a_limit_slot() {
let [u0, u1, _] = uids();
let held = risky(u0, "low", "high");
let clean = risky(u1, "high", "high");
let ranked = vec![ranked_cand(&held), ranked_cand(&clean)];
let floor = holdback_floor(None);
let eligible: Vec<&Candidate<'_>> = ranked
.iter()
.filter(|c| !held_back(c.memory, floor))
.collect();
let visible: Vec<&Candidate<'_>> = eligible.iter().take(1).copied().collect();
assert_eq!(visible.len(), 1);
assert_eq!(
visible[0].memory.uid, u1,
"clean memory takes the slot, not held"
);
}
#[test]
fn holdback_then_limit_caps_at_limit() {
let [u0, u1, u2] = uids();
let ms = [
risky(u0, "high", "none"),
risky(u1, "high", "none"),
risky(u2, "high", "none"),
];
let ranked: Vec<Candidate<'_>> = ms.iter().map(ranked_cand).collect();
let floor = holdback_floor(None);
let eligible: Vec<&Candidate<'_>> = ranked
.iter()
.filter(|c| !held_back(c.memory, floor))
.collect();
let v2: Vec<&Candidate<'_>> = eligible.iter().take(2).copied().collect();
assert_eq!(v2.len(), 2);
let v10: Vec<&Candidate<'_>> = eligible.iter().take(10).copied().collect();
assert_eq!(v10.len(), 3);
}
}