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:
2026-07-27 08:38:48 -05:00
parent 11dc138a93
commit 9f362c90e5
2 changed files with 79 additions and 6 deletions
+78 -6
View File
@@ -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);