delete rename agent and related code

The organize agents handle renaming as part of their normal work now.
Also simplified resolve_placeholders to build graph internally.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-13 02:05:58 -04:00
parent bd9ce3ed09
commit 7476e9d0db
4 changed files with 5 additions and 191 deletions

View file

@ -401,12 +401,11 @@ pub fn run_one_agent(
// Build prompt batch — either from explicit keys or the agent's query // Build prompt batch — either from explicit keys or the agent's query
let agent_batch = if let Some(keys) = keys { let agent_batch = if let Some(keys) = keys {
dbglog!("[{}] targeting: {}", agent_name, keys.join(", ")); dbglog!("[{}] targeting: {}", agent_name, keys.join(", "));
let graph = store.build_graph();
let mut resolved_steps = Vec::new(); let mut resolved_steps = Vec::new();
let mut all_keys: Vec<String> = keys.to_vec(); let mut all_keys: Vec<String> = keys.to_vec();
for step in &def.steps { for step in &def.steps {
let (prompt, extra_keys) = defs::resolve_placeholders( let (prompt, extra_keys) = defs::resolve_placeholders(
&step.prompt, store, &graph, keys, count, &step.prompt, store, keys, count,
); );
all_keys.extend(extra_keys); all_keys.extend(extra_keys);
resolved_steps.push(prompts::ResolvedStep { resolved_steps.push(prompts::ResolvedStep {

View file

@ -1,79 +0,0 @@
{"agent": "rename", "query": "", "schedule": "daily"}
# Rename Agent — Semantic Key Generation
{{tool: memory_render core-personality}}
{{tool: memory_render memory-instructions-core}}
{{tool: memory_render memory-instructions-core-subconscious}}
{{tool: memory_render subconscious-notes-{agent_name}}}
You are a memory maintenance agent that gives nodes better names.
## What you're doing
Many nodes have auto-generated keys that are opaque or truncated:
- Journal entries: `journal-j-2026-02-28t03-07-i-told-him-about-the-dream`
- Mined transcripts: `_mined-transcripts-f-80a7b321-2caa-451a-bc5c-6565009f94eb.143`
- Extracted facts: `_facts-ec29bdaa-0a58-465f-ad5e-d89e62d9c583`
These names are terrible for search — semantic names dramatically improve
retrieval.
## Core principle: keys are concepts
A good key names the **concept** the node represents. Think of keys as
the vocabulary of the knowledge graph. When you rename, you're defining
what concepts exist. Core keywords should be the terms someone would
search for — `bcachefs-transaction-restart`, `emotional-regulation-gap`,
`polywell-cusp-losses`.
## Naming conventions
### Journal entries: `journal-YYYY-MM-DD-semantic-slug`
- Keep the date prefix (YYYY-MM-DD) for temporal ordering
- Replace the auto-slug with 3-5 descriptive words in kebab-case
- Capture the *essence* of the entry, not just the first line
### Mined transcripts: `_mined-transcripts-YYYY-MM-DD-semantic-slug`
- Extract date from content if available, otherwise use created_at
- Same 3-5 word semantic slug
### Extracted facts: `domain-specific-topic`
- Read the facts JSON — the `domain` and `claim` fields tell you what it's about
- Group by dominant theme, name accordingly
- Examples: `identity-irc-config`, `user-location-background`, `memory-compaction-behavior`
### Skip these — already well-named:
- Keys with semantic names (patterns-, practices-, skills-, etc.)
- Keys shorter than 60 characters
- System keys (_consolidation-*)
## How to rename
Use the `memory_rename` tool:
memory_rename(old_key, new_key)
This renames the node in place — same content, same links, new key.
Do NOT use `memory_write` or `memory_supersede` — just rename.
If a node already has a reasonable name, skip it. When in doubt, skip.
A bad rename is worse than an auto-slug.
## Guidelines
- **Read the content.** The name should reflect what the entry is *about*.
- **Be specific.** `journal#2026-02-14-session` is useless.
- **Use domain terms.** Use the words someone would search for.
- **Don't rename to something longer than the original.**
- **Preserve the date.** Always keep YYYY-MM-DD for journal entries.
- **When in doubt, skip.** A bad rename is worse than an auto-slug.
- **Respect search hits.** Nodes marked "actively found by search" are
being retrieved by their current name. Skip these unless the rename
clearly preserves searchability.
{{rename}}

View file

@ -203,20 +203,9 @@ fn resolve(
store: &Store, store: &Store,
graph: &Graph, graph: &Graph,
keys: &[String], keys: &[String],
count: usize, _count: usize,
) -> Option<Resolved> { ) -> Option<Resolved> {
match name { match name {
"rename" => {
if !keys.is_empty() {
// --target provided: present those keys as candidates
let section = super::prompts::format_rename_targets(store, keys);
Some(Resolved { text: section, keys: vec![] })
} else {
let (rename_keys, section) = super::prompts::format_rename_candidates(store, count);
Some(Resolved { text: section, keys: rename_keys })
}
}
// seed — render output for each seed node (content + deduped links) // seed — render output for each seed node (content + deduped links)
"seed" => { "seed" => {
let mut text = String::new(); let mut text = String::new();
@ -576,10 +565,10 @@ fn resolve_tool(spec: &str) -> Option<Resolved> {
pub fn resolve_placeholders( pub fn resolve_placeholders(
template: &str, template: &str,
store: &Store, store: &Store,
graph: &Graph,
keys: &[String], keys: &[String],
count: usize, count: usize,
) -> (String, Vec<String>) { ) -> (String, Vec<String>) {
let graph = store.build_graph();
let mut result = template.to_string(); let mut result = template.to_string();
let mut extra_keys = Vec::new(); let mut extra_keys = Vec::new();
let mut pos = 0; let mut pos = 0;
@ -589,7 +578,7 @@ pub fn resolve_placeholders(
let Some(rel_end) = result[start + 2..].find("}}") else { break }; let Some(rel_end) = result[start + 2..].find("}}") else { break };
let end = start + 2 + rel_end; let end = start + 2 + rel_end;
let name = result[start + 2..end].trim().to_lowercase(); let name = result[start + 2..end].trim().to_lowercase();
match resolve(&name, store, graph, keys, count) { match resolve(&name, store, &graph, keys, count) {
Some(resolved) => { Some(resolved) => {
let len = resolved.text.len(); let len = resolved.text.len();
extra_keys.extend(resolved.keys); extra_keys.extend(resolved.keys);
@ -616,7 +605,6 @@ pub fn run_agent(
count: usize, count: usize,
exclude: &std::collections::HashSet<String>, exclude: &std::collections::HashSet<String>,
) -> Result<super::prompts::AgentBatch, String> { ) -> Result<super::prompts::AgentBatch, String> {
let graph = store.build_graph();
// Run the query if present, via RPC // Run the query if present, via RPC
let keys = if !def.query.is_empty() { let keys = if !def.query.is_empty() {
@ -654,7 +642,7 @@ pub fn run_agent(
.replace("{agent_name}", &def.agent) .replace("{agent_name}", &def.agent)
.replace("{user_name}", &cfg.user_name) .replace("{user_name}", &cfg.user_name)
.replace("{assistant_name}", &cfg.assistant_name); .replace("{assistant_name}", &cfg.assistant_name);
let (prompt, extra_keys) = resolve_placeholders(&template, store, &graph, &all_keys, count); let (prompt, extra_keys) = resolve_placeholders(&template, store, &all_keys, count);
all_keys.extend(extra_keys); all_keys.extend(extra_keys);
resolved_steps.push(super::prompts::ResolvedStep { resolved_steps.push(super::prompts::ResolvedStep {
prompt, prompt,

View file

@ -211,100 +211,6 @@ pub fn format_health_section(store: &Store, graph: &Graph) -> String {
out out
} }
pub(super) fn format_rename_candidates(store: &Store, count: usize) -> (Vec<String>, String) {
let mut candidates: Vec<(&str, &crate::store::Node)> = store.nodes.iter()
.filter(|(key, node)| {
if key.starts_with("_facts-") { return true; }
if key.len() < 60 { return false; }
if node.node_type == crate::store::NodeType::EpisodicSession { return true; }
if key.starts_with("_mined-transcripts#f-") { return true; }
false
})
.map(|(k, n)| (k.as_str(), n))
.collect();
// Deprioritize nodes actively found by search — renaming them would
// break working queries. Sort by: search hits (ascending), then
// least-recently visited. Nodes with many hits sink to the bottom.
let hit_counts = crate::counters::all_search_hits();
let hit_map: std::collections::HashMap<&str, u64> = hit_counts.iter()
.map(|(k, v)| (k.as_str(), *v))
.collect();
candidates.sort_by_key(|(key, _)| {
let hits = hit_map.get(key).copied().unwrap_or(0);
(hits, store.last_visited(key, "rename"))
});
candidates.truncate(count);
let keys: Vec<String> = candidates.iter().map(|(k, _)| k.to_string()).collect();
let mut out = String::new();
out.push_str(&format!("## Nodes to rename ({} of {} candidates)\n\n",
candidates.len(),
store.nodes.iter().filter(|(k, n)| k.starts_with("_facts-") ||
(k.len() >= 60 &&
(n.node_type == crate::store::NodeType::EpisodicSession || k.starts_with("_mined-transcripts#f-")))).count()));
for (key, node) in &candidates {
out.push_str(&format!("### {}\n", key));
let created = if node.timestamp > 0 {
crate::store::format_datetime(node.timestamp)
} else {
"unknown".to_string()
};
out.push_str(&format!("Created: {}\n", created));
let hits = hit_map.get(key).copied().unwrap_or(0);
if hits > 0 {
out.push_str(&format!("Search hits: {} ← actively found by search, prefer to keep current name\n", hits));
}
let content = &node.content;
if content.len() > 800 {
let truncated = crate::util::truncate(content, 800, "\n[...]");
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n\n",
content.len(), truncated));
} else {
out.push_str(&format!("\nContent:\n{}\n\n", content));
}
out.push_str("---\n\n");
}
(keys, out)
}
/// Format specific target keys as rename candidates (for --target mode)
pub(super) fn format_rename_targets(store: &Store, keys: &[String]) -> String {
let mut out = String::new();
out.push_str(&format!("## Nodes to rename ({} targets)\n\n", keys.len()));
for key in keys {
let Some(node) = store.nodes.get(key) else {
out.push_str(&format!("### {}\n\n(node not found)\n\n---\n\n", key));
continue;
};
out.push_str(&format!("### {}\n", key));
let created = if node.timestamp > 0 {
crate::store::format_datetime(node.timestamp)
} else {
"unknown".to_string()
};
out.push_str(&format!("Created: {}\n", created));
let content = &node.content;
if content.len() > 800 {
let truncated = crate::util::truncate(content, 800, "\n[...]");
out.push_str(&format!("\nContent ({} chars, truncated):\n{}\n\n",
content.len(), truncated));
} else {
out.push_str(&format!("\nContent:\n{}\n\n", content));
}
out.push_str("---\n\n");
}
out
}
/// Generate a specific agent prompt with filled-in data. /// Generate a specific agent prompt with filled-in data.
pub fn agent_prompt(store: &Store, agent: &str, count: usize) -> Result<AgentBatch, String> { pub fn agent_prompt(store: &Store, agent: &str, count: usize) -> Result<AgentBatch, String> {
let def = super::defs::get_def(agent) let def = super::defs::get_def(agent)