render: deduplicate footer links against inline references

Render now detects neighbor keys that already appear in the node's
content and omits them from the footer link list. Inline references
serve as the node's own navigation structure; the footer catches
only neighbors not mentioned in prose.

Also fixes PEG query parser to accept hyphens in field names
(content-len was rejected).

memory-instructions-core updated to v12: documents canonical inline
link format (→ `key`), adds note about normalizing references when
updating nodes, and guidance on splitting oversized nodes.

Content is never modified for display — render is round-trippable.
Agents can read rendered output and write it back without artifacts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kent Overstreet 2026-03-20 13:37:29 -04:00
parent 9517b1b310
commit 601a072cfd
2 changed files with 30 additions and 12 deletions

View file

@ -198,23 +198,41 @@ pub fn cmd_render(key: &[String]) -> Result<(), String> {
let node = store.nodes.get(&bare)
.ok_or_else(|| format!("Node not found: {}", bare))?;
print!("{}", node.content);
// Show links so the graph is walkable
let mut neighbors: Vec<(&str, f32)> = Vec::new();
// Build neighbor lookup: key → strength
let mut neighbor_strengths: std::collections::HashMap<&str, f32> = std::collections::HashMap::new();
for r in &store.relations {
if r.deleted { continue; }
if r.source_key == bare {
neighbors.push((&r.target_key, r.strength));
let e = neighbor_strengths.entry(&r.target_key).or_insert(0.0);
*e = e.max(r.strength);
} else if r.target_key == bare {
neighbors.push((&r.source_key, r.strength));
let e = neighbor_strengths.entry(&r.source_key).or_insert(0.0);
*e = e.max(r.strength);
}
}
if !neighbors.is_empty() {
neighbors.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
neighbors.dedup_by(|a, b| a.0 == b.0);
let total = neighbors.len();
let shown: Vec<_> = neighbors.iter().take(15)
// Detect which neighbors are already referenced inline in the content.
// These are omitted from the footer to avoid duplication.
let mut inline_keys: std::collections::HashSet<String> = std::collections::HashSet::new();
for nbr_key in neighbor_strengths.keys() {
// Match `key` (backtick-quoted) or bare key after → arrow
if node.content.contains(nbr_key) {
inline_keys.insert(nbr_key.to_string());
}
}
print!("{}", node.content);
// Footer: only show links NOT already referenced inline
let mut footer_neighbors: Vec<(&str, f32)> = neighbor_strengths.iter()
.filter(|(k, _)| !inline_keys.contains(**k))
.map(|(k, s)| (*k, *s))
.collect();
if !footer_neighbors.is_empty() {
footer_neighbors.sort_by(|a, b| b.1.total_cmp(&a.1));
let total = footer_neighbors.len();
let shown: Vec<_> = footer_neighbors.iter().take(15)
.map(|(k, s)| format!("({:.2}) `poc-memory render {}`", s, k))
.collect();
print!("\n\n---\nLinks:");