From fe6450223c3c8348114e80de5cf0cec218830ed2 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 13 Apr 2026 19:34:45 -0400 Subject: [PATCH] migrate local.rs and memory.rs to use index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Store::all_keys() method for iteration - Convert store.nodes.get() → store.get_node() - Convert store.nodes.contains_key() → store.contains_key() - Convert store.nodes.values() iteration → all_keys + get_node Co-Authored-By: Proof of Concept --- src/hippocampus/local.rs | 58 +++++++++++++++++++++--------------- src/hippocampus/memory.rs | 8 +++-- src/hippocampus/store/mod.rs | 7 +++++ 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/hippocampus/local.rs b/src/hippocampus/local.rs index a357cea..b49b61a 100644 --- a/src/hippocampus/local.rs +++ b/src/hippocampus/local.rs @@ -77,7 +77,9 @@ pub fn memory_links(store: &Store, _provenance: &str, key: &str) -> Result) -> Result { let reason = reason.unwrap_or("superseded"); - let content = store.nodes.get(old_key) - .map(|n| n.content.clone()) + let content = store.get_node(old_key) + .map_err(|e| anyhow::anyhow!("{}", e))? + .map(|n| n.content) .ok_or_else(|| anyhow::anyhow!("node not found: {}", old_key))?; let notice = format!("**SUPERSEDED** by `{}` — {}\n\n---\n\n{}", new_key, reason, content.trim()); @@ -198,7 +201,7 @@ pub fn keys_to_replay_items( ) -> Vec { keys.iter() .filter_map(|key| { - let node = store.nodes.get(key)?; + let node = store.get_node(key).ok()??; let priority = consolidation_priority(store, key, graph, None); let cc = graph.clustering_coefficient(key); @@ -265,12 +268,14 @@ pub fn journal_tail(store: &Store, _provenance: &str, count: Option, level: .map(|dt| dt.and_utc().timestamp()) }); - let mut entries: Vec<_> = store.nodes.values() + let all_keys = store.all_keys()?; + let mut entries: Vec<_> = all_keys.iter() + .filter_map(|key| store.get_node(key).ok()?) .filter(|n| n.node_type == node_type) .filter(|n| after_ts.map(|ts| n.created_at >= ts).unwrap_or(true)) .map(|n| JournalEntry { key: n.key.clone(), - content: n.content.clone(), + content: n.content, created_at: n.created_at, }) .collect(); @@ -302,11 +307,11 @@ pub fn journal_new(store: &mut Store, provenance: &str, name: &str, title: &str, .join("-"); let base_key = if base_key.len() > 80 { &base_key[..80] } else { base_key.as_str() }; - let key = if store.nodes.contains_key(base_key) { + let key = if store.contains_key(base_key).unwrap_or(false) { let mut n = 2; loop { let candidate = format!("{}-{}", base_key, n); - if !store.nodes.contains_key(&candidate) { break candidate; } + if !store.contains_key(&candidate).unwrap_or(false) { break candidate; } n += 1; } } else { @@ -324,14 +329,16 @@ pub fn journal_new(store: &mut Store, provenance: &str, name: &str, title: &str, pub fn journal_update(store: &mut Store, provenance: &str, body: &str, level: Option) -> Result { let level = level.unwrap_or(0); let node_type = level_to_node_type(level); - let latest_key = store.nodes.values() + let all_keys = store.all_keys()?; + let latest_key = all_keys.iter() + .filter_map(|key| store.get_node(key).ok()?) .filter(|n| n.node_type == node_type) .max_by_key(|n| n.created_at) .map(|n| n.key.clone()); let Some(key) = latest_key else { anyhow::bail!("no entry at level {} to update — use journal_new first", level); }; - let existing = store.nodes.get(&key).unwrap().content.clone(); + let existing = store.get_node(&key)?.ok_or_else(|| anyhow::anyhow!("node not found"))?.content; let new_content = format!("{}\n\n{}", existing.trim_end(), body); store.upsert_provenance(&key, &new_content, provenance) .map_err(|e| anyhow::anyhow!("{}", e))?; @@ -479,9 +486,10 @@ pub fn graph_hubs(store: &Store, _provenance: &str, count: Option) -> Res let graph = store.build_graph(); // Top hub nodes by degree, spread apart (skip neighbors of already-selected hubs) - let mut hubs: Vec<(String, usize)> = store.nodes.iter() - .filter(|(k, n)| !n.deleted && !k.starts_with('_')) - .map(|(k, _)| { + let all_keys = store.all_keys().unwrap_or_default(); + let mut hubs: Vec<(String, usize)> = all_keys.iter() + .filter(|k| !k.starts_with('_')) + .map(|k| { let degree = graph.neighbors(k).len(); (k.clone(), degree) }) @@ -508,7 +516,7 @@ pub fn graph_trace(store: &Store, _provenance: &str, key: &str) -> Result Result = Vec::new(); + let mut episodic_daily: Vec<(String, f32, crate::store::Node)> = Vec::new(); + let mut episodic_weekly: Vec<(String, f32, crate::store::Node)> = Vec::new(); + let mut semantic: Vec<(String, f32, crate::store::Node)> = Vec::new(); for (n, strength) in &neighbors { - if let Some(nnode) = store.nodes.get(n.as_str()) { - let entry = (n.as_str(), *strength, nnode); - match nnode.node_type { + if let Ok(Some(nnode)) = store.get_node(n) { + let node_type = nnode.node_type; + let key: String = (*n).clone(); + let entry = (key, *strength, nnode); + match node_type { crate::store::NodeType::EpisodicSession => episodic_session.push(entry), crate::store::NodeType::EpisodicDaily => episodic_daily.push(entry), crate::store::NodeType::EpisodicWeekly @@ -547,7 +557,7 @@ pub fn graph_trace(store: &Store, _provenance: &str, key: &str) -> Result Result Result Option { - let node = store.nodes.get(key)?; + let node = store.get_node(key).ok()??; // If set, tag links to nodes created after this timestamp as (new) let older_than: i64 = std::env::var("POC_MEMORIES_OLDER_THAN") @@ -45,7 +45,9 @@ impl MemoryNode { continue; }; - let is_new = older_than > 0 && store.nodes.get(neighbor_key.as_str()) + let is_new = older_than > 0 && store.get_node(neighbor_key) + .ok() + .flatten() .map(|n| n.created_at > older_than) .unwrap_or(false); @@ -61,7 +63,7 @@ impl MemoryNode { Some(MemoryNode { key: key.to_string(), - content: node.content.clone(), + content: node.content, links, version: node.version, weight: node.weight, diff --git a/src/hippocampus/store/mod.rs b/src/hippocampus/store/mod.rs index 8009c21..147f811 100644 --- a/src/hippocampus/store/mod.rs +++ b/src/hippocampus/store/mod.rs @@ -94,6 +94,13 @@ impl Store { index::contains_key(db, key) } + /// Get all node keys. + pub fn all_keys(&self) -> Result> { + let db = self.db.as_ref() + .ok_or_else(|| anyhow::anyhow!("store not loaded"))?; + index::all_keys(db) + } + pub fn resolve_key(&self, target: &str) -> Result { // Strip .md suffix if present — keys no longer use it let bare = strip_md_suffix(target);