From 6b9d9e6c4af4fa902deebbd5b49ec46fa5d97d55 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Wed, 15 Jul 2026 04:07:33 -0500 Subject: [PATCH] Add engram_get_node_by_label runtime native to unblock soul link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chat.el calls the runtime native engram_get_node_by_label to fetch well-known nodes (conv:history, session:summary) by stable label rather than by ID — immune to vector-index drift across restarts. The current runtime never defined it, so the regenerated dist/soul.c fails to link. Backport the function verbatim (idiom-adapted to jb_finish) from release runtime v1.0.0-20260501 and register it as an EL builtin exactly like its siblings: runtime definition + prototype, __-prefixed seed wrapper + prototype, and codegen arity entry. No search-site code is touched. --- lang/el-compiler/runtime/el_runtime.c | 29 +++++++++++++++++++++++++++ lang/el-compiler/runtime/el_runtime.h | 1 + lang/el-compiler/runtime/el_seed.c | 1 + lang/el-compiler/runtime/el_seed.h | 1 + lang/el-compiler/src/codegen.el | 1 + 5 files changed, 33 insertions(+) diff --git a/lang/el-compiler/runtime/el_runtime.c b/lang/el-compiler/runtime/el_runtime.c index 68e6dca..57a9042 100644 --- a/lang/el-compiler/runtime/el_runtime.c +++ b/lang/el-compiler/runtime/el_runtime.c @@ -7761,6 +7761,35 @@ el_val_t engram_get_node_json(el_val_t id) { return el_wrap_str(jb_finish(&b)); } +/* engram_get_node_by_label — find the first node whose label field exactly + * matches the given string. Returns the node as a JSON object string, or "{}" + * if no match is found. + * + * Used by chat.el to retrieve well-known nodes (e.g. "conv:history", + * "session:summary") by their stable label rather than by ID, which is immune + * to vector index drift across restarts. + * + * Exact match (strcmp, not istr_contains) because labels like "conv:history" + * must not collide with nodes whose content happens to contain that substring. + * + * Backported verbatim (idiom-adapted to jb_finish) from release runtime + * v1.0.0-20260501 to unblock the soul regen link: chat.el references this + * native but the current runtime lacked its definition. */ +el_val_t engram_get_node_by_label(el_val_t label) { + const char* lbl = EL_CSTR(label); + if (!lbl || !*lbl) return el_wrap_str(el_strdup("{}")); + EngramStore* g = engram_get(); + for (int64_t i = 0; i < g->node_count; i++) { + EngramNode* n = &g->nodes[i]; + if (n->label && strcmp(n->label, lbl) == 0) { + JsonBuf b; jb_init(&b); + engram_emit_node_json(&b, n); + return el_wrap_str(jb_finish(&b)); + } + } + return el_wrap_str(el_strdup("{}")); +} + el_val_t engram_search_json(el_val_t query, el_val_t limit) { EngramStore* g = engram_get(); const char* q = EL_CSTR(query); diff --git a/lang/el-compiler/runtime/el_runtime.h b/lang/el-compiler/runtime/el_runtime.h index f64bf63..87348f5 100644 --- a/lang/el-compiler/runtime/el_runtime.h +++ b/lang/el-compiler/runtime/el_runtime.h @@ -632,6 +632,7 @@ el_val_t engram_load(el_val_t path); * can pass results straight through without round-tripping ElList/ElMap * through json_stringify. */ el_val_t engram_get_node_json(el_val_t id); +el_val_t engram_get_node_by_label(el_val_t label); el_val_t engram_search_json(el_val_t query, el_val_t limit); el_val_t engram_scan_nodes_json(el_val_t limit, el_val_t offset); el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset); diff --git a/lang/el-compiler/runtime/el_seed.c b/lang/el-compiler/runtime/el_seed.c index b509e48..cda893c 100644 --- a/lang/el-compiler/runtime/el_seed.c +++ b/lang/el-compiler/runtime/el_seed.c @@ -1072,6 +1072,7 @@ el_val_t __engram_save(el_val_t path) { return engram_save el_val_t __engram_load(el_val_t path) { return engram_load(path); } el_val_t __engram_get_node_json(el_val_t id) { return engram_get_node_json(id); } +el_val_t __engram_get_node_by_label(el_val_t label) { return engram_get_node_by_label(label); } el_val_t __engram_search_json(el_val_t query, el_val_t limit) { return engram_search_json(query, limit); diff --git a/lang/el-compiler/runtime/el_seed.h b/lang/el-compiler/runtime/el_seed.h index c502f10..7597511 100644 --- a/lang/el-compiler/runtime/el_seed.h +++ b/lang/el-compiler/runtime/el_seed.h @@ -226,6 +226,7 @@ el_val_t __engram_activate(el_val_t query, el_val_t depth); el_val_t __engram_save(el_val_t path); el_val_t __engram_load(el_val_t path); el_val_t __engram_get_node_json(el_val_t id); +el_val_t __engram_get_node_by_label(el_val_t label); el_val_t __engram_search_json(el_val_t query, el_val_t limit); el_val_t __engram_scan_nodes_json(el_val_t limit, el_val_t offset); el_val_t __engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset); diff --git a/lang/el-compiler/src/codegen.el b/lang/el-compiler/src/codegen.el index 917fb31..a20569a 100644 --- a/lang/el-compiler/src/codegen.el +++ b/lang/el-compiler/src/codegen.el @@ -2670,6 +2670,7 @@ fn builtin_arity(name: String) -> Int { if str_eq(name, "engram_save") { return 1 } if str_eq(name, "engram_load") { return 1 } if str_eq(name, "engram_get_node_json") { return 1 } + if str_eq(name, "engram_get_node_by_label") { return 1 } if str_eq(name, "engram_search_json") { return 2 } if str_eq(name, "engram_scan_nodes_json") { return 2 } if str_eq(name, "engram_neighbors_json") { return 3 }