runtime: the link set is multi-file — name it once, ship all of it #160
Reference in New Issue
Block a user
Delete Branch "fix/runtime-shim-retire"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Root cause
Two things, and the second is the one that kept it growing.
1. The file is a temporary shim nobody retired. On 2026-05-03 at 17:10,
el_runtime.cwas deleted (10,607 lines) with the message "remove el_runtime.c — runtime is 100% native El", andAGENTS.mdwas updated to nameel_seed.c"the sole C dependency". 25 minutes later it was restored verbatim, becauseelbresolves the runtime by a hardcoded relative path andelccodegen emits#include "el_runtime.h". The restoring commit says: "restore el_runtime.c as build shim ... UNTIL the compiler is updated to emit#include el_seed.hand link el_seed.c directly."That
untilnever happened. 3.5 months later the shim is 20,527 lines / 961 KB. A file scheduled for deletion never gets a size budget, an owner, or a boundary.2. The active forcing function is a false sentence in the instruction file.
lang/AGENTS.mdsaidel_runtime.c"is the authoritative single-file link target for the compiler ... this is where a new C builtin's implementation must currently live to be linkable", and made it step 1 of the add-a-builtin recipe.That claim is false and provably so:
builtin_arityincodegen.elmaps NAME → ARITY INT only (~413 entries). The same doc already said it is "an arity guard only, not a dispatch table". The El name is emitted as the exact C symbol andldresolves it — placement is a link-time concern and the compiler cannot tell which.ca symbol came from.nm lang/dist/platform/elcon the shipped compiler showsT _engram_geo_reify_index_new,T _vindex_insert,T _engram_think,T _engram_reason_abduce. It is already linked from ten translation units.In a repo where agents write most of the code, a false instruction in the instruction file is the forcing function. The file grew because the recipe told every agent to grow it.
The live bug this exposed
The multi-file runtime is already real; distribution and docs never caught up.
el_runtime.c#includes six engram headers and makes hard cross-TU calls into all six — so every link line that namesel_runtime.calone is broken:Measured consequences, all fixed here:
sdk-release.yamlel_runtime.c/.h+engram_store.c/.h, none of the other 5 required.c— downstreamel-runtime-c/install.shconsumers got alib/that cannot link.githooks/pre-commitel_runtime.calone with2>/dev/null→ reported all 13 native suites FAILED, reallderror invisibleAGENTS.mdself-host recipeel-compiler/runtime/el_runtime.c— a path the same file's "DO NOT EDIT" list names as a lagging forkci-dev.yamlgcc -cprecompile stepAGENTS.md:211What changed
The root fix is to stop writing the link set down eight times.
lang/runtime/SOURCES(new) — the canonical link set, one place, in link order. 10 translation units.scripts/el-runtime-sources.sh(new) — prints it, optionally prefixed.--checkfails loudly on a missing file;--headersfor the shipped headers.AGENTS.md,lang/AGENTS.md,DESIGN.md,lang/spec/language.md, all three workflows and the pre-commit hook now reads that one list..cis one line in SOURCES — so a new builtin no longer has to be appended toel_runtime.cjust because appending was the cheaper edit.lang/AGENTS.md— the false instruction is corrected. New C builtins are pointed at the concern-owning.c(engram_store.c,engram_vindex.c,engram_geometry.c,engram_reason.c,engram_verify.c,engram_cognition.c), or a new file plus one line in SOURCES, orc_sourcein a program'smanifest.el(elbalready links those —parse_manifest_c_sources,lang/elb.el:82). It now states plainly that placement is link-time, with thenmevidence.AGENTS.md:211— resolved. Neither file supersedes the other; the canonical unit is the set, until the seed-only migration lands.Distribution — chose to ship the siblings, not amalgamate. Amalgamation needs a new tool and contradicts
DESIGN.md's compile-once-link-many. The siblings are already independently authored and independently tested (engram/test/*.shlink subsets directly), andengram_store.cwas already shipped — so this completes a mechanism that existed rather than inventing one. Source is also a superset: a consumer that wants one file can concatenate; one that wants separate TUs cannot undo an amalgamation.el-runtime-c/-hstay for backward compatibility;el-runtime-srcis added carrying the complete set plusSOURCES.Before / after
lang/runtime/el_runtime.clang/runtime/SOURCES).cfiles shipped by the SDKSOURCESThis PR is deliberately zero-line-change to
el_runtime.c. It removes the forcing function and fixes the shipped bug. The actual shrink is Stage 3.Verified locally (the bar — not CI)
engram/src/server.elcompiles and links against the SOURCES set ✓libel.alinks the same program ✓elbbuilds from the corrected recipe ✓What remains
5 pre-existing failing suites, untouched here — builtins registered in
builtin_aritywith no implementation or no declaration anywhere. Now visible because the linker error is no longer suppressed:test_fs—fs_list_jsonundeclaredtest_state—state_has,state_get_orundeclaredtest_json—json_build_array,json_build_object,json_escape_stringundefinedtest_time—now_nsundefinedtest_env— 1 assertionStage 2 (next PR): a CI growth guard beside
check-single-runtime.sh. A guard existed for the copy problem and none for the growth problem.Stage 3: move engram code out of
el_runtime.cinto the six sibling files. ~47.5% of the file (~9,700 lines) is engram code; only ~8.1% is actual EL core language runtime.Not attempted, recommended next: make
elcemit#include el_seed.hand dropelb's hardcoded runtime path — finishing the 2026-05-03 migration. It is the correct long-term fix but touches codegen and self-hosting, and belongs in its own change.Coordination
Does not touch
engram/src/server.el,peripheral/, orelp/. Deliberately does not editlang/AGENTS.md's "Rebuilding the Compiler" code block (lines 111-125) — PR #157 already corrects it; this PR edits only the Layer 2 section above it and the stale sentence in the caveat paragraph below it, which #157 does not touch.el_runtime.c was created 2026-05-03 as an explicitly temporary build shim. It was deleted that afternoon ("runtime is 100% native El") and restored 25 minutes later "UNTIL the compiler is updated to emit #include el_seed.h". The `until` never came. 3.5 months on it is 20,527 lines, and nothing was ever set up to notice — a file scheduled for deletion gets no owner, no budget, no boundary. What kept it growing is not inertia, it is an instruction. lang/AGENTS.md said el_runtime.c "is the authoritative single-file link target ... THIS IS WHERE A NEW C BUILTIN'S IMPLEMENTATION MUST CURRENTLY LIVE TO BE LINKABLE", and made it step 1 of the add-a-builtin recipe. That is false. Placement is a link-time concern: builtin_arity maps NAME -> ARITY INT only, the El name is emitted as the exact C symbol, and `ld` resolves it — the compiler cannot tell which .c a symbol came from. `nm lang/dist/platform/elc` on the shipped compiler already shows T _engram_geo_reify_index_new, T _vindex_insert, T _engram_think, T _engram_reason_abduce: it is linked from ten translation units today. In a repo where agents write most of the code, a false instruction in the instruction file is the forcing function. The file grew because the recipe said to grow it. The multi-file runtime is therefore already real, and the docs and the distribution never caught up — which left a live, shipped bug: * Linking el_runtime.c alone FAILS at `ld` (undefined engram_ground_json, engram_activate_inner, eg_find_relation, cog_assert_two_axis, ...) because el_runtime.c #includes six engram headers and calls into all six siblings. * sdk-release.yaml shipped el_runtime.c/.h + engram_store.c/.h and none of the other five required .c files, so downstream consumers of the el-runtime-c Artifact Registry package and of install.sh got a lib/ that cannot link. * .githooks/pre-commit linked el_runtime.c alone with stderr to /dev/null, so it reported all 13 native suites as FAILED with the real ld error invisible. * AGENTS.md's self-host recipe compiled el-compiler/runtime/el_runtime.c — a path the same file's "DO NOT EDIT" list names as a lagging fork. The root fix is to stop writing the list down eight times: * lang/runtime/SOURCES — the canonical link set, in one place, in link order. * scripts/el-runtime-sources.sh — prints it, optionally prefixed; --check fails loudly on a missing file, --headers for the shipped headers. * Every link line in AGENTS.md, lang/AGENTS.md, DESIGN.md, lang/spec/language.md, the three workflows and the pre-commit hook now reads that one list. * Adding a concern's .c is one line in SOURCES, so a new builtin no longer has to be appended to el_runtime.c just because appending was the cheaper edit. Distribution: ship the siblings rather than amalgamate. Amalgamation needs a new tool and contradicts DESIGN.md's compile-once-link-many; the siblings are already independently authored and independently tested (engram/test/*.sh link subsets directly), and engram_store.c was already shipped, so this completes a mechanism that existed rather than inventing one. Source is also a superset: a consumer that wants one file can concatenate, one that wants separate TUs cannot undo an amalgamation. el-runtime-c/-h stay for backward compatibility; el-runtime-src is added carrying the complete set plus SOURCES. lang/AGENTS.md now points new C builtins at the concern-owning .c and states plainly that the compiler cannot tell which .c a symbol came from, with the nm evidence. AGENTS.md's "reconcile which is canonical (verify)" note is resolved: neither file supersedes the other, the canonical unit is the set. Verified locally (the bar; not CI): * engram/src/server.el compiles and links against the SOURCES set. * Compile-once-link-many into libel.a links the same program. * elb builds from the corrected recipe. * Self-host fixpoint byte-identical (11,110 lines, stage2 == stage3) built with the SOURCES-driven link line. * pre-commit hook: 0 of 13 native suites passing -> 8 of 13. The 5 still-failing suites are PRE-EXISTING and untouched here: test_fs (fs_list_json undeclared), test_state (state_has, state_get_or undeclared), test_json (json_build_array/json_build_object/json_escape_string undefined), test_time (now_ns undefined), test_env (1 assertion). Builtins registered in builtin_arity with no implementation or no declaration anywhere — the same recipe defect, now visible because the linker error is no longer suppressed. Not attempted: making elc emit #include el_seed.h and dropping elb's hardcoded runtime path. That is the correct long-term fix and finishes the 2026-05-03 migration, but it touches codegen and self-hosting and belongs in its own change.