use crate::memory::Note;
use std::collections::BTreeMap;
pub struct Index {
pub total: usize,
pub groups: Vec<(String, usize)>,
}
const NAMED_GROUPS: usize = 4;
impl Index {
pub fn of(notes: &[Note]) -> Index {
let mut counts: BTreeMap<&str, usize> = BTreeMap::new();
for note in notes {
let group = note
.key
.split_once('/')
.map(|(head, _)| head)
.unwrap_or("other");
*counts.entry(group).or_insert(0) += 1;
}
let mut ranked: Vec<(String, usize)> = counts
.into_iter()
.map(|(name, n)| (name.to_string(), n))
.collect();
ranked.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0)));
let mut groups: Vec<(String, usize)> = Vec::new();
let mut rest = 0;
for (name, n) in ranked {
if groups.len() < NAMED_GROUPS && name != "other" {
groups.push((name, n));
} else {
rest += n;
}
}
if rest > 0 {
groups.push(("other".to_string(), rest));
}
Index {
total: notes.len(),
groups,
}
}
}
pub fn describe(index: &Index) -> String {
if index.total == 0 {
return "Why this repo is the way it is: what was tried, what failed, what \
surprised somebody. Not what the code *is* — the code graph answers \
that, and is never out of date. The store is empty so far; it fills \
as work turns up things that were not obvious. Ask anyway — an empty \
answer is itself a fact about what has been learned here."
.to_string();
}
let breakdown: Vec<String> = index
.groups
.iter()
.map(|(name, n)| format!("{n} {name}"))
.collect();
format!(
"Why this repo is the way it is: what was tried, what failed, what surprised \
somebody. Not what the code *is* — the code graph answers that. {} note{}: \
{}. Most exist because an assumption turned out wrong, so query before \
assuming how something here behaves.",
index.total,
if index.total == 1 { "" } else { "s" },
breakdown.join(", "),
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::{Kind, Layer};
use std::path::PathBuf;
fn note(layer: Layer, key: &str) -> Note {
Note {
key: key.to_string(),
kind: Kind::Surprise,
source: "session s01, claude".into(),
recorded: "2026-08-07".into(),
invalidated_by: None,
body: "# t\n\n## Expected\na\n\n## Observed\nb\n\n## Evidence\nc\n".into(),
layer,
path: PathBuf::from(format!("{key}.md")),
}
}
fn store(n: usize, namespaces: usize) -> Vec<Note> {
(0..n)
.map(|i| {
let layer = if i % 2 == 0 {
Layer::Team
} else {
Layer::Local
};
note(layer, &format!("ns{}/note-{i}", i % namespaces.max(1)))
})
.collect()
}
#[test]
fn the_description_does_not_grow_with_the_store() {
let shape = |s: &str| {
s.chars()
.filter(|c| !c.is_ascii_digit())
.collect::<String>()
};
let big = describe(&Index::of(&store(400, 40)));
let bigger = describe(&Index::of(&store(4_000, 400)));
assert_eq!(
shape(&big),
shape(&bigger),
"ten times the store, same sentence:\n{big}\n{bigger}"
);
for (notes, namespaces) in [(3, 2), (50, 7), (4_000, 400)] {
let index = Index::of(&store(notes, namespaces));
assert!(
index.groups.len() <= NAMED_GROUPS + 1,
"{notes} notes over {namespaces} namespaces listed {} groups",
index.groups.len()
);
}
}
#[test]
fn the_groups_account_for_every_note() {
for (notes, namespaces) in [(3, 2), (50, 7), (400, 40)] {
let index = Index::of(&store(notes, namespaces));
let counted: usize = index.groups.iter().map(|(_, n)| n).sum();
assert_eq!(counted, index.total, "the breakdown must add up");
}
}
#[test]
fn the_description_counts_both_layers() {
let notes = vec![
note(Layer::Team, "surprise/a"),
note(Layer::Local, "surprise/b"),
];
assert_eq!(Index::of(¬es).total, 2);
assert!(describe(&Index::of(¬es)).contains('2'));
}
#[test]
fn keys_with_no_namespace_collapse_rather_than_each_becoming_a_group() {
let notes: Vec<Note> = (0..30)
.map(|i| note(Layer::Local, &format!("bare-key-{i}")))
.collect();
let index = Index::of(¬es);
assert_eq!(index.groups.len(), 1);
assert_eq!(index.groups[0], ("other".to_string(), 30));
}
#[test]
fn an_empty_store_describes_itself_as_empty_rather_than_saying_nothing() {
let text = describe(&Index::of(&[]));
assert!(!text.trim().is_empty());
assert!(text.to_lowercase().contains("empty"), "{text}");
}
#[test]
fn the_description_is_stable_for_an_unchanged_store() {
let notes = store(20, 6);
let mut shuffled = notes.clone();
shuffled.reverse();
assert_eq!(
describe(&Index::of(¬es)),
describe(&Index::of(&shuffled))
);
}
#[test]
fn the_description_says_why_the_notes_exist_not_only_how_many() {
let text = describe(&Index::of(&store(5, 2))).to_lowercase();
assert!(text.contains("wrong") || text.contains("assum"), "{text}");
}
}