From db7dae8236a559d4d9c42303413970312618e7bf Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Tue, 19 May 2026 08:53:02 -0500 Subject: [PATCH] =?UTF-8?q?self-review=202026-05-19:=20ACT-R=20WM=20persis?= =?UTF-8?q?tence=20=E2=80=94=20decay=20non-reached=20nodes=20instead=20of?= =?UTF-8?q?=20zeroing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The activation persist step was writing wm_weight=0 for every node not reached by the current BFS fan-out. This destroyed working memory accumulated by MCP-layer activations within one tick of the awareness loop firing on an empty inbox. ACT-R and Soar treat spreading activation as additive: absent seeds contribute zero spread, not a zero override of existing WM state. Fix: non-reached nodes now decay by ENGRAM_WM_DECAY (0.7) per activation call rather than being immediately zeroed. A hard floor of 0.005 clears near-zero values to prevent infinite decay tails. Reached nodes behave unchanged. --- lang/releases/v1.0.0-20260501/el_runtime.c | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lang/releases/v1.0.0-20260501/el_runtime.c b/lang/releases/v1.0.0-20260501/el_runtime.c index 35b006f..129986c 100644 --- a/lang/releases/v1.0.0-20260501/el_runtime.c +++ b/lang/releases/v1.0.0-20260501/el_runtime.c @@ -7077,16 +7077,33 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) { } /* Persist working_memory_weight (post Pass 3) to node store. - * Also emit ISE when a node transitions into working memory (was 0, now > 0.5), + * + * ACT-R semantics: spreading activation is additive. An absent spreading + * source contributes zero to the spread term but does NOT override existing + * base-level / WM state. Nodes not reached by this activation pass therefore + * retain their current working_memory_weight, decayed by ENGRAM_WM_DECAY + * (the per-turn attenuation factor). This replaces the prior behaviour of + * zeroing every non-reached node on each call, which destroyed WM state + * accumulated by MCP-layer activations within one tick of idle operation. + * + * Hard floor: weights decayed below 0.005 are cleared to 0.0 to avoid + * an infinite tail of near-zero values accumulating. + * + * Also emit ISE when a node transitions into working memory (was 0, now > 0.1), * excluding InternalStateEvent nodes to prevent ISEs about ISEs. */ for (int64_t i = 0; i < g->node_count; i++) { EngramNode* wn = &g->nodes[i]; double prev_wm = wn->working_memory_weight; + /* Nodes not participating in this pass: decay rather than zero. */ + if (!reached[i] || best_bg[i] <= 0.0) { + if (prev_wm > 0.0) { + double decayed = prev_wm * ENGRAM_WM_DECAY; + wn->working_memory_weight = (decayed < 0.005) ? 0.0 : decayed; + } + continue; + } wn->working_memory_weight = wm_weights[i]; - /* ISE: working memory promotion — on 0→>0.1 transition, non-ISE nodes. - * Threshold lowered from 0.5 to 0.1 so quieter promotions are observable. - * Previously only very strong promotions (wm_weight>0.5) fired an ISE, - * leaving most activations invisible in the state-event log. */ + /* ISE: working memory promotion — on 0→>0.1 transition, non-ISE nodes. */ if (prev_wm <= 0.0 && wm_weights[i] > 0.1 && wn->node_type && strcmp(wn->node_type, "InternalStateEvent") != 0) { char ise_buf[512];