#116 shipped the ggml strategy at 0.9933 id-recall against the CPU oracle while the hand-rolled Metal kernel it replaced scored 0.9997 — a ~150x worse error margin. That was not an inherent property of ggml. It was a usage bug in this file, and this commit fixes it. ggml-metal has two F32xF32 matmul kernels and picks between them purely on ne11, the number of B rows, which for us is the query-batch size: ne11 <= 8 -> kernel_mul_mv_ext_f32_f32_* / kernel_mul_mv_f32_f32_*, templated <float, float> — genuine F32. ne11 > 8 -> kernel_mul_mm_f32_f32, templated <half, half4x4, simdgroup_half8x8, half, half2x4, ...> — BOTH operands narrowed to F16, despite F32 tensors on both sides. The old code issued one ggml_mul_mat with ne11 = nq (300 in the benchmark), landing squarely on the F16 path. The file's own header comment asserted the opposite ("computes in F32 on the Metal backend"); that claim was wrong and is replaced with the measurement. Fix: emit ceil(nq/8) mul_mats over ne11<=8 ggml_view_2d slices of one query tensor, all expanded into ONE graph and one ggml_backend_graph_compute, so the node matrix is still uploaded and shared exactly once. EL_GGML_MULMAT_CHUNK overrides the 8; setting it >= nq reproduces the old behaviour exactly, which is also how the before/after below was measured in a single binary. Measured, real store snapshot, 13415 live embedded nodes, dim=768, 300 real queries, vs the CPU double-accumulated oracle (vindex_bench, offline copy of the store — no live service touched): id-recall same-rank |Δdist| max mean old (ne11=300) 0.9933 6.80e-05 1.43e-05 new (ne11<=8) 0.9987 4.77e-07 9.30e-08 hand-rolled 0.9997 3.58e-07 7.55e-08 ~145x better max error, ~154x better mean — now the same order of magnitude as the hand-rolled kernel rather than 150x off it. The cost is real and is documented rather than buried. Median of 15 reps of the whole batch_multi() call, three runs: 13.2-14.4ms unchunked, 19.9-20.2ms chunked, 17.7-18.0ms hand-rolled. Correctness costs ~+6.7ms per 300-query batch and leaves ggml ~12% behind the hand-rolled kernel instead of ~35% ahead. It cannot be recovered inside ggml: an fp32 matmul on Metal must re-stream the node matrix once per <=8 queries, and ggml's Metal backend ships no fp32 TILED matmul, so "fast" and "fp32" are genuinely exclusive there. Two things that did NOT work, recorded so nobody retries them: - ggml_mul_mat_set_prec(t, GGML_PREC_F32) does nothing here. Error was bit-identical with and without it (1.038e-05 either way) — ggml-metal has no F32-accumulating mul_mm kernel to switch to. ne11 is the only lever. - The ACCEL/BLAS device looked excellent in an isolated compute-only probe (3.4-4.0ms, mean |Δdot| 1.5e-08) but is dominated on BOTH axes end-to-end (0.191 ms/query at 0.9973 recall vs 0.125-0.142 at 0.9987), because the probe was not competing for the same CPU cores the real call path is. It stays reachable via EL_GGML_DEVICE as a no-Metal fallback, labelled as measured-and-rejected, not as a recommendation. Also corrected: the ~7.8s "cold start" blamed on this file is not this file re-initialising per call — init was already cached. It is Apple's shader cache missing on ggml's embedded metallib (~650 kernels), keyed on the library and shared across processes: the first load on a machine reports "loaded in 7.670 sec", the next run of a *different* binary reports 0.009 sec. Once per machine per ggml version, not once per process, and not ours to fix. Warm ggml init is 44-53ms vs 36-117ms for the hand-rolled strategy. Loading only libggml-metal.so instead of every plugin in the directory is kept for tidiness, and explicitly documented as NOT a speedup: 44.7-52.4ms against 46.9-58.9ms, the same number inside noise. The -2.0 sentinel contract is unchanged and re-verified at batch sizes that straddle the chunk boundary (1,7,8,9,16,17,33), plus NULL rows, dim mismatches, zero-norm rows, and an all-invalid population. Notably the old ne11=300 path fails that same check at a 2e-6 cosine tolerance with 2299 mismatches, which is an independent confirmation of the defect.
El
A self-hosting, statically-typed language that compiles to C — built around a graph-native runtime instead of a database driver.
El is the execution substrate for the Neuron agent runtime, the DHARMA network, and the Engram knowledge graph. This repository is the monorepo for the whole stack: the language itself, the graph memory engine it's built to talk to natively, and the tools (package manager, IDE, UI framework, diagramming) built on top of it.
Why El exists
Every other language treats persistent, associative state as something you reach for through a driver — a SQL client, an ORM, a Redis library bolted on from outside. El inverts that: graph operations (engram_*) are runtime primitives, on the same footing as string or list operations. There is no separate database driver because the database is not separate.
El has four defining properties:
- Self-hosting compiler. The compiler (
lexer.el,parser.el,codegen.el,compiler.el) is written in El. It compiles El source to C, whichcccompiles against a fixed runtime into a native binary. A Rust genesis compiler bootstrapped the first iteration; the self-hosted binary atlang/dist/platform/elchas been the canonical compiler ever since — every binary indist/platform/was produced by an earlier version of itself compilingel-compiler/src/. The chain is auditable: source is the ground truth, not the binary. See lang/BOOTSTRAP.md for the full recovery path if that binary is ever lost. - C compilation target. Every compiled program is plain C11. Every El value is
el_val_t(int64_t); strings are heap pointers cast through it. Functions become C functions; top-level statements becomemain(). - Graph-native runtime. The runtime provides first-class graph operations over an in-process Engram store — no separate DB driver, no ORM.
- DHARMA-aware identity. A
cgiblock declares a program's DHARMA identity at compile time. The runtime resolves identity before user code runs, sodharma_*calls have a stable principal and channel surface throughout.
Architecture map
┌─────────────┐
│ lang │ El compiler + C runtime
│ (El itself) │ everything below is written in it,
└──────┬──────┘ or compiles down through it
│
┌─────────────┼─────────────┐
│ │ │
┌──────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ engram │ │ epm │ │ ide │
│ graph/mem │ │ package │ │ editor + │
│ substrate │ │ manager │ │ LSP │
└──────┬─────┘ └───────────┘ └───────────┘
│
┌───────┼────────────────┬─────────────────────┐
│ │ │ │
┌─────▼───┐ ┌─▼──────────┐ ┌──▼──────────┐ ┌─────▼──────┐
│ elp │ │ ql │ │ ui │ │ arbor │
│ NLG / │ │engram-el. │ |spreading- │ |arbor │
│ 31 langs│ │studio+tests│ |activation UI│ |diagram lang│
└─────────┘ └────────────┘ └─────────────┘ └────────────┘
lang is the foundation — the compiler and C runtime everything else builds on. engram is the graph-native memory/state engine that gives El its identity (property 3 above). Everything else is either a tool for working with El (epm, ide) or a system built on top of Engram's graph model (elp, ql, ui, arbor).
Repository layout
lang/ — the El language
The compiler and runtime. Self-hosting: elc-cli.el → compiler.el → lexer.el / parser.el / codegen.el / codegen-js.el, textually inlined and compiled in one pass. Compiles to C11 and links against el-compiler/runtime/el_seed.c, a hand-maintained OS-boundary layer (libcurl HTTP, pthreads, filesystem, arena allocation) — everything else in the runtime is native El (runtime/*.el).
Two layers to know: El programs (.el files — where nearly all work belongs) and the C seed (el_seed.c — edit only for genuine OS-level access; never re-implement what El can already express).
Current status (single source of truth: lang/spec/language.md): lexer/parser/codegen and the C runtime's core (I/O, strings, math, lists, maps, filesystem, args) are implemented. In flight: % operator, match-statement codegen, ? nil-propagation, cgi block parsing + DHARMA identity resolution, VBD role enforcement (@manager/@engine/@accessor), the real engram_* and dharma_* runtimes (currently stubs), and libcurl-backed http_get/http_post/http_serve. Bitwise operators, ??, and as casts are explicitly not in this language.
Key docs: AGENTS.md (agent-facing orientation), BOOTSTRAP.md (compiler recovery from scratch), spec/language.md, spec/codegen-js.md.
engram/ — graph intelligence substrate
A local-first memory substrate for accumulating intelligence, and the reason El's runtime doesn't need a database driver. Rust core (engram-core, engram-ffi) exposed to El and other languages (Kotlin, TypeScript/WASM, Go bindings).
The model: retrieval is spreading activation, not query. You name seed nodes and a query embedding; activation propagates outward through weighted edges, attenuating multiplicatively per hop (strength = parent_strength × edge_weight × target_salience × cosine_sim), gets pruned below a threshold, and the top-N nodes by activation strength come back. Storage and retrieval are the same structure — the way long-term potentiation works in biological memory, not the way a relational or vector database works.
Nodes live in four tiers (Working / Episodic / Semantic / Procedural, mirroring prefrontal / hippocampal / neocortical / cerebellar memory) and migrate between them based on salience decay — importance × recency-decay × log(activation_count). Forgetting is adaptive pruning, not a bug: unreinforced memories stop competing for attention without being deleted.
Backed by sled (embedded, local-first, no daemon) with flat cosine scan for vector search — deliberately simple until scale demands an HNSW layer. Full API and design rationale in engram/README.md.
elp/ — Engram Language Protocol
Bidirectional engine mapping between Engram semantic forms and natural-language surface text, across 31 languages — from Spanish and Japanese through historical/liturgical languages (Old Norse, Sanskrit, Sumerian, Coptic, Akkadian, Ge'ez). Compilation order runs language-profile + vocabulary → per-language morphology-* → grammar → realizer → semantics → elp. This is what lets an Engram graph node round-trip to and from readable text in any of those languages.
epm/ — El Package Manager
Manages vessels (El's package unit): publish, install, resolve dependencies. Vessels are stored in Engram as graph nodes, not files in a registry index — epm reads the local manifest.el, talks to Engram over HTTP, and writes resolved vessels to .epm/vessels/. Source: registry.el, install.el, update.el, manifest.el.
ide/ — El IDE
Three vessels: el-ide-server (HTTP backend — file ops, build/run, LSP bridge, plugin host, settings), el-lsp (the language server — completion, hover, diagnostics, outline, format, type graph), and el-plugin-host (first-party plugin lifecycle: install/remove/enable/disable). ide/projects/ and ide/examples/ hold sample projects, including the canonical hello-friends first-program walkthrough.
ql/ — engram-el
The El-native integration layer for a live Engram server — not a library (no importable modules, no build artifact), a set of standalone .el programs run directly via el run-file. Three components: Studio (studio/studio.el, a full terminal graph explorer), a Hebbian field-model proof of concept, and El builtin / LLM-builtin smoke test suites. This is the reference for correct patterns when an El program uses Engram as its substrate. Spec: ql/spec/elql.md.
ui/ — el-ui
A frontend framework where component state is an Engram graph and reactivity is spreading activation — not virtual-DOM diffing (React), Proxy-based dependency tracking (Vue), or compile-time analysis (Svelte). Re-renders are activated and propagated the same way associative memory retrieval works in engram/.
~15 vessels covering the full frontend surface: el-platform (env/fs/network/clock abstraction), el-config, el-html (SSR emit primitives), el-layout, el-style (design tokens/themes), el-i18n, el-auth / el-identity (JWT, sessions, OAuth PKCE — Engram-native), el-services (REST/gRPC/WebSocket bindings), el-aop (@authenticate/@authorize/@cache/@rate_limit decorators), el-secrets, el-graph (graph rendering/editor), el-publish (App Store / Play Store automation), and el-ui-compiler (El→JS component compiler; currently a stub pending a JS backend in elc). Spec: ui/spec/framework.md.
arbor/ — diagram language
A .arbor diagram language and toolchain: arbor-core (NodeId/shape/edge-kind types), arbor-parse (recursive-descent parser), arbor-diagram (IR + Mermaid serializer + architecture-diagram builders), arbor-layout (hierarchical layout — rank assignment, positioning, group bounds), arbor-render (SVG renderer), arbor-cli. (The architecture map above is the kind of diagram this is for.)
Getting started
Install the El SDK from the latest release:
bash lang/install.sh
# EL_VERSION=v1.0.0 bash lang/install.sh # pin a specific release tag
# EL_PREFIX=/opt/el bash lang/install.sh # custom install prefix
Or build the compiler from source and verify the self-hosting chain:
cd lang
./dist/platform/elc elc-cli.el > elc-new.c
cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
-o dist/platform/elc-new \
elc-new.c el-compiler/runtime/el_seed.c
# Confirm the new binary reproduces itself exactly
./dist/platform/elc-new elc-cli.el > elc-verify.c
diff elc-new.c elc-verify.c # should be identical
mv dist/platform/elc-new dist/platform/elc
Run your first program:
./lang/dist/platform/elc lang/examples/hello.el > hello.c
cc -std=c11 -I lang/el-compiler/runtime -lcurl -lpthread \
-o hello hello.c lang/el-compiler/runtime/el_seed.c
./hello
More examples in lang/examples/, including a full starter project at lang/examples/hello-project/.
If the compiler binary is ever lost or corrupted, lang/BOOTSTRAP.md is the authoritative recovery path.
Development workflow
Branching follows dev → stage → main: work lands on dev, promotes to stage for integration testing, and is promoted to main for release (visible directly in the git history of this repo). CI is defined per-subproject under .gitea/workflows/ — lang/epm/ide share the root pipeline; engram and ql carry their own (ci-dev, ci-stage, and a release workflow each).
- Language/runtime specs live at
*/spec/*.md(lang/spec/,ql/spec/,ui/spec/) and are the single source of truth for implemented-vs-planned status — code and docs are expected to agree with the spec's status markers, not the other way around. - Agent-facing orientation guides live at
*/AGENTS.md(currentlylang/AGENTS.md); more subprojects may grow their own as they need agent-specific conventions documented. - Tagged releases live under
lang/releases/, each with its ownRELEASE.md.
Status
This is an actively developed, internal monorepo — not yet published under an open license. Treat everything here as proprietary to Neuron Technologies unless told otherwise.