From b4bbafdf1c770efcf7c8df92f969b3cb00c0ea4f Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Tue, 3 Mar 2026 18:44:44 -0500 Subject: [PATCH] search: trim default output to 5 results, gate spectral with --expand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default search was 15 results + 5 spectral neighbors — way too much for the recall hook context window. Now: 5 results by default, no spectral. --expand restores the full 15 + spectral output. Co-Authored-By: ProofOfConcept --- src/main.rs | 74 +++++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/main.rs b/src/main.rs index fada520..0425554 100644 --- a/src/main.rs +++ b/src/main.rs @@ -145,7 +145,7 @@ fn usage() { eprintln!("poc-memory v0.4.0 — graph-structured memory store Commands: - search QUERY [QUERY...] Search memory (AND logic across terms) + search QUERY [--expand] Search memory (AND logic across terms) init Scan markdown files, index all memory units migrate Migrate from old weights.json system health Report graph metrics (CC, communities, small-world) @@ -214,9 +214,15 @@ fn cmd_search(args: &[String]) -> Result<(), String> { use store::StoreView; if args.is_empty() { - return Err("Usage: poc-memory search QUERY [QUERY...]".into()); + return Err("Usage: poc-memory search QUERY [QUERY...] [--expand]".into()); } - let query = args.join(" "); + + let expand = args.iter().any(|a| a == "--expand"); + let query: String = args.iter() + .filter(|a| *a != "--expand") + .cloned() + .collect::>() + .join(" "); let view = store::AnyView::load()?; let results = search::search(&query, &view); @@ -226,19 +232,21 @@ fn cmd_search(args: &[String]) -> Result<(), String> { return Ok(()); } + let limit = if expand { 15 } else { 5 }; + // Log retrieval to a small append-only file (avoid 6MB state.bin rewrite) store::Store::log_retrieval_static(&query, &results.iter().map(|r| r.key.clone()).collect::>()); // Bump daily lookup counters (fast path, no store needed) - let bump_keys: Vec<&str> = results.iter().take(15).map(|r| r.key.as_str()).collect(); + let bump_keys: Vec<&str> = results.iter().take(limit).map(|r| r.key.as_str()).collect(); let _ = lookups::bump_many(&bump_keys); // Show text results let text_keys: std::collections::HashSet = results.iter() - .take(15).map(|r| r.key.clone()).collect(); + .take(limit).map(|r| r.key.clone()).collect(); - for (i, r) in results.iter().enumerate().take(15) { + for (i, r) in results.iter().enumerate().take(limit) { let marker = if r.is_direct { "→" } else { " " }; let weight = view.node_weight(&r.key); print!("{}{:2}. [{:.2}/{:.2}] {}", marker, i + 1, r.activation, weight, r.key); @@ -248,36 +256,36 @@ fn cmd_search(args: &[String]) -> Result<(), String> { } } - // Spectral expansion: find neighbors of top text hits - if let Ok(emb) = spectral::load_embedding() { - let seeds: Vec<&str> = results.iter() - .take(5) - .map(|r| r.key.as_str()) - .filter(|k| emb.coords.contains_key(*k)) - .collect(); - - if !seeds.is_empty() { - let spectral_hits = spectral::nearest_to_seeds(&emb, &seeds, 10); - // Filter to nodes not already in text results - let new_hits: Vec<_> = spectral_hits.into_iter() - .filter(|(k, _)| !text_keys.contains(k)) + // Spectral expansion: only with --expand + if expand { + if let Ok(emb) = spectral::load_embedding() { + let seeds: Vec<&str> = results.iter() .take(5) + .map(|r| r.key.as_str()) + .filter(|k| emb.coords.contains_key(*k)) .collect(); - if !new_hits.is_empty() { - println!("\nSpectral neighbors (structural, not keyword):"); - for (k, _dist) in &new_hits { - let weight = view.node_weight(k); - print!(" ~ [{:.2}] {}", weight, k); - println!(); - // Show first line of content as snippet - if let Some(content) = view.node_content(k) { - let snippet: String = content.lines() - .find(|l| !l.trim().is_empty() && !l.starts_with('#')) - .unwrap_or("") - .chars().take(100).collect(); - if !snippet.is_empty() { - println!(" {}", snippet); + if !seeds.is_empty() { + let spectral_hits = spectral::nearest_to_seeds(&emb, &seeds, 10); + let new_hits: Vec<_> = spectral_hits.into_iter() + .filter(|(k, _)| !text_keys.contains(k)) + .take(5) + .collect(); + + if !new_hits.is_empty() { + println!("\nSpectral neighbors (structural, not keyword):"); + for (k, _dist) in &new_hits { + let weight = view.node_weight(k); + print!(" ~ [{:.2}] {}", weight, k); + println!(); + if let Some(content) = view.node_content(k) { + let snippet: String = content.lines() + .find(|l| !l.trim().is_empty() && !l.starts_with('#')) + .unwrap_or("") + .chars().take(100).collect(); + if !snippet.is_empty() { + println!(" {}", snippet); + } } } }