use crate::memory::{Layer, Note};
#[cfg(test)]
use anyhow::{bail, Result};
use std::collections::{BTreeMap, BTreeSet};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Cite {
pub key: String,
pub layer: Layer,
pub recorded: String,
}
#[derive(Debug, Clone)]
pub struct Hit {
pub cite: Cite,
pub depth: u8,
}
#[derive(Debug)]
pub struct Neighbourhood {
pub question: String,
pub hits: Vec<Hit>,
pub omitted: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct Budget {
pub roots: usize,
}
impl Default for Budget {
fn default() -> Self {
Self { roots: 8 }
}
}
fn stem(word: &str) -> String {
let w: Vec<char> = word.chars().collect();
let is_vowel = |c: &[char], i: usize| match c[i] {
'a' | 'e' | 'i' | 'o' | 'u' => true,
'y' => i > 0 && !matches!(c[i - 1], 'a' | 'e' | 'i' | 'o' | 'u'),
_ => false,
};
let has_vowel = |c: &[char]| (0..c.len()).any(|i| is_vowel(c, i));
let measure = |c: &[char]| {
let mut m = 0;
let mut prev_vowel = false;
for i in 0..c.len() {
let v = is_vowel(c, i);
if prev_vowel && !v {
m += 1;
}
prev_vowel = v;
}
m
};
let ends = |c: &[char], suf: &str| {
let s: Vec<char> = suf.chars().collect();
c.len() > s.len() && c[c.len() - s.len()..] == s[..]
};
let mut w = w;
if ends(&w, "sses") || ends(&w, "ies") {
w.truncate(w.len() - 2);
} else if w.len() > 1 && w[w.len() - 1] == 's' && !ends(&w, "ss") {
w.pop();
}
let mut fix_up = false;
if ends(&w, "eed") {
if measure(&w[..w.len() - 3]) > 0 {
w.pop();
}
} else if ends(&w, "ed") && has_vowel(&w[..w.len() - 2]) {
w.truncate(w.len() - 2);
fix_up = true;
} else if ends(&w, "ing") && has_vowel(&w[..w.len() - 3]) {
w.truncate(w.len() - 3);
fix_up = true;
}
if fix_up {
let cvc = w.len() >= 3
&& !is_vowel(&w, w.len() - 1)
&& is_vowel(&w, w.len() - 2)
&& !is_vowel(&w, w.len() - 3)
&& !matches!(w[w.len() - 1], 'w' | 'x' | 'y');
if ends(&w, "at") || ends(&w, "bl") || ends(&w, "iz") {
w.push('e');
} else if w.len() > 1
&& w[w.len() - 1] == w[w.len() - 2]
&& !matches!(w[w.len() - 1], 'l' | 's' | 'z')
{
w.pop();
} else if measure(&w) == 1 && cvc {
w.push('e');
}
}
if ends(&w, "y") && has_vowel(&w[..w.len() - 1]) {
let n = w.len();
w[n - 1] = 'i';
}
if ends(&w, "e") {
let stem = &w[..w.len() - 1];
let cvc = stem.len() >= 3
&& !is_vowel(stem, stem.len() - 1)
&& is_vowel(stem, stem.len() - 2)
&& !is_vowel(stem, stem.len() - 3)
&& !matches!(stem[stem.len() - 1], 'w' | 'x' | 'y');
let m = measure(stem);
if m > 1 || (m == 1 && !cvc) {
w.pop();
}
}
if w.is_empty() {
return word.to_lowercase();
}
w.into_iter().collect()
}
fn tokens(text: &str) -> BTreeSet<String> {
text.split(|c: char| !c.is_alphanumeric())
.filter(|t| !t.is_empty())
.map(|t| stem(&t.to_lowercase()))
.collect()
}
struct Indexed<'a> {
note: &'a Note,
asked: BTreeSet<String>,
all: BTreeSet<String>,
}
fn index(notes: &[Note]) -> Vec<Indexed<'_>> {
notes
.iter()
.map(|note| {
let asked = tokens(¬e.key);
let mut all = tokens(¬e.body);
all.extend(asked.iter().cloned());
Indexed { note, asked, all }
})
.collect()
}
fn document_frequency<'a>(indexed: &'a [Indexed<'_>]) -> BTreeMap<&'a str, usize> {
let mut df: BTreeMap<&str, usize> = BTreeMap::new();
for doc in indexed {
for term in &doc.all {
*df.entry(term.as_str()).or_insert(0) += 1;
}
}
df
}
fn score(doc: &Indexed<'_>, terms: &BTreeSet<String>, df: &BTreeMap<&str, usize>, n: usize) -> u64 {
let mut total = 0.0_f64;
for term in terms {
if !doc.all.contains(term) {
continue;
}
let Some(seen) = df.get(term.as_str()) else {
continue;
};
let rarity = ((n as f64 + 1.0) / *seen as f64).ln();
let where_it_matched = if doc.asked.contains(term) { 2.0 } else { 1.0 };
total += rarity * where_it_matched;
}
(total * 1000.0) as u64
}
fn inbound_counts(notes: &[Note]) -> BTreeMap<&str, usize> {
let mut counts: BTreeMap<&str, usize> = BTreeMap::new();
for note in notes {
for target in crate::memory::links(¬e.body) {
if let Some(existing) = notes.iter().find(|n| n.key == target) {
*counts.entry(existing.key.as_str()).or_insert(0) += 1;
}
}
}
counts
}
fn cite(note: &Note) -> Cite {
Cite {
key: note.key.clone(),
layer: note.layer,
recorded: note.recorded.clone(),
}
}
#[cfg(test)]
pub fn search(notes: &[Note], question: &str, budget: Budget) -> Neighbourhood {
search_phrased(notes, &[question.to_string()], budget)
}
pub fn search_phrased(notes: &[Note], phrasings: &[String], budget: Budget) -> Neighbourhood {
let asked: Vec<BTreeSet<String>> = phrasings.iter().map(|p| tokens(p)).collect();
let indexed = index(notes);
let df = document_frequency(&indexed);
let mut scored: Vec<(u64, &Note)> = indexed
.iter()
.map(|doc| {
let best = asked
.iter()
.map(|terms| score(doc, terms, &df, notes.len()))
.max()
.unwrap_or(0);
(best, doc.note)
})
.filter(|(s, _)| *s > 0)
.collect();
let inbound = inbound_counts(notes);
scored.sort_by(|(a_score, a), (b_score, b)| {
b_score
.cmp(a_score)
.then(b.recorded.cmp(&a.recorded))
.then(
inbound
.get(b.key.as_str())
.unwrap_or(&0)
.cmp(inbound.get(a.key.as_str()).unwrap_or(&0)),
)
.then(a.key.cmp(&b.key))
.then(a.layer.cmp(&b.layer))
});
let omitted = scored.len().saturating_sub(budget.roots);
let roots: Vec<&Note> = scored
.into_iter()
.take(budget.roots)
.map(|(_, n)| n)
.collect();
let by_identity: BTreeMap<(Layer, &str), &Note> = notes
.iter()
.map(|n| ((n.layer, n.key.as_str()), n))
.collect();
let mut seen: BTreeSet<(Layer, String)> = BTreeSet::new();
let mut hits: Vec<Hit> = Vec::new();
for note in &roots {
if !seen.insert((note.layer, note.key.clone())) {
continue;
}
hits.push(Hit {
cite: cite(note),
depth: 0,
});
}
for note in &roots {
for target in crate::memory::links(¬e.body) {
for layer in Layer::ALL {
let Some(child) = by_identity.get(&(layer, target.as_str())) else {
continue;
};
if !seen.insert((child.layer, child.key.clone())) {
continue;
}
hits.push(Hit {
cite: cite(child),
depth: 1,
});
}
}
}
Neighbourhood {
question: phrasings.join(" / "),
hits,
omitted,
}
}
const SEP: &str = " · ";
pub fn render(n: &Neighbourhood) -> String {
if n.hits.is_empty() {
return format!(
"No notes about `{}`. The store holds nothing on this yet — \
which is a fact about the store, not about the repo.\n",
n.question
);
}
let label_of = |i: usize, hit: &Hit| {
let last = n.hits.get(i + 1).is_none_or(|next| next.depth == 0);
let prefix = match (hit.depth, last) {
(0, _) => "",
(_, false) => "├─ ",
(_, true) => "└─ ",
};
format!("{prefix}{}", hit.cite.key)
};
let width = n
.hits
.iter()
.enumerate()
.map(|(i, h)| label_of(i, h).chars().count())
.max()
.unwrap_or(0)
+ 2;
let mut out = String::new();
for (i, hit) in n.hits.iter().enumerate() {
let label = label_of(i, hit);
out.push_str(&format!(
"{label:width$}{}{SEP}{}\n",
hit.cite.layer, hit.cite.recorded
));
}
if n.omitted > 0 {
out.push_str(&format!(
"\n… {} more match{}; ask a narrower question.\n",
n.omitted,
if n.omitted == 1 { "" } else { "es" }
));
}
out
}
#[cfg(test)]
pub fn parse_rendered(rendered: &str) -> Result<Vec<Cite>> {
let mut out = Vec::new();
for line in rendered.lines() {
let line = line.trim_end();
if line.trim().is_empty() {
continue;
}
if line.trim_start().starts_with('…') {
continue;
}
let stripped = line.trim_start_matches(['├', '└', '─', '│', ' ']);
let Some((key, rest)) = stripped.split_once(" ") else {
bail!("`{line}` carries no provenance at all");
};
let Some((layer, recorded)) = rest.trim().split_once(SEP) else {
bail!("`{line}` is missing its layer or its date");
};
let layer: Layer = layer
.trim()
.parse()
.map_err(|_| anyhow::anyhow!("`{line}` names no layer omh knows"))?;
let recorded = recorded.trim();
if !crate::memory::is_calendar_date(recorded) {
bail!("`{line}` carries `{recorded}`, which is not a date");
}
out.push(Cite {
key: key.trim().to_string(),
layer,
recorded: recorded.to_string(),
});
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::{Kind, Layer, Note};
use std::collections::BTreeSet;
use std::path::PathBuf;
fn note(layer: Layer, key: &str, recorded: &str, links: &[&str]) -> Note {
let mut body = format!(
"# {key}\n\n## Expected\nsomething predictable\n\n## Observed\nsomething else\n\n## Evidence\nthe recorded output\n"
);
if !links.is_empty() {
body.push_str("\n## Related\n\n");
for target in links {
body.push_str(&format!("- [[{target}]]\n"));
}
}
Note {
key: key.to_string(),
kind: Kind::Surprise,
source: "session s01, claude".into(),
recorded: recorded.to_string(),
invalidated_by: None,
body,
layer,
path: PathBuf::from(format!("{key}.md")),
}
}
fn store() -> Vec<Note> {
vec![
note(
Layer::Team,
"credentials-are-a-named-volume",
"2026-06-12",
&[
"credentials-file-mount-returns-ebusy",
"accounts-are-single-path-components",
],
),
note(
Layer::Local,
"credentials-are-a-named-volume",
"2026-07-02",
&[],
),
note(
Layer::Local,
"credentials-file-mount-returns-ebusy",
"2026-08-07",
&[],
),
note(
Layer::Team,
"accounts-are-single-path-components",
"2026-06-14",
&[],
),
note(
Layer::Team,
"credentials-the-image-ends-unprivileged",
"2026-07-20",
&["credentials-file-mount-returns-ebusy"],
),
]
}
#[test]
fn no_note_is_retrieved_without_its_date_and_layer() {
let notes = store();
let found = search(¬es, "credentials", Budget::default());
assert!(!found.hits.is_empty(), "the fixture must match something");
let rendered = render(&found);
let cites = parse_rendered(&rendered).unwrap_or_else(|e| {
panic!("every rendered line must carry provenance: {e}\n{rendered}")
});
assert_eq!(
cites.len(),
rendered.lines().filter(|l| !l.trim().is_empty()).count(),
"a line that did not parse is a line missing its provenance:\n{rendered}"
);
let got: BTreeSet<Cite> = cites.into_iter().collect();
let want: BTreeSet<Cite> = found.hits.iter().map(|h| h.cite.clone()).collect();
assert_eq!(
got, want,
"each note carries its own layer and its own date"
);
}
#[test]
fn a_note_and_its_local_counterpart_both_retrieve() {
let found = search(
&store(),
"credentials-are-a-named-volume",
Budget::default(),
);
let both: Vec<&Hit> = found
.hits
.iter()
.filter(|h| h.cite.key == "credentials-are-a-named-volume")
.collect();
assert_eq!(both.len(), 2, "one key in two layers is two notes");
let layers: BTreeSet<Layer> = both.iter().map(|h| h.cite.layer).collect();
assert_eq!(layers.len(), 2, "and they are not the same layer");
}
#[test]
fn an_expanded_neighbour_carries_its_own_layer_not_its_parents() {
let found = search(&store(), "named volume", Budget::default());
let parent = found
.hits
.iter()
.find(|h| h.depth == 0 && h.cite.layer == Layer::Team)
.expect("a team note must match the question directly");
let child = found
.hits
.iter()
.find(|h| h.cite.key == "credentials-file-mount-returns-ebusy")
.expect("the local neighbour must be expanded");
assert_eq!(child.depth, 1, "it arrives by expansion, not by matching");
assert_eq!(
child.cite.layer,
Layer::Local,
"a local child of a team parent must not read as reviewed"
);
assert_ne!(child.cite.layer, parent.cite.layer, "inheritance, caught");
assert_eq!(
child.cite.recorded, "2026-08-07",
"and it keeps its own date"
);
assert_ne!(child.cite.recorded, parent.cite.recorded);
let sibling = found
.hits
.iter()
.find(|h| h.cite.key == "accounts-are-single-path-components")
.expect("the team neighbour must be expanded too");
assert_eq!(sibling.depth, 1);
assert_eq!(sibling.cite.layer, Layer::Team);
}
#[test]
fn a_note_reachable_from_two_roots_is_listed_once() {
let found = search(&store(), "credentials", Budget::default());
let seen: Vec<&Hit> = found
.hits
.iter()
.filter(|h| h.cite.key == "credentials-file-mount-returns-ebusy")
.collect();
assert_eq!(seen.len(), 1, "reached twice, listed once: {seen:?}");
}
#[test]
fn a_long_key_never_runs_into_its_provenance() {
let notes = vec![
note(
Layer::Team,
"short",
"2026-01-01",
&["a-very-much-longer-key-than-its-parent"],
),
note(
Layer::Local,
"a-very-much-longer-key-than-its-parent",
"2026-02-02",
&[],
),
];
let rendered = render(&search(¬es, "short", Budget::default()));
parse_rendered(&rendered)
.unwrap_or_else(|e| panic!("provenance must stay separable: {e}\n{rendered}"));
for line in rendered.lines().filter(|l| !l.trim().is_empty()) {
assert!(line.contains(" "), "key and layer must not fuse: {line:?}");
}
}
#[test]
fn a_cycle_terminates() {
let notes = vec![
note(Layer::Local, "a", "2026-01-01", &["b"]),
note(Layer::Local, "b", "2026-01-02", &["a"]),
];
let found = search(¬es, "a", Budget::default());
assert_eq!(found.hits.len(), 2);
}
#[test]
fn retrieval_never_picks_a_winner_between_contradicting_notes() {
let found = search(
&store(),
"credentials-are-a-named-volume",
Budget::default(),
);
let both: Vec<&Hit> = found
.hits
.iter()
.filter(|h| h.cite.key == "credentials-are-a-named-volume")
.collect();
assert_eq!(both.len(), 2, "both claims come back; neither is filtered");
}
#[test]
fn order_follows_recency_not_layer() {
let key = "deploy";
let newer_is_local = vec![
note(Layer::Team, key, "2026-01-01", &[]),
note(Layer::Local, key, "2026-09-09", &[]),
];
let newer_is_team = vec![
note(Layer::Team, key, "2026-09-09", &[]),
note(Layer::Local, key, "2026-01-01", &[]),
];
for notes in [newer_is_local, newer_is_team] {
let found = search(¬es, key, Budget::default());
assert_eq!(
found.hits[0].cite.recorded, "2026-09-09",
"the newer claim leads, whichever layer it is in"
);
}
}
#[test]
fn no_result_is_silently_dropped() {
let notes = store();
let found = search(¬es, "credentials", Budget { roots: 1 });
assert!(found.omitted > 0, "the fixture must exceed a budget of one");
assert!(
render(&found).contains(&found.omitted.to_string()),
"say how many were left out:\n{}",
render(&found)
);
}
#[test]
fn an_empty_store_says_so_instead_of_looking_broken() {
let found = search(&[], "anything", Budget::default());
let rendered = render(&found);
assert!(!rendered.trim().is_empty(), "silence is not an answer");
assert!(
parse_rendered(&rendered).is_err() || parse_rendered(&rendered).unwrap().is_empty(),
"and it is prose, not a note nobody wrote"
);
}
#[test]
fn a_question_matching_nothing_says_so_rather_than_returning_the_store() {
let found = search(&store(), "kubernetes", Budget::default());
assert!(found.hits.is_empty(), "no match is not every note");
assert!(!render(&found).trim().is_empty());
}
#[test]
fn a_note_is_found_when_the_question_inflects_a_word_differently() {
let notes = vec![
note(
Layer::Local,
"the-harness-rewrites-in-place",
"2026-01-01",
&[],
),
note(Layer::Local, "an-unrelated-topic", "2026-01-02", &[]),
];
let hits = search(¬es, "rewriting", Budget::default()).hits;
assert_eq!(
hits.first().map(|h| h.cite.key.as_str()),
Some("the-harness-rewrites-in-place"),
"got: {:?}",
hits.iter().map(|h| &h.cite.key).collect::<Vec<_>>()
);
}
#[test]
fn a_word_is_never_stemmed_into_nothing() {
for w in ["s", "is", "as", "ing", "ed", "ss", "ies", "a", "i"] {
assert!(!stem(w).is_empty(), "stem({w:?}) vanished");
}
}
#[test]
fn the_index_and_the_question_go_through_the_same_stemmer() {
let notes = vec![
note(
Layer::Local,
"credentials-refresh-in-place",
"2026-01-01",
&[],
),
note(Layer::Local, "an-unrelated-topic", "2026-01-02", &[]),
];
let hits = search(¬es, "refreshing credential", Budget::default()).hits;
assert_eq!(
hits.first().map(|h| h.cite.key.as_str()),
Some("credentials-refresh-in-place"),
"neither word appears in the note as written: {:?}",
hits.iter().map(|h| &h.cite.key).collect::<Vec<_>>()
);
}
#[test]
fn stemming_does_not_merge_words_that_merely_look_alike() {
for (a, b) in [
("sing", "sin"),
("bring", "brin"),
("mount", "mound"),
("session", "sessile"),
] {
assert_ne!(stem(a), stem(b), "{a:?} and {b:?} collapsed");
}
}
fn asking(layer: Layer, key: &str, recorded: &str, answers: &[&str], prose: &str) -> Note {
let mut body = format!(
"# {key}\n\n## Expected\nsomething\n\n## Observed\n{prose}\n\n## Evidence\nc\n"
);
body.push_str("\n## Answers\n\n");
for a in answers {
body.push_str(&format!("- {a}\n"));
}
Note {
key: key.to_string(),
kind: Kind::Surprise,
source: "session s01, claude".into(),
recorded: recorded.to_string(),
invalidated_by: None,
body,
layer,
path: PathBuf::from(format!("{key}.md")),
}
}
#[test]
fn a_note_is_found_by_the_question_it_says_it_answers() {
let notes = vec![
asking(
Layer::Local,
"one-inode",
"2026-01-01",
&["why does my login not persist"],
"the harness rewrites the file in place",
),
asking(
Layer::Local,
"unrelated",
"2026-01-02",
&["how do I attach an editor"],
"editors connect over ssh",
),
];
let hits = search(¬es, "login does not persist", Budget::default()).hits;
assert_eq!(
hits.first().map(|h| h.cite.key.as_str()),
Some("one-inode"),
"got: {:?}",
hits.iter().map(|h| &h.cite.key).collect::<Vec<_>>()
);
}
#[test]
fn a_declared_question_outweighs_the_same_words_buried_in_prose() {
let notes = vec![
asking(
Layer::Local,
"declared",
"2026-01-01",
&["how does caching behave"],
"unrelated observation about mounting",
),
asking(
Layer::Local,
"buried",
"2026-01-02",
&["something else entirely"],
"a long observation that mentions how caching behaves in passing",
),
];
let hits = search(¬es, "how does caching behave", Budget::default()).hits;
assert_eq!(hits.first().map(|h| h.cite.key.as_str()), Some("declared"));
}
#[test]
fn an_alternative_phrasing_finds_what_the_first_one_missed() {
let notes = vec![
asking(
Layer::Local,
"ebusy",
"2026-01-01",
&["why does mounting a token file fail"],
"one inode, so the write fails",
),
asking(
Layer::Local,
"other",
"2026-01-02",
&["how do sessions end"],
"sessions stop",
),
];
let miss = search_phrased(
¬es,
&["credential persistence".into()],
Budget::default(),
);
assert!(
miss.hits.first().map(|h| h.cite.key.as_str()) != Some("ebusy"),
"the fixture must actually miss on the first phrasing"
);
let found = search_phrased(
¬es,
&[
"credential persistence".into(),
"mounting a token file fails".into(),
],
Budget::default(),
);
assert_eq!(
found.hits.first().map(|h| h.cite.key.as_str()),
Some("ebusy")
);
}
#[test]
fn adding_a_phrasing_never_demotes_what_a_better_one_found() {
let notes = vec![
asking(
Layer::Local,
"target",
"2026-01-01",
&["why does mounting a token file fail"],
"one inode",
),
asking(
Layer::Local,
"noise",
"2026-01-02",
&[
"what is a session",
"how do I start work",
"where does state live",
],
"sessions and state and work and starting",
),
];
let precise = "mounting a token file fail".to_string();
let alone = search_phrased(¬es, std::slice::from_ref(&precise), Budget::default());
assert_eq!(
alone.hits.first().map(|h| h.cite.key.as_str()),
Some("target")
);
let with_noise = search_phrased(
¬es,
&[precise, "session state work".into()],
Budget::default(),
);
assert_eq!(
with_noise.hits.first().map(|h| h.cite.key.as_str()),
Some("target"),
"a vague second phrasing must not outvote a precise first one"
);
}
#[test]
fn one_phrasing_is_the_same_as_asking_once() {
let notes = vec![asking(Layer::Local, "k", "2026-01-01", &["why"], "prose")];
let a = render(&search(¬es, "why", Budget::default()));
let b = render(&search_phrased(¬es, &["why".into()], Budget::default()));
assert_eq!(a, b);
}
#[test]
fn a_question_matches_whole_tokens_not_fragments_of_them() {
let notes = vec![
note(Layer::Local, "named-volume-mounts", "2026-01-01", &[]),
note(Layer::Local, "a-thing", "2026-01-02", &[]),
];
let hits = search(¬es, "a", Budget::default()).hits;
let keys: Vec<&str> = hits.iter().map(|h| h.cite.key.as_str()).collect();
assert!(
keys.contains(&"a-thing"),
"`a` is a whole token there: {keys:?}"
);
assert!(
!keys.contains(&"named-volume-mounts"),
"`a` inside `named` is not a match: {keys:?}"
);
}
#[test]
fn a_word_in_every_note_cannot_decide_the_ranking() {
let mut notes: Vec<Note> = (0..9)
.map(|i| {
note(
Layer::Local,
&format!("the-note-about-things-{i}"),
&format!("2026-02-0{}", i + 1),
&[],
)
})
.collect();
notes.push(note(
Layer::Local,
"ebusy-on-file-mounts",
"2026-01-01",
&[],
));
let hits = search(¬es, "the note ebusy", Budget::default()).hits;
assert_eq!(
hits[0].cite.key,
"ebusy-on-file-mounts",
"one rare term beats two common ones, and beats recency: {:?}",
hits.iter().map(|h| &h.cite.key).collect::<Vec<_>>()
);
}
#[test]
fn a_real_question_puts_the_note_that_answers_it_first() {
let notes = vec![
note(
Layer::Local,
"mounting-a-credential-file-returns-ebusy",
"2026-01-01",
&[],
),
note(
Layer::Local,
"the-graph-cache-is-keyed-by-repo",
"2026-06-01",
&[],
),
note(
Layer::Local,
"a-latest-tag-skips-the-rebuild",
"2026-06-02",
&[],
),
note(
Layer::Local,
"the-image-ends-unprivileged",
"2026-06-03",
&[],
),
];
let hits = search(
¬es,
"why does a credential mount fail",
Budget::default(),
)
.hits;
assert_eq!(hits[0].cite.key, "mounting-a-credential-file-returns-ebusy");
assert!(
!hits
.iter()
.any(|h| h.cite.key == "the-graph-cache-is-keyed-by-repo"),
"a note sharing only filler words is not a match: {:?}",
hits.iter().map(|h| &h.cite.key).collect::<Vec<_>>()
);
}
#[test]
fn a_term_the_store_has_never_seen_matches_nothing() {
let notes = vec![note(Layer::Local, "credentials", "2026-01-01", &[])];
assert!(search(¬es, "kubernetes", Budget::default())
.hits
.is_empty());
let hits = search(¬es, "kubernetes credentials", Budget::default()).hits;
assert_eq!(hits.len(), 1);
}
#[test]
fn a_store_where_every_note_shares_a_term_is_still_searchable() {
let notes = vec![
note(
Layer::Local,
"credentials-are-a-named-volume",
"2026-01-01",
&[],
),
note(
Layer::Local,
"credentials-refresh-in-place",
"2026-01-02",
&[],
),
];
assert_eq!(
search(¬es, "credentials", Budget::default()).hits.len(),
2,
"a universal term still retrieves; it just cannot rank"
);
}
#[test]
fn a_note_the_store_points_at_wins_a_tie() {
let tied = || {
vec![
note(Layer::Local, "alpha-topic", "2026-01-01", &[]),
note(Layer::Local, "beta-topic", "2026-01-01", &[]),
]
};
let position = |notes: &[Note], key: &str| {
search(notes, "topic", Budget::default())
.hits
.iter()
.position(|h| h.cite.key == key)
.unwrap_or_else(|| panic!("`{key}` must be retrieved"))
};
let alone = tied();
assert!(position(&alone, "alpha-topic") < position(&alone, "beta-topic"));
let mut linked = tied();
linked.push(note(
Layer::Local,
"referrer",
"2026-01-01",
&["beta-topic"],
));
assert!(
position(&linked, "beta-topic") < position(&linked, "alpha-topic"),
"the note the store points at leads once the tie is broken"
);
}
#[test]
fn ranking_is_deterministic() {
let notes = store();
let once = render(&search(¬es, "credentials", Budget::default()));
let mut shuffled = notes.clone();
shuffled.reverse();
let twice = render(&search(&shuffled, "credentials", Budget::default()));
assert_eq!(once, twice);
}
#[test]
fn the_rendered_form_parser_rejects_a_line_missing_its_provenance() {
assert!(parse_rendered("just-a-key\n").is_err());
assert!(parse_rendered("a-key team\n").is_err());
assert!(parse_rendered("a-key team · not-a-date\n").is_err());
assert!(parse_rendered("a-key sideways · 2026-08-07\n").is_err());
assert!(parse_rendered("a-key team · 2026-08-07\n").is_ok());
}
}