From 96e573f2e5ca3957a2e8ab110f8a438abfe43d54 Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Fri, 10 Apr 2026 15:44:10 -0400 Subject: [PATCH] Delete similarity module, rewrite module, and all text-similarity code Text cosine similarity was being used as a crutch for operations the graph structure should handle: interference detection, orphan linking, triangle closing, hub differentiation. These are all graph-structural operations that the agents (linker, extractor) handle with actual semantic understanding. Removed: similarity.rs (stemming + cosine), rewrite.rs (orphan linking, triangle closing, hub differentiation), detect_interference, and all CLI commands and consolidation steps that used them. -794 lines. Co-Authored-By: Proof of Concept --- src/cli/graph.rs | 119 +---------- src/hippocampus/mod.rs | 1 - src/hippocampus/neuro/mod.rs | 15 +- src/hippocampus/neuro/rewrite.rs | 348 ------------------------------- src/hippocampus/neuro/scoring.rs | 57 +---- src/hippocampus/similarity.rs | 140 ------------- src/lib.rs | 2 +- src/main.rs | 46 ---- src/subconscious/consolidate.rs | 13 +- src/subconscious/defs.rs | 13 -- src/subconscious/digest.rs | 6 - src/subconscious/prompts.rs | 45 +--- 12 files changed, 11 insertions(+), 794 deletions(-) delete mode 100644 src/hippocampus/neuro/rewrite.rs delete mode 100644 src/hippocampus/similarity.rs diff --git a/src/cli/graph.rs b/src/cli/graph.rs index 8712af4..0bc391e 100644 --- a/src/cli/graph.rs +++ b/src/cli/graph.rs @@ -1,11 +1,10 @@ // cli/graph.rs — graph subcommand handlers // // Extracted from main.rs. All graph-related CLI commands: -// link, link-add, link-impact, link-audit, link-orphans, -// triangle-close, cap-degree, normalize-strengths, differentiate, -// trace, spectral-*, organize, interference. +// link, link-add, link-impact, link-audit, cap-degree, +// normalize-strengths, trace, spectral-*, organize, communities. -use crate::{store, graph, neuro}; +use crate::{store, graph}; use crate::store::StoreView; pub fn cmd_graph() -> Result<(), String> { @@ -19,14 +18,6 @@ pub fn cmd_graph() -> Result<(), String> { Ok(()) } -pub fn cmd_link_orphans(min_deg: usize, links_per: usize, sim_thresh: f32) -> Result<(), String> { - let mut store = store::Store::load()?; - let (orphans, links) = neuro::link_orphans(&mut store, min_deg, links_per, sim_thresh); - println!("Linked {} orphans, added {} connections (min_degree={}, links_per={}, sim>{})", - orphans, links, min_deg, links_per, sim_thresh); - Ok(()) -} - pub fn cmd_cap_degree(max_deg: usize) -> Result<(), String> { let mut store = store::Store::load()?; let (hubs, pruned) = store.cap_degree(max_deg)?; @@ -162,16 +153,6 @@ pub fn cmd_link(key: &[String]) -> Result<(), String> { &format!("neighbors('{}') | select strength,clustering_coefficient", resolved)) } -pub fn cmd_triangle_close(min_degree: usize, sim_threshold: f32, max_per_hub: usize) -> Result<(), String> { - println!("Triangle closure: min_degree={}, sim_threshold={}, max_per_hub={}", - min_degree, sim_threshold, max_per_hub); - - let mut store = store::Store::load()?; - let (hubs, added) = neuro::triangle_close(&mut store, min_degree, sim_threshold, max_per_hub); - println!("\nProcessed {} hubs, added {} lateral links", hubs, added); - Ok(()) -} - pub fn cmd_link_add(source: &str, target: &str, reason: &[String]) -> Result<(), String> { super::check_dry_run(); let mut store = store::Store::load()?; @@ -179,11 +160,6 @@ pub fn cmd_link_add(source: &str, target: &str, reason: &[String]) -> Result<(), let target = store.resolve_key(target)?; let reason = reason.join(" "); - // Refine target to best-matching section - let source_content = store.nodes.get(&source) - .map(|n| n.content.as_str()).unwrap_or(""); - let target = neuro::refine_target(&store, source_content, &target); - match store.add_link(&source, &target, "manual") { Ok(strength) => { store.save()?; @@ -226,60 +202,6 @@ pub fn cmd_link_impact(source: &str, target: &str) -> Result<(), String> { Ok(()) } -pub fn cmd_differentiate(key_arg: Option<&str>, do_apply: bool) -> Result<(), String> { - let mut store = store::Store::load()?; - - if let Some(key) = key_arg { - let resolved = store.resolve_key(key)?; - let moves = neuro::differentiate_hub(&store, &resolved) - .ok_or_else(|| format!("'{}' is not a file-level hub with sections", resolved))?; - - // Group by target section for display - let mut by_section: std::collections::BTreeMap> = - std::collections::BTreeMap::new(); - for mv in &moves { - by_section.entry(mv.to_section.clone()).or_default().push(mv); - } - - println!("Hub '{}' — {} links to redistribute across {} sections\n", - resolved, moves.len(), by_section.len()); - - for (section, section_moves) in &by_section { - println!(" {} ({} links):", section, section_moves.len()); - for mv in section_moves.iter().take(5) { - println!(" [{:.3}] {} — {}", mv.similarity, - mv.neighbor_key, mv.neighbor_snippet); - } - if section_moves.len() > 5 { - println!(" ... and {} more", section_moves.len() - 5); - } - } - - if !do_apply { - println!("\nTo apply: poc-memory differentiate {} --apply", resolved); - return Ok(()); - } - - let (applied, skipped) = neuro::apply_differentiation(&mut store, &moves); - store.save()?; - println!("\nApplied: {} Skipped: {}", applied, skipped); - } else { - let hubs = neuro::find_differentiable_hubs(&store); - if hubs.is_empty() { - println!("No file-level hubs with sections found above threshold"); - return Ok(()); - } - - println!("Differentiable hubs (file-level nodes with sections):\n"); - for (key, degree, sections) in &hubs { - println!(" {:40} deg={:3} sections={}", key, degree, sections); - } - println!("\nRun: poc-memory differentiate KEY to preview a specific hub"); - } - - Ok(()) -} - pub fn cmd_link_audit(apply: bool) -> Result<(), String> { let mut store = store::Store::load()?; let stats = crate::audit::link_audit(&mut store, apply)?; @@ -420,24 +342,7 @@ pub fn cmd_organize(term: &str, threshold: f32, key_only: bool, create_anchor: b println!(" {:60} {:>4} lines {:>5} words", key, lines, words); } - // Step 2: pairwise similarity - let pairs = crate::similarity::pairwise_similar(&topic_nodes, threshold); - - if pairs.is_empty() { - println!("\nNo similar pairs above threshold {:.2}", threshold); - } else { - println!("\n=== Similar pairs (cosine > {:.2}) ===\n", threshold); - for (a, b, sim) in &pairs { - let a_words = topic_nodes.iter().find(|(k,_)| k == a) - .map(|(_,c)| c.split_whitespace().count()).unwrap_or(0); - let b_words = topic_nodes.iter().find(|(k,_)| k == b) - .map(|(_,c)| c.split_whitespace().count()).unwrap_or(0); - - println!(" [{:.3}] {} ({} words) ↔ {} ({} words)", sim, a, a_words, b, b_words); - } - } - - // Step 3: check connectivity within cluster + // Step 2: check connectivity within cluster let g = store.build_graph(); println!("=== Connectivity ===\n"); @@ -507,22 +412,6 @@ pub fn cmd_organize(term: &str, threshold: f32, key_only: bool, create_anchor: b Ok(()) } -pub fn cmd_interference(threshold: f32) -> Result<(), String> { - let store = store::Store::load()?; - let g = store.build_graph(); - let pairs = neuro::detect_interference(&store, &g, threshold); - - if pairs.is_empty() { - println!("No interfering pairs above threshold {:.2}", threshold); - } else { - println!("Interfering pairs (similarity > {:.2}, different communities):", threshold); - for (a, b, sim) in &pairs { - println!(" [{:.3}] {} ↔ {}", sim, a, b); - } - } - Ok(()) -} - /// Show communities sorted by isolation (most isolated first). /// Useful for finding poorly-integrated knowledge clusters that need /// organize agents aimed at them. diff --git a/src/hippocampus/mod.rs b/src/hippocampus/mod.rs index bd98675..9e1300a 100644 --- a/src/hippocampus/mod.rs +++ b/src/hippocampus/mod.rs @@ -11,7 +11,6 @@ pub mod graph; pub mod lookups; pub mod cursor; pub mod query; -pub mod similarity; pub mod spectral; pub mod neuro; pub mod counters; diff --git a/src/hippocampus/neuro/mod.rs b/src/hippocampus/neuro/mod.rs index 31e2580..6186a45 100644 --- a/src/hippocampus/neuro/mod.rs +++ b/src/hippocampus/neuro/mod.rs @@ -1,25 +1,14 @@ -// Neuroscience-inspired memory algorithms, split by concern: +// Neuroscience-inspired memory algorithms: // -// scoring — pure analysis: priority, replay queues, interference, plans -// prompts — agent prompt generation and formatting -// rewrite — graph topology mutations: differentiation, closure, linking +// scoring — pure analysis: priority, replay queues, plans mod scoring; -mod rewrite; pub use scoring::{ ReplayItem, ConsolidationPlan, consolidation_priority, replay_queue, replay_queue_with_graph, - detect_interference, consolidation_plan, consolidation_plan_quick, format_plan, daily_check, }; - -pub use rewrite::{ - refine_target, LinkMove, - differentiate_hub, - apply_differentiation, find_differentiable_hubs, - triangle_close, link_orphans, -}; diff --git a/src/hippocampus/neuro/rewrite.rs b/src/hippocampus/neuro/rewrite.rs deleted file mode 100644 index 054c345..0000000 --- a/src/hippocampus/neuro/rewrite.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Graph topology mutations: hub differentiation, triangle closure, -// orphan linking, and link refinement. These modify the store. - -use crate::store::{Store, new_relation}; -use crate::graph::Graph; -use crate::similarity; - -/// Collect (key, content) pairs for all section children of a file-level node. -fn section_children<'a>(store: &'a Store, file_key: &str) -> Vec<(&'a str, &'a str)> { - let prefix = format!("{}#", file_key); - store.nodes.iter() - .filter(|(k, _)| k.starts_with(&prefix)) - .map(|(k, n)| (k.as_str(), n.content.as_str())) - .collect() -} - -/// Find the best matching candidate by cosine similarity against content. -/// Returns (key, similarity) if any candidate exceeds threshold. -fn best_match(candidates: &[(&str, &str)], content: &str, threshold: f32) -> Option<(String, f32)> { - let (best_key, best_sim) = candidates.iter() - .map(|(key, text)| (*key, similarity::cosine_similarity(content, text))) - .max_by(|a, b| a.1.total_cmp(&b.1))?; - if best_sim > threshold { - Some((best_key.to_string(), best_sim)) - } else { - None - } -} - -/// Refine a link target: if the target is a file-level node with section -/// children, find the best-matching section by cosine similarity against -/// the source content. Returns the original key if no sections exist or -/// no section matches above threshold. -/// -/// This prevents hub formation at link creation time — every new link -/// targets the most specific available node. -pub fn refine_target(store: &Store, source_content: &str, target_key: &str) -> String { - // Only refine file-level nodes (no # in key) - if target_key.contains('#') { return target_key.to_string(); } - - let sections = section_children(store, target_key); - - if sections.is_empty() { return target_key.to_string(); } - - best_match(§ions, source_content, 0.05) - .map(|(key, _)| key) - .unwrap_or_else(|| target_key.to_string()) -} - -/// A proposed link move: from hub→neighbor to section→neighbor -pub struct LinkMove { - pub neighbor_key: String, - pub from_hub: String, - pub to_section: String, - pub similarity: f32, - pub neighbor_snippet: String, -} - -/// Analyze a hub node and propose redistributing its links to child sections. -/// -/// Returns None if the node isn't a hub or has no sections to redistribute to. -pub fn differentiate_hub(store: &Store, hub_key: &str) -> Option> { - let graph = store.build_graph(); - differentiate_hub_with_graph(store, hub_key, &graph) -} - -/// Like differentiate_hub but uses a pre-built graph. -fn differentiate_hub_with_graph(store: &Store, hub_key: &str, graph: &Graph) -> Option> { - let degree = graph.degree(hub_key); - - // Only differentiate actual hubs - if degree < 20 { return None; } - - // Only works on file-level nodes that have section children - if hub_key.contains('#') { return None; } - - let sections = section_children(store, hub_key); - if sections.is_empty() { return None; } - - // Get all neighbors of the hub - let neighbors = graph.neighbors(hub_key); - let prefix = format!("{}#", hub_key); - - let mut moves = Vec::new(); - - for (neighbor_key, _strength) in &neighbors { - // Skip section children — they should stay linked to parent - if neighbor_key.starts_with(&prefix) { continue; } - - let neighbor_content = match store.nodes.get(neighbor_key.as_str()) { - Some(n) => &n.content, - None => continue, - }; - - // Find best-matching section by content similarity - if let Some((best_section, best_sim)) = best_match(§ions, neighbor_content, 0.05) { - let snippet = crate::util::first_n_chars( - neighbor_content.lines() - .find(|l| !l.is_empty() && !l.starts_with("