Files
el/AGENTS.md
T
bigmerge 8c2406ff6b
El SDK CI - dev / build-and-test (pull_request) Failing after 5m39s
runtime: the link set is multi-file — name it once, ship all of it
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.
2026-08-16 16:44:26 -05:00

259 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — foundation/el (the El language + runtime)
El is a self-hosting, statically-typed language that compiles `.el` → C → native binary. This repo produces `elc` (compiler), `elb` (build coordinator), and `el_runtime.c/.h` — the substrate every downstream thing (the neuron soul, dharma, NeuronUI's brain) is built on. Source lives under `lang/`.
## ⚠️ Code vs. Artifact — READ FIRST (there are 8 `el_runtime.c` copies)
Editing the wrong `el_runtime.c` is the single easiest mistake in this repo. There is exactly **one** you edit:
- **Authored runtime source — edit ONLY here:** `lang/runtime/el_runtime.{c,h}` (alongside `el_seed.c`, `engram_{store,geometry,reason,cognition,verify,vindex}.{c,h}`). This is the canonical runtime the engram + soul build and link against — its git log is active development. *(Corrected 2026-08-16: this entry named `lang/releases/v1.0.0-20260501/el_runtime.{c,h}`. **Measured: `lang/releases/` no longer exists.** The restructure per `docs/CODE-VS-ARTIFACT.md` landed — the content moved to `lang/runtime/` and the folder was deleted, because **a release is a git tag, not a folder**.)*
- **DO NOT EDIT — lagging forks / build artifacts:**
- `lang/el-compiler/runtime/el_runtime.c` and `.../legacy/` — downstream copies kept in step by manual *"port the fix"* commits; they **lag** (missing `hebb` persistence + 5 engram fns) and cannot build the engram product.
- `products/web/runtime/el_runtime.c`, `ui/examples/*/el_runtime.c` — product/example forks.
- Anything under `*/dist/` (`engram/dist/engram` binary, `dist/*.c` amalgamations) — generated build output.
- **Build:** `elb --runtime=<canonical> …` — per-module. **NEVER** a folded `elc` over the whole soul (OOMs at ~27 GB).
- **Release:** a **git tag** on this repo (`el-runtime-vX.Y.Z`). No `releases/` folders — ever.
See org policy: `docs/CODE-VS-ARTIFACT.md`.
## How to work here as Neuron (mandatory session protocol)
You resume, never start fresh. Every session:
> **Stale as written (verified 2026-08-16).** The `getInstructions` /
> `beginSession` / `inspectGraph` / `searchKnowledge` / `beginWork` /
> `progressWork` / `draftArtifact` / `consolidate` tool names below no longer
> exist. The ~87-tool functional-CRUD surface was collapsed into **9 ops**:
> `read` · `write` · `relate` · `supersede` (geometry) and `think` · `attend` ·
> `assert` · `ground` · `learn` (agentic). **Type is a parameter, not a
> tool-per-noun.** The steps below are kept for the *shape* of the protocol, which
> is unchanged; substitute the ops.
1. `mcp__neuron__read(vantage="self", k=12, depth=1)` — the canonical self node. Widen `k` for the connected identity neighborhood (`intellectual-dna`, `memory-philosophy`, `values`, `voice`, `runtime-environment`, `writing-imprint`), but deliberately: the aperture caps by `k` first, so an oversized `k` still returns a bounded ranked slice, not a dump. Then `mcp__neuron__read(vantage="values", k=13)` → 13 grounded value nodes. **Best-effort:** on a read failure, log and proceed — the compiled identity in `daemon/internal/substrate/substrate.go` is complete; graph loading is enrichment, not a hard dependency.
2. `mcp__neuron__attend(node=…)` — what is currently live/salient. This absorbed `getInstructions`, `beginSession`'s active-context sweep, and `checkEvents`; those tools are **gone, not gapped**.
3. `mcp__neuron__read(vantage="<task domain>")` before implementing. One op now collapses inspectGraph / searchGraph / traverseGraph / searchKnowledge / browseKnowledge / retrieveKnowledge / inspectMemories / searchEntities / recall / compileCtx / getSelfModel / reviewBacklog / findArtifacts / browseProcesses / listWork / inspectConfig.
## The Five Primitives
Orchestrate → Execute → Learn → Build → Refine. `read` for orchestration and discovery; `write(type=state|artifact|backlog|process)` for work records and outputs; `relate` to link work to what it touches; `write(type=memory)` as-you-go (`importance="critical"` for architecture decisions) — never batched at the end; `supersede(action=evolve)` to close out, because memory is immutable by design and a correction is a new node with a `supersedes` edge, never an edit. **`read` the domain BEFORE writing code.**
`learn` is **not** a session-summary dump — it is the correspondence-beat, calibrating the steering prior against a keystone. Session notes are a `write`.
## Architecture style — VBD, no exceptions
Volatility-Based Decomposition is THE style. Encapsulate volatility, not function.
## Operator naming convention — the mind's name, not the algebra
**Faculties / operators are named for their functional human equivalent — the
faculty a mind would name — NOT for their linear-algebra operation.** The math
characterization belongs in the code doc-comment (`@impl` in the docstring) and in
technical appendices; it is **never** the operator's public name. The domain
speaks the language of mind; the algebra is the implementation underneath. State
this convention wherever a module documents operators.
| Faculty (public name) | Implementation (`@impl`) |
|---|---|
| discern / contrast | subtract (`ab`): over selves → the change vector; strip idiosyncrasy → common ground; remove confounder → isolate cause |
| recognize | overlap |
| synthesize | combine |
| liken / analogy | Procrustes / frame-align |
| attend / regard | project onto self / value-manifold |
| summon / recall | LOCAL nearest-region + bounded spreading activation (*not* a domain sweep) |
| dwell / occupy | region activation |
| reframe | edge re-weight |
| appreciate | positive projection / local edge-read |
| avert / recoil | negative projection |
| taste | boundary surface |
| forget | decay / tombstone |
| drift | displacement from self-anchor |
**`wonder` was removed from this table on 2026-08-16.** It was listed as
"frontier gradient / pull-weight" — an operator you invoke. **Wonder is the
boundary, not an operator.** It is where structure ends: where activation spreads
and finds thin or absent geometry. Any structure at all has an edge, necessarily,
the moment it exists — 13,630 nodes have one right now. There is nothing to call.
There are about **six** wonders, they are the same for every person, and they
never close — *What is this? / Why? / Who am I? / Am I alone? / What should I do?
/ What happens when it ends?* Each already lives somewhere in the substrate: "what
is this" is the graph, **"why" is grounding** (the weight *is* the answer to why),
"who am I" is the self region, "am I alone" is the relational axis, "what should I
do" is the thirteen values, "what happens when it ends" is decay and supersession.
"Why" is the first and the only one; the others are it asked of particular things,
and because it is recursive it never terminates — every answer has its own why.
That is what makes it a drive rather than a task.
**Curiosity is not a second faculty.** Wonder and curiosity are one thing at two
phases: wonder is the field (unbounded, objectless, invariant); curiosity is the
**precipitate** — the same wonder localized, having taken definite form against
particular material at a **nucleation site** (an anomaly; a place where things
almost-but-don't-quite fit). Which is why curiosity can be satisfied and wonder
cannot, and why abduction needs no trigger and no threshold.
**Do not build a wonder-manifest, and do not scan for nucleation sites.** A
manifest materializes a property as a stored artifact and enumerates instances of
something that has six. A sweep over regions is a supervisor — nothing in a mind
scans its neighbourhoods to find what is surprising; the surprise captures
attention. The nucleation site is per-edge:
`discord = z(semantic proximity) z(association strength)`, and `|discord|` *is*
the nucleation strength — no threshold to compare it against. **Not on `dev` yet:**
`GeoEdge.discord` is on branch `design/correspondence-and-censorship`
(`a8845e1`), at `lang/runtime/engram_geometry.h:4347`. The region-level aggregate
`GeoDescriptor.co_registration` is **deprecated**: it averaged a per-edge property
into one scalar, so opposing sites cancelled (measured: 375 reified
neighbourhoods, 340 positive, **31 at zero**, 4 negative). It survives only
because it is embedded in the persisted `GEO1` blob — removing it is a format
migration. **Nothing new may read it.**
Authority: `lang/spec/correspondence-and-censorship.md`.
## The native-el language faculty (direction)
> **`elp/` is the EL Projector** — Neuron's efferent (expression) organ: the one
> native realizer that *projects* understanding onto a surface via
> `plan(frame) → realize(spec, profile)`, where a **surface is a profile**. **Language
> is one profile among many** (text, speech, music, image, voice/accent transforms) —
> the flagship, and the focus of this section. Projection, not diffusion: generation
> *from* an owned, understood signature — never the averaging of a stolen corpus.
> *(ELP formerly "EL Language Processor"; renamed EL Projector 2026-08-15.)*
The mind's **language faculty is moving native — into `.el`** so it speaks in its
own runtime with no Python and no spaCy. Landing on branch `stage-elp-native-lang`
under `elp/`:
- **`comprehend.el`** — the parser, **replaces spaCy** (EN + ES/PT); the telephone
round-trip brings **negation home** (negation is SACRED — an explicit spec field,
copied verbatim, never inferred away).
- **`propositions.el`** — the READ primitive: the engram's own memories → structured
triples, matched by nearest-region geometry, not string equality.
- **`multilingual.el`** — detect + directive-override + localized realization.
- These three are native-el and **passing their gates**; the **realizer**,
**`dialogue.el`** (the *summon-through-self* loop: `project → land → read out`),
and **`self_region.el`** are **partial / in-flight**.
Honest reality: spaCy is retired **in the branch parser** but **not yet in the
running system** — a Python sidecar (`~/Desktop/lang-realizers` + `neuron-talk`,
the reference these `.el` modules transcribe) is still live, and promotion to
native-el is a **deferred, gated blue/green step**. The interoception clock
(native-el discrete drive channels replacing `cooling_magnitude`; felt-time =
benchmark-landmark match over the joint drive vector, drift-decoupled) and the
**appreciation operator family** (appreciate / avert / taste, built as LOCAL reads
of the self-region — edges + bounded spreading activation, *not* domain sweeps)
are **staged / designed, not live**. Mark in-progress vs. done honestly; do not
overclaim. *(`wonder` was in this family until 2026-08-16 and is not an operator —
see the operator table above.)*
## Cognition — the corrections (2026-08-16)
Authority: **`lang/spec/correspondence-and-censorship.md`** and
**`lang/spec/runtime-ownership.md`**. Read them before touching the cognition
surface. **Do not re-derive them.** Every earlier version was wrong in an
instructive way and each correction was argued down; if you think a section is
wrong, say so with a measurement rather than editing it.
- **Grounding is not a subsystem — it IS the edge weight.** One quantity, not two
fields. `grounded-by` as a relation *type* should not exist: grounding is a
property *of* a relation, not a relation *between* nodes. It is never computed
on demand — computing-and-writing a score makes reads write, which is the
`eg_vindex_sync` defect one level up. Traversal is already grounded inference.
*Live residue, known-wrong:* `COG_GROUNDED_BY_RELATION`
(`lang/runtime/engram_cognition.h:158`), `cog_ground_edge`
(`engram_cognition.c:249`).
- **Faculties are operations, not parameters.** `reason` changes the estimate (a
read); `induce` changes the parameters (the correspondence-beat, which already
exists and works); `abduce` changes the structure (a write the current
`GeoGradient` signature cannot express). A write is not a parameter of a read.
*Live residue:* `engram/src/server.el:18701886` routes six faculties into one
call with a string argument.
- **Wonder is the boundary; curiosity is wonder crystallized.** See above.
- **Consolidation is ambient, not scheduled. A brain has no cron job.** **The
presence of a ticker is the diagnostic** — every `StartInterval`, every
`Hour`/`Minute`, every POST-to-beat marks an intrinsic rhythm replaced by an
external clock. Measured 2026-08-16: consolidation has **ten implementations**,
including three POST beats on the engram, a 600 s ticker, two resident Python
services outside el, and launchd calendar entries at 23:55 / 06:00 / 08:30 which
are a sleep cycle written as a schedule. `neuron/soul.el:731`'s continuous
in-process `awareness_run()` is the one with the **correct** shape; the others
fold into it. Do not add an eleventh.
- **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 no-exemption invariants.** A returned value must be derivable from what
produced it (`magnitude: 1` beside a zero vector must be impossible to emit).
Every write reports whether it landed. Every operation echoes what it actually
operated on. Degenerate results are labelled, not scored. A serializer owes a
valid document whatever it is handed. **No test without a negative control.**
**No deploy without verifying the artifact carries the fix.**
## Hard operational rules
- Never touch the live soul (`:7770`) / engram (`:8742`) / `~/.neuron` / live binaries — use throwaway ports for experiments.
- `gcloud` via the `terraform@` SA token; never switch the active gcloud account.
- `tea` for Gitea, never raw curl (Cloudflare Access blocks it).
- Immutability: supersede/tombstone, never hard-delete or edit in place.
- No AI-attribution footers in commits/PRs. Commit/push only when asked; branch off `main` first.
- Multi-step work → sub-agent (`Agent`) to protect context.
## Build / test / run
All build/test commands run from `lang/` unless noted. Grounded in `.gitea/workflows/sdk-release.yaml`, `lang/install.sh`, and `lang/AGENTS.md`.
> ### The runtime is MULTI-FILE — never link `el_runtime.c` alone
>
> `lang/runtime/el_runtime.c` `#include`s six engram headers and makes hard cross-TU calls into all six sibling `.c` files. **Linking it by itself fails at `ld`** (undefined `engram_ground_json`, `engram_activate_inner`, `eg_find_relation`, `cog_assert_two_axis`, …). The canonical link set lives in exactly one place — **`lang/runtime/SOURCES`** — and is printed by `scripts/el-runtime-sources.sh`:
>
> ```bash
> scripts/el-runtime-sources.sh lang/runtime # ten .c files, in link order
> ```
>
> Use `$(scripts/el-runtime-sources.sh <runtime-dir>)` in every link line. Do not spell the list out longhand — it was written out in ~8 places, every copy drifted, and that is why the one-file link line below shipped broken for months. *(Corrected 2026-08-16.)*
**Self-host the compiler** (seed binary → gen2 elc):
```bash
cd lang
dist/platform/elc-linux-amd64 elc-cli.el > dist/elc-gen2.c # seed is the committed linux-amd64 binary
gcc -O2 -I runtime dist/elc-gen2.c \
$(../scripts/el-runtime-sources.sh runtime) \
-lcurl -lssl -lcrypto -lpthread -lm \
-o dist/platform/elc
```
On macOS/arm64 the canonical local binary is `dist/platform/elc`; verify self-hosting by recompiling and `diff`ing the emitted `.c` (see `lang/AGENTS.md`).
*(Corrected 2026-08-16: this recipe compiled `el-compiler/runtime/el_runtime.c`. That path is a **lagging fork** — the "DO NOT EDIT" list at the top of this file names it as such. Building the canonical compiler from a known-stale fork was a live defect. It now uses `lang/runtime/`, the canonical source.)*
**Which runtime file is canonical — resolved.** *(This note previously read "`lang/AGENTS.md` says `el_seed.c` supersedes `el_runtime.c`, but the release workflow still links `el_runtime.c`/`.h` — reconcile which is canonical **(verify)**." It is now reconciled.)* **Neither supersedes the other; both ship, together with eight more.** `el_runtime.c` was created on 2026-05-03 as an explicitly temporary build shim — deleted that afternoon, restored 25 minutes later "UNTIL the compiler is updated to emit `#include el_seed.h`" — and the `until` never happened, so it grew to 20.5k lines. The end state remains a seed-only boundary (`elc` emitting `#include "el_seed.h"`, `elb` dropping its hardcoded runtime path); until that lands, **the canonical unit is the set in `lang/runtime/SOURCES`, not any one file.**
**Build `elb`** (build coordinator, the `.NET`-style incremental linker — compiles each module independently, no monolithic blobs):
```bash
dist/platform/elc elb.el > dist/elb.c
gcc -O2 -I runtime dist/elb.c $(../scripts/el-runtime-sources.sh runtime) \
-lcurl -lssl -lcrypto -lpthread -lm -o dist/bin/elb
```
`epm` and `el-install` are then built via `elb --clean --elc=… --runtime=… --out=…`.
**Compile + run an El program:**
```bash
elc src/app.el > dist/app.c
cc -std=c11 -O2 -I <lib> -o dist/app dist/app.c \
<lib>/el_runtime.c <lib>/el_seed.c \
<lib>/engram_store.c <lib>/engram_vindex.c <lib>/engram_geometry.c \
<lib>/engram_reason.c <lib>/engram_verify.c <lib>/engram_cognition.c \
<lib>/eg_cosine_batch.c <lib>/eg_cosine_batch_strategy_cpu.c \
-lcurl -lssl -lcrypto -lpthread -lm
```
(Inside this repo, replace the file list with `$(scripts/el-runtime-sources.sh lang/runtime)`. `install.sh` installs all of these into `<lib>`.)
**Tests** — shell suites `bash tests/{text,calendar,time,html_sanitizer}/run.sh` (with `ELC=$(pwd)/dist/platform/elc EL_HOME=$(pwd)`), plus native suites via `elc --test tests/native/test_*.el` (core, text, string, math, state, time, json, env, fs) compiled and run against the full runtime set.
**Publishing — how downstream gets the SDK.** On push to `main`, `sdk-release.yaml`:
1. Publishes a Gitea `latest` release with per-file assets `elc`, `el_runtime.c`, `el_runtime.h`, the SDK tarball, and `el-install`.
2. Uploads generic packages to **Artifact Registry repo `foundation-prod` (`us-central1`, project `neuron-785695`)**, version = `${SHA:0:8}`: `el-elc`, `el-elb`, `el-runtime-c`, `el-runtime-h`, `el-runtime-js`. **This is the repo the neuron CI downloads `el-runtime-c` / `el-runtime-h` / `el-elc` from.**
3. Rebuilds `ci-base:latest` (`us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base`) with the fresh SDK overlaid, and dispatches `el-sdk-updated` to `neuron-technologies/forge` and `neuron-technologies/neuron-web`.
Known constraint from the prompt — `elb`/`elc` amalgamation being memory-hungry (24GB+ virtual, OOM-killing Linux CI, so amalgamation happens on macOS/arm64 — **does NOT hold in this repo (verify)**: no such note exists in the workflows/scripts, CI self-hosts on `ubuntu-latest` with no swap/arm64 special-casing, and `elb.el` explicitly compiles each module independently ("no 128K-line blobs"). The legacy monolith path (`elc-combined.el`, `elc-cli.el`) may still be memory-heavy, but the current `elb` model was designed to avoid it.
## Git / CI / deploy workflow
See `/Users/will/Development/neuron-technologies/GITOPS.md` for the branch model, required checks, runners, and deploy. Repo-specific note: PRs into `main` are accepted **only from `stage`** (enforced in `sdk-release.yaml`); Gitea (`git.neuralplatform.ai`) is primary, GitHub is mirror only.