self-review 2026-07-26: fix WM frozen-anchor fixation, strengthen self-inhibition, load-path emb leak
- Carry-over branch: occupancy inhibition m = t_c/(t_c+t_hold), t_c=3600s (ENGRAM_CARRY_TC). An unreached incumbent held its wm_anchor verbatim (keep~1.0 for BLL inflated in the pre-07-25 era) — observed 23h at WM top while every reached node rotated at the 0.10 breakthrough floor. STI only runs in the reached branch; inhibition must key on occupancy, not retrieval recency (Morita 2021 / Lebiere & Best 2009). - engram_strengthen: drop the 07-22 BLL access record — the 07-25 STI multiplier reads the same ring, so novelty reinforcement self-inhibited its target for ~2 minutes. - engram_load reset: free n->emb (~3KB/embedded node leaked per reload). - engram_wm_top_json: emit id — its absence made the heartbeat's wm_top0_streak compare ""=="" and measure uptime, not fixation.
This commit is contained in:
@@ -5698,6 +5698,28 @@ void el_cgi_init(el_val_t name, el_val_t dharma_id, el_val_t principal,
|
||||
* Source: act-r.psy.cmu.edu/.../894Cogsci09-Lebiere-Best.pdf */
|
||||
#define ENGRAM_STI_TS 120.0
|
||||
|
||||
/* Carry-over occupancy inhibition (2026-07-26 self-review).
|
||||
* The STI multiplier above only runs in the REACHED branch of Pass 2.
|
||||
* A node carried over WITHOUT being reached (Pass 4½ below) kept
|
||||
* w = wm_anchor * keep, and for a node whose base-level was inflated
|
||||
* during the pre-07-25 unconditional-reinforcement era, keep ≈ 1.0 for
|
||||
* days — the anchor weight is re-emitted verbatim forever. Observed
|
||||
* live: wm_top0_streak = 1407 heartbeats (~23 h), one node frozen at
|
||||
* its anchor 0.589 while every reached candidate rotated at the 0.10
|
||||
* breakthrough floor. Deterministic argmax over a quasi-static score
|
||||
* fixates regardless of any inhibition applied only to the reached set
|
||||
* (Morita et al. 2021, citing Lebiere & Best 2009).
|
||||
* Fix: key inhibition on OCCUPANCY, not retrieval recency — multiply
|
||||
* the carried weight by the same d_s=1 closed form over hold time
|
||||
* m(t_h) = t_c / (t_c + t_h), t_h = seconds since last_activated
|
||||
* (carry-over nodes are deliberately never reinforced, so
|
||||
* last_activated marks when the node last EARNED its slot). Power-law
|
||||
* self-healing: a node re-reached by any future activation is
|
||||
* re-scored fresh in Pass 2 and re-anchors. t_c = 3600 s → a carried
|
||||
* node keeps ~92% after 5 min, 50% after 1 h, ~4% after 23 h. Unlike
|
||||
* m(t_n), this cannot saturate to no-op while the node camps. */
|
||||
#define ENGRAM_CARRY_TC 3600.0
|
||||
|
||||
/* qsort comparator — descending double, used by WM cap enforcement. */
|
||||
static int engram_cmp_double_desc(const void* a, const void* b) {
|
||||
double da = *(const double*)a;
|
||||
@@ -6895,8 +6917,16 @@ void engram_strengthen(el_val_t node_id) {
|
||||
n->activation_count++;
|
||||
n->last_activated = engram_now_ms();
|
||||
n->updated_at = n->last_activated;
|
||||
/* Explicit strengthen is a presentation too (2026-07-22 self-review). */
|
||||
engram_bll_record_access(n, n->last_activated);
|
||||
/* 2026-07-26 self-review: REMOVED the BLL access record added on
|
||||
* 2026-07-22 ("explicit strengthen is a presentation too"). The
|
||||
* 2026-07-25 STI multiplier reads the same access ring — so an
|
||||
* explicit strengthen crushed the strengthened node's promotion
|
||||
* score by ×t_n/(t_n+120) for the next ~2 minutes. The awareness
|
||||
* loop strengthens exactly when a node NEWLY reaches WM top
|
||||
* (novelty gating); recording an access here made that
|
||||
* reinforcement self-defeating. Salience/activation_count bumps
|
||||
* above carry the reinforcement; the access ring stays reserved
|
||||
* for genuine retrieval events (promotions in engram_activate). */
|
||||
}
|
||||
|
||||
void engram_forget(el_val_t node_id) {
|
||||
@@ -7989,7 +8019,14 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
||||
} else {
|
||||
double keep = 1.0 / (1.0 + exp(-(B - ENGRAM_BLL_TAU)
|
||||
/ ENGRAM_BLL_S));
|
||||
double w = anchor * keep;
|
||||
/* Occupancy inhibition (2026-07-26): decay the carried
|
||||
* weight with hold time so an unreached incumbent cannot
|
||||
* hold its anchor verbatim indefinitely. See
|
||||
* ENGRAM_CARRY_TC comment for derivation. */
|
||||
double hold_s = (double)(now_ms - cn->last_activated) / 1000.0;
|
||||
if (hold_s < 0.0) hold_s = 0.0;
|
||||
double occ = ENGRAM_CARRY_TC / (ENGRAM_CARRY_TC + hold_s);
|
||||
double w = anchor * keep * occ;
|
||||
cn->working_memory_weight = (w < 0.01) ? 0.0 : w;
|
||||
}
|
||||
} else {
|
||||
@@ -8361,6 +8398,10 @@ el_val_t engram_load(el_val_t path) {
|
||||
free(g->nodes[i].id); free(g->nodes[i].content); free(g->nodes[i].node_type);
|
||||
free(g->nodes[i].label); free(g->nodes[i].tier); free(g->nodes[i].tags);
|
||||
free(g->nodes[i].metadata);
|
||||
/* 2026-07-26 self-review: emb was the one heap field not freed
|
||||
* here — ~3 KB leaked per embedded node per reload (~11 MB per
|
||||
* reload at 3.7k embedded). forget/prune already free it. */
|
||||
free(g->nodes[i].emb); g->nodes[i].emb = NULL; g->nodes[i].emb_dim = 0;
|
||||
}
|
||||
g->node_count = 0;
|
||||
for (int64_t i = 0; i < g->edge_count; i++) {
|
||||
@@ -9065,7 +9106,13 @@ el_val_t engram_wm_top_json(el_val_t n_v) {
|
||||
EngramNode* n = &g->nodes[idx[k]];
|
||||
if (k > 0) jb_putc(&b, ',');
|
||||
jb_putc(&b, '{');
|
||||
jb_puts(&b, "\"label\":");
|
||||
/* 2026-07-26 self-review: id was never emitted here, so the
|
||||
* awareness heartbeat's wm_top0_streak compared ""=="" and
|
||||
* incremented unconditionally — the streak metric measured
|
||||
* uptime, not fixation. */
|
||||
jb_puts(&b, "\"id\":");
|
||||
jb_emit_escaped(&b, n->id ? n->id : "");
|
||||
jb_puts(&b, ",\"label\":");
|
||||
jb_emit_escaped(&b, n->label ? n->label : "");
|
||||
jb_puts(&b, ",\"node_type\":");
|
||||
jb_emit_escaped(&b, n->node_type ? n->node_type : "");
|
||||
|
||||
Reference in New Issue
Block a user