Three improvements from today's self-review:
1. ENGRAM_BREAKTHROUGH_WEIGHT 0.25→0.10
Live data showed 524/525 WM nodes at breakthrough floor (0.25). Knowledge
nodes promoted at 0.21 decayed to 0.147 in one call, fell below the old
0.25 floor, and were immediately evicted for fresh breakthrough candidates.
Natural promotion was invisible. Invariant maintained: 0.10 < all
per-type thresholds (min=0.15 Canonical).
2. ENGRAM_WM_CAP=24 with Pass 4 (per-call) + Pass 5 (global) enforcement
Without a cap, broad queries like 'knowledge' promote 525+ nodes
simultaneously. WM is now bounded to 24 nodes. Algorithm: qsort on
promoted weights, keep top-24 by cutoff, evict the rest. Global pass
enforces cap across nodes that were promoted in prior calls and persist
via working_memory_weight. Validated: WM promoted goes 525→24.
Cognitive basis: Cowan (2001) WM ~4 chunks; 24 gives richer multi-topic
context while preventing flooding.
3. ISE exclusion from WM + /api/neuron/state-events route
InternalStateEvent nodes were reaching WM via breakthrough (5 suppression
cycles) because their content (curiosity seed JSON with 'knowledge',
'memory', etc.) triggered lexical seeding. ISEs are observability-only
and must never surface in context. Fix: guard in Pass 2 clears
suppression_count and skips to wm_weights[i]=0.0.
Also added POST /api/neuron/state-events route to server.el (auth-exempt,
internal endpoint). The main soul daemon posts ISEs here but the route
was missing — all ise_post() calls were silently returning 'not found'.
Research: SYNAPSE (arXiv 2601.02744) validates spreading factor 0.8 (our
0.7), top-M WM cap design, and cosine similarity seeding. Next priority:
implement cosine similarity initial seeding from the other branch.
el_strdup tracks pointers in the arena. The BFS arrays in
engram_neighbors_json are manually freed — using el_strdup caused a
double-free when the arena was later popped. Changed to plain strdup
for those allocations.
engram/dist/engram.c rebuilt from engram/src/server.el with current
elc (minor codegen diff: parenthesisation and _argc/_argv rename).
Dharma's EngramDB client calls /nodes/list to retrieve all nodes.
Add this as an alias for the existing /nodes (and /api/nodes) route
so downstream clients don't need to be updated when the API drifts.
Also update dist/engram.c to match server.el.
When the query string includes node_type, we route to the new
engram_scan_nodes_by_type_json builtin instead of the unfiltered
scan. Existing callers without the param get identical behaviour.
Smoke-tested live on the neuron engram (3,200+ nodes):
?node_type=Knowledge → all Knowledge
?node_type=BacklogItem → all BacklogItem
?node_type=Imprint → 1 Imprint (only one cultivated so far)
?node_type=DoesNotExist → []
Engram is now a thin HTTP face over the El runtime's in-process graph
store. The C runtime owns the data; engram_*_json builtins serialize
results directly. There is no SQL, no SQLite, no db layer, no state
machine — the runtime IS the database.
src/server.el (348 lines, replacing 5797 lines across 15 legacy files):
GET /health
GET /api/stats
POST /api/nodes (auth required)
GET /api/nodes
GET /api/nodes/:id
DELETE /api/nodes/:id (auth required)
POST /api/edges (auth required)
GET /api/neighbors/:id
POST /api/activate
GET /api/activate
POST /api/search
GET /api/search
POST /api/strengthen (auth required)
POST /api/save (auth required)
POST /api/load (auth required)
Auth: ENGRAM_API_KEY in env. GET routes pass through (read-only).
Mutating routes require {"_auth": "<key>"} in the JSON body until
http_serve surfaces request headers and we can switch to Bearer.
Persistence: engram_save / engram_load via JSON snapshot at
$ENGRAM_DATA_DIR/snapshot.json. Loaded best-effort on startup.
Build: dist/platform/elc src/server.el > dist/engram.c
cc -std=c11 -O2 -I <runtime> -lcurl -lpthread -o dist/engram
dist/engram.c <runtime>/el_runtime.c
Live: native binary at dist/engram (113 KB), running under
~/Library/LaunchAgents/ai.neuron.engram.plist on :8742. Verified:
GET /api/stats returns counts; POST /api/nodes with auth creates
node with UUID; GET /api/search returns full node JSON; spreading
activation returns hop-decayed strengths (0.8 × edge × decay per
hop) with epistemic confidence filtering.
Legacy (5797 lines of SQLite-era src) sealed at
~/Archives/engram-src-legacy-20260430.tar.gz and removed from disk.
Memory is not stored and retrieved — it is activated and propagated.
Implements the spreading activation model with salience decay, typed edges,
four memory tiers, and flat cosine vector search over a sled embedded store.