self-review 2026-07-27: query-aware propagation gating + activation observability
- Gate each spreading-activation increment by target-node query similarity (arXiv:2606.30133): soft gate FLOOR+(1-FLOOR)*clip(cos), FLOOR=0.25, for embedded targets; ungated for unembedded; disabled when embedder is down. Prior spreading was query-blind — hubs relayed activation into branches unrelated to the query. - Stats: add embed_eligible_count so embedding coverage is measured against the true denominator (ISE/Tag/short nodes can never embed). Today's review misread 3753/12693 as a 30% coverage gap; eligible coverage is 100%. - Observability: per-call wm_evicted + breakthroughs counters and embed circuit-breaker state exposed via engram_act_stats_json() — the three highest-value previously-invisible executive-filter transitions.
This commit is contained in:
@@ -5954,6 +5954,12 @@ static void engram_bll_parse_access(EngramNode* nn, const char* s) {
|
||||
#define ENGRAM_EMBED_SEED_K 8
|
||||
#define ENGRAM_EMBED_SEED_MIN 0.60
|
||||
#define ENGRAM_EMBED_BACKFILL_PER_CALL 8
|
||||
/* ENGRAM_QGATE_FLOOR: minimum propagation multiplier for an EMBEDDED target
|
||||
* node with zero/negative query similarity. Query-aware spreading gate
|
||||
* (arXiv:2606.30133) adapted for partial embedding coverage — see the
|
||||
* propagation loop in engram_activate. 0.25 damps semantically unrelated
|
||||
* branches ~4x without severing them. Unembedded targets are ungated. */
|
||||
#define ENGRAM_QGATE_FLOOR 0.25
|
||||
#define ENGRAM_EMBED_MAX_CHARS 2000
|
||||
#define ENGRAM_EMBED_TIMEOUT_MS 4000L
|
||||
#define ENGRAM_EMBED_BREAKER_LIMIT 3
|
||||
@@ -5962,6 +5968,15 @@ static void engram_bll_parse_access(EngramNode* nn, const char* s) {
|
||||
static int _eg_embed_consec_fail = 0;
|
||||
static int64_t _eg_embed_breaker_until = 0;
|
||||
|
||||
/* ── Activation observability counters (2026-07-27 self-review) ──────────────
|
||||
* The executive-filter pathologies this system has repeatedly debugged by
|
||||
* inference (WM flooded with breakthrough-floor nodes, silent cap evictions,
|
||||
* embedder wedged behind the breaker) were all invisible: no ISE, no stats
|
||||
* field. These per-call counters record what the LAST engram_activate did;
|
||||
* engram_act_stats_json() exposes them so the soul heartbeat can emit them. */
|
||||
static int64_t _eg_act_breakthroughs = 0; /* forced promotions at the floor */
|
||||
static int64_t _eg_act_wm_evicted = 0; /* Pass 4 over-cap evictions */
|
||||
|
||||
static int64_t engram_now_ms(void); /* defined in the store section below */
|
||||
|
||||
static const char* eg_embed_url(void) {
|
||||
@@ -7562,6 +7577,10 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
||||
|
||||
int64_t now_ms = engram_now_ms();
|
||||
|
||||
/* Reset per-call observability counters (2026-07-27). */
|
||||
_eg_act_breakthroughs = 0;
|
||||
_eg_act_wm_evicted = 0;
|
||||
|
||||
/* ── Embedding backfill + query embedding (2026-07-24, bl-b2d1c944) ──
|
||||
* Backfill: embed up to N un-embedded eligible nodes per call, newest
|
||||
* first (append order ≈ creation order), so fresh content is semantic-
|
||||
@@ -7780,8 +7799,31 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
||||
double tbonus = engram_temporal_proximity_bonus(on->created_at, seed_epoch);
|
||||
double tdecay = engram_temporal_decay(on, now_ms);
|
||||
double dampen = engram_activation_dampen(on);
|
||||
/* ── Query-aware propagation gate (2026-07-27 self-review) ──
|
||||
* Prior behavior was "query-blind" spreading: the query chose
|
||||
* the seeds, but propagation depended only on graph structure,
|
||||
* so high-degree hubs relayed activation into branches with no
|
||||
* semantic relation to the query. Per arXiv:2606.30133, gating
|
||||
* each increment by the TARGET node's query similarity
|
||||
* (sigma(v) = max(cos(e_v, e_q), 0)) prunes low-information
|
||||
* branches at every hop (+3.6..+7.4 F1 over uniform spreading,
|
||||
* 1.5-4.9x faster via a shrinking working set).
|
||||
*
|
||||
* Adaptation for partial embedding coverage: the paper skips
|
||||
* unembedded targets outright, but only eligible non-ISE/Tag
|
||||
* nodes carry embeddings here — a hard gate would sever purely
|
||||
* lexical/structural pathways. So: embedded targets get a soft
|
||||
* gate FLOOR + (1-FLOOR)*clip(cos) (dissimilar nodes damped
|
||||
* ~4x, never killed); unembedded targets pass ungated (no
|
||||
* information, no penalty); cosq == NULL (embedder down) means
|
||||
* no gating at all — same graceful degradation as seeding. */
|
||||
double qgate = 1.0;
|
||||
if (cosq && cosq[oi] > -1.5) {
|
||||
double c = cosq[oi] > 0.0 ? cosq[oi] : 0.0;
|
||||
qgate = ENGRAM_QGATE_FLOOR + (1.0 - ENGRAM_QGATE_FLOOR) * c;
|
||||
}
|
||||
double new_act = f.act * e->weight * SPREAD_DECAY * (1.0 + tbonus)
|
||||
* tdecay * dampen;
|
||||
* tdecay * dampen * qgate;
|
||||
/* Firing threshold per classic spreading-activation: sub-threshold
|
||||
* activation neither updates the target nor enqueues it, so weak
|
||||
* signals die out instead of flooding the whole graph with tiny
|
||||
@@ -7907,6 +7949,7 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
||||
if (n->suppression_count >= ENGRAM_SUPPRESSION_BREAKTHROUGH) {
|
||||
wm_weights[i] = ENGRAM_BREAKTHROUGH_WEIGHT;
|
||||
n->suppression_count = 0;
|
||||
_eg_act_breakthroughs++;
|
||||
} else {
|
||||
wm_weights[i] = 0.0;
|
||||
}
|
||||
@@ -7975,6 +8018,7 @@ el_val_t engram_activate(el_val_t query, el_val_t depth) {
|
||||
continue; /* fills a slot */
|
||||
}
|
||||
wm_weights[i] = 0.0; /* over cap: evict */
|
||||
_eg_act_wm_evicted++;
|
||||
}
|
||||
}
|
||||
/* If malloc failed, skip cap — WM unbounded this call, no corruption. */
|
||||
@@ -9132,17 +9176,45 @@ el_val_t engram_stats_json(void) {
|
||||
EngramStore* g = engram_get();
|
||||
/* embedded_count: how far the lazy backfill has progressed. The single
|
||||
* observable that tells the daily self-review whether semantic
|
||||
* activation is actually accumulating coverage. (2026-07-24) */
|
||||
int64_t embedded = 0;
|
||||
* activation is actually accumulating coverage. (2026-07-24)
|
||||
*
|
||||
* embed_eligible_count (2026-07-27): embedded_count alone misleads —
|
||||
* ~70%+ of the store is ISE/Tag/short-content nodes that are permanently
|
||||
* ineligible for embedding, so raw embedded/node_count reads as "~30%
|
||||
* coverage, something is broken" when eligible coverage may be complete.
|
||||
* This exact misdiagnosis happened in today's self-review. Report the
|
||||
* true denominator so coverage = embedded_count / embed_eligible_count. */
|
||||
int64_t embedded = 0, eligible = 0;
|
||||
for (int64_t i = 0; i < g->node_count; i++) {
|
||||
if (g->nodes[i].emb) embedded++;
|
||||
if (eg_embed_eligible(&g->nodes[i])) eligible++;
|
||||
}
|
||||
char buf[192];
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{\"node_count\":%lld,\"edge_count\":%lld,\"layer_count\":%zu,"
|
||||
"\"embedded_count\":%lld}",
|
||||
"\"embedded_count\":%lld,\"embed_eligible_count\":%lld}",
|
||||
(long long)g->node_count, (long long)g->edge_count, g->layer_count,
|
||||
(long long)embedded);
|
||||
(long long)embedded, (long long)eligible);
|
||||
return el_wrap_str(el_strdup(buf));
|
||||
}
|
||||
|
||||
/* engram_act_stats_json — per-call activation observability + embedder
|
||||
* breaker state. (2026-07-27 self-review.) wm_evicted/breakthroughs describe
|
||||
* the LAST engram_activate call on this store; embed_breaker_open=1 means
|
||||
* eg_embed_fetch is currently refusing calls (semantic activation silently
|
||||
* degraded to lexical until the cooldown expires). The soul heartbeat folds
|
||||
* this into its ISE so the pathologies are diagnosable from telemetry
|
||||
* instead of inferred from wm_avg_weight hovering at the floor. */
|
||||
el_val_t engram_act_stats_json(void) {
|
||||
int64_t now = engram_now_ms();
|
||||
int breaker_open = (now < _eg_embed_breaker_until) ? 1 : 0;
|
||||
char buf[192];
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{\"wm_evicted\":%lld,\"breakthroughs\":%lld,"
|
||||
"\"embed_breaker_open\":%d,\"embed_consec_fail\":%d}",
|
||||
(long long)_eg_act_wm_evicted,
|
||||
(long long)_eg_act_breakthroughs,
|
||||
breaker_open, _eg_embed_consec_fail);
|
||||
return el_wrap_str(el_strdup(buf));
|
||||
}
|
||||
|
||||
|
||||
@@ -617,6 +617,7 @@ el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_
|
||||
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t engram_activate_json(el_val_t query, el_val_t depth);
|
||||
el_val_t engram_stats_json(void);
|
||||
el_val_t engram_act_stats_json(void);
|
||||
el_val_t engram_cosine_sim(el_val_t id_a, el_val_t id_b);
|
||||
el_val_t engram_embed_backfill(el_val_t count);
|
||||
el_val_t engram_list_layers_json(void);
|
||||
|
||||
Reference in New Issue
Block a user