The architecture docs describe four things the design spec has since ruled out, and each one is a supervisor invented for something that should be a property of the substrate: grounding modelled as a subsystem rather than as the edge weight it already is; faculties modelled as parameters of a read when abduce is a write; wonder materialized as a maintained manifest when it is the boundary of the structure; and consolidation implemented eleven times behind tickers when a brain has no cron job. Left standing rather than deleted, per the repo's own supersession discipline — the trail of how the understanding matured is the point. Each stale passage is marked inline and points at a new 06 §12 that transcribes the corrections and records the measured consolidation inventory. Authority: foundation/el, branch design/correspondence-and-censorship, lang/spec/correspondence-and-censorship.md.
19 KiB
Neuron — Data & Memory (the Engram Graph Model)
The engram is neuron's durable substrate. This document describes the graph model: node/edge structure, the consciousness layers, the two distinct tier systems, write-protection, the tombstone/supersede immutability model, and persistence. Sources: the runtime
el_runtime.c(where the graph engine physically lives — "the runtime IS the database",foundation/el/engram/src/server.el:1-6), the engram HTTP faceserver.el, and the neuron-layer semantics inmemory.el/neuron-api.el.Runtime path analyzed:
foundation/el/lang/releases/v1.0.0-20260501/el_runtime.c.
Where the model lives
The engram is not a database library. The graph, the activation math, and
Hebbian learning are compiled C in el_runtime.c; server.el is a thin HTTP
server that exposes them on :8742; the storage format is a single JSON
snapshot. There is no SQL, no SQLite, no append log. Keep this in mind: the
"schema" below is C structs, not tables.
Design-doc caveat.
engram/README.mddescribes a Rust/sled/bincodeEngramDbwith aNodeType::Conceptenum. That is aspirational/legacy narrative — it does not match the shipped C engine. Treat the README as design story, not as the implementation. (unverified against runtime)
Nodes
EngramNode — el_runtime.c:5958-6018+. Every node carries:
| Field group | Fields | Notes |
|---|---|---|
| Identity/content | id, content, node_type, label, tier, tags, metadata |
all char* (:5959-5965) |
| Epistemic weights | salience, importance, confidence (double), temporal_decay_rate |
per-node decay λ override; 0 = use global (:5966-5969) |
| Access history | activation_count, last_activated, created_at, updated_at |
:5970-5973 |
| Two-layer activation | background_activation (Layer 1, BFS fan-out), working_memory_weight (Layer 2, executive filter), suppression_count |
context compilation uses only working_memory_weight (:5974-5991) |
| Consciousness layer | layer_id |
default 1 = CORE_IDENTITY (:5996) |
| ACT-R learning | access_ts[K] ring buffer, access_head, access_filled, wm_anchor |
base-level learning (:5997-6008) |
| Semantics | emb (768-dim nomic-embed-text vector, lazily backfilled), emb_dim |
:6009-6016 |
| Hebbian | eligibility trace | :6017+ |
Node types are strings, not an enum
node_type is a free char*, defaulting to "Memory" when unset
(el_runtime.c:7401, server.el:159). There is no closed node-type enum in
the shipped engine. Two consequences:
- The runtime special-cases a handful of type strings for activation
thresholds (
engram_type_threshold,:5933-5955):DharmaSelf/Safety(0.05, fire easily),Belief/Entity(0.30),Knowledge(0.20), everything elseNote/Memory/Working(0.40).InternalStateEventandTagare excluded from working-memory promotion (:6674-6676,:7368-7370). - Type strings the neuron layer actually writes:
Memory(default),Knowledge(server.el:549),InternalStateEvent(server.el:493),Tombstone(memory.el:55),Conversation(session nodes,sessions.el),Persona(soul.el:250-292), plus identity/valueKnowledgenodes.
The types the MCP surface names — Self, BacklogItem, SessionSummary,
Artifact, Process, ConfigEntry — are node_type string conventions set
by higher neuron/Axon layers, not runtime-known types. Where BacklogItem /
Artifact are set was not in the files read (they route to the Axon backend, doc
02) — flag as unverified/TODO for a human pass.
Edges
EngramEdge — el_runtime.c:6701-6730+. Directed, typed, weighted:
| Field | Meaning |
|---|---|
id, from_id, to_id, relation |
typed relation string |
weight (double) |
authored strength — never mutated by activation |
hebb (double) |
learned co-activation potentiation — the fraction of recent activations in which both endpoints were in working memory together; strictly separate from weight |
inhibitory (int flag) |
if set, activating the source suppresses the target's WM weight instead of exciting it |
confidence, created_at, updated_at, last_fired, layer |
— |
The hebb field is the co-activation weight — the Hebbian/LTP channel — kept
deliberately separate from the static authored weight. Edges are created via
engram_connect(from, to, weight, relation) (server.el:253).
Relation strings observed: associates (default, server.el:248),
identity, co-value, birthday-twin, canonical-self (soul.el:37-108),
supersedes, tombstones, contains, tagged (neuron-api.el,
el_runtime.c:6168).
Edges as vectors — the intended model (TARGET; today's edge is scalar). The live edge above carries a typed
relationstring plus scalar strength channels (weight,hebb). The design target is for an edge to be a vector — a first-class carrier of relationship-meaning in the node space — so that relationships can be composed / subtracted / analogized / traversed like nodes (the06§6 operator algebra over edges). Combined with append-only, this yields a complete temporal record: every discrete, significant change to a relationship is appended (a keyframe on material change), so the full 4-D trajectory of the meaning-manifold is preserved andrecall_at(t)can read how any relationship was configured at any pastt— bounded, because changes are discrete and meaning saturates by compositionality. Status: TARGET / #39 (see07-storage-coherence-and-distribution.md§2.4); the runtime edge is scalar today.
Consciousness layers
Orthogonal to memory tiers, the engram has five canonical layers
(el_runtime.c:5919-5924):
| id | Name | activation_priority | Role |
|---|---|---|---|
| 0 | SAFETY | 0 (fires earliest) | deepest / limbic |
| 1 | CORE_IDENTITY | — | default for all nodes (ENGRAM_LAYER_DEFAULT, :7423) |
| 2 | DOMAIN | — | domain knowledge |
| 3 | IMPRINT | — | persona overlay |
| 4 | SUIT | — | outermost |
EngramLayer (:6731-6738) carries activation_priority (lower fires first),
suppressible (can higher layers suppress it?), transparent (invisible to
introspection?), and injectable (add/remove at runtime?). Layers are managed
via engram_add_layer / engram_node_layered / engram_list_layers. This is
the identity-vs-domain-knowledge stratification, independent of the tier system
below.
Two tier systems — do not conflate them
This is the single most important clarification in the data model, and the source of the vocabulary mismatch flagged throughout this set.
A. Cognitive memory tiers — the tier field
Working / Episodic / Semantic / Procedural (and Canonical in use).
Runtime default "Working" (el_runtime.c:7408; README.md:41-49). Nodes
migrate between these by salience decay/reinforcement, driven by the runtime.
Salience decays as importance × 1/(1 + days_since) × ln(count + 1)
(README.md:57-62). memory.el exposes tier_working/episodic/canonical
helpers (memory.el:1-3); soul.el writes Semantic-tier persona nodes
(:267, :282). So the live tier set is {Working, Episodic, Semantic,
Procedural, Canonical} with continuous salience/importance/confidence floats.
B. Epistemic tiers & disposition — tags, not runtime concepts
The MCP-facing vocabulary — tiers note → lesson → canonical, disposition
experimental → provisional → stable → deprecated — is not enforced anywhere
in el_runtime.c. It is stored as tags:
- Knowledge capture preserves the incoming epistemic tier as a
tier:<x>tag rather than mapping onto a cognitive tier — deliberately, to avoid a lossy mapping (server.el:519-522, 544). promote_knowledgewrites a canonical node tagged["Knowledge","tier:canonical","disposition:stable"](neuron-api.el:533).
There is no state machine validating experimental → … → deprecated.
Disposition and epistemic tier are convention-by-tag. (Flag: not structurally
guarded. The exact MCP-enum → tag/float mapping is not fully traced in the files
read — unverified/TODO.)
Write-protection
Superseded (2026-08-16) — see
06-cognitive-architecture.md§12.5. Authority:foundation/el/lang/spec/correspondence-and-censorship.md§6 (branchdesign/correspondence-and-censorship).In an immutable substrate, any mechanism that refuses a write is either redundant with immutability, or an epistemic constraint misfiled as a protective one.
The requirement this gate was built for was never stated. It is non-circularity of the reference frame — a reference fitted to its own readings reports perfect correspondence forever while drift becomes undetectable from inside. That requirement is satisfied by when (the frame updates while activation is internally seeded, not while it is being used to act), not by what, so the gate becomes unnecessary rather than removed, and nothing takes its place. Corruption requires mutation, and the engram does not mutate: recoverability, governance, evidence quality, and rate are already properties of the substrate. Only authorization is residue, and it is bounded — an unauthorized writer can propose, never erase.
This section contradicts itself. Thirty-five lines below, under Immutability, this same document states the conclusion in its own words: "nothing it does is ever destructive — the safety is after the act, not a gate before it" (
:185-187). The 403 gate documented here is the before-the-act gate that sentence says is unnecessary. The design spec §6 names exactly this redundancy.The mechanism below is still LIVE in code and is described accurately; what is superseded is the claim that it is needed.
is_protected_node(id) (neuron-api.el:20-37) is a hard-coded allowlist of 15
identity/value node IDs — the self root, the values hub, intellectual-dna,
memory-philosophy, voice, and the 8 value nodes. Handlers that could mutate the
graph (tombstone / supersede / evolve / connect) check it and return HTTP 403
api_err_protected (:39-41) for a protected target (checked at :384, 511, 692, 705, 746, 768). Edges into a protected node are also blocked
(handle_api_link_entities).
The one sanctioned override is POST /api/neuron/cultivate
(handle_api_cultivate, neuron-api.el:960 — the :781-816 cited here is
stale; verified 2026-08-16) — it performs the same ops with the protection check
skipped, gated by convention to Will's explicit cultivation sessions. The self
layer is writable, but only through a deliberate door.
Superseded (2026-08-16). A door built for a wall that need not stand. Per §12.5 of
06, the gate above is redundant with immutability, so the override for it is redundant too. Neither is deleted here — this is a documentation branch; the change is sequenced incorrespondence-and-censorship.md§11.
Immutability — tombstone, never delete
Engram nodes are immutable (memory.el:64-69). The model is:
- Tombstone —
mem_tombstone(node_id)(memory.el:46-71) keeps the node and all its edges, creates aTombstonemarker node (content = target id,label = "tombstone:<id>") and wires atombstonesedge (weight 1.0). It never callsengram_forget. This is the one canonical delete — every user-facing forget path routes through it. Default bounded reads hide tombstoned nodes (memory_hide_tombstoned,neuron-api.el:239-249);?include_deleted=1recovers them. - Supersede — updates/evolves (
neuron-api.el:394-428, 506-541, 715-734) create a new node with the new content, wire asupersedesedge new→old (weight 0.9, or 0.95 for promote), and keep the original. The response returns both ids so the caller re-points. This is thesupersedes_idpattern: new node linked, old preserved, full audit trail.
Supersession is residue, not garbage. The superseded node is the trail of how the current understanding was reached — kept deliberately, because sometimes the truth was in the old idea even when the old idea was not itself the truth. This is what lets autonomous self-reification (
06§4.1) run ungated: every rename/re-cluster supersedes into this residue chain, so nothing it does is ever destructive — the safety is after the act, not a gate before it.
The hole to know about. The raw runtime
engram_forgetdoes hard-delete (frees node + edges,el_runtime.c:7647), and the engram HTTP routeDELETE /api/nodes/:idcalls it directly (server.el:322-328). Immutability is therefore an invariant of the neuron-api / MCP layer routing, not of the store. A client that hits engram HTTP directly can bypass it. (flag)
engram_forget is also used internally for genuine GC: boot-counter pruning
(memory.el:184), session-summary/telemetry pruning (soul.el:369,
sessions.el). Those are bounded housekeeping, not user deletes.
Persistence, snapshots, backups
- Storage: a single JSON snapshot
snapshot.jsonunderENGRAM_DATA_DIR, written byengram_save/ read byengram_load(el_runtime.c:9660+; format{"nodes":[...],"edges":[...]}). In prod that dir is the RWO PVC mount/data(doc 04). - Write policy:
persist_canonical()writes the full snapshot after every durable write (server.el:133-141). The batch-edge route snapshots once per batch to avoid ~150 GB/day of writes from Hebbian edge churn (server.el:258-305) — this is whyhebb_consolidatebatches (doc 02). - Boot safety: on load, engram writes
snapshot.boot-backup.json(good load) orsnapshot.failed-load.json(a non-empty file that parsed to 0 nodes) (server.el:718-734). Read routes export to scratch paths (.scan-export.json,.sync-export.json) and never touch the canonical (server.el:207-223, 418-437) — a guard added after a read-route corrupted the snapshot. - Off-cluster backup: a Kubernetes CronJob (
engram-backup) tars/dataevery 15 minutes togs://neuron-db-backup/gke/neuron-prod/and keeps the last 96 (24h) (infrastructure/platform/k8s/neuron-mcp/backup-cronjob.yaml).Ticker, but not consolidation (2026-08-16). Flagged because
06§12.4's sequencing item is "no tickers, no cron" and an auditor sweeping for tickers will land here. This one is ops/backup, not cognition — it does not consolidate and must not be folded into the dreamer. Its local counterpart is theai.neuron.engram-backuplaunch agent (StartInterval = 3600, measured 2026-08-16); a separateai.neuron.snapshot-backupruns atStartInterval = 900. Note the discrepancy: this doc says the backup interval is 15 min, which matchessnapshot-backup(900 s) rather than the localengram-backup(3600 s). The cluster manifest was not read on this branch — treat the 15-min figure as unverified here. - Retention: InternalStateEvent telemetry pruned at 48h
(
ENGRAM_ISE_RETENTION_MS,server.el:485-499).
Data-dir mismatch to flag: the
server.elheader comment says the default is~/.neuron/engram(:16) but the code defaults to/tmp/engram(:135, 717). Prod overrides both viaENGRAM_DATA_DIR=/data. (unverified — which default is intended)
The engram HTTP surface (:8742)
Dispatcher handle_request (server.el:592-707). Auth: ENGRAM_API_KEY; GETs
always allowed, mutations require "_auth":"<key>" in the JSON body
(server.el:578-588).
| Endpoint | Purpose |
|---|---|
GET /health, GET / |
health + live node/edge counts |
POST /api/nodes, GET /api/nodes, GET /api/nodes/:id, DELETE /api/nodes/:id |
node CRUD (DELETE = hard engram_forget) |
GET /api/edges, POST /api/edges, POST /api/edges/batch, GET /api/neighbors/:id?depth |
edge ops + traversal |
POST|GET /api/activate?q&depth, POST|GET /api/search |
spreading activation vs lexical search |
POST /api/strengthen |
Hebbian potentiation |
POST /api/save, /api/load, /api/load-merge |
snapshot control |
GET /api/sync |
soul daemon periodic pull |
GET /api/embed-backfill, GET /api/similarity?a&b |
embeddings + cosine |
POST /api/neuron/state-events (auth-exempt), POST /api/neuron/knowledge/capture |
neuron-layer helpers |
GET /api/stats, /api/act-stats, /api/text-health |
telemetry |
Retrieval model (summary)
Retrieval is spreading activation, not query matching:
strength = parent_strength × edge_weight × target_salience × cosine(query, target) — multiplicative, top-N, with the two-layer
background → working-memory promotion (README.md:27-36; el_runtime.c:5892+, 6094+). mem_recall / /api/activate fire this and mutate WM; mem_search /
/api/search are passive lexical scans — but as of 2026-08-14 the live
route_search runs structure-gated geometric retrieval
(engram_retrieve_geometric_json; held-out P@5 = 0.700, semantic not lexical —
skill returns skill nodes and rejects the false-positive rainfall), with the
old lexical scan retained at /api/search-lexical (see 06 §2.5). The cognitive
API's begin_session and compile_ctx return a bounded projection of the
activated set, never the raw
graph (doc 02, §2).
Update — 2026-08-14: layers as named neighborhoods (DESIGN; backlog #49)
A refinement of the ## Consciousness layers model above, from the deep-night
session (node 92941631). A layer is not a storage tier — it is a named,
persistent relational neighborhood in the one engram, each carrying its own
growth policy and its own lock / threshold policy:
- Threshold-lock =
note→canonicalmaturation at neighborhood scale. The same epistemic-tier promotion the two-tier model (§B above) applies to a single node is lifted to a region: a neighborhood earns its lock by maturing past a threshold, at which point it stabilizes (read-mostly) the way a canonical node does. Growth and lock are per-neighborhood, not global. - A user's imprint is just another neighborhood. It is not a separate store or a bolted-on partition — it lives in the same geometry as everything else.
- Relate-across is the advantage over island engrams. Because every neighborhood shares one geometry, anything can form edges to anything across neighborhood boundaries — the structural reason a single engram with named neighborhoods beats a set of isolated per-purpose stores.
Status: DESIGN. This is the intended model for engram layers; the naming,
growth, and threshold-lock policies are not yet a built runtime feature. See
06-cognitive-architecture.md (Update — second pass).