self-review 2026-07-24: wire embedding cosine similarity into activation (bl-b2d1c944)

Semantic activation was spec-only since 2026-06-30 — the seed loop used
istr_contains and nothing else. Per the 07-21 integration brief:

- EngramNode gains a lazily-backfilled nomic-embed-text vector (8/call
  inside engram_activate, newest-first; no create-path latency, no bulk
  Ollama hammering during sync seeds)
- query embedding (cached) drives a top-K cosine seed supplement
  (HippoRAG use-similarity-twice) plus an additive WM term with
  shift-and-floor at 0.45 — raw cosine is a constant bias in anisotropic
  spaces (unrelated pairs read 0.4-0.7), floor-and-ramp makes it a signal
- 4s embed timeout (http_do_t) + 3-strike circuit breaker: activation
  never wedges on a dead embedder; everything degrades to lexical
- embeddings persist as %.4g comma lists in snapshots, parsed by both
  loaders; embedded_count in /api/stats tracks coverage
- engram_cosine_sim + http_delete_json exposed (DELETE now carries a
  body — the server's _auth scheme requires it)
- route_create_node honored only content/node_type/salience; label,
  importance, tier, tags were silently dropped (label defaulted to
  content). Now honored via engram_node_full.

Verified live: embedded_count 0->96 across activations, semantic-only
promotion observed (zero token overlap), snapshot round-trip intact.
This commit is contained in:
2026-07-24 08:52:54 -05:00
parent 8f8ccc945e
commit 97e484221d
5 changed files with 415 additions and 38 deletions
BIN
View File
Binary file not shown.
+31 -24
View File
@@ -130,7 +130,14 @@ el_val_t route_create_node(el_val_t method, el_val_t path, el_val_t body) {
el_val_t node_type = ({ el_val_t _if_result_2 = 0; if (str_eq(nt_raw, EL_STR(""))) { _if_result_2 = (EL_STR("Memory")); } else { _if_result_2 = (nt_raw); } _if_result_2; });
el_val_t sal_raw = json_get_float(body, EL_STR("salience"));
el_val_t salience = ({ el_val_t _if_result_3 = 0; if ((sal_raw == el_from_float(0.0))) { _if_result_3 = (el_from_float(0.5)); } else { _if_result_3 = (sal_raw); } _if_result_3; });
el_val_t id = engram_node(content, node_type, salience);
el_val_t label_raw = json_get_string(body, EL_STR("label"));
el_val_t label = ({ el_val_t _if_result_4 = 0; if (str_eq(label_raw, EL_STR(""))) { _if_result_4 = (content); } else { _if_result_4 = (label_raw); } _if_result_4; });
el_val_t imp_raw = json_get_float(body, EL_STR("importance"));
el_val_t importance = ({ el_val_t _if_result_5 = 0; if ((imp_raw == el_from_float(0.0))) { _if_result_5 = (el_from_float(0.5)); } else { _if_result_5 = (imp_raw); } _if_result_5; });
el_val_t tier_raw = json_get_string(body, EL_STR("tier"));
el_val_t tier = ({ el_val_t _if_result_6 = 0; if (str_eq(tier_raw, EL_STR(""))) { _if_result_6 = (EL_STR("Working")); } else { _if_result_6 = (tier_raw); } _if_result_6; });
el_val_t tags = json_get_string(body, EL_STR("tags"));
el_val_t id = engram_node_full(content, node_type, label, el_from_float(salience), el_from_float(importance), el_from_float(1.0), tier, tags);
el_val_t saved = persist_canonical();
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"id\":\""), id), EL_STR("\",\"content\":\"")), content), EL_STR("\",\"node_type\":\"")), node_type), EL_STR("\"}"));
return 0;
@@ -158,7 +165,7 @@ el_val_t route_scan_nodes(el_val_t method, el_val_t path, el_val_t body) {
el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body) {
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_4 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_4 = (EL_STR("/tmp/engram")); } else { _if_result_4 = (dir_raw); } _if_result_4; });
el_val_t dir = ({ el_val_t _if_result_7 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_7 = (EL_STR("/tmp/engram")); } else { _if_result_7 = (dir_raw); } _if_result_7; });
el_val_t snap_path = el_str_concat(dir, EL_STR("/.scan-export.json"));
engram_save(snap_path);
el_val_t snap = fs_read(snap_path);
@@ -174,22 +181,22 @@ el_val_t route_scan_edges(el_val_t method, el_val_t path, el_val_t body) {
}
el_val_t route_search(el_val_t method, el_val_t path, el_val_t body) {
el_val_t q = ({ el_val_t _if_result_5 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_5 = (query_param(path, EL_STR("q"))); } else { _if_result_5 = (json_get_string(body, EL_STR("query"))); } _if_result_5; });
el_val_t q = ({ el_val_t _if_result_8 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_8 = (query_param(path, EL_STR("q"))); } else { _if_result_8 = (json_get_string(body, EL_STR("query"))); } _if_result_8; });
el_val_t lim_url = query_int(path, EL_STR("limit"), 0);
el_val_t lim_body = json_get_int(body, EL_STR("limit"));
el_val_t lim_either = ({ el_val_t _if_result_6 = 0; if ((lim_url > 0)) { _if_result_6 = (lim_url); } else { _if_result_6 = (lim_body); } _if_result_6; });
el_val_t limit = ({ el_val_t _if_result_7 = 0; if ((lim_either > 0)) { _if_result_7 = (lim_either); } else { _if_result_7 = (20); } _if_result_7; });
el_val_t lim_either = ({ el_val_t _if_result_9 = 0; if ((lim_url > 0)) { _if_result_9 = (lim_url); } else { _if_result_9 = (lim_body); } _if_result_9; });
el_val_t limit = ({ el_val_t _if_result_10 = 0; if ((lim_either > 0)) { _if_result_10 = (lim_either); } else { _if_result_10 = (20); } _if_result_10; });
return engram_search_json(q, limit);
return 0;
}
el_val_t route_activate(el_val_t method, el_val_t path, el_val_t body) {
el_val_t q = ({ el_val_t _if_result_8 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_8 = (query_param(path, EL_STR("q"))); } else { _if_result_8 = (json_get_string(body, EL_STR("query"))); } _if_result_8; });
el_val_t q = ({ el_val_t _if_result_11 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_11 = (query_param(path, EL_STR("q"))); } else { _if_result_11 = (json_get_string(body, EL_STR("query"))); } _if_result_11; });
if (str_eq(q, EL_STR(""))) {
return err_json(EL_STR("missing query"));
}
el_val_t d_raw = ({ el_val_t _if_result_9 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_9 = (query_int(path, EL_STR("depth"), 3)); } else { _if_result_9 = (json_get_int(body, EL_STR("depth"))); } _if_result_9; });
el_val_t depth = ({ el_val_t _if_result_10 = 0; if ((d_raw > 0)) { _if_result_10 = (d_raw); } else { _if_result_10 = (3); } _if_result_10; });
el_val_t d_raw = ({ el_val_t _if_result_12 = 0; if (str_eq(method, EL_STR("GET"))) { _if_result_12 = (query_int(path, EL_STR("depth"), 3)); } else { _if_result_12 = (json_get_int(body, EL_STR("depth"))); } _if_result_12; });
el_val_t depth = ({ el_val_t _if_result_13 = 0; if ((d_raw > 0)) { _if_result_13 = (d_raw); } else { _if_result_13 = (3); } _if_result_13; });
return el_str_concat(el_str_concat(EL_STR("{\"results\":"), engram_activate_json(q, depth)), EL_STR("}"));
return 0;
}
@@ -198,9 +205,9 @@ el_val_t route_create_edge(el_val_t method, el_val_t path, el_val_t body) {
el_val_t from_id = json_get_string(body, EL_STR("from_id"));
el_val_t to_id = json_get_string(body, EL_STR("to_id"));
el_val_t rel_raw = json_get_string(body, EL_STR("relation"));
el_val_t relation = ({ el_val_t _if_result_11 = 0; if (str_eq(rel_raw, EL_STR(""))) { _if_result_11 = (EL_STR("associates")); } else { _if_result_11 = (rel_raw); } _if_result_11; });
el_val_t relation = ({ el_val_t _if_result_14 = 0; if (str_eq(rel_raw, EL_STR(""))) { _if_result_14 = (EL_STR("associates")); } else { _if_result_14 = (rel_raw); } _if_result_14; });
el_val_t w_raw = json_get_float(body, EL_STR("weight"));
el_val_t weight = ({ el_val_t _if_result_12 = 0; if ((w_raw == el_from_float(0.0))) { _if_result_12 = (el_from_float(0.5)); } else { _if_result_12 = (w_raw); } _if_result_12; });
el_val_t weight = ({ el_val_t _if_result_15 = 0; if ((w_raw == el_from_float(0.0))) { _if_result_15 = (el_from_float(0.5)); } else { _if_result_15 = (w_raw); } _if_result_15; });
engram_connect(from_id, to_id, weight, relation);
el_val_t saved = persist_canonical();
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"from_id\":\""), from_id), EL_STR("\",\"to_id\":\"")), to_id), EL_STR("\",\"relation\":\"")), relation), EL_STR("\"}"));
@@ -242,8 +249,8 @@ el_val_t route_forget(el_val_t method, el_val_t path, el_val_t body) {
el_val_t route_save(el_val_t method, el_val_t path, el_val_t body) {
el_val_t p_raw = json_get_string(body, EL_STR("path"));
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_13 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_13 = (EL_STR("/tmp/engram")); } else { _if_result_13 = (dir_raw); } _if_result_13; });
el_val_t p = ({ el_val_t _if_result_14 = 0; if (str_eq(p_raw, EL_STR(""))) { _if_result_14 = (el_str_concat(dir, EL_STR("/snapshot.json"))); } else { _if_result_14 = (p_raw); } _if_result_14; });
el_val_t dir = ({ el_val_t _if_result_16 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_16 = (EL_STR("/tmp/engram")); } else { _if_result_16 = (dir_raw); } _if_result_16; });
el_val_t p = ({ el_val_t _if_result_17 = 0; if (str_eq(p_raw, EL_STR(""))) { _if_result_17 = (el_str_concat(dir, EL_STR("/snapshot.json"))); } else { _if_result_17 = (p_raw); } _if_result_17; });
engram_save(p);
return el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"path\":\""), p), EL_STR("\"}"));
return 0;
@@ -252,8 +259,8 @@ el_val_t route_save(el_val_t method, el_val_t path, el_val_t body) {
el_val_t route_load(el_val_t method, el_val_t path, el_val_t body) {
el_val_t p_raw = json_get_string(body, EL_STR("path"));
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_15 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_15 = (EL_STR("/tmp/engram")); } else { _if_result_15 = (dir_raw); } _if_result_15; });
el_val_t p = ({ el_val_t _if_result_16 = 0; if (str_eq(p_raw, EL_STR(""))) { _if_result_16 = (el_str_concat(dir, EL_STR("/snapshot.json"))); } else { _if_result_16 = (p_raw); } _if_result_16; });
el_val_t dir = ({ el_val_t _if_result_18 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_18 = (EL_STR("/tmp/engram")); } else { _if_result_18 = (dir_raw); } _if_result_18; });
el_val_t p = ({ el_val_t _if_result_19 = 0; if (str_eq(p_raw, EL_STR(""))) { _if_result_19 = (el_str_concat(dir, EL_STR("/snapshot.json"))); } else { _if_result_19 = (p_raw); } _if_result_19; });
engram_load(p);
return ok_json();
return 0;
@@ -266,7 +273,7 @@ el_val_t route_health(el_val_t method, el_val_t path, el_val_t body) {
el_val_t route_sync(el_val_t method, el_val_t path, el_val_t body) {
el_val_t dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
el_val_t dir = ({ el_val_t _if_result_17 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_17 = (EL_STR("/tmp/engram")); } else { _if_result_17 = (dir_raw); } _if_result_17; });
el_val_t dir = ({ el_val_t _if_result_20 = 0; if (str_eq(dir_raw, EL_STR(""))) { _if_result_20 = (EL_STR("/tmp/engram")); } else { _if_result_20 = (dir_raw); } _if_result_20; });
el_val_t snap_path = el_str_concat(dir, EL_STR("/.sync-export.json"));
engram_save(snap_path);
el_val_t snap = fs_read(snap_path);
@@ -305,7 +312,7 @@ el_val_t route_emit_ise(el_val_t method, el_val_t path, el_val_t body) {
el_val_t conf = el_from_float(0.8);
el_val_t id = engram_node_full(content, EL_STR("InternalStateEvent"), EL_STR("state-event"), sal, imp, conf, EL_STR("Episodic"), EL_STR("[\"internal-state\",\"InternalStateEvent\"]"));
el_val_t ret_raw = env(EL_STR("ENGRAM_ISE_RETENTION_MS"));
el_val_t ret_ms = ({ el_val_t _if_result_18 = 0; if (str_eq(ret_raw, EL_STR(""))) { _if_result_18 = (172800000); } else { _if_result_18 = (str_to_int(ret_raw)); } _if_result_18; });
el_val_t ret_ms = ({ el_val_t _if_result_21 = 0; if (str_eq(ret_raw, EL_STR(""))) { _if_result_21 = (172800000); } else { _if_result_21 = (str_to_int(ret_raw)); } _if_result_21; });
el_val_t pruned = engram_prune_telemetry(ret_ms);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"ok\":true,\"id\":\""), id), EL_STR("\",\"pruned\":")), int_to_str(pruned)), EL_STR("}"));
return 0;
@@ -317,21 +324,21 @@ el_val_t route_capture_knowledge(el_val_t method, el_val_t path, el_val_t body)
return err_json(EL_STR("missing content"));
}
el_val_t title = json_get_string(body, EL_STR("title"));
el_val_t label = ({ el_val_t _if_result_19 = 0; if (str_eq(title, EL_STR(""))) { _if_result_19 = (str_slice(content, 0, 60)); } else { _if_result_19 = (title); } _if_result_19; });
el_val_t label = ({ el_val_t _if_result_22 = 0; if (str_eq(title, EL_STR(""))) { _if_result_22 = (str_slice(content, 0, 60)); } else { _if_result_22 = (title); } _if_result_22; });
el_val_t category_raw = json_get_string(body, EL_STR("category"));
el_val_t category = ({ el_val_t _if_result_20 = 0; if (str_eq(category_raw, EL_STR(""))) { _if_result_20 = (EL_STR("other")); } else { _if_result_20 = (category_raw); } _if_result_20; });
el_val_t category = ({ el_val_t _if_result_23 = 0; if (str_eq(category_raw, EL_STR(""))) { _if_result_23 = (EL_STR("other")); } else { _if_result_23 = (category_raw); } _if_result_23; });
el_val_t ktier_raw = json_get_string(body, EL_STR("tier"));
el_val_t ktier = ({ el_val_t _if_result_21 = 0; if (str_eq(ktier_raw, EL_STR(""))) { _if_result_21 = (EL_STR("note")); } else { _if_result_21 = (ktier_raw); } _if_result_21; });
el_val_t ktier = ({ el_val_t _if_result_24 = 0; if (str_eq(ktier_raw, EL_STR(""))) { _if_result_24 = (EL_STR("note")); } else { _if_result_24 = (ktier_raw); } _if_result_24; });
el_val_t project = json_get_string(body, EL_STR("project"));
el_val_t tags_raw = json_get_raw(body, EL_STR("tags"));
el_val_t tags_base = ({ el_val_t _if_result_22 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_22 = (EL_STR("[]")); } else { _if_result_22 = (tags_raw); } _if_result_22; });
el_val_t tags_base = ({ el_val_t _if_result_25 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_25 = (EL_STR("[]")); } else { _if_result_25 = (tags_raw); } _if_result_25; });
el_val_t base_len = str_len(tags_base);
el_val_t head = str_slice(tags_base, 0, (base_len - 1));
el_val_t sep = ({ el_val_t _if_result_23 = 0; if (str_eq(head, EL_STR("["))) { _if_result_23 = (EL_STR("")); } else { _if_result_23 = (EL_STR(",")); } _if_result_23; });
el_val_t sep = ({ el_val_t _if_result_26 = 0; if (str_eq(head, EL_STR("["))) { _if_result_26 = (EL_STR("")); } else { _if_result_26 = (EL_STR(",")); } _if_result_26; });
el_val_t safe_cat = str_replace(category, EL_STR("\""), EL_STR("'"));
el_val_t safe_tier = str_replace(ktier, EL_STR("\""), EL_STR("'"));
el_val_t safe_proj = str_replace(project, EL_STR("\""), EL_STR("'"));
el_val_t proj_tag = ({ el_val_t _if_result_24 = 0; if (str_eq(safe_proj, EL_STR(""))) { _if_result_24 = (EL_STR("")); } else { _if_result_24 = (el_str_concat(el_str_concat(EL_STR(",\"project:"), safe_proj), EL_STR("\""))); } _if_result_24; });
el_val_t proj_tag = ({ el_val_t _if_result_27 = 0; if (str_eq(safe_proj, EL_STR(""))) { _if_result_27 = (EL_STR("")); } else { _if_result_27 = (el_str_concat(el_str_concat(EL_STR(",\"project:"), safe_proj), EL_STR("\""))); } _if_result_27; });
el_val_t tags = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(head, sep), EL_STR("\"category:")), safe_cat), EL_STR("\",\"tier:")), safe_tier), EL_STR("\"")), proj_tag), EL_STR("]"));
el_val_t sal = el_from_float(0.5);
el_val_t imp = el_from_float(0.5);
@@ -432,10 +439,10 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
int main(int _argc, char** _argv) {
el_runtime_init_args(_argc, _argv);
bind_raw = env(EL_STR("ENGRAM_BIND"));
bind_str = ({ el_val_t _if_result_25 = 0; if (str_eq(bind_raw, EL_STR(""))) { _if_result_25 = (EL_STR(":8742")); } else { _if_result_25 = (bind_raw); } _if_result_25; });
bind_str = ({ el_val_t _if_result_28 = 0; if (str_eq(bind_raw, EL_STR(""))) { _if_result_28 = (EL_STR(":8742")); } else { _if_result_28 = (bind_raw); } _if_result_28; });
port = parse_port(bind_str);
data_dir_raw = env(EL_STR("ENGRAM_DATA_DIR"));
data_dir = ({ el_val_t _if_result_26 = 0; if (str_eq(data_dir_raw, EL_STR(""))) { _if_result_26 = (EL_STR("/tmp/engram")); } else { _if_result_26 = (data_dir_raw); } _if_result_26; });
data_dir = ({ el_val_t _if_result_29 = 0; if (str_eq(data_dir_raw, EL_STR(""))) { _if_result_29 = (EL_STR("/tmp/engram")); } else { _if_result_29 = (data_dir_raw); } _if_result_29; });
snapshot_path = el_str_concat(data_dir, EL_STR("/snapshot.json"));
engram_load(snapshot_path);
boot_snap = fs_read(snapshot_path);
+18 -1
View File
@@ -105,13 +105,30 @@ fn persist_canonical() -> Int {
return 1
}
// INCOMPLETE-ROUTE FIX (2026-07-24 self-review): this route silently dropped
// label, importance, tier, and tags engram_node() defaults label to content
// and importance to 0.5, so every node created over HTTP lost its metadata.
// Observed live: the soul's boot-counter write-back landed with
// label="soul:boot_count:99" (content), importance 0.5, no tags. Honor the
// full field set via engram_node_full when any of them is supplied.
fn route_create_node(method: String, path: String, body: String) -> String {
let content: String = json_get_string(body, "content")
let nt_raw: String = json_get_string(body, "node_type")
let node_type: String = if str_eq(nt_raw, "") { "Memory" } else { nt_raw }
let sal_raw: Float = json_get_float(body, "salience")
let salience: Float = if sal_raw == 0.0 { 0.5 } else { sal_raw }
let id: String = engram_node(content, node_type, salience)
let label_raw: String = json_get_string(body, "label")
let label: String = if str_eq(label_raw, "") { content } else { label_raw }
let imp_raw: Float = json_get_float(body, "importance")
let importance: Float = if imp_raw == 0.0 { 0.5 } else { imp_raw }
let tier_raw: String = json_get_string(body, "tier")
let tier: String = if str_eq(tier_raw, "") { "Working" } else { tier_raw }
let tags: String = json_get_string(body, "tags")
let id: String = engram_node_full(
content, node_type, label,
el_from_float(salience), el_from_float(importance), el_from_float(1.0),
tier, tags
)
let saved: Int = persist_canonical()
"{\"id\":\"" + id + "\",\"content\":\"" + content + "\",\"node_type\":\"" + node_type + "\"}"
}