Archived
self-review 2026-05-28: checkpoint ISE decay fix + auto-linking for MCP nodes
Three changes: 1. Fix checkpoint ISE temporal_decay_rate: engram_emit_ise_internal was hardcoded to 0.0 (global 168h default) instead of 2.310 (Working-tier 48h). Result: checkpoint ISEs accumulated at 3.5x intended rate. 2. Raise CHECKPOINT_INTERVAL 1→10: checkpoint ISE fires on every single node write, producing 2:1 checkpoint:content ratio in ISE stream. MCP routes still call engram_write_binary_el explicitly after each important write, so no knowledge durability is lost. 3. Add auto_link_content_node to server.el: route_neuron_memory and route_neuron_knowledge_capture were creating nodes with zero edges — invisible to BFS traversal, only reachable via lexical/semantic seed. New helper runs BM25 over top-20 results, skips ISE nodes (which dominate the 14K-node corpus), connects up to 3 related nodes.
This commit is contained in:
+80
-2
@@ -227,6 +227,76 @@ fn bm25_search_json(query: String, limit: Int) -> String {
|
||||
out + "]"
|
||||
}
|
||||
|
||||
// ── Auto-linking ─────────────────────────────────────────────────────────────
|
||||
//
|
||||
// auto_link_content_node — link a newly-created Knowledge or Memory node to
|
||||
// semantically related non-ISE nodes via BM25 search.
|
||||
//
|
||||
// Problem it solves: route_neuron_memory and route_neuron_knowledge_capture
|
||||
// both call engram_node_full directly, creating nodes with zero edges. With
|
||||
// 14K+ ISEs dominating the corpus, BFS traversal contributes nothing — every
|
||||
// query relies solely on lexical/semantic seed matching. Auto-linking builds
|
||||
// explicit "related" edges so activated knowledge nodes fan out to connected
|
||||
// neighbors during BFS.
|
||||
//
|
||||
// Design choices:
|
||||
// - BM25 (not substring search): ranks by relevance, not just occurrence
|
||||
// - Skip InternalStateEvent nodes: ISEs dominate the corpus and are not
|
||||
// useful link targets for knowledge/memory nodes
|
||||
// - Up to 3 edges per node: enough to build graph structure without over-linking
|
||||
// - weight=0.6: moderately strong; causal edges (field-validated at 2.0) are
|
||||
// much stronger, so these "related" edges don't flood activation paths
|
||||
// - state_set for linked counter: EL `let` in nested if-blocks creates inner
|
||||
// scope only; state_set persists across block boundaries (2026-05-25 lesson)
|
||||
//
|
||||
// (2026-05-28 self-review)
|
||||
fn auto_link_content_node(node_id: String, content: String) -> Int {
|
||||
let clen: Int = str_len(content)
|
||||
if clen < 20 { return 0 }
|
||||
|
||||
// Find search term: first word >= 5 chars, or second word.
|
||||
let sp1: Int = str_index_of(content, " ")
|
||||
let w1end: Int = if sp1 < 0 { clen } else { sp1 }
|
||||
let word1: String = str_slice(content, 0, w1end)
|
||||
state_set("aln_term", "")
|
||||
if str_len(word1) >= 5 {
|
||||
state_set("aln_term", word1)
|
||||
}
|
||||
if str_eq(state_get("aln_term"), "") {
|
||||
if sp1 >= 0 {
|
||||
let rest: String = str_slice(content, sp1 + 1, clen)
|
||||
let sp2: Int = str_index_of(rest, " ")
|
||||
let w2end: Int = if sp2 < 0 { str_len(rest) } else { sp2 }
|
||||
let word2: String = str_slice(rest, 0, w2end)
|
||||
if str_len(word2) >= 5 {
|
||||
state_set("aln_term", word2)
|
||||
}
|
||||
}
|
||||
}
|
||||
let search_term: String = state_get("aln_term")
|
||||
if str_eq(search_term, "") { return 0 }
|
||||
|
||||
// BM25 over top-20 results; skip ISE nodes; connect up to 3.
|
||||
let results: String = bm25_search_json(search_term, 20)
|
||||
let n: Int = json_array_len(results)
|
||||
state_set("aln_linked", "0")
|
||||
let i: Int = 0
|
||||
while i < n {
|
||||
let linked_so_far: Int = str_to_int(state_get("aln_linked"))
|
||||
if linked_so_far < 3 {
|
||||
let elem: String = json_array_get(results, i)
|
||||
let rid: String = json_get_string(elem, "id")
|
||||
let rtype: String = json_get_string(elem, "node_type")
|
||||
if !str_eq(rtype, "InternalStateEvent") && !str_eq(rid, "") && !str_eq(rid, node_id) {
|
||||
engram_connect(node_id, rid, 0.6, "related")
|
||||
state_set("aln_linked", int_to_str(linked_so_far + 1))
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
return str_to_int(state_get("aln_linked"))
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
fn parse_port(bind: String) -> Int {
|
||||
@@ -565,13 +635,18 @@ fn route_neuron_memory(method: String, path: String, body: String) -> String {
|
||||
|
||||
let id: String = engram_node_full(content, node_type, label, 0.5, 0.5, 1.0, tier, tags_str)
|
||||
|
||||
// Auto-link to related non-ISE nodes so this memory is reachable via BFS traversal.
|
||||
// Without this, MCP-created nodes arrive with zero edges and are invisible to
|
||||
// graph spread during activation (only lexical/semantic seed matching finds them).
|
||||
let auto_linked: Int = auto_link_content_node(id, content)
|
||||
|
||||
// Checkpoint after write
|
||||
let dir: String = env("ENGRAM_DATA_DIR")
|
||||
if str_eq(dir, "") { let dir = "/tmp/engram" }
|
||||
let db_path: String = dir + "/engram.db"
|
||||
engram_write_binary_el(db_path)
|
||||
|
||||
"{\"ok\":true,\"id\":\"" + id + "\",\"content\":\"" + str_replace(str_replace(content, "\\", "\\\\"), "\"", "\\\"") + "\"}"
|
||||
"{\"ok\":true,\"id\":\"" + id + "\",\"auto_linked\":" + int_to_str(auto_linked) + ",\"content\":\"" + str_replace(str_replace(content, "\\", "\\\\"), "\"", "\\\"") + "\"}"
|
||||
}
|
||||
|
||||
// route_neuron_knowledge_capture — create a Knowledge node
|
||||
@@ -611,13 +686,16 @@ fn route_neuron_knowledge_capture(method: String, path: String, body: String) ->
|
||||
|
||||
let id: String = engram_node_full(content, "Knowledge", title, 0.7, 0.7, 1.0, tier, tags_str)
|
||||
|
||||
// Auto-link to related non-ISE nodes for BFS reachability (same rationale as route_neuron_memory).
|
||||
let auto_linked: Int = auto_link_content_node(id, content)
|
||||
|
||||
// Checkpoint
|
||||
let dir: String = env("ENGRAM_DATA_DIR")
|
||||
if str_eq(dir, "") { let dir = "/tmp/engram" }
|
||||
let db_path: String = dir + "/engram.db"
|
||||
engram_write_binary_el(db_path)
|
||||
|
||||
"{\"ok\":true,\"id\":\"" + id + "\"}"
|
||||
"{\"ok\":true,\"id\":\"" + id + "\",\"auto_linked\":" + int_to_str(auto_linked) + "}"
|
||||
}
|
||||
|
||||
// route_neuron_knowledge_evolve — create updated node (evolution via new node)
|
||||
|
||||
Reference in New Issue
Block a user