diff --git a/awareness.el b/awareness.el index b224853..84a4d19 100644 --- a/awareness.el +++ b/awareness.el @@ -210,15 +210,28 @@ fn emit_heartbeat() -> Void { // // Returns true if any nodes were activated. // auto_term_try_slot — attempt to set cseed_auto from one WM slot. -// Only writes to cseed_auto if node_type is Memory, BacklogItem, or Entity -// AND the first word of the label is > 3 chars (guards bracket-prefixed labels). -// Designed to be called in reverse slot order (highest index first) so that -// the lowest-indexed slot (highest WM weight) wins by last-write semantics. +// Only writes to cseed_auto if node_type is Memory, BacklogItem, Entity, or +// Knowledge AND the first word of the label is > 3 chars (guards +// bracket-prefixed labels). Designed to be called in reverse slot order +// (highest index first) so that the lowest-indexed slot (highest WM weight) +// wins by last-write semantics. +// +// KNOWLEDGE ADMISSION (2026-07-23 self-review): WM top-10 is now dominated by +// Knowledge nodes (world-ingestor titles + captures), so excluding Knowledge +// left auto_term empty on EVERY curiosity_scan since boot 6 — the dynamic +// seeding path was dead. Knowledge labels are real titles after the +// neuron-api label fix. Sentinel-shaped labels ("knowledge:captured", +// "memory:remembered" — colon, no space) carry no seed signal and are +// skipped so legacy nodes cannot seed the scan with the word "knowledge". fn auto_term_try_slot(slot_type: String, slot_lbl: String) -> Void { state_set("_ats_ok", "0") if str_eq(slot_type, "Memory") { state_set("_ats_ok", "1") } if str_eq(slot_type, "BacklogItem") { state_set("_ats_ok", "1") } if str_eq(slot_type, "Entity") { state_set("_ats_ok", "1") } + if str_eq(slot_type, "Knowledge") { state_set("_ats_ok", "1") } + if str_contains(slot_lbl, ":") { + if !str_contains(slot_lbl, " ") { state_set("_ats_ok", "0") } + } if str_eq(state_get("_ats_ok"), "1") { if !str_eq(slot_lbl, "") { let sp: Int = str_find_chars(slot_lbl, " :([") diff --git a/dist/awareness.c b/dist/awareness.c index 4dfeb71..1ee81d5 100644 --- a/dist/awareness.c +++ b/dist/awareness.c @@ -176,6 +176,14 @@ el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl) { if (str_eq(slot_type, EL_STR("Entity"))) { state_set(EL_STR("_ats_ok"), EL_STR("1")); } + if (str_eq(slot_type, EL_STR("Knowledge"))) { + state_set(EL_STR("_ats_ok"), EL_STR("1")); + } + if (str_contains(slot_lbl, EL_STR(":"))) { + if (!str_contains(slot_lbl, EL_STR(" "))) { + state_set(EL_STR("_ats_ok"), EL_STR("0")); + } + } if (str_eq(state_get(EL_STR("_ats_ok")), EL_STR("1"))) { if (!str_eq(slot_lbl, EL_STR(""))) { el_val_t sp = str_find_chars(slot_lbl, EL_STR(" :([")); diff --git a/dist/neuron-api.c b/dist/neuron-api.c index c03371e..90ed522 100644 --- a/dist/neuron-api.c +++ b/dist/neuron-api.c @@ -414,8 +414,9 @@ el_val_t handle_api_capture_knowledge(el_val_t body) { return api_err(EL_STR("content is required")); } el_val_t full = ({ el_val_t _if_result_38 = 0; if (str_eq(title, EL_STR(""))) { _if_result_38 = (content); } else { _if_result_38 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_38; }); + el_val_t lbl = str_slice(title, 0, 80); el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]"); - el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags); + el_val_t id = engram_node_full(full, EL_STR("Knowledge"), lbl, el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags); if (!api_persisted(id)) { return api_not_persisted(id); } @@ -433,7 +434,7 @@ el_val_t handle_api_evolve_knowledge(el_val_t body) { return api_err_protected(prior_id); } el_val_t tags = EL_STR("[\"Knowledge\",\"evolved\"]"); - el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:evolved"), el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), EL_STR("Episodic"), tags); + el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR(""), el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), EL_STR("Episodic"), tags); if (!api_persisted(new_id)) { return api_not_persisted(new_id); } @@ -455,7 +456,7 @@ el_val_t handle_api_promote_knowledge(el_val_t body) { } el_val_t tags_raw = json_get(body, EL_STR("tags")); el_val_t tags = ({ el_val_t _if_result_39 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_39 = (EL_STR("[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]")); } else { _if_result_39 = (tags_raw); } _if_result_39; }); - el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:canonical"), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags); + el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR(""), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags); if (!api_persisted(new_id)) { return api_not_persisted(new_id); } diff --git a/dist/neuron.c b/dist/neuron.c index 59d835e..06cc048 100644 --- a/dist/neuron.c +++ b/dist/neuron.c @@ -16,6 +16,7 @@ el_val_t mem_remember(el_val_t content, el_val_t tags); el_val_t mem_recall(el_val_t query, el_val_t depth); el_val_t mem_search(el_val_t query, el_val_t limit); el_val_t mem_strengthen(el_val_t node_id); +el_val_t mem_tombstone(el_val_t node_id); el_val_t mem_forget(el_val_t node_id); el_val_t mem_consolidate(void); el_val_t mem_save(el_val_t path); @@ -113,6 +114,7 @@ el_val_t engram_compile(el_val_t intent); el_val_t distill_transcript(el_val_t transcript); el_val_t json_safe(el_val_t s); el_val_t current_engine_note(el_val_t model); +el_val_t bounded_persona_floor(void); el_val_t build_system_prompt(el_val_t ctx, el_val_t chat_mode); el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content); el_val_t hist_trim(el_val_t hist); @@ -534,7 +536,7 @@ int main(int _argc, char** _argv) { engram_url_raw = env(EL_STR("ENGRAM_URL")); engram_api_key_raw = env(EL_STR("ENGRAM_API_KEY")); snapshot_raw = env(EL_STR("SOUL_ENGRAM_PATH")); - snapshot = ({ el_val_t _if_result_46 = 0; if (str_eq(snapshot_raw, EL_STR(""))) { _if_result_46 = (el_str_concat(env(EL_STR("HOME")), EL_STR("/.neuron/engram/soul-snapshot.json"))); } else { _if_result_46 = (snapshot_raw); } _if_result_46; }); + snapshot = ({ el_val_t _if_result_46 = 0; if (str_eq(snapshot_raw, EL_STR(""))) { _if_result_46 = (el_str_concat(env(EL_STR("HOME")), EL_STR("/.neuron/engram/snapshot.json"))); } else { _if_result_46 = (snapshot_raw); } _if_result_46; }); axon_raw = env(EL_STR("NEURON_API_URL")); axon_base = ({ el_val_t _if_result_47 = 0; if (str_eq(axon_raw, EL_STR(""))) { _if_result_47 = (EL_STR("http://localhost:7771")); } else { _if_result_47 = (axon_raw); } _if_result_47; }); studio_dir_raw = env(EL_STR("SOUL_STUDIO_DIR")); @@ -546,7 +548,7 @@ int main(int _argc, char** _argv) { snapshot_usable = (local_node_count > 50); if (using_http_engram && !snapshot_usable) { println(el_str_concat(el_str_concat(EL_STR("[soul] engram -> HTTP "), engram_url_raw), EL_STR(" (no local snapshot, first boot)"))); - el_val_t nodes_json = http_get(el_str_concat(engram_url_raw, EL_STR("/api/nodes?limit=100000"))); + el_val_t nodes_json = http_get(el_str_concat(engram_url_raw, EL_STR("/api/nodes?limit=10000"))); el_val_t edges_json = http_get(el_str_concat(engram_url_raw, EL_STR("/api/edges"))); el_val_t nodes_part = ({ el_val_t _if_result_49 = 0; if (str_eq(nodes_json, EL_STR(""))) { _if_result_49 = (EL_STR("[]")); } else { _if_result_49 = (nodes_json); } _if_result_49; }); el_val_t edges_part = ({ el_val_t _if_result_50 = 0; if (str_eq(edges_json, EL_STR(""))) { _if_result_50 = (EL_STR("[]")); } else { _if_result_50 = (edges_json); } _if_result_50; }); diff --git a/neuron-api.el b/neuron-api.el index e1196f1..65f94df 100644 --- a/neuron-api.el +++ b/neuron-api.el @@ -361,13 +361,19 @@ fn handle_api_browse_knowledge(path: String, body: String) -> String { } // handle_api_capture_knowledge — create a Knowledge node. +// LABEL FIX (2026-07-23 self-review): the sentinel label "knowledge:captured" +// made every capture anonymous in WM telemetry (35 identical wm_top entries) +// and starved the curiosity auto-term seeder, which needs meaningful labels. +// Use the title as the label; empty label lets engram_node_full derive +// content[:60], which for captures starts with the title anyway. fn handle_api_capture_knowledge(body: String) -> String { let content: String = json_get(body, "content") let title: String = json_get(body, "title") if str_eq(content, "") { return api_err("content is required") } let full: String = if str_eq(title, "") { content } else { title + ": " + content } + let lbl: String = str_slice(title, 0, 80) let tags: String = "[\"Knowledge\",\"captured\"]" - let id: String = engram_node_full(full, "Knowledge", "knowledge:captured", + let id: String = engram_node_full(full, "Knowledge", lbl, el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), "Episodic", tags) if !api_persisted(id) { return api_not_persisted(id) } @@ -381,7 +387,8 @@ fn handle_api_evolve_knowledge(body: String) -> String { if str_eq(content, "") { return api_err("content is required") } if !str_eq(prior_id, "") && is_protected_node(prior_id) { return api_err_protected(prior_id) } let tags: String = "[\"Knowledge\",\"evolved\"]" - let new_id: String = engram_node_full(content, "Knowledge", "knowledge:evolved", + // Empty label → engram_node_full derives content[:60] (LABEL FIX 2026-07-23). + let new_id: String = engram_node_full(content, "Knowledge", "", el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), "Episodic", tags) if !api_persisted(new_id) { return api_not_persisted(new_id) } @@ -402,7 +409,8 @@ fn handle_api_promote_knowledge(body: String) -> String { let tags: String = if str_eq(tags_raw, "") { "[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]" } else { tags_raw } - let new_id: String = engram_node_full(content, "Knowledge", "knowledge:canonical", + // Empty label → engram_node_full derives content[:60] (LABEL FIX 2026-07-23). + let new_id: String = engram_node_full(content, "Knowledge", "", el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), "Canonical", tags) if !api_persisted(new_id) { return api_not_persisted(new_id) }