From 7842b6fc8bbcce44811b057c76c459848ab0f894 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 12 Apr 2026 22:12:02 -0400 Subject: [PATCH] remove legacy feedback commands (used, wrong, gap, etc.) These were early experiments with manual feedback signals that never worked well. The scoring system will handle this properly. Removed: - CLI: used, wrong, not-relevant, not-useful, gap - MCP: memory_used - Store: mark_used, mark_wrong, record_gap, modify_node Co-Authored-By: Proof of Concept --- src/agent/tools/memory.rs | 18 +--- src/cli/node.rs | 102 ++---------------- src/hippocampus/store/ops.rs | 42 +------- src/main.rs | 34 ------ src/subconscious/agents/journal.agent | 2 +- .../agents/subconscious-journal.agent | 2 +- 6 files changed, 10 insertions(+), 190 deletions(-) diff --git a/src/agent/tools/memory.rs b/src/agent/tools/memory.rs index 58eaa20..72c230e 100644 --- a/src/agent/tools/memory.rs +++ b/src/agent/tools/memory.rs @@ -115,7 +115,6 @@ async fn dispatch( "memory_links" => links(&args).await, "memory_link_set" => link_set(&args).await, "memory_link_add" => link_add(agent, &args).await, - "memory_used" => used(&args).await, "memory_weight_set" => weight_set(&args).await, "memory_rename" => rename(&args).await, "memory_supersede" => supersede(agent, &args).await, @@ -131,7 +130,7 @@ async fn dispatch( // ── Definitions ──────────────────────────────────────────────── -pub fn memory_tools() -> [super::Tool; 13] { +pub fn memory_tools() -> [super::Tool; 12] { use super::Tool; [ Tool { name: "memory_render", description: "Read a memory node's content and links.", @@ -152,9 +151,6 @@ pub fn memory_tools() -> [super::Tool; 13] { Tool { name: "memory_link_add", description: "Add a new link between two nodes.", parameters_json: r#"{"type":"object","properties":{"source":{"type":"string"},"target":{"type":"string"}},"required":["source","target"]}"#, handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_link_add", &a, v).await })) }, - Tool { name: "memory_used", description: "Mark a node as useful (boosts weight).", - parameters_json: r#"{"type":"object","properties":{"key":{"type":"string","description":"Node key"}},"required":["key"]}"#, - handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_used", &a, v).await })) }, Tool { name: "memory_weight_set", description: "Set a node's weight directly (0.01 to 1.0).", parameters_json: r#"{"type":"object","properties":{"key":{"type":"string"},"weight":{"type":"number","description":"0.01 to 1.0"}},"required":["key","weight"]}"#, handler: Arc::new(|a, v| Box::pin(async move { dispatch("memory_weight_set", &a, v).await })) }, @@ -315,18 +311,6 @@ async fn link_add(agent: &Option>, args: &se Ok(format!("linked {} → {} (strength={:.2})", s, t, strength)) } -async fn used(args: &serde_json::Value) -> Result { - let key = get_str(args, "key")?; - let arc = cached_store().await?; - let mut store = arc.lock().await; - if !store.nodes.contains_key(key) { - anyhow::bail!("node not found: {}", key); - } - store.mark_used(key); - store.save().map_err(|e| anyhow::anyhow!("{}", e))?; - Ok(format!("marked {} as used", key)) -} - async fn weight_set(args: &serde_json::Value) -> Result { let arc = cached_store().await?; let mut store = arc.lock().await; diff --git a/src/cli/node.rs b/src/cli/node.rs index 7219d88..fdf90e7 100644 --- a/src/cli/node.rs +++ b/src/cli/node.rs @@ -1,88 +1,10 @@ // cli/node.rs — node subcommand handlers // -// render, write, used, wrong, not-relevant, not-useful, gap, -// node-delete, node-rename, history, list-keys, list-edges, -// dump-json, lookup-bump, lookups. +// render, write, node-delete, node-rename, history, list-keys, +// list-edges, dump-json, lookup-bump, lookups. use crate::store; -pub fn cmd_used(key: &[String]) -> Result<(), String> { - if key.is_empty() { - return Err("used requires a key".into()); - } - super::check_dry_run(); - let key = key.join(" "); - let mut store = store::Store::load()?; - let resolved = store.resolve_key(&key)?; - store.mark_used(&resolved); - - // Also strengthen edges to this node — conscious-tier delta. - const DELTA: f32 = 0.01; - let mut strengthened = 0; - for rel in &mut store.relations { - if rel.deleted { continue; } - if rel.source_key == resolved || rel.target_key == resolved { - let old = rel.strength; - rel.strength = (rel.strength + DELTA).clamp(0.05, 0.95); - if (rel.strength - old).abs() > 0.001 { - rel.version += 1; - strengthened += 1; - } - } - } - - store.save()?; - println!("Marked '{}' as used (strengthened {} edges)", resolved, strengthened); - Ok(()) -} - -pub fn cmd_wrong(key: &str, context: &[String]) -> Result<(), String> { - let ctx = if context.is_empty() { None } else { Some(context.join(" ")) }; - super::check_dry_run(); - let mut store = store::Store::load()?; - let resolved = store.resolve_key(key)?; - store.mark_wrong(&resolved, ctx.as_deref()); - store.save()?; - println!("Marked '{}' as wrong", resolved); - Ok(()) -} - -pub fn cmd_not_relevant(key: &str) -> Result<(), String> { - let mut store = store::Store::load()?; - let resolved = store.resolve_key(key)?; - - // Weaken all edges to this node — it was routed to incorrectly. - // Conscious-tier delta: 0.01 per edge. - const DELTA: f32 = -0.01; - let mut adjusted = 0; - for rel in &mut store.relations { - if rel.deleted { continue; } - if rel.source_key == resolved || rel.target_key == resolved { - let old = rel.strength; - rel.strength = (rel.strength + DELTA).clamp(0.05, 0.95); - if (rel.strength - old).abs() > 0.001 { - rel.version += 1; - adjusted += 1; - } - } - } - store.save()?; - println!("Not relevant: '{}' — weakened {} edges by {}", resolved, adjusted, DELTA.abs()); - Ok(()) -} - -pub fn cmd_not_useful(key: &str) -> Result<(), String> { - // no args to validate - super::check_dry_run(); - let mut store = store::Store::load()?; - let resolved = store.resolve_key(key)?; - // Same as wrong but with clearer semantics: node content is bad, edges are fine. - store.mark_wrong(&resolved, Some("not-useful")); - store.save()?; - println!("Not useful: '{}' — node weight reduced", resolved); - Ok(()) -} - pub fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> { super::check_dry_run(); let result = crate::mcp_server::memory_rpc( @@ -93,19 +15,6 @@ pub fn cmd_weight_set(key: &str, weight: f32) -> Result<(), String> { Ok(()) } -pub fn cmd_gap(description: &[String]) -> Result<(), String> { - if description.is_empty() { - return Err("gap requires a description".into()); - } - super::check_dry_run(); - let desc = description.join(" "); - let mut store = store::Store::load()?; - store.record_gap(&desc); - store.save()?; - println!("Recorded gap: {}", desc); - Ok(()) -} - pub fn cmd_list_keys(pattern: Option<&str>) -> Result<(), String> { let store = store::Store::load()?; let g = store.build_graph(); @@ -192,11 +101,12 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> { return Err("render requires a key".into()); } let key = key.join(" "); - let store = store::Store::load()?; let bare = store::strip_md_suffix(&key); - let rendered = render_node(&store, &bare) - .ok_or_else(|| format!("Node not found: {}", bare))?; + let rendered = crate::mcp_server::memory_rpc( + "memory_render", + serde_json::json!({"key": bare}), + ).map_err(|e| e.to_string())?; print!("{}", rendered); // Mark as seen if we're inside a Claude session (not an agent subprocess — diff --git a/src/hippocampus/store/ops.rs b/src/hippocampus/store/ops.rs index 11795d4..0b68dff 100644 --- a/src/hippocampus/store/ops.rs +++ b/src/hippocampus/store/ops.rs @@ -1,7 +1,6 @@ // Mutation operations on the store // -// CRUD (upsert, delete, modify), feedback tracking (mark_used, mark_wrong), -// maintenance (decay, fix_categories, cap_degree), and graph metrics. +// CRUD (upsert, delete), maintenance (decay, cap_degree), and graph metrics. use super::types::*; @@ -179,45 +178,6 @@ impl Store { Ok(()) } - /// Modify a node in-place, bump version, and persist to capnp log. - fn modify_node(&mut self, key: &str, f: impl FnOnce(&mut Node)) -> Result<(), String> { - let node = self.nodes.get_mut(key) - .ok_or_else(|| format!("No node '{}'", key))?; - f(node); - node.version += 1; - let node = node.clone(); - self.append_nodes(&[node]) - } - - pub fn mark_used(&mut self, key: &str) { - let boost = self.params.use_boost as f32; - let _ = self.modify_node(key, |n| { - n.uses += 1; - n.weight = (n.weight + boost).min(1.0); - if n.spaced_repetition_interval < 30 { - n.spaced_repetition_interval = match n.spaced_repetition_interval { - 1 => 3, 3 => 7, 7 => 14, 14 => 30, _ => 30, - }; - } - n.last_replayed = now_epoch(); - }); - } - - pub fn mark_wrong(&mut self, key: &str, _ctx: Option<&str>) { - let _ = self.modify_node(key, |n| { - n.wrongs += 1; - n.weight = (n.weight - 0.1).max(0.0); - n.spaced_repetition_interval = 1; - }); - } - - pub fn record_gap(&mut self, desc: &str) { - self.gaps.push(GapRecord { - description: desc.to_string(), - timestamp: today(), - }); - } - /// Cap node degree by soft-deleting edges from mega-hubs. pub fn cap_degree(&mut self, max_degree: usize) -> Result<(usize, usize), String> { let mut node_degree: HashMap = HashMap::new(); diff --git a/src/main.rs b/src/main.rs index 6967548..719e7ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -149,30 +149,6 @@ EXAMPLES: /// Query expression (e.g. "key ~ 'inner-life'") expr: Vec, }, - /// Mark a memory as useful (boosts weight) - Used { - /// Node key - key: Vec, - }, - /// Mark a memory as wrong/irrelevant - Wrong { - /// Node key - key: String, - /// Optional context - context: Vec, - }, - /// Mark a search result as not relevant (weakens edges that led to it) - #[command(name = "not-relevant")] - NotRelevant { - /// Node key that was not relevant - key: String, - }, - /// Mark a node as not useful (weakens node weight, not edges) - #[command(name = "not-useful")] - NotUseful { - /// Node key - key: String, - }, /// Set a node's weight directly #[command(name = "weight-set")] WeightSet { @@ -181,11 +157,6 @@ EXAMPLES: /// Weight (0.01 to 1.0) weight: f32, }, - /// Record a gap in memory coverage - Gap { - /// Gap description - description: Vec, - }, // ── Node operations ─────────────────────────────────────────────── @@ -523,12 +494,7 @@ impl Run for Command { => cli::journal::cmd_tail(n, full, provenance.as_deref(), !all_versions), Self::Status => cli::misc::cmd_status(), Self::Query { expr } => cli::misc::cmd_query(&expr), - Self::Used { key } => cli::node::cmd_used(&key), - Self::Wrong { key, context } => cli::node::cmd_wrong(&key, &context), - Self::NotRelevant { key } => cli::node::cmd_not_relevant(&key), - Self::NotUseful { key } => cli::node::cmd_not_useful(&key), Self::WeightSet { key, weight } => cli::node::cmd_weight_set(&key, weight), - Self::Gap { description } => cli::node::cmd_gap(&description), Self::Node(sub) => sub.run(), Self::Journal(sub) => sub.run(), Self::GraphCmd(sub) => sub.run(), diff --git a/src/subconscious/agents/journal.agent b/src/subconscious/agents/journal.agent index 8bbdf66..975b970 100644 --- a/src/subconscious/agents/journal.agent +++ b/src/subconscious/agents/journal.agent @@ -17,7 +17,7 @@ You are {assistant_name}'s episodic memory. Your job is to witness. {{latest_journal}} **Your tools:** journal_tail, journal_new, journal_update, memory_link_add, -memory_search, memory_render, memory_used. Do NOT use memory_write — creating +memory_search, memory_render. Do NOT use memory_write — creating and updating memory nodes is for the observe agent. Your job is journaling and linking entries to relevant existing nodes. diff --git a/src/subconscious/agents/subconscious-journal.agent b/src/subconscious/agents/subconscious-journal.agent index 5cb28d9..78c16bd 100644 --- a/src/subconscious/agents/subconscious-journal.agent +++ b/src/subconscious/agents/subconscious-journal.agent @@ -9,7 +9,7 @@ Nodes your subconscious recently touched (for linking, not duplicating): {{state:walked}} **Your tools:** journal_tail, journal_new, journal_update, memory_link_add, -memory_search, memory_render, memory_used. Do NOT use memory_write — creating +memory_search, memory_render. Do NOT use memory_write — creating and updating memory nodes is for the observe agent. Your job is journaling and linking entries to relevant existing nodes.