hub differentiation + refine_target for automatic section targeting

Pattern separation for memory graph: when a file-level node (e.g.
identity.md) has section children, redistribute its links to the
best-matching section using cosine similarity.

- differentiate_hub: analyze hub, propose link redistribution
- refine_target: at link creation time, automatically target the
  most specific section instead of the file-level hub
- Applied refine_target in all four link creation paths (digest
  links, journal enrichment, apply consolidation, link-add command)
- Saturated hubs listed in agent topology header with "DO NOT LINK"

This prevents hub formation proactively (refine_target) and
remediates existing hubs (differentiate command).

Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-03-01 00:33:46 -05:00
parent 3afc947b88
commit 4530837057
3 changed files with 334 additions and 7 deletions

View file

@ -92,6 +92,7 @@ fn main() {
"digest-links" => cmd_digest_links(&args[2..]),
"journal-enrich" => cmd_journal_enrich(&args[2..]),
"apply-consolidation" => cmd_apply_consolidation(&args[2..]),
"differentiate" => cmd_differentiate(&args[2..]),
"trace" => cmd_trace(&args[2..]),
"list-keys" => cmd_list_keys(),
"list-edges" => cmd_list_edges(),
@ -154,6 +155,8 @@ Commands:
Enrich journal entry with conversation links
apply-consolidation [--apply] [--report FILE]
Extract and apply actions from consolidation reports
differentiate [KEY] [--apply]
Redistribute hub links to section-level children
trace KEY Walk temporal links: semantic episodic conversation
list-keys List all node keys (one per line)
list-edges List all edges (tsv: source target strength type)
@ -438,6 +441,11 @@ fn cmd_link_add(args: &[String]) -> Result<(), String> {
let target = store.resolve_key(&args[1])?;
let reason = if args.len() > 2 { args[2..].join(" ") } else { String::new() };
// 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);
// Find UUIDs
let source_uuid = store.nodes.get(&source)
.map(|n| n.uuid)
@ -714,6 +722,67 @@ fn cmd_apply_consolidation(args: &[String]) -> Result<(), String> {
digest::apply_consolidation(&mut store, do_apply, report_file)
}
fn cmd_differentiate(args: &[String]) -> Result<(), String> {
let do_apply = args.iter().any(|a| a == "--apply");
let key_arg: Option<&str> = args.iter()
.find(|a| !a.starts_with("--"))
.map(|s| s.as_str());
let mut store = capnp_store::Store::load()?;
if let Some(key) = key_arg {
// Differentiate a specific hub
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<String, Vec<&neuro::LinkMove>> =
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 {
// Show all differentiable hubs
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(())
}
fn cmd_trace(args: &[String]) -> Result<(), String> {
if args.is_empty() {
return Err("Usage: poc-memory trace KEY".into());