High-fanout identity anchors (voice, writing-imprint, self-root) have ~670KB
neighborhoods. inspect_graph returned the full traversal, which overflowed the
MCP client's context and socket-closed the wrapper mid self-load -- the soul
could not traverse its own identity graph.
handle_api_inspect_graph gains an opt-in `compact` projection (compact=1|true):
the neighborhood is relevance-ranked, the top K (default 12) keep a UTF-8-safe
content snippet (default snip=600), and the remainder collapse to lightweight
{id,label,node_type,tier,edge,pointer:true} stubs. This bounds the voice node
from 669,799B -> 25,353B (HTTP 200, valid JSON) and the wrapper's soul-load no
longer socket-closes. New helpers: api_compact_neighbors, api_neigh_full,
api_neigh_pointer, api_neigh_rank, api_neigh_better, api_float_or.
The flag is gated: ABSENT it, the response is byte-identical to the old plain
traversal, so the studio app (which never sends it) is unaffected. The MCP
wrapper (mcp-wrapper/src/main.el) appends &compact=1 on its inspectGraph and
fetch-by-id paths.
dist/soul.c is REGENERATED so CI ships the fix: CI compiles the committed
single-TU dist/soul.c directly (running elb/elc on the Linux runner OOM-kills
it), so an .el-only change would build the OLD behavior. Regenerated and verified
on macOS -- compiles with the CI cc line (0 errors) and, on a throwaway soul over
a copy of the live snapshot, serves compact ~25KB / non-compact ~670KB. The regen
also syncs the amalgamation to this branch's .el sources, which had drifted
several self-review commits ahead of the previously-committed soul.c.
Docs: docs/architecture/00-05 added; 01/02/05 corrected so the relevance-ranked
inspect_graph projection reads as committed source, not an in-flight concern.
8.9 KiB
Neuron — El & the Build Pipeline
The soul and the engram are written in El, a self-hosted language that compiles to C11. This document covers the language layer, the amalgamation →
soul.c→ binary pipeline, how the soul is composed from its layers, and the compile-time capability gates. Sources:manifest.el,soul.el,dist/soul.c, the El toolchain underfoundation/el/(elc.c,elb.el,BOOTSTRAP.md), and.gitea/workflows/.
The El language layer
El is a compiled, Lisp-family language transpiled to C11. Every El program links
a shared runtime, el_runtime.c / el_runtime.h, which implements all
builtins: the engram graph engine (engram_*), HTTP (http_*), JSON
(json_*), crypto, time, LLM calls, and DHARMA primitives (el_runtime.h,
BOOTSTRAP.md:599-644). The runtime also provides an arena allocator (server
mode) and ARC refcounting. Practically: the runtime is both the standard
library and the database — the graph physically lives in el_runtime.c, and El
source files are the orchestration/logic on top.
A recurring texture in the source is workaround comments for codegen quirks
(e.g. broken %/* operators). These are El-compiler maturity issues, not
architecture — but they explain some of the hand-rolled arithmetic in
awareness.el/memory.el.
The toolchain: elc, elb, el_runtime
| Tool | What it is | Role |
|---|---|---|
elc |
the El compiler, self-hosted (written in El) | compiles one El translation unit → C11. Import resolution is textual, depth-first, dedup'd — it inlines all imports into one string and emits forward decls for every fn (BOOTSTRAP.md:927-936). |
elb |
the build coordinator (elb.el, ~367 lines) |
reads manifest.el, walks the import graph, does incremental separate compilation using .elh header files (extern fn decls), links the final binary (".NET-style incremental build", BOOTSTRAP.md:886, 916-925). |
el_runtime.c/.h |
the C runtime | linked by every compiled El binary; implements all builtins and the graph engine. |
The .elh files present in this repo (soul.elh, memory.elh,
neuron-api.elh, routes.elh, …) are auto-generated headers (elc --emit-header) — the extern fn interface each module exports. They are the
contract surface elb uses for incremental builds, and they double as a concise
map of each module's public functions.
Self-hosting fixed point
elc is bootstrapped from a seed binary (dist/platform/elc, Mach-O arm64) and
verified by a fixed-point self-recompile: the compiler must compile its own
source to a byte-identical binary (BOOTSTRAP.md:7-58, 801-816). Pipeline:
elc-cli.el → compiler.el → lexer/parser/codegen.el.
Building the soul: .el → elc → .c → cc → binary
The concrete pipeline (mirrored in the engram build, engram/src/server.el:8-11):
soul.el (+ imports)
│ elc (self-hosted El→C11, inlines imports)
▼
dist/soul.c (~31,300 lines — single amalgamated translation unit)
│ cc -std=c11 -O2 soul.c el_runtime.c
▼
dist/neuron (native binary)
Why dist/soul.c is committed
dist/soul.c is the authoritative combined translation unit, regenerated on
macOS by running elb. It is checked into the repo on purpose: CI compiles it
directly and skips elb entirely (ci.yaml). The reason is operational, not
aesthetic —
elbsucceeds on arm64/macOSld, but fails on Linux (duplicate strong symbols), andelcuses 24GB+ virtual memory, which OOM-kills the 16GB CI runner.
So the pattern is: compile on the Mac, commit the amalgamation, and let Linux
CI do only the cheap cc step. dist/ also holds the per-module .c outputs
(memory.c, awareness.c, chat.c, the NLG morphology tables, …) —
intermediate artifacts of the same process.
Mechanism note (observed during the self-load regen). The single-TU
dist/soul.cis produced by runningelcover the flattened import set — every module source insoul.el's transitive import graph, concatenated withimportlines stripped, compiled in one pass (elchoists forward decls for all functions, so concat order doesn't affect correctness).elbon its own emits per-module.c+ a linked binary, not the combinedsoul.c; it is the separate-compilation coordinator, andelc soul.elalone yields only the soul module. Because the amalgamation is regenerated only on demand, it can lag the.elsources: this PR regenerated it after it had fallen behind several source commits, and folded in theinspect_graphrelevance-ranked projection (thecompact=1self-load fix, doc 02) so CI ships it.
How the soul is composed (layer stack)
manifest.el declares the build:
package "neuron" { version "0.1.0" edition "2026" }
build { entry "soul.el" }
The comment in manifest.el:8-16 documents the intended layer composition
order: a base layer ../foundation/nlg (the NLG engine — 31-language
morphology, grammar, realizer, semantics) with the soul layer (soul.el)
injected on top. New layers are added by importing them in soul.el before the
soul's own code. (The ../foundation/nlg path is the manifest's stated NLG base;
the NLG sources compile into the dist/*.c morphology/grammar tables seen in the
tree.)
soul.el itself imports, in order (soul.el:1-10): elp.el, memory.el,
safety.el, stewardship.el, imprint.el, awareness.el, chat.el,
studio.el, elp-input.el, routes.el — then declares the cgi "neuron-soul"
identity block (:12-17): dharma_id, principal, network, and
engram: http://localhost:8742. Because elc inlines imports depth-first, this
import list is the amalgamation order that produces dist/soul.c.
The cgi block is not just metadata — it sets the program's capability tier
(next section).
Compile-time capability gates
El's codegen classifies each program by its top-level declaration and enforces
capabilities at compile time (BOOTSTRAP.md:958-965):
| Declaration | Tier | Allowed |
|---|---|---|
cgi { … } |
full | everything — llm_call_agentic, llm_register_tool, dharma_emit, dharma_field, LLM, DHARMA |
service { … } |
restricted | no llm_call_agentic / llm_register_tool / dharma_emit / dharma_field |
| neither | utility | no DHARMA, no LLM |
A program that calls a capability its tier forbids fails to compile: codegen
emits a C #error naming the forbidding call, so the downstream cc aborts.
This is the primary hard gate in the build — capability escalation is caught
by the compiler, not at runtime. The soul is a cgi, so it gets the full tier;
engram is declared without cgi/service semantics that would grant LLM
access (it is a store).
Verification gates
| Gate | Where | What it checks |
|---|---|---|
| Capability tier | El codegen (BOOTSTRAP.md:958) |
no capability escalation; hard #error at compile |
| Self-hosting fixed point | elc bootstrap (BOOTSTRAP.md:801) |
compiler reproduces itself byte-identically |
test_soul_guard.el |
tests/ |
the genesis safe_to_seed boot guard — a sparse/oversized snapshot must not clobber the graph |
test_layer_contract.el |
tests/ |
JSON interface shapes between composition-stack layers that layered_cycle depends on (e.g. safety_screen always returns an action field) |
other tests/*.el |
tests/ |
test_sessions.el, test_safety.el, test_bell_safety.el, test_layered_cycle.el, test_imprint.el, test_stewardship.el, test_api_define_process.el, … |
| CI smoke test | ci.yaml |
dist/neuron --help runs |
Flag (unverified/TODO). CI (
ci.yaml) runs only thecccompile + thedist/neuron --helpsmoke test — it does not invoke thetests/*.elsoul-guard / layer-contract suites, and.githooks/is empty. Whether these tests are gated anywhere (a pre-merge hook, a separate workflow, or manual discipline on the Mac before regeneratingsoul.c) is not evident in the files read. This is the most important build-integrity gap to confirm with a human: the contract tests exist but their enforcement point is unproven.
Practical consequences for a contributor
- You cannot rebuild the whole soul on Linux/CI. Regenerate
dist/soul.con a Mac (elb), commit it, then CI compiles it. Changing an.elfile without regeneratingsoul.cships nothing. - The
.elhfiles are your API map. To see what a module exposes, read its.elh— it's the generatedextern fnlist. - Memory/activation behavior often can't be changed from this repo. The
volatile numeric core is in
foundation/elel_runtime.c. Doc 01, Divergence 6 explains why this is the sharpest edge in the architecture. - The engram is a separate repo. It is cloned and compiled by CI
(
Dockerfile,.gitea/workflows/), not vendored here. Its source of truth isfoundation/el/engram.