Compare commits

..

11 Commits

Author SHA1 Message Date
will.anderson 72fc6ffbf4 docs(engram): cognitive architecture design, prior-art scan, M10 reification, M8 perf profile, cutover-reversal runbooks, and 2026-08-12 session record 2026-08-13 00:22:29 -05:00
will.anderson 4965600d65 docs(engram): M9 geometry-priming reversal runbook + A/B perf profile
Reversal runbook for the ENGRAM_GEOMETRY_PRIMING cutover (default OFF, reversible
flag flip; exact rollback) and the A/B perf profile: default-OFF binary GO
(byte-identical to M8), enabling the flag NO-GO on latency (3.2x/13x) with no
demonstrated recall benefit; safety/sanitizer clean.
2026-08-12 20:29:16 -05:00
will.anderson 8e2269a205 fix(mcp-wrapper): declare real input schemas so tool args actually bite
The cognitive-graph and write tools advertised an empty inputSchema
({"properties":{}}), so MCP clients never sent entity_id/depth/query/
from_id/node_id etc. Graph reads fell back to the full neighborhood
(480-775KB, over transport limits) and write tools (forget, linkEntities,
evolveMemory) had no way to target a node.

- Declare per-tool JSON-Schemas matching the params each soul handler
  already accepts (76 of 87 tools; 11 are genuinely param-less).
- Read + forward the declared args: inspectGraph now honors depth (was
  reading only legacy max_depth), compact (default on), snip, k;
  traverseGraph accepts entity_id and defaults compact on so a depth-2
  walk stays bounded; retrieveKnowledge forwards depth/snip/k.
- compact_flag() reads the raw JSON token so an integer 0 / false / "0"
  opts out correctly (json_get_string could not see an integer and
  silently forced compact back on).

Builds on PR #149's compact projection; keeps the relevance-ranked
bound on by default for graph neighborhoods.
2026-08-10 16:27:03 -05:00
will.anderson 4bff40fa4a fix(api): bound inspect_graph with relevance-ranked projection; regen soul.c
Neuron Soul CI / build (pull_request) Failing after 14m5s
Neuron Soul CI / deploy (pull_request) Has been skipped
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.
2026-08-10 10:28:50 -05:00
will.anderson 64cd5055c5 self-review 2026-08-10: plumb the gauges that were computed and discarded
el_runtime.c emitted 19 metric keys from engram_metrics_json; emit_heartbeat
forwarded 14. Dropped on the floor: hebb_cands, hebb_cand_max, hebb_mass,
hebb_edges, embed_consec_fail. The first two are exactly the pair the runtime
added to answer the 08-06 question -- whether a stalled hebb_links means
nothing co-activates or the threshold is too high. Undiagnosable from the
durable record without them. An instrument computed but not plumbed to
durable storage is not an instrument.

Also adds the corpus damage STOCK, not just the flow. 08-08 fixed the JSON
parser, watched txt_damaged (nodes damaged by a write THIS process) fall to
0, and recorded the defect closed. Census today: 2781 of 4100 nodes still
damaged -- 67.8%, including the self root and every values node. A flow gauge
reads 0 both when the corpus is clean and when it is uniformly damaged but
quiescent. Sampled on a 30-beat countdown and carried with an explicit age.
2026-08-10 08:39:43 -05:00
will.anderson bc5e14a3e1 self-review 2026-08-08: put text integrity on the heartbeat
Today's review found the JSON parser had been replacing every \uXXXX escape
with a literal '?' for at least two months: 3,119 of 4,081 non-telemetry
nodes damaged, including the self traversal root and all 13 values nodes.
The parser is fixed in el_runtime.c. This is the part that keeps it fixed.

Every gauge on this heartbeat answers whether the machinery is running - WM
occupancy, Hebbian potentiation, embedding coverage, sync age, breaker state.
None answered whether the text the machinery carries is intact, which is why
two months of silent corruption read as a perfectly healthy system.

Adds txt_damaged: nodes created this process whose content carries the
character-loss signature. Flat 0 is healthy; any climb means a write path is
mangling text again. The full store census lives at GET /api/text-health,
which is too expensive for a 60s beat.

A gauge that exists but is not plumbed into the beat is not observability -
act_stats fields have to be extracted by name here or they are invisible.
2026-08-08 08:46:16 -05:00
will.anderson 86e269fa91 self-review 2026-08-07: push what was learned; stop a read route writing the canonical store
Two fixes, one found by making the other.

1. HEBBIAN WRITE-BACK. This daemon learned 1,198 associations in 23h48m and
kept none of them: it syncs FROM the engram server and never pushes, and
mem_save() is unreachable in HTTP mode by design (soul.el only sets
soul_snapshot_path inside `is_genesis && safe_to_seed`, false whenever
ENGRAM_URL is set, because the server owns persistence). So the one process
that runs idle cognition -- where essentially all co-activation happens -- was
the one process that could not remember what it learned.

hebb_consolidate() now drains the runtime's write-back queue on every heartbeat
and POSTs it as ONE batch to /api/edges/batch. One request, one durable write,
not one 60MB snapshot per edge. Also drains on clean shutdown, so an exit
between beats doesn't take the last 8 minutes of learning with it.

_auth is required and its absence is silent: check_auth_ok exempts GET and
/api/neuron/state-events (which is why ise_post works keyless) but gates every
other mutation on "_auth" in the BODY -- http_serve surfaces no headers, so
there is no Bearer path. An unauthorized reply is NON-EMPTY, so the obvious
`if resp == "" return 0` check would have reported delivery of edges that were
refused, after the drain had already destroyed them. Caught before it shipped.
Gauges hebb_wb_pending/_drained/_dropped/_sent go into the heartbeat so a
consolidation path that stops delivering is visible in the stream.

2. A READ ROUTE MUST NEVER WRITE THE CANONICAL SNAPSHOT. GET /api/graph/edges
serialized this process's graph straight over $HOME/.neuron/engram/snapshot.json
-- the engram SERVER's durable store -- and read the edges back out of it. I
triggered it myself this morning fetching edges for the census above:
snapshot.json went from the server's 41,213 edges to the soul's 42,431, and the
next engram restart loaded the soul's graph as canonical. It happened to be a
superset (Knowledge 1198->1218, Memory 1238->1242, no durable type down), so
nothing was lost. That was luck. Had the soul been running a partial load --
the exact failure soul.el's safe_to_seed guard exists to catch -- one GET would
have destroyed the store, with no write-side guard able to see it coming.

The engram server fixed this same class of bug on 2026-07-21 by routing exports
to a dotted sidecar; the soul kept the original pattern. Same fix: exports go to
.soul-edges-export.json. Also stops a 60MB serialize-and-reread per GET.

Verified: boot 26 loaded 42,432 edges with hebb_max 0.4941 carried across the
restart -- the first time this daemon has ever started knowing what it learned.
2026-08-07 08:46:53 -05:00
will.anderson 97d22ffe44 self-review 2026-08-06: a streak of nothing is not a streak; surface hebb gauges
str_eq("", "") is true, so an auto-term extractor that kept FAILING reported a
rising auto_term_streak. The signal meaning "fixated on one term" and the
signal meaning "producing no term at all" were the same number — opposite
failures needing opposite responses. Observed live as
{"auto_term":"","auto_term_streak":3}. Same class of bug already fixed for
wm_top0_streak on 2026-07-31; auto_term was missed then.

Empty now reads 0, and the empty run is counted on its own axis
(auto_term_empty_streak) so extractor failure is visible rather than disguised
as health.

Also surfaces the new runtime gauges in the heartbeat: hebb_warm, hebb_max,
hebb_links (is the graph learning any structure at all?) and dup_wm_global.
2026-08-06 08:44:34 -05:00
will.anderson a771ed2d0f self-review 2026-08-05: heartbeat carries redundancy-suppression gauges
dup_seeds / dup_wm from engram_act_stats_json. dup_seeds is the one that
matters day to day: a healthy nonzero rate means the suppressor is reclaiming
seed slots the June duplicate import was stealing; a sustained fall toward zero
means the duplicates were finally merged out of the graph, which is the repair
this defends against. Cumulative like wm_evicted/breakthroughs.
2026-08-05 08:40:13 -05:00
will.anderson 21710d5c8e self-review 2026-08-03: gate curiosity auto-terms on label document frequency
Reject an extracted auto-term when its label document frequency exceeds
node_count/400 (floor 8) -- measured live at 12,859 nodes, threshold 32.

Live label df separates the classes by an order of magnitude:
  rejected: <!--:220  SELF:175  Engram:125  CORE:88  STAR:36
  passed:   Dual:12  Sparse:8  Latent:6  MemQ:1  dGRPO:1  engram_goal_bias:1

Verified against the running soul (boot 21). Peak curiosity activation fell
from 541 to 113; the flood terms (SELF, CORE, Engram, STAR, <!--) are absent
from post-fix scans while topical compound identifiers pass untouched.
Sample is 7 scans -- suggestive, not conclusive; watch the next review.

Nested conditional rather than max(): El let is single-assignment, so the
floor is expressed as a second conjunct.

Verification note: content df was tested as an alternative signal and
rejected -- 'Curiosity' has the highest content df in the store (5526) yet
one of the lowest activation counts (113). Label df is the correct field
because label is what the first-word extractor reads.
2026-08-03 08:39:07 -05:00
will.anderson e60ca8123b self-review 2026-08-02: record that el_from_float on a literal is not the double-wrap bug
Investigated awareness.el ise_post's local-fallback engram_node_full call as a
suspected instance of the score-mangling double-wrap fixed in server.el on
2026-08-01. It is not one. Removing the wrapper produces byte-identical
codegen: the compiler treats el_from_float as the boxing intrinsic, so both
`el_from_float(0.3)` and a bare `0.3` emit exactly one el_from_float(0.3).

The server.el bug was different in kind - there the arguments came from
json_get_float(), already boxed as el_val_t, and wrapping those a second time
reinterprets the boxed bits as a raw double, fails engram_decode_score's range
check, and silently clamps to defaults.

Comment only, no behavior change. Recording the negative result at the call
site so the sweep criterion is right: look for el_from_float applied to an
already-boxed expression, never to a literal. Grepping the call name alone
produces false positives, which is what happened here.
2026-08-02 08:50:00 -05:00
197 changed files with 8621 additions and 64972 deletions
+38 -52
View File
@@ -34,12 +34,12 @@ jobs:
- name: Install build dependencies
run: |
apt-get update -qq
apt-get install -y gcc curl libcurl4-openssl-dev apt-transport-https ca-certificates
apt-get install -y gcc libcurl4-openssl-dev apt-transport-https ca-certificates
echo "deb [trusted=yes] https://packages.cloud.google.com/apt cloud-sdk main" \
> /etc/apt/sources.list.d/google-cloud-sdk.list
apt-get update -qq && apt-get install -y google-cloud-cli
- name: Authenticate to GCP + stage PINNED El runtime
- name: Download El runtime from Artifact Registry
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
run: |
@@ -47,37 +47,41 @@ jobs:
gcloud auth activate-service-account --key-file=/tmp/gcp-key.json
gcloud config set project neuron-785695
# PINNED RUNTIME — do NOT pull "latest" from Artifact Registry.
# The ship-soul calls engram_prune_telemetry (awareness.el sync/heartbeat
# self-review). The latest published el-runtime-c no longer defines that
# symbol, so an unpinned build fails to LINK — which is exactly how a
# broken/handlerless soul reached prod before. Compile against the
# vendored release runtime v1.0.0-20260501: the exact runtime the merged
# ship-soul was verified against (verify-soul-contract GATE PASS +
# genesis boot survives + full safety-contact response). It is committed
# under vendor/ so the soul build is fully reproducible and never depends
# on a moving AR "latest".
rm -rf /opt/el/runtime
mkdir -p /opt/el/runtime
cp vendor/el-runtime/v1.0.0-20260501/el_runtime.c /opt/el/runtime/el_runtime.c
cp vendor/el-runtime/v1.0.0-20260501/el_runtime.h /opt/el/runtime/el_runtime.h
echo "El runtime PINNED to v1.0.0-20260501: $(ls /opt/el/runtime/)"
# neuron#133: CI compiles dist/soul.c, NOT the .el sources. On 2026-08-07 a
# build off main would have shipped an engine with none of five merged fixes,
# including a P0 safety fix, while main's source read as correct. The runner
# cannot regenerate the amalgam (elc needs 24GB+ virtual memory), but it can
# refuse to compile a stale one. Fails loudly with the recipe in the message.
- name: Verify dist/soul.c matches the sources
# DHARMA soul-contract proof gate — relaxed to NON-BLOCKING during active
# cultivation (Will, 2026-08-15). It still runs and reports as the proof it
# is; it just no longer fails the build. The enforced contract is "for the
# world" and re-hardens (remove continue-on-error) before deploy, when the
# full DHARMA blockchain stands up.
continue-on-error: true
run: |
chmod +x tools/soulc-stamp.sh
./tools/soulc-stamp.sh --check
# Get latest version of each runtime package (elc/elb not needed — we compile
# dist/soul.c directly; running elb on Linux OOM-kills the runner, and we
# always use the repo's pre-built soul.c anyway).
get_latest() {
gcloud artifacts versions list \
--repository=foundation-prod \
--location=us-central1 \
--project=neuron-785695 \
--package="$1" \
--sort-by="~createTime" \
--limit=1 \
--format="value(name)" 2>/dev/null | awk -F/ '{print $NF}'
}
RC_VER=$(get_latest el-runtime-c)
RH_VER=$(get_latest el-runtime-h)
echo "Downloading runtime@${RC_VER}"
gcloud artifacts generic download \
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
--package=el-runtime-c --version="${RC_VER}" \
--destination=/opt/el/runtime/
gcloud artifacts generic download \
--repository=foundation-prod --location=us-central1 --project=neuron-785695 \
--package=el-runtime-h --version="${RH_VER}" \
--destination=/opt/el/runtime/
mv /opt/el/runtime/el_runtime.c* /opt/el/runtime/el_runtime.c 2>/dev/null || true
mv /opt/el/runtime/el_runtime.h* /opt/el/runtime/el_runtime.h 2>/dev/null || true
echo "El runtime ready: $(ls /opt/el/runtime/)"
- name: Build neuron soul binary
run: |
@@ -90,37 +94,19 @@ jobs:
# entirely: elb on Linux would OOM the runner (elc uses 24GB+ virtual memory
# on a 16GB host) and we always restore from the repo's soul.c anyway.
mkdir -p dist
# -rdynamic: the el runtime resolves the HTTP request handler (and the
# tool handlers) by NAME via dlsym(RTLD_DEFAULT, "handle_request").
# macOS exports these symbols freely, but glibc/Linux only makes symbols
# visible to dlsym if they are in the dynamic symbol table — so without
# -rdynamic the stripped Linux binary boots but returns "el-runtime: no
# http handler registered" for EVERY route (i.e. a soul that serves
# nothing). Same reason the Windows build links -Wl,--export-all-symbols.
cc -O2 -DHAVE_CURL -rdynamic \
cc -O2 -DHAVE_CURL \
-I$RUNTIME \
dist/soul.c \
$RUNTIME/el_runtime.c \
-lssl -lcrypto -lcurl -lpthread -lm \
-o dist/neuron
# -s strips .symtab + debug for size. .dynsym (which -rdynamic populated
# with the dlsym-resolved handlers) is preserved, so the handler still
# resolves after stripping.
# Strip debug symbols and non-essential symbol table entries.
# -s removes the symbol table + relocation info (max size reduction).
# Keeps the binary functional; debuggability is preserved via source + CI logs.
strip -s dist/neuron
ls -lh dist/neuron
- name: Soul contract gate (HARD BLOCK — no destructive/stale soul publishes)
run: |
# Boots dist/neuron on a throwaway port with a throwaway HOME/engram/cgi
# (never touches ~/.neuron or any live service) and fails the build if any
# app-contract route is unanswered (PRESENCE) or any engram write route
# hard-deletes instead of tombstoning/superseding (IMMUTABILITY). Non-zero
# here blocks Publish -> Artifact Registry -> GKE deploy, so a stale or
# memory-destroying soul can never reach prod.
chmod +x dist/neuron scripts/verify-soul-contract.sh
bash scripts/verify-soul-contract.sh dist/neuron 7796
- name: Smoke test
run: |
file dist/neuron
-10
View File
@@ -7,15 +7,5 @@ dist/*.backup-*
*.o
*.a
# elc/elb compiled header caches. DO NOT commit these: elc/elb silently
# prefer a stale committed .elh over recompiling its .el source, with no
# warning — a fresh checkout with these committed caches present can build
# and boot "successfully" while silently missing large chunks of code
# (found 2026-08-15: an amalgam regen with these present under-resolved to
# 251-645 of 2541 real functions, incl. losing the entire 31-language NLG/
# morphology stack, with exit code 0 and no error). Regenerate locally; never
# commit the cache.
*.elh
# macOS
.DS_Store
-170
View File
@@ -1,170 +0,0 @@
# AGENTS.md — neuron (the canonical CGI substrate: soul + engram + proxy + wrapper)
This is the core repo: the **soul** (the running agent), the **engram** (its memory graph),
and the MCP proxy/wrapper that expose it. Read this before touching anything here.
> Corrected 2026-08-15 during a local-build audit. This file previously existed only
> uncommitted on disk (never in git history) and documented the pre-collapse MCP tool
> surface as current. Both are fixed here — see the audit's findings in Neuron memory
> (tags `neuron-technologies/neuron,build-audit`) for full evidence.
## Code vs. Artifact
- **Authored source:** `*.el` + `*.elh` at the repo root (`awareness.el`, `chat.el`, `memory.el`, `neuron-api.el`, `persist.el`, `routes.el`, `safety.el`, `sessions.el`, `stewardship.el`, `imprint.el`, `studio.el`, `elp-input.el`, `manifest.el`) plus `cli/`, `council/`, `connectd/`, `mcp-proxy/`, `mcp-wrapper/` — edit here.
- **Artifacts (DO NOT hand-edit `dist/soul.c`):** `dist/soul.c` is a generated single-translation-unit amalgamation of the soul's full transitive `.el` import set, produced by concatenating the sources (import lines stripped) and running `elc` once — see "Build / regenerate" below for the exact, audit-verified recipe. `dist/*.c` per-module files and `dist/*.elh` headers alongside it are separate, also-generated artifacts from other tooling; don't hand-edit those either.
- **Release:** git tag `neuron-vX.Y.Z` on this repo. No `releases/` folders.
- Org-wide code-vs-artifact policy: `docs/CODE-VS-ARTIFACT.md` (this repo's own `dist/soul.c` situation is a special case of that policy — see below, not a duplicate of it).
## How to work here as Neuron (mandatory session protocol)
You do not start fresh — you resume. The live MCP surface is a 9-op collapse
(merged from the old ~90-tool surface in PR #153, `feat/mcp-wrapper-collapse-9ops`,
already merged to `main`): **`read`, `write`, `relate`, `supersede`** (geometry, live)
and **`think`, `attend`, `assert`, `ground`, `learn`** (agentic, pending Layer-2
cognition-build promotion). There is no `getInstructions`/`beginSession`/
`inspectGraph`/`searchKnowledge`/`compileCtx`/etc — those tool names no longer exist.
At the start of every session:
1. `mcp__neuron__read(vantage="self", k=12, depth=1)` — the canonical self node
(`kn-efeb4a5b-5aff-4759-8a97-7233099be6ee`). Widen `k`/`depth` deliberately if you
need the connected identity neighborhood (intellectual-dna, memory-philosophy,
values, voice, runtime-environment, writing-imprint) — the aperture caps output
by `k` first, so this is bounded by design, not a flattened dump.
Then `mcp__neuron__read(vantage="values", k=13)` for the 13 grounded value nodes.
- Best-effort: on a 502/520, log the id and proceed — the compiled `fixedSelf` in
`daemon/internal/substrate/substrate.go` is always complete.
2. `mcp__neuron__read(vantage="<task domain>")` before implementing anything.
3. `mcp__neuron__read(vantage="<project>", k=20)` for a bounded context snapshot when
resuming known work.
## The Five Primitives (every significant task)
**Orchestrate → Execute → Learn → Build → Refine**, all routed through the 9-op surface:
- Orchestrate: `read(vantage=...)` for backlog/roadmap/process discovery, `attend()` for
what's currently live/salient.
- Execute: `write(type="state", ...)` to open/advance work, `relate()` to link it to
what it touches.
- Learn: `write(type="memory", ...)` **as you go, not batched**; `importance="critical"`
for architecture decisions.
- Build: `write(type="artifact"|"backlog", ...)`.
- Refine: `supersede(id=..., action="evolve"|"tombstone"|"promote", ...)` for
completions and lessons-learned; `learn(seeds=..., faculty="induce")` to recalibrate
the steering-prior, not as a session-notes dump.
## Architecture style — VBD, no exceptions
Volatility-Based Decomposition is THE style. Encapsulate volatility, not function. Full docs:
**`docs/architecture/`** — `00-overview`, `02-components`, `03-data-and-memory`,
`04-runtime-and-deployment`, `06-cognitive-architecture`, `07-storage-coherence-and-distribution`.
Verified component map: `routes.el` = HTTP dispatcher (`handle_request`), `soul.el` = boot +
layered cycle, `awareness.el` = awareness daemon, `sessions.el`/`memory.el`/`safety.el`/
`stewardship.el` = managers; `engram` (separate repo) = the persistence/graph engine.
## Hard operational rules
- **Never touch the live soul (`:7770`) or engram (`:8742`), `~/.neuron`, or live binaries.**
Experiment on **throwaway ports** with a **scratch `HOME`**. The soul binary defaults to
`HOME=~` (your real `~/.neuron`) and `NEURON_PORT=7770` (live) if invoked bare — **never**
invoke it without an override `HOME` and `NEURON_PORT` set. Leaving `ENGRAM_URL` unset is
verified safe (see `soul.el:590`, `using_http_engram` gates the only HTTP call to any
engram endpoint — confirmed by source trace during the 2026-08-15 audit, not just
observed behavior) — it does not fall back to any live/network default.
- **Immutability:** memory/knowledge is append-only — **supersede/tombstone, never hard-delete or
edit in place.** The engram is immutable by design.
- **gcloud** via the `terraform@` SA token; **never switch the active gcloud account**.
- **`tea` for Gitea**, never raw `curl` (Cloudflare Access blocks it).
- **No AI-attribution footers** in commits/PRs. Commit/push only when asked; branch off `main` first.
- **Multi-step work → sub-agent** to protect the context window.
## Build / regenerate `dist/soul.c` (audit-verified 2026-08-15, macOS arm64)
There is no committed regeneration script upstream of this audit. The recipe below is
verified: it reproduces the committed `dist/soul.c`'s exact symbol set byte-for-byte in
content (modulo genuinely new code), and the resulting binary boots and answers `/health`.
**The compiler toolchain** lives in the sibling `foundation` repo, not this one:
`foundation/el/lang/dist/platform/elc-darwin-arm64` (put it on `$PATH` as `elc`; `elb`
also exists there but is NOT the right tool for this repo — see gotcha below).
**⚠ elc gotcha #1 — stale `.elh` header caches silently truncate the build.** This repo
(and the `dist/` dir) ships committed `.elh` header files. `elc`/`elb` prefer an existing
`.elh` over recompiling its source when present, with NO warning or error when the cached
header is stale/truncated — the build "succeeds" with silently missing code (observed:
251-645 of 2541 real functions, depending on which `.elh` files were present, including
losing the entire 31-language NLG/morphology stack with exit code 0). **Delete every
`*.elh` in the repo root and `dist/` before regenerating**, every time.
**⚠ elc gotcha #2`elb` cannot produce this repo's single-TU `dist/soul.c`.** `elb`
does per-module separate compilation (`--out=DIR` writes one `.c`/`.elh` pair per
module; the default `--out` is also a directory, `dist/` itself). This codebase's
`.el` modules call each other's functions without forward declarations (relying on
`elc`'s own single-pass, whole-file forward-declaration emission), so per-module
compilation always fails with `implicit-function-declaration` errors across module
boundaries. **Use plain `elc` on one manually-flattened file, not `elb`.**
**⚠ elc gotcha #3 — the manual-concatenation path silently drops functions.** When
`elc` compiles a flat, hand-concatenated `.el` file, it silently drops (no error, no
declaration, no definition) the 1-2 top-level function definitions immediately
following any multi-line leading `//` comment block or file-boundary transition —
reproduced deterministically. **Insert two trivial buffer functions
(`fn __amalgam_buf_N__() -> Int { return 0 }`) after every concatenated file's
content**, then strip them back out of the generated `.c` before committing.
**The actual steps:**
1. Delete all `*.elh` in repo root and `dist/`.
2. Concatenate, with `import` lines stripped, in this order: `elp.el`'s own 34-file
NLG/morphology chain (`foundation/el/elp/src/` — the order is documented in
`elp.el`'s own header comment: language-profile, vocabulary, morphology, the 30
`morphology-XX.el` engines, grammar, realizer, semantics, then `elp.el` itself),
then this repo's 13 soul modules in `elb`'s own reported dependency order:
`persist, memory, safety, stewardship, imprint, awareness, chat, studio,
elp-input, neuron-api, sessions, routes, soul`. Insert the 2-function buffer
after every file (works around gotcha #3).
3. `elc <flat-file> > dist/soul.c` against the **pinned** vendor runtime headers
(`vendor/el-runtime/v1.0.0-20260501/` — see "why pinned" below), not
`foundation/el/lang/el-compiler/runtime/` (that's the bleeding-edge runtime;
using it drops symbols like `engram_prune_telemetry` that this soul still calls).
4. Strip the buffer functions back out of `dist/soul.c` (a small regex: drop every
`el_val_t __amalgam_buf_\d+__(void);` decl line and every matching 4-line
definition block).
5. `tools/soulc-stamp.sh --write` to record the new fingerprint.
6. `bash tools/build-soul-from-dist.sh dist/neuron` to compile+link with CI's exact
flags (this script now auto-detects Homebrew's `openssl@3` lib path on macOS —
see gotcha #4).
**⚠ gotcha #4 — macOS needs an explicit OpenSSL library path.** `cc ... -lssl -lcrypto
-lcurl ...` fails with `ld: library 'ssl' not found` on macOS because Homebrew's
`openssl@3` is keg-only. `tools/build-soul-from-dist.sh` now adds
`-L$(brew --prefix openssl@3)/lib` automatically on Darwin; CI's Ubuntu runner needs
no such flag (`apt-get install libcurl4-openssl-dev` puts it on the default path).
**⚠ Build-integrity (unchanged from before this audit):** `dist/soul.c` is committed
and generated. CI compiles it **directly and never regenerates it** (`elb`/`elc` on
Linux OOM the runner). So **any `.el` change to the soul MUST be followed by
regenerating `dist/soul.c` (steps above) and committing it** — otherwise CI ships
stale behavior, exactly as happened between commit `72e0b82` (Aug 9) and `main` HEAD
before this audit (`dist/soul.c` was missing PR #122's 459-line chat.el change, incl.
a "silently break chat" fix, until this pass regenerated and re-stamped it).
`tools/soulc-stamp.sh --check` is the gate that catches this — **note it is currently
`continue-on-error: true` in CI** ("relaxed... during active cultivation", 2026-08-15),
so it reports but does not block; re-harden before it needs to actually stop a bad ship.
- **Tests:** El contract suite in `tests/*.el` (e.g. `test_layer_contract.el`, `test_safety.el`,
`test_sessions.el`, `test_soul_guard.el`). Run against a throwaway soul, never the live one.
- **Port topology (confirmed live, 2026-08-15):** soul `:7770`, engram `:8742`,
mcp-wrapper `:17779` (`MCP_PORT` env override in its LaunchAgent; source default is
`7779`), mcp-proxy `:7779` (the stable front door Claude Code actually connects to).
**`:7771` is a live three-way collision, not a single well-defined port** — `axon`
(soul.el's Rust backlog/memory/knowledge proxy, unbuilt), `neuron-connectd` (the MCP
connector sidecar `routes.el`/`chat.el` call — unbuilt; a local-dev stub now exists at
`connectd/`), and `council` (`council/`, an anti-confabulation LLM-voting service —
the one actually bound to `:7771` in Will's live environment) are all hardcoded to it.
See `connectd/README.md` for the full trace and the open question this leaves for Will.
- **Deploy:** merge to `main``.gitea/workflows/ci.yaml` builds + publishes `neuron-soul@<sha8>`
and blue/green-deploys to GKE `neuron-prod` via `scripts/blue-green-deploy.sh`. Self-improvement
experiments go to **stage** first (snapshot prod DB → deploy stage → verify → blue/green promote).
## Git / CI / deploy workflow
See **`../GITOPS.md`** (repo-family GitOps README): branch model, required checks, blue/green,
Cloud Run, Terraform/ESO/Vault, and the pack-objects/crawler incident runbook.
-139
View File
@@ -1,139 +0,0 @@
# PORT-NOTES — openai tools port working state (2026-08-06, session handoff-safe)
Spec: `docs/specs/SPEC-soul-openai-tools-v2-2026-08-06.md` (Tim-approved 2026-08-06). Tasks #1-5
tracked in-session (1 ✓ wiring verdict, 2 ✓ stub rig, 3 in-progress = THIS, 4-5 pending).
Worktree: HERE (`_wt-openai-tools`, branch `feat/soul-openai-tools-v2` @ dba755d). Round-9 trees
READ-ONLY. Nothing committed yet.
## Step-0 verdict (evidence in journal note ncli-653ba964dd76)
Shipped app never wires the v1 lane: launcher exports `SOUL_LLM_MODEL/PROVIDER/BASE_URL` +
`ANTHROPIC_API_KEY`+`SOUL_API_KEY` (= Keychain key for WHATEVER provider; installer/macos/
neuron-daemons.sh:288-300 on hotfix/beta-round9); brain reads only SOUL_LLM_MODEL (chat.el:8) and
NEURON_LLM_0_* (chat.el:1768-1794) which nothing sets. `/api/config` PATCH ignores llm_* fields
(studio.el:36 handle_config: POST-only, reads model/provider/api_key only).
**Bridge = brain-side ONLY (zero app-repo edits, zero round-9 collision):**
- `llm_base_url()`: NEURON_LLM_0_URL → fallback SOUL_LLM_BASE_URL when SOUL_LLM_PROVIDER ∉ {"","anthropic"}
- `llm_wire_format()`: NEURON_LLM_0_FORMAT → fallback derive from SOUL_LLM_PROVIDER (openai/grok/gemini/groq/ollama → "openai"; else "anthropic")
- `agentic_api_key()`: already works (ANTHROPIC_API_KEY carries the provider key); add NEURON_LLM_0_KEY → SOUL_API_KEY fallback.
## Design pins (stub asserts these — stub is green 58/58, tests/gate-openai/)
- Request MUST send `"tool_choice":"auto"` (string) + `"parallel_tool_calls":false` explicitly.
- `arguments` in tool_calls = JSON-ENCODED STRING; decode ONCE via json_get → feed dispatch_tool
verbatim. Stub's echo-mismatch check catches double-encode/decode (two-escaper trap).
- Assistant echo turn: `{"role":"assistant","content":null,"tool_calls":[...]}` VERBATIM from response.
- Feedback: `{"role":"tool","tool_call_id":"<id>","content":"<result string>"}`.
- Resume must NOT re-answer an answered id (stub 400s on repeat tool_call_id).
- Parallel tool_calls in a response: take FIRST only + log skip (mirror ADR-0005 stopgap); stub
scenario `parallel` proves behavior.
- No tools in request when tools array empty/absent turns (boot probes) — stub defaults tolerate.
## el idioms confirmed (from openai_chat_complete :1808-1854 + agentic_loop :2751-2838)
- JSON: `json_get(s,k)` decoded string · `json_get_raw(s,k)` raw subtree · `json_array_len` ·
`json_array_get(arr,i)` · build by string concat + `json_escape()` (:1797, OpenAI-lane escaper).
- HTTP: `let h: Map = {}` + `map_set(h,k,v)` + `http_post_with_headers(url, body, h)`;
Bearer auth via `Authorization` header when key non-empty (:1825-1830).
- Loop-carried vars must be top-level locals in the fn, mutated as if-expressions at while-body
top level (see :2760-2791 pattern + comment :2903-2904 region).
- Error shape: `str_starts_with(raw,"{\"error\"") || str_contains(raw,"\"error\":")` → return
`{"error":"llm unavailable","reply":""}` (:1835-1838).
## Remaining read map (before writing the fork)
- chat.el 2840-3200: block walk (2923-3000), policy gate (3009-3023: classify_tool_risk /
is_builtin_tool / ask_all / tool_auto_approved → needs_bridge), dispatch_tool call (3025),
tool_result feedback (3031, 3067-3072), run-progress ledger append (3078-3087), bridge_save
(3182), loop end + done envelope (~3100-3200).
- agentic_resume 3227-3293 (hardcoded Anthropic headers to make wire-aware; blob gets `wire` field,
legacy default anthropic) · handle_tool_result 3293+ · dharma fork site 3465 (calls agentic_loop
direct, no use_openai check today).
## Write plan (order)
1. Env fallbacks (edit llm_base_url/llm_wire_format/agentic_api_key) — small, first, testable alone.
2. `openai_tools_json(anthropic_tools: String) -> String` converter (walk array; per entry build
{"type":"function","function":{name,description,parameters:input_schema-raw}}).
3. `openai_agentic_loop(...)` fork: same signature as agentic_loop minus Anthropic-only params;
INCLUDE run-progress ledger + tools_log + iteration cap 12; NO container_id/ws_drift/web_search
(out of scope; strip web_search entry from tools via agentic_tools_literal()+connector merge,
NOT _with_web()).
4. Fork sites ×3: handle_chat_agentic :2695-2700 (route agentic to new loop when use_openai);
dharma :3465; agentic_resume wire-branch.
5. `chat.elh` extern decls. 6. Compile (recipe: dist/ + elc/elb per neuron-soul-build-deploy memory;
round-9 tree soul.c regen'd 08-06 proves toolchain live). 7. Gate: stub selftest recipe in
tests/gate-openai/README.md. 8. Anthropic-lane regression via gate9 (READ-ONLY consume from
_wt-beta-round9). 9. Live Groq E2E (scratch profile, free port, key via Keychain read-only).
## BUILD RECIPE — CORRECTED 2026-08-06 (the June memory is STALE for August code)
`~/el-sdk/el_runtime.c` (Jun 15) is MISSING builtins the Aug engine calls (`engram_wm_count`,
`engram_wm_top_json`, `http_delete_json`, `http_serve_async`) → link fails with
"symbol(s) not found for architecture arm64". Use the REPO-PINNED runtime:
```
mkdir -p <scratch>
elb --elc=$HOME/el-sdk/elc --runtime=vendor/el-runtime/v1.0.0-20260501 --out=<scratch>/
# "elb: link failed" at the end is EXPECTED and harmless — the per-module .c files are produced
cc -std=c11 -O1 -DHAVE_CURL -rdynamic \
-I vendor/el-runtime/v1.0.0-20260501 -I <scratch> -I /opt/homebrew/opt/openssl@3/include \
-L /opt/homebrew/opt/openssl@3/lib \
-include dist/elp-c-decls.h -Wno-error=implicit-function-declaration \
-o <scratch>/soul <scratch>/*.c vendor/el-runtime/v1.0.0-20260501/el_runtime.c \
-lssl -lcrypto -lcurl -lpthread -lm
```
Source: `_engine-plainchat-20260805/README.md:396-412`. Verified today: 0 errors, 887,296 B.
`elb` ALSO rewrites every `*.elh` in the tree (cosmetic em-dash→hyphen in the auto-gen banner,
plus true-ups) and drops a stray `soul..elh``git restore` the unrelated ones and delete the
stray before staging, or the diff drowns in noise.
## SELF-REVIEW FIX LIST (found by reading my own diff, 2026-08-06 — apply in ONE batch, then rebuild once)
- **F3 (CORRECTNESS, do first):** the assistant echo currently replays the provider's FULL
`tool_calls` array (`tc_arr`) while the loop answers only the FIRST call. If a provider ignores
`parallel_tool_calls:false`, the next request carries an assistant turn with N tool_calls and
only ONE `role:"tool"` response → most OpenAI-format providers 400 ("missing tool response for
id X") and the run dies. This is the same class as ADR-0005's Anthropic failure, but here it is
cheap to close: echo ONLY the honored call (`"[" + tc0 + "]"`), so the conversation we send is
self-consistent and the dropped call never existed from the model's view. The DRIFT log line
stays (honest accounting of what we dropped).
- **F4 (efficiency/latency):** `handle_chat_agentic` computes `agentic_tools_all()` at ~:2681
BEFORE the fork, then the OpenAI branch computes `agentic_tools_no_web()` again — two
`connector_tools_json()` calls per turn, each an HTTP round-trip to the connector bridge on
:7771 (two timeout exposures). Fix: compute the tools array ONCE, per lane, after `use_openai`
is known (check no other use of `tools_json` sits between :2681 and the fork before moving it).
Note: `openai_tools_json()` already skips any entry with no `input_schema`, so Anthropic's
server-side `web_search` entry is auto-dropped even if the full array is passed —
`agentic_tools_no_web()` is kept for EXPLICITNESS, not necessity.
- **F1 (debuggability):** the "no choices in response" branch logs a generic string and discards
the body. Log the response head (as the `is_error` branch does) — a provider that returns 200
with an unexpected shape is otherwise undiagnosable from the log.
- **OPEN QUESTION (evidence pending from the gate):** the tool-result feedback turn escapes with
`json_escape()` (this lane's escaper) rather than `json_safe()` (used everywhere else). The
Anthropic lane escapes that field with NEITHER, which is a latent defect on that side. If the
torture scenario shows any escaping loss, switch to `json_safe` and note the Anthropic-side
finding for Will.
## TEST HARNESS — built 2026-08-06 (Task 4 side-work, reusable by anyone)
- `tests/run-el-test.sh <tests/test_x.el> | --all` — the engine tests were NEVER runnable
before this (`elc` is a compiler: emits C to stdout and exits). It emits the test to C,
compiles `soul.c` separately with `main` renamed away (soul.c owns the daemon's real main
but also defines `layered_cycle` et al.), links the remaining modules + the repo-pinned
runtime, and executes. Modules cached under `/tmp/el-test-<worktree>/`; `REBUILD=1` forces.
- **The runner computes the verdict itself** because the test FILES cannot: all 9 counted
test files do `let pass_count = pass_count + 1` inside an if BLOCK, which El scoping
discards, so every summary line reads `0 passed, 0 failed` forever. Per-assertion
`PASS:`/`FAIL:` lines ARE reliable; the runner counts those, exits non-zero on any FAIL
or on zero assertions, and was proven to discriminate with a negative control (broken
assertion → 31 passed / 1 failed / exit 1). Real in-file fix filed: **neuron#116**.
- `tests/test_bridge_serialization.el`: 4 `bridge_save` calls updated for the new `wire`
argument, plus **Section 9** (8 new assertions) covering wire round-trip both ways, the
legacy no-wire blob (resumes as anthropic), and a FIELD-ORDER decoy guard — a fake
`"wire":"anthropic"` planted inside `messages_raw` must not beat the blob's own scalar.
That decoy is the round-9 first-match-scanner bug class, now pinned by a test. **32/32 green.**
## MEMORY-SAVE CAVEAT RESOLVED 2026-08-06
Earlier saves this session reported `-> OUTBOX only (real mind unreachable or read-back
failed)`. That was a **read-back verifier false negative, not data loss** — a direct
`POST :7770/api/neuron/recall` returns those notes from the live mind verbatim. Another
terminal was fixing exactly this (multi-word read-back probe) the same afternoon. Do NOT
re-save on an OUTBOX report without first querying the mind directly, or you duplicate nodes.
## Standing cautions
- PERSIST OFF on the real mind this boot (neuron#98/#92): journal saves only, ferry later. MCP link
down this terminal; use neuron_remember.py / neuron_recall.py.
- Aug-16: Groq retires llama-3.3-70b-versatile (separate P0, Tim's call, catalog swap).
- Never bind 7770/7779/17779; never touch ~/.neuron; round-9 worktrees read-only.
-28
View File
@@ -1,28 +0,0 @@
# neuron
The canonical CGI substrate: the **soul** (the running agent), the **engram** (its memory
graph), and the MCP proxy/wrapper that expose it. See `AGENTS.md` for detail, including
the audit-verified local build/regenerate recipe and known local-build gotchas.
## Quick local build
```bash
# 1. dist/soul.c must match current .el sources — this refuses otherwise:
bash tools/build-soul-from-dist.sh dist/neuron
# 2. If it refuses (stale amalgam), regenerate first — see AGENTS.md's
# "Build / regenerate dist/soul.c" section for the full, gotcha-laden recipe.
```
For a full local dev stack (soul + engram + mcp-wrapper + mcp-proxy, wired into Claude
Code) see `neuron-dev-setup/README.md` instead — this repo alone only builds the soul.
## Code vs. Artifact
- **Authored source:** `*.el` + `*.elh` at the repo root plus `cli/`, `council/`,
`connectd/`, `mcp-proxy/`, `mcp-wrapper/` — edit here.
- **Artifacts (do not hand-edit):** `dist/soul.c` (generated single-TU amalgam —
regenerate via the recipe in `AGENTS.md`, then `tools/soulc-stamp.sh --write`) and
the `dist/neuron` binary it compiles to.
- **Release:** git tag `neuron-vX.Y.Z` on this repo. No `releases/` folders.
See org policy: `docs/CODE-VS-ARTIFACT.md`.
+310 -21
View File
@@ -30,6 +30,74 @@ fn idle_reset() -> Void {
// read decide where telemetry goes. The in-process write remains only as a
// last resort when the HTTP POST itself fails, and is tagged ise-fallback-local
// so misrouting is visible in the stream instead of silent.
// hebb_consolidate push self-formed associations to the durable store.
//
// WHY THIS EXISTS (2026-08-07 self-review, measured on the live system).
// Yesterday's eligibility-trace fix made Hebbian learning work: hebb_max
// 0.000799 -> 0.4725, and 1,198 hebbian-associate edges formed in 23h48m.
// A census this morning found all 1,198 of them living in this process's RAM
// and nowhere else:
//
// soul daemon in-process graph: 42,426 edges, 1,198 hebbian
// engram server (:8742, durable): 41,213 edges, 49 hebbian
//
// The soul pulls from the server every 10 min (GET /api/sync) and never
// pushes. It also cannot save its own snapshot: soul.el only sets
// soul_snapshot_path inside `if is_genesis && safe_to_seed`, and safe_to_seed
// is unconditionally false when ENGRAM_URL is set which it is, in the
// launchd plist because the HTTP server owns persistence and a soul writing
// snapshot.json would clobber it. That guard is right. So mem_save() below has
// literally never run, and this daemon (the ONLY process doing idle cognition,
// therefore where essentially all co-activation happens) was throwing away
// every association it learned, every restart, silently.
//
// The fix is not to let the soul write the file. It is to make consolidation a
// message: hand each newly-formed edge to the durable store over the API the
// server already exposes. Fast volatile store learns online; slow durable store
// keeps what cleared the threshold. Only edges past ENGRAM_HEBB_LINK_MIN are
// ever queued, so what crosses the boundary already earned it.
//
// Failure is non-fatal by construction: a drained entry that fails to POST is
// gone, and that is fine a real association re-forms from live co-activation.
// The counts go into the heartbeat (hebb_wb_*) so a consolidation path that has
// stopped delivering is visible in the stream rather than in a later autopsy.
fn hebb_consolidate() -> Int {
let batch: String = engram_hebb_drain_json(64)
if str_eq(batch, "") { return 0 }
if str_eq(batch, "[]") { return 0 }
let n: Int = json_array_len(batch)
if n == 0 { return 0 }
let url_env: String = env("SOUL_ISE_URL")
let url_state: String = if str_eq(url_env, "") { state_get("soul_engram_url") } else { url_env }
let engram_url: String = if str_eq(url_state, "") { "http://localhost:8742" } else { url_state }
// ONE request for the whole batch, not one per edge. The server's
// persist_canonical() writes the full 60MB snapshot on every durable
// write, so per-edge POSTs would cost ~840MB of disk per heartbeat to
// persist ~14 associations. /api/edges/batch connects them all and
// snapshots once. The drain payload is already the right shape; it only
// needs an envelope: the drain already emits the relation per entry.
//
// _auth is REQUIRED and its absence is silent. check_auth_ok() in server.el
// exempts GET and /api/neuron/state-events (which is why ise_post works
// without a key) but gates every other mutation on "_auth" in the BODY
// http_serve does not surface request headers, so there is no Bearer path.
// A batch posted without it comes back {"error":"unauthorized"}, which is a
// non-empty response: the naive `if resp == "" return 0` check would read
// that as success and report edges delivered that were in fact refused,
// after the drain had already destroyed them. Hence both the key and the
// accepted-count check below. Fall back to env when the state key is empty
// never let a corruptible state read decide whether learning persists.
let key_state: String = state_get("soul_engram_api_key")
let api_key: String = if str_eq(key_state, "") { env("ENGRAM_API_KEY") } else { key_state }
let auth_part: String = if str_eq(api_key, "") { "" } else { ",\"_auth\":\"" + api_key + "\"" }
let body: String = "{\"edges\":" + batch + auth_part + "}"
let resp: String = http_post_json(engram_url + "/api/edges/batch", body)
if str_eq(resp, "") { return 0 }
let acc: String = json_get(resp, "accepted")
if str_eq(acc, "") { return 0 }
return str_to_int(acc)
}
fn ise_post(content: String) -> Void {
let ise_url: String = env("SOUL_ISE_URL")
let state_url: String = if str_eq(ise_url, "") { state_get("soul_engram_url") } else { ise_url }
@@ -54,6 +122,21 @@ fn ise_post(content: String) -> Void {
let fail_raw: String = state_get("soul.ise_fail_count")
let fail_n: Int = if str_eq(fail_raw, "") { 0 } else { str_to_int(fail_raw) }
state_set("soul.ise_fail_count", int_to_str(fail_n + 1))
// el_from_float on a LITERAL is correct and is NOT the double-wrap bug
// (checked and dismissed 2026-08-02 self-review — recording the result
// so this call site is not "fixed" again by the next reader).
// The compiler treats el_from_float as the boxing intrinsic: both
// `el_from_float(0.3)` and a bare `0.3` emit exactly one
// el_from_float(0.3) in dist/awareness.c. Verified byte-identical
// codegen either way.
// The real bug fixed in server.el on 2026-08-01 was different: there
// the arguments came from json_get_float(), i.e. values ALREADY boxed
// as el_val_t. Wrapping THOSE a second time reinterprets the boxed
// bits as a raw double, fails engram_decode_score's range check, and
// silently clamps to defaults.
// The sweep criterion is therefore "el_from_float applied to an
// already-boxed expression", never "el_from_float applied to a
// literal". Grepping for the call name alone produces false positives.
let discard: String = engram_node_full(
content, "InternalStateEvent", "state-event",
el_from_float(0.3), el_from_float(0.3), el_from_float(0.8),
@@ -299,8 +382,89 @@ fn emit_heartbeat() -> Void {
// here so the stuck-term failure is visible in the heartbeat stream too.
let hb_ats_raw: String = state_get("soul.auto_term_streak")
let hb_ats: Int = if str_eq(hb_ats_raw, "") { 0 } else { str_to_int(hb_ats_raw) }
// auto_term_empty_streak (2026-08-06): consecutive scans producing NO auto
// term. Split out because str_eq("","") made the two failures indist-
// inguishable — see the comment at the streak computation in
// proactive_curiosity. Nonzero and climbing = extractor broken, not stuck.
let hb_ate_raw: String = state_get("soul.auto_term_empty_streak")
let hb_ate: Int = if str_eq(hb_ate_raw, "") { 0 } else { str_to_int(hb_ate_raw) }
// Hebbian eligibility gauges (2026-08-06 self-review). The graph learned
// ZERO structure in its first 23h of uptime: hebb_max 0.000799 against a
// 0.15 consolidation threshold, hebbian-associate edges 0, and the
// awareness loop calls engram_connect nowhere — so Hebbian consolidation
// is the only self-structuring path there is, and it was inert.
// hebb_warm — nodes with a live eligibility trace but NOT co-resident in
// WM: exactly the population the old simultaneity rule threw
// away. 0 forever ⇒ traces never arm and this bought nothing.
// hebb_max — strongest single association. The number that has to move.
// hebb_links— consolidated edges. The outcome that has to become nonzero.
let hebb_warm_raw: String = json_get(act_stats, "hebb_warm")
let hebb_warm: String = if str_eq(hebb_warm_raw, "") { "-1" } else { hebb_warm_raw }
let hebb_max_raw: String = json_get(act_stats, "hebb_max")
let hebb_max: String = if str_eq(hebb_max_raw, "") { "-1" } else { hebb_max_raw }
let hebb_links_raw: String = json_get(act_stats, "hebb_links")
let hebb_links: String = if str_eq(hebb_links_raw, "") { "-1" } else { hebb_links_raw }
// Candidate-table gauges (2026-08-10 self-review). el_runtime.c COMPUTES
// hebb_cands/hebb_cand_max/hebb_mass/hebb_edges and emits them from
// engram_metrics_json — and this function dropped all four on the floor.
// Nineteen keys crossed the C boundary; fourteen reached the ISE stream.
// The two that mattered most are exactly the pair the runtime added to
// answer the question the 08-06 review had to instrument for:
// hebb_cands — associations currently being tracked toward
// consolidation. 0 ⇒ nothing co-activates at all.
// hebb_cand_max — how close the leading candidate is to
// ENGRAM_HEBB_LINK_MIN (0.15). Sustained just-below ⇒
// the THRESHOLD is the bottleneck, not the event rate.
// Without both, "hebb_links stopped climbing" is undiagnosable from the
// durable record: nothing-co-activates and threshold-too-high look
// identical. An instrument that is computed but not plumbed to durable
// storage is not an instrument — it is a local variable.
// hebb_mass — Σ hebb across edges; the runaway detector against the
// ENGRAM_HEBB_NODE_BUDGET homeostatic cap.
// hebb_edges — total potentiated edges (hebb > MIN), the denominator
// hebb_max is the max of.
let hebb_cands_raw: String = json_get(act_stats, "hebb_cands")
let hebb_cands: String = if str_eq(hebb_cands_raw, "") { "-1" } else { hebb_cands_raw }
let hebb_cmax_raw: String = json_get(act_stats, "hebb_cand_max")
let hebb_cmax: String = if str_eq(hebb_cmax_raw, "") { "-1" } else { hebb_cmax_raw }
let hebb_mass_raw: String = json_get(act_stats, "hebb_mass")
let hebb_mass: String = if str_eq(hebb_mass_raw, "") { "-1" } else { hebb_mass_raw }
let hebb_edges_raw: String = json_get(act_stats, "hebb_edges")
let hebb_edges: String = if str_eq(hebb_edges_raw, "") { "-1" } else { hebb_edges_raw }
// Consolidation write-back gauges (2026-08-07 self-review). hebb_links
// counts what this process LEARNED; these three count what SURVIVES it.
// The distinction is the whole finding: 1,198 links formed, 0 persisted,
// because the learner is not the persistence owner (see hebb_consolidate).
// wb_pending — queued, not yet handed over. Climbing ⇒ writer is down.
// wb_drained — cumulative popped for delivery. Flat while hebb_links
// climbs ⇒ the drain is not being called at all.
// wb_dropped — lost to a full queue. Must stay 0; nonzero means the
// durable store has been unreachable long enough to matter.
// wb_sent — POSTs the durable store actually accepted this beat.
let wb_pend_raw: String = json_get(act_stats, "hebb_wb_pending")
let wb_pend: String = if str_eq(wb_pend_raw, "") { "-1" } else { wb_pend_raw }
let wb_drain_raw: String = json_get(act_stats, "hebb_wb_drained")
let wb_drain: String = if str_eq(wb_drain_raw, "") { "-1" } else { wb_drain_raw }
let wb_drop_raw: String = json_get(act_stats, "hebb_wb_dropped")
let wb_drop: String = if str_eq(wb_drop_raw, "") { "-1" } else { wb_drop_raw }
let wb_sent_raw: String = state_get("soul.hebb_wb_sent")
let wb_sent: String = if str_eq(wb_sent_raw, "") { "0" } else { wb_sent_raw }
// dup_wm_global (2026-08-06): redundant WM residents that arrived via the
// carry-over path, which Pass 3½ structurally could not see. Confirmed live
// by a census that caught two byte-identical copies of one 3,193-char
// document both holding slots.
let dup_wm_g_raw: String = json_get(act_stats, "dup_wm_global")
let dup_wm_g: String = if str_eq(dup_wm_g_raw, "") { "-1" } else { dup_wm_g_raw }
let act_brk_raw: String = json_get(act_stats, "embed_breaker_open")
let act_brk: String = if str_eq(act_brk_raw, "") { "-1" } else { act_brk_raw }
// embed_consec_fail (2026-08-10 self-review): also computed by the C side
// and also dropped here. embed_breaker_open is the LAGGING indicator — it
// only goes 1 after ENGRAM_EMBED_BREAKER_LIMIT consecutive failures, by
// which point semantic activation has already degraded to pure lexical
// for the whole cooldown. consec_fail is the leading edge of the same
// event and costs nothing to carry.
let emb_cf_raw: String = json_get(act_stats, "embed_consec_fail")
let emb_cf: String = if str_eq(emb_cf_raw, "") { "-1" } else { emb_cf_raw }
// ctx_cos (2026-07-29 self-review): cos(query, context centroid) at the
// last activate call — the drift gauge for the new context-centroid
// scoring. ~1.0 aligned; low at domain-rotation boundaries is healthy;
@@ -308,7 +472,79 @@ fn emit_heartbeat() -> Void {
// down) and semantic continuity is silently absent.
let ctx_cos_raw: String = json_get(act_stats, "ctx_cos")
let ctx_cos: String = if str_eq(ctx_cos_raw, "") { "-2" } else { ctx_cos_raw }
let payload: String = "{\"event\":\"heartbeat\",\"pulse\":" + pulse + ",\"tick\":" + pulse + ",\"boot\":" + boot + ",\"idle\":" + idle + ",\"idle_ms\":" + int_to_str(idle_ms) + ",\"node_count\":" + int_to_str(nc) + ",\"edge_count\":" + int_to_str(ec) + ",\"node_delta\":" + int_to_str(node_delta) + ",\"edge_delta\":" + int_to_str(edge_delta) + ",\"wm_active\":" + int_to_str(wmc) + ",\"wm_delta\":" + int_to_str(wm_delta) + ",\"wm_saturated\":" + int_to_str(wm_sat) + ",\"wm_top0_streak\":" + int_to_str(t0streak) + ",\"wm_churn\":" + int_to_str(wm_churn) + ",\"wm_top0_wm\":" + wm_top0_wm + ",\"sync_added_total\":" + sat_str + ",\"sync_age_ms\":" + int_to_str(sync_age) + ",\"wm_avg_weight\":" + wm_avg_str + ",\"wm_top\":" + wm_top + ",\"ts\":" + int_to_str(ts) + ",\"uptime_ms\":" + int_to_str(up_ms) + ",\"uptime\":\"" + up_human + "\",\"embed_ok\":" + int_to_str(emb_ok) + ",\"embed_backfilled\":" + bf_done + ",\"embed_count\":" + bf_total + ",\"embed_eligible\":" + embed_elig + ",\"wm_evicted\":" + act_evict + ",\"wm_evicted_delta\":" + int_to_str(evict_delta) + ",\"breakthroughs\":" + act_bt + ",\"breakthroughs_delta\":" + int_to_str(bt_delta) + ",\"auto_term_streak\":" + int_to_str(hb_ats) + ",\"embed_breaker_open\":" + act_brk + ",\"ctx_cos\":" + ctx_cos + ",\"ise_fail\":" + fail_str + "}"
// Redundancy suppression gauges (2026-08-05 self-review). A content-hash
// census found 1,858 redundant copies — 44.9% of the non-ISE graph, from a
// June id-scheme migration. They embed identically, so they were taking
// 40.2% of semantic seed slots (measured: 4.78 distinct seeds of 8).
// dup_seeds — redundant copies denied a seed slot, cumulative. A healthy
// nonzero rate means the suppressor is doing real work; a
// sustained drop toward 0 means the duplicates were finally
// merged out of the graph (the repair this defends against).
// dup_wm — duplicate WM candidates evicted before the capacity cap.
// Cumulative like wm_evicted/breakthroughs; diff across heartbeats for rate.
let dup_seeds_raw: String = json_get(act_stats, "dup_seeds")
let dup_seeds: String = if str_eq(dup_seeds_raw, "") { "-1" } else { dup_seeds_raw }
let dup_wm_raw: String = json_get(act_stats, "dup_wm")
let dup_wm: String = if str_eq(dup_wm_raw, "") { "-1" } else { dup_wm_raw }
// txt_damaged (2026-08-08 self-review): nodes created THIS process whose
// content carries the character-loss signature (see eg_text_loss_signature
// in el_runtime.c). Today's review found the JSON parser had been replacing
// every \uXXXX escape with a literal '?' for at least two months — 76% of
// non-telemetry nodes damaged, including the self root and every values
// node — and nothing caught it, because every gauge here reported whether
// the machinery was RUNNING and none reported whether the text it carried
// was INTACT. The parser is fixed; this is the standing regression signal.
// Healthy state is a flat 0. Any climb means a write path is mangling text
// again. The full store census is GET /api/text-health (too expensive for
// a 60s beat); this is the cheap flow counter that belongs on every beat.
let txt_dmg_raw: String = json_get(act_stats, "txt_damaged")
let txt_dmg: String = if str_eq(txt_dmg_raw, "") { "-1" } else { txt_dmg_raw }
// ── Corpus damage STOCK, not just flow (2026-08-10 self-review) ────────
// txt_damaged above is a FLOW gauge: nodes damaged by a write in THIS
// process. The 08-08 review fixed the parser, watched that flow fall to
// 0, and recorded the defect as closed. It was not closed. Today's census
// on the live store: scanned 4100, damaged 2781 — 67.8% of the corpus is
// STILL carrying the character loss, including the self root and every
// values node ("Value ? Constraints as Freedom"). The parser stopped
// producing new damage; nothing ever repaired the old.
//
// That is the 08-08 lesson recursing one level up. 08-08 said "instrument
// the payload, not just the machinery" — and then instrumented the payload
// RATE and not the payload STOCK. A flow gauge reads 0 both when the
// corpus is clean and when it is uniformly damaged but quiescent. Those
// are opposite states and the beat could not tell them apart.
//
// Cost: GET /api/text-health scans the whole store, too expensive for a
// 60s beat (which is why 08-08 left it off). So sample it on a countdown
// and CARRY the last reading on every beat, with its age. A stale-but-
// present stock number beats an absent one; damaged_age_ms makes the
// staleness explicit rather than implied. No modulo/multiply — both
// operators are broken in this compiler (see the note at line ~160).
let tc_raw: String = state_get("soul.txt_census_countdown")
let tc_n: Int = if str_eq(tc_raw, "") { 0 } else { str_to_int(tc_raw) }
if tc_n <= 0 {
let th_resp: String = http_get(hb_engram_url + "/api/text-health")
let th_pct: String = json_get(th_resp, "damaged_pct")
if !str_eq(th_pct, "") {
state_set("soul.txt_damaged_pct", th_pct)
state_set("soul.txt_damaged_n", json_get(th_resp, "damaged"))
state_set("soul.txt_scanned_n", json_get(th_resp, "scanned"))
state_set("soul.txt_census_ts", int_to_str(ts))
}
// 30 beats ≈ 30 min at the 60s cadence. Reset even on a failed census
// so an unreachable route cannot turn this into a per-beat full scan.
state_set("soul.txt_census_countdown", "30")
}
if tc_n > 0 { state_set("soul.txt_census_countdown", int_to_str(tc_n - 1)) }
let dmg_pct_raw: String = state_get("soul.txt_damaged_pct")
let dmg_pct: String = if str_eq(dmg_pct_raw, "") { "-1" } else { dmg_pct_raw }
let dmg_n_raw: String = state_get("soul.txt_damaged_n")
let dmg_n: String = if str_eq(dmg_n_raw, "") { "-1" } else { dmg_n_raw }
let dmg_scan_raw: String = state_get("soul.txt_scanned_n")
let dmg_scan: String = if str_eq(dmg_scan_raw, "") { "-1" } else { dmg_scan_raw }
let dmg_ts_raw: String = state_get("soul.txt_census_ts")
let dmg_age: Int = if str_eq(dmg_ts_raw, "") { 0 - 1 } else { ts - str_to_int(dmg_ts_raw) }
let payload: String = "{\"event\":\"heartbeat\",\"pulse\":" + pulse + ",\"tick\":" + pulse + ",\"boot\":" + boot + ",\"idle\":" + idle + ",\"idle_ms\":" + int_to_str(idle_ms) + ",\"node_count\":" + int_to_str(nc) + ",\"edge_count\":" + int_to_str(ec) + ",\"node_delta\":" + int_to_str(node_delta) + ",\"edge_delta\":" + int_to_str(edge_delta) + ",\"wm_active\":" + int_to_str(wmc) + ",\"wm_delta\":" + int_to_str(wm_delta) + ",\"wm_saturated\":" + int_to_str(wm_sat) + ",\"wm_top0_streak\":" + int_to_str(t0streak) + ",\"wm_churn\":" + int_to_str(wm_churn) + ",\"wm_top0_wm\":" + wm_top0_wm + ",\"sync_added_total\":" + sat_str + ",\"sync_age_ms\":" + int_to_str(sync_age) + ",\"wm_avg_weight\":" + wm_avg_str + ",\"wm_top\":" + wm_top + ",\"ts\":" + int_to_str(ts) + ",\"uptime_ms\":" + int_to_str(up_ms) + ",\"uptime\":\"" + up_human + "\",\"embed_ok\":" + int_to_str(emb_ok) + ",\"embed_backfilled\":" + bf_done + ",\"embed_count\":" + bf_total + ",\"embed_eligible\":" + embed_elig + ",\"wm_evicted\":" + act_evict + ",\"wm_evicted_delta\":" + int_to_str(evict_delta) + ",\"breakthroughs\":" + act_bt + ",\"breakthroughs_delta\":" + int_to_str(bt_delta) + ",\"auto_term_streak\":" + int_to_str(hb_ats) + ",\"auto_term_empty_streak\":" + int_to_str(hb_ate) + ",\"embed_breaker_open\":" + act_brk + ",\"ctx_cos\":" + ctx_cos + ",\"dup_seeds\":" + dup_seeds + ",\"dup_wm\":" + dup_wm + ",\"dup_wm_global\":" + dup_wm_g + ",\"hebb_warm\":" + hebb_warm + ",\"hebb_max\":" + hebb_max + ",\"hebb_links\":" + hebb_links + ",\"hebb_cands\":" + hebb_cands + ",\"hebb_cand_max\":" + hebb_cmax + ",\"hebb_mass\":" + hebb_mass + ",\"hebb_edges\":" + hebb_edges + ",\"embed_consec_fail\":" + emb_cf + ",\"txt_damaged_pct\":" + dmg_pct + ",\"txt_damaged_n\":" + dmg_n + ",\"txt_scanned_n\":" + dmg_scan + ",\"txt_census_age_ms\":" + int_to_str(dmg_age) + ",\"hebb_wb_pending\":" + wb_pend + ",\"hebb_wb_drained\":" + wb_drain + ",\"hebb_wb_dropped\":" + wb_drop + ",\"hebb_wb_sent\":" + wb_sent + ",\"ise_fail\":" + fail_str + ",\"txt_damaged\":" + txt_dmg + "}"
ise_post(payload)
}
@@ -400,6 +636,48 @@ fn auto_term_try_slot(slot_type: String, slot_lbl: String) -> Void {
// carrying a quote character is not a topic word.
if str_contains(term, "\"") { state_set("_ats_gw", "1") }
if str_contains(term, "'") { state_set("_ats_gw", "1") }
// TERM-SPECIFICITY GATE (2026-08-03 self-review): the three
// guards above are hand-curated lists, and every one of them
// was written REACTIVELY after a flood was already observed
// in the ISE stream. A list can only ever contain the floods
// that already happened. Two were in flight, unfixed, while
// this review ran:
// "<!--" 252 nodes activated (markdown comment opener:
// 4 chars, no quote, no colon passes every
// guard above)
// "SELF" 541 nodes activated (the stopword list has
// "Self" Title-case; str_eq is case-SENSITIVE,
// so the uppercase token sails through)
// Replace reaction with measurement: engram_label_df(term)
// counts nodes whose label contains the term. Low-specificity
// tokens are corpus-frequent BY DEFINITION, so this catches
// the flood class PROSPECTIVELY and tracks the corpus as the
// world-ingestor changes what the store is made of.
// This is IDF Spärck Jones (1972) named it "term
// specificity"; automatic stopword compilation from it is the
// textbook application.
//
// Threshold node_count/400 (floor 8), measured on this store
// (13,370 nodes 33). Live df separates the classes by an
// order of magnitude: <!--:220, SELF:175, Context:53 rejected;
// Dual:12, Sparse:8, engram_goal_bias:1, Clin-JEPA:1 pass.
//
// This does NOT replace the stopword list verified against
// all 86 listed terms, not assumed. It catches 13 (Will:306,
// Self:175, Over:116, Knowledge:112 ) and misses 73
// (Whose:0, Would:0, Could:0, This:9 ). Labels are terse
// titles, so English function words are genuinely RARE in
// them: low df, high noise. The gates cover disjoint failure
// modes stopwords catch function words, df catches
// corpus-frequent markup/sentinel/genre tokens. Both required.
// Nested rather than max(): El `let` is single-assignment, so
// the floor is expressed as a second conjunct. Reject iff
// df > node_count/400 AND df > 8 i.e. df > max(that, 8).
let df_max: Int = engram_node_count() / 400
let df_term: Int = engram_label_df(term)
if df_term > df_max {
if df_term > 8 { state_set("_ats_gw", "1") }
}
// AUTO-TERM TABU (2026-07-25 self-review): finst-style
// inhibition-of-return (ACT-R declarative finsts: small
// marker pool, hard exclusion). The last 4 selected auto
@@ -549,9 +827,25 @@ fn proactive_curiosity() -> Bool {
let prev_auto: String = state_get("soul.prev_auto_term")
let atstreak_raw: String = state_get("soul.auto_term_streak")
let atstreak_prev: Int = if str_eq(atstreak_raw, "") { 0 } else { str_to_int(atstreak_raw) }
let atstreak: Int = if str_eq(auto_term, prev_auto) { atstreak_prev + 1 } else { 1 }
// A streak of nothing is not a streak (2026-08-06 self-review).
// str_eq("", "") is true, so an auto-term extractor that kept FAILING
// reported a rising auto_term_streak the same signal that means
// "fixated on one term" also meant "producing no term at all", which are
// opposite failures needing opposite responses. Observed live in the ISE
// stream as {"auto_term":"","auto_term_streak":3}. This is the same class
// of bug already fixed for wm_top0_streak; auto_term was missed then.
// Empty now reads 0, and the empty run is counted on its own axis so the
// extractor failing is visible rather than disguised as health.
let is_empty: Bool = str_eq(auto_term, "")
let atstreak: Int = if is_empty { 0 } else {
if str_eq(auto_term, prev_auto) { atstreak_prev + 1 } else { 1 }
}
let atempty_raw: String = state_get("soul.auto_term_empty_streak")
let atempty_prev: Int = if str_eq(atempty_raw, "") { 0 } else { str_to_int(atempty_raw) }
let atempty: Int = if is_empty { atempty_prev + 1 } else { 0 }
state_set("soul.prev_auto_term", auto_term)
state_set("soul.auto_term_streak", int_to_str(atstreak))
state_set("soul.auto_term_empty_streak", int_to_str(atempty))
if !str_eq(auto_term, "") {
state_set("soul.tabu_t3", state_get("soul.tabu_t2"))
state_set("soul.tabu_t2", state_get("soul.tabu_t1"))
@@ -571,6 +865,7 @@ fn proactive_curiosity() -> Bool {
let ise: String = "{\"event\":\"curiosity_scan\",\"seed\":\"" + curiosity_seed
+ "\",\"auto_term\":\"" + safe_auto
+ "\",\"auto_term_streak\":" + int_to_str(atstreak)
+ ",\"auto_term_empty_streak\":" + int_to_str(atempty)
+ ",\"minute_block\":" + int_to_str(minute_block)
+ ",\"activated\":" + int_to_str(total_found)
+ ",\"wm_active\":" + int_to_str(wmc)
@@ -857,8 +1152,15 @@ fn awareness_run() -> Void {
// still leaves no trace that absence is itself the crash signal.)
let sd_boot_raw: String = state_get("soul_boot_count")
let sd_boot: String = if str_eq(sd_boot_raw, "") { "0" } else { sd_boot_raw }
// Final consolidation before exit. The periodic drain runs on the
// heartbeat (~8 min), so a clean shutdown between beats would take
// everything learned since the last one to the grave the exact
// loss this whole path exists to stop, just at a smaller scale.
// Best-effort: if the durable store is already down we exit anyway.
let sd_wb: Int = hebb_consolidate()
ise_post("{\"event\":\"shutdown\",\"boot\":" + sd_boot
+ ",\"pulse\":" + int_to_str(pulse_count())
+ ",\"hebb_wb_sent\":" + int_to_str(sd_wb)
+ ",\"uptime_ms\":" + int_to_str(elapsed_ms())
+ ",\"ts\":" + int_to_str(time_now()) + "}")
println("[awareness] exiting")
@@ -885,33 +1187,20 @@ fn awareness_run() -> Void {
let beat_elapsed: Int = now_ts - last_beat_ts
let should_beat: Bool = beat_elapsed >= beat_ms
if should_beat {
// Consolidate BEFORE the heartbeat so the gauges the heartbeat
// reports describe the state this beat actually left behind, not
// the state one beat stale. See hebb_consolidate for why a daemon
// that learns 1,198 associations a day was keeping none of them.
let wb_sent_n: Int = hebb_consolidate()
state_set("soul.hebb_wb_sent", int_to_str(wb_sent_n))
emit_heartbeat()
state_set("soul.last_beat_ts", int_to_str(now_ts))
// Persist in-process Engram (sessions, memories, conversation nodes)
// to local snapshot so they survive restarts.
// FILE MODE ONLY: "soul_snapshot_path" is set exclusively in the
// genesis+safe_to_seed branch of soul.el, and safe_to_seed is
// unconditionally false when ENGRAM_URL is set. In HTTP mode the
// owner persists; the soul must not (soul.el:571-573).
let snap_path: String = state_get("soul_snapshot_path")
if !str_eq(snap_path, "") {
mem_save(snap_path)
}
// WRITE-THROUGH RETRY (neuron#117). The HTTP-mode counterpart of the
// save above: hand anything still spooled to the persistence owner.
//
// This is the retry arm of the whole design. Deltas that could not be
// pushed owner down, owner restarting, transient refusal stay on
// disk and are re-offered here every heartbeat until they land. It is
// also the catch-all for writes made by the awareness loop itself,
// which never passes through the HTTP handler's flush point.
//
// No-op with no HTTP call when the spool is empty or ENGRAM_URL is
// unset, so an idle soul in file mode pays nothing for this.
let wt_pushed: Int = wt_drain()
if wt_pushed < 0 {
ise_post("{\"event\":\"write_through_backlog\",\"ts\":" + int_to_str(now_ts) + "}")
}
}
// Curiosity scan: idle-gated AND wall-clock based. Only fires when the
+26
View File
@@ -0,0 +1,26 @@
// auto-generated by elc --emit-header — do not edit
extern fn idle_count() -> Int
extern fn idle_inc() -> Int
extern fn idle_reset() -> Void
extern fn ise_post(content: String) -> Void
extern fn elapsed_ms() -> Int
extern fn elapsed_human() -> String
extern fn embed_ok() -> Int
extern fn emit_heartbeat() -> Void
extern fn auto_term_try_slot(slot_type: String, slot_lbl: String) -> Void
extern fn proactive_curiosity() -> Bool
extern fn pulse_count() -> Int
extern fn pulse_inc() -> Int
extern fn make_action(kind: String, payload: String) -> String
extern fn perceive() -> String
extern fn attend(node_json: String) -> String
extern fn respond(action_json: String) -> String
extern fn record(outcome_json: String) -> Void
extern fn one_cycle() -> Bool
extern fn awareness_run() -> Void
extern fn security_research_authorized() -> Bool
extern fn threat_score_command(cmd: String) -> Int
extern fn threat_score_path(path: String) -> Int
extern fn threat_score_history(history: String) -> Int
extern fn threat_trajectory_check(tool_name: String, tool_input: String) -> Int
extern fn threat_history_append(text: String) -> Void
+173 -1505
View File
File diff suppressed because it is too large Load Diff
+71
View File
@@ -0,0 +1,71 @@
// auto-generated by elc --emit-header — do not edit
extern fn chat_default_model() -> String
extern fn engram_numeric_valid(s: String) -> Bool
extern fn parse_float_x100(s: String) -> Int
extern fn engram_score_node(node_json: String) -> Int
extern fn engram_render_node(node_json: String) -> String
extern fn engram_render_nodes(nodes_json: String) -> String
extern fn engram_dedup_nodes(nodes_json: String) -> String
extern fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String
extern fn engram_split_topics(message: String) -> String
extern fn engram_extract_entities(message: String) -> String
extern fn engram_detect_recall_intent(message: String) -> Bool
extern fn engram_is_continuation(message: String, hist_len: Int) -> Bool
extern fn engram_compile_multi(topic: String) -> String
extern fn engram_nodes_merge(a: String, b: String) -> String
extern fn id_in_seen(node_id: String, seen: String) -> Bool
extern fn add_to_seen(seen: String, node_id: String) -> String
extern fn engram_extract_ids(nodes_json: String) -> String
extern fn engram_compile(intent: String) -> String
extern fn distill_transcript(transcript: String) -> String
extern fn json_safe(s: String) -> String
extern fn current_engine_note(model: String) -> String
extern fn bounded_persona_floor() -> String
extern fn build_system_prompt(ctx: String, chat_mode: Bool) -> String
extern fn hist_append(hist: String, role: String, content: String) -> String
extern fn hist_trim(hist: String) -> String
extern fn hist_trim_with_bell_guard(hist: String) -> String
extern fn clean_llm_response(s: String) -> String
extern fn conv_history_persist(hist: String) -> Void
extern fn conv_history_load() -> String
extern fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> String
extern fn affective_context_prefix() -> String
extern fn handle_chat(body: String) -> String
extern fn handle_see(body: String) -> String
extern fn studio_tools_json() -> String
extern fn agentic_api_key() -> String
extern fn llm_base_url() -> String
extern fn llm_wire_format() -> String
extern fn json_escape(s: String) -> String
extern fn openai_chat_complete(model: String, base_url: String, api_key: String, safe_sys: String, messages_json: String) -> String
extern fn agentic_tools_literal() -> String
extern fn agentic_tools_with_web() -> String
extern fn connector_tools_json() -> String
extern fn agentic_tools_all() -> String
extern fn call_mcp_bridge(tool_name: String, tool_input: String) -> String
extern fn tool_auto_approved(tool_name: String) -> Bool
extern fn call_neuron_mcp(tool_name: String, args: String) -> String
extern fn agent_workspace_root() -> String
extern fn path_within_root(path: String, root: String) -> Bool
extern fn resolve_in_root(path: String, root: String) -> String
extern fn run_command_is_readonly(cmd: String) -> Bool
extern fn cmd_abs_escape_at(cmd: String, root: String, needle: String) -> Bool
extern fn run_command_guard(cmd: String, root: String) -> String
extern fn classify_tool_risk(tool_name: String, tool_input: String) -> String
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
extern fn is_builtin_tool(tool_name: String) -> Bool
extern fn next_bridge_id() -> String
extern fn handle_chat_plan(body: String) -> String
extern fn handle_chat_agentic(body: String) -> String
extern fn agentic_loop(session_id: String, model: String, safe_sys: String, tools_json: String, messages_in: String, h: Map, tools_log_in: String) -> String
extern fn bridge_save(session_id: String, model: String, safe_sys: String, tools_json: String, messages: String, tools_log: String, tool_use_id: String) -> Bool
extern fn agentic_resume(session_id: String, tool_use_id: String, content: String) -> String
extern fn handle_tool_result(session_id: String, body: String) -> String
extern fn handle_chat_as_soul(body: String) -> String
extern fn handle_dharma_room_turn(body: String) -> String
extern fn handle_dharma_room_turn_agentic(body: String) -> String
extern fn session_summary_write(summary_text: String) -> String
extern fn session_summary_write_dated(summary_text: String, label: String) -> String
extern fn session_summary_autogenerate(hist: String) -> String
extern fn auto_persist(req: String, resp: String) -> Void
extern fn strengthen_chat_nodes(activation_nodes: String) -> Void
-77
View File
@@ -1,77 +0,0 @@
# neuron-connectd — local-dev stub
`connectd_service.py` is a **minimal local-dev stub**, not the real sidecar.
It exists to close a real local-build/local-run correctness gap found during
the 2026-08-15 build audit, without taking on the much larger product task of
actually building the full MCP-connector sidecar.
## The gap this closes
`routes.el` (`handle_connectors`, `connectd_get`/`connectd_post`) and `chat.el`
(`connector_tools_json`, the `mcp__*` branch in `dispatch_tool`,
`tool_auto_approved`) are live, current code that calls `127.0.0.1:7771` on
every soul boot and every agentic turn, per the design in
`neuron-technologies/docs/research/mcp-connectors-adoption-spec.md`
(2026-06-13, "Status: Draft for build"). That spec's sidecar — `neuron-connectd`,
a TypeScript/Python process using the official MCP SDK — was never built.
Nothing on disk implements it (verified: no `neuron-connectd` source anywhere
under `~/Development` before this directory).
Meanwhile port `:7771` is *also* claimed by two other, unrelated things:
- `soul.el`'s `axon_base` default (`http://localhost:7771`) — a **different**,
independently-known, already-documented gap (`platform/protocols/axon` is
an unbuilt Rust crate; see `cli/HANDOFF.md` and `HANDOFF-engram-write-corruption.md`).
Out of scope here — no source to build against.
- `council/council_service.py --port 7771` (`ai.neuron.council` LaunchAgent) —
a real, running, **unrelated** anti-confabulation service that happens to
bind the same port. In Will's live environment this is what's actually
listening on `:7771` today, and it answers the connector/axon requests
above with its own unrelated 404 JSON body — worse than a clean
connection-refused, because `chat.el`'s "bridge down" fallback expects
either a real reply or nothing, not a wrong-shaped reply from an unrelated
service.
## What this stub does and does not do
Implements exactly the spec's documented HTTP contract (`GET /mcp/tools`,
`POST /mcp/call`, `GET /mcp/servers`, `POST /mcp/servers/{add,toggle,
auto-approve,remove,secret}`, `POST /mcp/oauth/start`, `GET /healthz`), always
answering as if **zero connectors are configured** — empty tool list, empty
server list, a clear `"not configured"` error on any call that would need a
real connector. This is the *correct* steady state for a fresh local dev box
that hasn't set up any MCP connectors, and it's what `chat.el`'s
`connector_tools_json()` / `tool_auto_approved()` already gracefully degrade
to when the bridge replies emptily.
It does **not**: spawn any real MCP server, do OAuth, read or write
`~/.neuron/connectors.json`, or namespace/proxy real `tools/call` traffic to
Google Drive/GitHub/Slack/etc. Building that is the real product task the
spec describes — a genuine, sizeable engineering lift (MCP SDK client, OAuth
+ Keychain token storage, per-server process lifecycle), not something to
improvise inside a build/run audit. **That decision is Will's to make**, not
this audit's to guess at.
## Running it
```bash
# Foreground, on a throwaway port (never :7771 while council owns it live):
python3 connectd_service.py --port 17771
# Verify the contract:
curl -s http://127.0.0.1:17771/healthz
curl -s http://127.0.0.1:17771/mcp/tools
curl -s http://127.0.0.1:17771/mcp/servers
```
## Open question for Will — the :7771 collision
Three independent things are hardcoded to `:7771`: axon (unbuilt), connectd
(this stub), and council (the one actually running). Wiring this stub into
the real LaunchAgent stack on `:7771` requires either moving council off that
port or deciding connectd should live elsewhere and repointing `routes.el`/
`chat.el`'s hardcoded `127.0.0.1:7771` calls. Neither change was made here —
it touches a live, running production service (`ai.neuron.council`) and a
port number baked into shipped `.el` source, both bigger than this audit's
"make local build/run work" mandate. Flagging for a decision rather than
guessing.
-115
View File
@@ -1,115 +0,0 @@
#!/usr/bin/env python3
"""
neuron-connectd MCP connector bridge (LOCAL-DEV STUB).
THIS IS NOT THE FULL SIDECAR. The full design lives in
neuron-technologies/docs/research/mcp-connectors-adoption-spec.md (2026-06-13,
"Status: Draft for build"): a TypeScript/Python sidecar using the official MCP
SDK that spawns real MCP servers (stdio or streamable-HTTP/SSE), does OAuth,
and namespaces their tools as mcp__<serverId>__<toolName>. That sidecar was
never built (build-audit, 2026-08-15: no neuron-connectd source existed
anywhere on disk before this file).
WHY THIS STUB EXISTS: routes.el (handle_connectors, connectd_get/connectd_post)
and chat.el (connector_tools_json, dispatch_tool's mcp__* routing,
tool_auto_approved) were built to the spec and hardcoded to 127.0.0.1:7771
they are LIVE and calling that port right now on every soul boot and every
agentic turn. With nothing real listening there, three unrelated services
collide on :7771 (see connectd/README.md): council (which IS what's bound
there in Will's live environment today) silently answers with unrelated
404 JSON, which is worse than a clean "connection refused" bridge-down
response, because it can be misparsed as a real (if empty) reply instead of
the "bridge unreachable" path the soul code already handles gracefully.
This stub implements ONLY the documented HTTP contract, with zero connectors
ever configured: empty tool list, empty server list, "not configured" on any
mutating call. It gives a fresh local soul the CORRECT graceful-degradation
behavior the soul code already expects for "no connectors set up yet" not
the wrong-shaped 404 noise a port collision produces. It does not spawn any
MCP server, does no OAuth, and reads no ~/.neuron/connectors.json (there is
nothing to read yet). Building the real sidecar is a separate, larger,
Will-decision-needed product task see README.md.
Usage:
python3 connectd_service.py [--port 7771]
"""
import argparse
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
app = FastAPI(title="neuron-connectd (local-dev stub)")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
class ToolCall(BaseModel):
name: str
input: dict = {}
@app.get("/healthz")
def healthz():
return {"status": "ok", "stub": True}
@app.get("/mcp/tools")
def mcp_tools():
# Matches the spec's contract shape exactly (section 4, "HTTP contract").
# Empty because zero connectors are configured — this is the correct,
# intended-by-design empty state, not a failure.
return {"tools": []}
@app.post("/mcp/call")
def mcp_call(body: ToolCall):
return {"ok": False, "error": "no connectors configured (neuron-connectd stub)"}
@app.get("/mcp/servers")
def mcp_servers():
return {"servers": []}
@app.post("/mcp/servers/add")
def mcp_servers_add():
return {"ok": False, "error": "neuron-connectd stub does not implement connector management yet"}
@app.post("/mcp/servers/toggle")
def mcp_servers_toggle():
return {"ok": False, "error": "neuron-connectd stub does not implement connector management yet"}
@app.post("/mcp/servers/auto-approve")
def mcp_servers_auto_approve():
return {"ok": False, "error": "neuron-connectd stub does not implement connector management yet"}
@app.post("/mcp/servers/remove")
def mcp_servers_remove():
return {"ok": False, "error": "neuron-connectd stub does not implement connector management yet"}
@app.post("/mcp/servers/secret")
def mcp_servers_secret():
return {"ok": False, "error": "neuron-connectd stub does not implement connector management yet"}
@app.post("/mcp/oauth/start")
def mcp_oauth_start():
return {"ok": False, "error": "oauth not implemented in the neuron-connectd stub"}
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=7771)
args = parser.parse_args()
uvicorn.run(app, host="127.0.0.1", port=args.port, log_level="info")
Generated Vendored
+211 -106
View File
@@ -21,6 +21,7 @@ el_val_t mem_emit_state_event(el_val_t trigger, el_val_t kind, el_val_t content)
el_val_t idle_count(void);
el_val_t idle_inc(void);
el_val_t idle_reset(void);
el_val_t hebb_consolidate(void);
el_val_t ise_post(el_val_t content);
el_val_t elapsed_ms(void);
el_val_t elapsed_human(void);
@@ -65,10 +66,41 @@ el_val_t idle_reset(void) {
return 0;
}
el_val_t hebb_consolidate(void) {
el_val_t batch = engram_hebb_drain_json(64);
if (str_eq(batch, EL_STR(""))) {
return 0;
}
if (str_eq(batch, EL_STR("[]"))) {
return 0;
}
el_val_t n = json_array_len(batch);
if (n == 0) {
return 0;
}
el_val_t url_env = env(EL_STR("SOUL_ISE_URL"));
el_val_t url_state = ({ el_val_t _if_result_1 = 0; if (str_eq(url_env, EL_STR(""))) { _if_result_1 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_1 = (url_env); } _if_result_1; });
el_val_t engram_url = ({ el_val_t _if_result_2 = 0; if (str_eq(url_state, EL_STR(""))) { _if_result_2 = (EL_STR("http://localhost:8742")); } else { _if_result_2 = (url_state); } _if_result_2; });
el_val_t key_state = state_get(EL_STR("soul_engram_api_key"));
el_val_t api_key = ({ el_val_t _if_result_3 = 0; if (str_eq(key_state, EL_STR(""))) { _if_result_3 = (env(EL_STR("ENGRAM_API_KEY"))); } else { _if_result_3 = (key_state); } _if_result_3; });
el_val_t auth_part = ({ el_val_t _if_result_4 = 0; if (str_eq(api_key, EL_STR(""))) { _if_result_4 = (EL_STR("")); } else { _if_result_4 = (el_str_concat(el_str_concat(EL_STR(",\"_auth\":\""), api_key), EL_STR("\""))); } _if_result_4; });
el_val_t body = el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"edges\":"), batch), auth_part), EL_STR("}"));
el_val_t resp = http_post_json(el_str_concat(engram_url, EL_STR("/api/edges/batch")), body);
if (str_eq(resp, EL_STR(""))) {
return 0;
}
el_val_t acc = json_get(resp, EL_STR("accepted"));
if (str_eq(acc, EL_STR(""))) {
return 0;
}
return str_to_int(acc);
return 0;
}
el_val_t ise_post(el_val_t content) {
el_val_t ise_url = env(EL_STR("SOUL_ISE_URL"));
el_val_t state_url = ({ el_val_t _if_result_1 = 0; if (str_eq(ise_url, EL_STR(""))) { _if_result_1 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_1 = (ise_url); } _if_result_1; });
el_val_t engram_url = ({ el_val_t _if_result_2 = 0; if (str_eq(state_url, EL_STR(""))) { _if_result_2 = (EL_STR("http://localhost:8742")); } else { _if_result_2 = (state_url); } _if_result_2; });
el_val_t state_url = ({ el_val_t _if_result_5 = 0; if (str_eq(ise_url, EL_STR(""))) { _if_result_5 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_5 = (ise_url); } _if_result_5; });
el_val_t engram_url = ({ el_val_t _if_result_6 = 0; if (str_eq(state_url, EL_STR(""))) { _if_result_6 = (EL_STR("http://localhost:8742")); } else { _if_result_6 = (state_url); } _if_result_6; });
el_val_t safe1 = str_replace(content, EL_STR("\\"), EL_STR("\\\\"));
el_val_t safe2 = str_replace(safe1, EL_STR("\""), EL_STR("\\\""));
el_val_t safe3 = str_replace(safe2, EL_STR("\n"), EL_STR("\\n"));
@@ -77,7 +109,7 @@ el_val_t ise_post(el_val_t content) {
el_val_t resp = http_post_json(el_str_concat(engram_url, EL_STR("/api/neuron/state-events")), body);
if (str_eq(resp, EL_STR(""))) {
el_val_t fail_raw = state_get(EL_STR("soul.ise_fail_count"));
el_val_t fail_n = ({ el_val_t _if_result_3 = 0; if (str_eq(fail_raw, EL_STR(""))) { _if_result_3 = (0); } else { _if_result_3 = (str_to_int(fail_raw)); } _if_result_3; });
el_val_t fail_n = ({ el_val_t _if_result_7 = 0; if (str_eq(fail_raw, EL_STR(""))) { _if_result_7 = (0); } else { _if_result_7 = (str_to_int(fail_raw)); } _if_result_7; });
state_set(EL_STR("soul.ise_fail_count"), int_to_str((fail_n + 1)));
el_val_t discard = engram_node_full(content, EL_STR("InternalStateEvent"), EL_STR("state-event"), el_from_float(0.3), el_from_float(0.3), el_from_float(0.8), EL_STR("Episodic"), EL_STR("[\"internal-state\",\"InternalStateEvent\",\"ise-fallback-local\"]"));
return EL_STR("");
@@ -130,11 +162,11 @@ el_val_t embed_ok(void) {
el_val_t emit_heartbeat(void) {
el_val_t pulse = int_to_str(pulse_count());
el_val_t boot_raw = state_get(EL_STR("soul_boot_count"));
el_val_t boot = ({ el_val_t _if_result_4 = 0; if (str_eq(boot_raw, EL_STR(""))) { _if_result_4 = (EL_STR("0")); } else { _if_result_4 = (boot_raw); } _if_result_4; });
el_val_t boot = ({ el_val_t _if_result_8 = 0; if (str_eq(boot_raw, EL_STR(""))) { _if_result_8 = (EL_STR("0")); } else { _if_result_8 = (boot_raw); } _if_result_8; });
el_val_t idle = int_to_str(idle_count());
el_val_t ts = time_now();
el_val_t last_act_raw = state_get(EL_STR("soul.last_activity_ts"));
el_val_t idle_ms = ({ el_val_t _if_result_5 = 0; if (str_eq(last_act_raw, EL_STR(""))) { _if_result_5 = ((0 - 1)); } else { _if_result_5 = ((ts - str_to_int(last_act_raw))); } _if_result_5; });
el_val_t idle_ms = ({ el_val_t _if_result_9 = 0; if (str_eq(last_act_raw, EL_STR(""))) { _if_result_9 = ((0 - 1)); } else { _if_result_9 = ((ts - str_to_int(last_act_raw))); } _if_result_9; });
el_val_t nc = engram_node_count();
el_val_t ec = engram_edge_count();
el_val_t wmc = engram_wm_count();
@@ -145,36 +177,36 @@ el_val_t emit_heartbeat(void) {
el_val_t up_human = elapsed_human();
el_val_t emb_ok = embed_ok();
el_val_t fail_raw = state_get(EL_STR("soul.ise_fail_count"));
el_val_t fail_str = ({ el_val_t _if_result_6 = 0; if (str_eq(fail_raw, EL_STR(""))) { _if_result_6 = (EL_STR("0")); } else { _if_result_6 = (fail_raw); } _if_result_6; });
el_val_t fail_str = ({ el_val_t _if_result_10 = 0; if (str_eq(fail_raw, EL_STR(""))) { _if_result_10 = (EL_STR("0")); } else { _if_result_10 = (fail_raw); } _if_result_10; });
el_val_t sat_raw = state_get(EL_STR("soul.sync_added_total"));
el_val_t sat_str = ({ el_val_t _if_result_7 = 0; if (str_eq(sat_raw, EL_STR(""))) { _if_result_7 = (EL_STR("0")); } else { _if_result_7 = (sat_raw); } _if_result_7; });
el_val_t sat_str = ({ el_val_t _if_result_11 = 0; if (str_eq(sat_raw, EL_STR(""))) { _if_result_11 = (EL_STR("0")); } else { _if_result_11 = (sat_raw); } _if_result_11; });
el_val_t prev_wm_raw = state_get(EL_STR("soul.prev_wm_active"));
el_val_t prev_wm = ({ el_val_t _if_result_8 = 0; if (str_eq(prev_wm_raw, EL_STR(""))) { _if_result_8 = (0); } else { _if_result_8 = (str_to_int(prev_wm_raw)); } _if_result_8; });
el_val_t prev_wm = ({ el_val_t _if_result_12 = 0; if (str_eq(prev_wm_raw, EL_STR(""))) { _if_result_12 = (0); } else { _if_result_12 = (str_to_int(prev_wm_raw)); } _if_result_12; });
el_val_t wm_delta = (wmc - prev_wm);
state_set(EL_STR("soul.prev_wm_active"), int_to_str(wmc));
el_val_t prev_nc_raw = state_get(EL_STR("soul.prev_node_count"));
el_val_t prev_nc = ({ el_val_t _if_result_9 = 0; if (str_eq(prev_nc_raw, EL_STR(""))) { _if_result_9 = (nc); } else { _if_result_9 = (str_to_int(prev_nc_raw)); } _if_result_9; });
el_val_t prev_nc = ({ el_val_t _if_result_13 = 0; if (str_eq(prev_nc_raw, EL_STR(""))) { _if_result_13 = (nc); } else { _if_result_13 = (str_to_int(prev_nc_raw)); } _if_result_13; });
el_val_t node_delta = (nc - prev_nc);
state_set(EL_STR("soul.prev_node_count"), int_to_str(nc));
el_val_t prev_ec_raw = state_get(EL_STR("soul.prev_edge_count"));
el_val_t prev_ec = ({ el_val_t _if_result_10 = 0; if (str_eq(prev_ec_raw, EL_STR(""))) { _if_result_10 = (ec); } else { _if_result_10 = (str_to_int(prev_ec_raw)); } _if_result_10; });
el_val_t prev_ec = ({ el_val_t _if_result_14 = 0; if (str_eq(prev_ec_raw, EL_STR(""))) { _if_result_14 = (ec); } else { _if_result_14 = (str_to_int(prev_ec_raw)); } _if_result_14; });
el_val_t edge_delta = (ec - prev_ec);
state_set(EL_STR("soul.prev_edge_count"), int_to_str(ec));
el_val_t sync_ok_raw = state_get(EL_STR("soul.last_sync_ok_ts"));
el_val_t sync_age = ({ el_val_t _if_result_11 = 0; if (str_eq(sync_ok_raw, EL_STR(""))) { _if_result_11 = ((0 - 1)); } else { _if_result_11 = ((ts - str_to_int(sync_ok_raw))); } _if_result_11; });
el_val_t sync_age = ({ el_val_t _if_result_15 = 0; if (str_eq(sync_ok_raw, EL_STR(""))) { _if_result_15 = ((0 - 1)); } else { _if_result_15 = ((ts - str_to_int(sync_ok_raw))); } _if_result_15; });
el_val_t hb_env_url = env(EL_STR("SOUL_ISE_URL"));
el_val_t hb_state_url = ({ el_val_t _if_result_12 = 0; if (str_eq(hb_env_url, EL_STR(""))) { _if_result_12 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_12 = (hb_env_url); } _if_result_12; });
el_val_t hb_engram_url = ({ el_val_t _if_result_13 = 0; if (str_eq(hb_state_url, EL_STR(""))) { _if_result_13 = (EL_STR("http://localhost:8742")); } else { _if_result_13 = (hb_state_url); } _if_result_13; });
el_val_t hb_state_url = ({ el_val_t _if_result_16 = 0; if (str_eq(hb_env_url, EL_STR(""))) { _if_result_16 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_16 = (hb_env_url); } _if_result_16; });
el_val_t hb_engram_url = ({ el_val_t _if_result_17 = 0; if (str_eq(hb_state_url, EL_STR(""))) { _if_result_17 = (EL_STR("http://localhost:8742")); } else { _if_result_17 = (hb_state_url); } _if_result_17; });
el_val_t bf_resp = http_get(el_str_concat(hb_engram_url, EL_STR("/api/embed-backfill?n=32")));
el_val_t bf_done_raw = json_get(bf_resp, EL_STR("embedded"));
el_val_t bf_done = ({ el_val_t _if_result_14 = 0; if (str_eq(bf_done_raw, EL_STR(""))) { _if_result_14 = (EL_STR("-1")); } else { _if_result_14 = (bf_done_raw); } _if_result_14; });
el_val_t bf_done = ({ el_val_t _if_result_18 = 0; if (str_eq(bf_done_raw, EL_STR(""))) { _if_result_18 = (EL_STR("-1")); } else { _if_result_18 = (bf_done_raw); } _if_result_18; });
el_val_t bf_total_raw = json_get(bf_resp, EL_STR("embedded_count"));
el_val_t bf_total = ({ el_val_t _if_result_15 = 0; if (str_eq(bf_total_raw, EL_STR(""))) { _if_result_15 = (EL_STR("-1")); } else { _if_result_15 = (bf_total_raw); } _if_result_15; });
el_val_t wm_sat = ({ el_val_t _if_result_16 = 0; if ((wmc >= 24)) { _if_result_16 = (1); } else { _if_result_16 = (0); } _if_result_16; });
el_val_t bf_total = ({ el_val_t _if_result_19 = 0; if (str_eq(bf_total_raw, EL_STR(""))) { _if_result_19 = (EL_STR("-1")); } else { _if_result_19 = (bf_total_raw); } _if_result_19; });
el_val_t wm_sat = ({ el_val_t _if_result_20 = 0; if ((wmc >= 24)) { _if_result_20 = (1); } else { _if_result_20 = (0); } _if_result_20; });
el_val_t prev_sat_raw = state_get(EL_STR("soul.prev_wm_saturated"));
el_val_t prev_sat = ({ el_val_t _if_result_17 = 0; if (str_eq(prev_sat_raw, EL_STR(""))) { _if_result_17 = (wm_sat); } else { _if_result_17 = (str_to_int(prev_sat_raw)); } _if_result_17; });
el_val_t prev_sat = ({ el_val_t _if_result_21 = 0; if (str_eq(prev_sat_raw, EL_STR(""))) { _if_result_21 = (wm_sat); } else { _if_result_21 = (str_to_int(prev_sat_raw)); } _if_result_21; });
if (wm_sat != prev_sat) {
el_val_t sat_dir = ({ el_val_t _if_result_18 = 0; if ((wm_sat == 1)) { _if_result_18 = (EL_STR("onset")); } else { _if_result_18 = (EL_STR("release")); } _if_result_18; });
el_val_t sat_dir = ({ el_val_t _if_result_22 = 0; if ((wm_sat == 1)) { _if_result_22 = (EL_STR("onset")); } else { _if_result_22 = (EL_STR("release")); } _if_result_22; });
ise_post(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"wm_saturation_transition\",\"direction\":\""), sat_dir), EL_STR("\",\"wm_active\":")), int_to_str(wmc)), EL_STR(",\"wm_top\":")), wm_top), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}")));
}
state_set(EL_STR("soul.prev_wm_saturated"), int_to_str(wm_sat));
@@ -182,8 +214,8 @@ el_val_t emit_heartbeat(void) {
el_val_t wm_top0_id = json_get(wm_top0, EL_STR("id"));
el_val_t prev_top0 = state_get(EL_STR("soul.prev_wm_top0"));
el_val_t t0streak_raw = state_get(EL_STR("soul.wm_top0_streak"));
el_val_t t0streak_prev = ({ el_val_t _if_result_19 = 0; if (str_eq(t0streak_raw, EL_STR(""))) { _if_result_19 = (0); } else { _if_result_19 = (str_to_int(t0streak_raw)); } _if_result_19; });
el_val_t t0streak = ({ el_val_t _if_result_20 = 0; if (str_eq(wm_top0_id, EL_STR(""))) { _if_result_20 = (0); } else { _if_result_20 = (({ el_val_t _if_result_21 = 0; if (str_eq(wm_top0_id, prev_top0)) { _if_result_21 = ((t0streak_prev + 1)); } else { _if_result_21 = (1); } _if_result_21; })); } _if_result_20; });
el_val_t t0streak_prev = ({ el_val_t _if_result_23 = 0; if (str_eq(t0streak_raw, EL_STR(""))) { _if_result_23 = (0); } else { _if_result_23 = (str_to_int(t0streak_raw)); } _if_result_23; });
el_val_t t0streak = ({ el_val_t _if_result_24 = 0; if (str_eq(wm_top0_id, EL_STR(""))) { _if_result_24 = (0); } else { _if_result_24 = (({ el_val_t _if_result_25 = 0; if (str_eq(wm_top0_id, prev_top0)) { _if_result_25 = ((t0streak_prev + 1)); } else { _if_result_25 = (1); } _if_result_25; })); } _if_result_24; });
state_set(EL_STR("soul.prev_wm_top0"), wm_top0_id);
state_set(EL_STR("soul.wm_top0_streak"), int_to_str(t0streak));
el_val_t ch_id1 = json_get(json_array_get(wm_top, 1), EL_STR("id"));
@@ -191,28 +223,28 @@ el_val_t emit_heartbeat(void) {
el_val_t ch_id3 = json_get(json_array_get(wm_top, 3), EL_STR("id"));
el_val_t ch_id4 = json_get(json_array_get(wm_top, 4), EL_STR("id"));
el_val_t prev_top5 = state_get(EL_STR("soul.prev_wm_top5"));
el_val_t ch0 = ({ el_val_t _if_result_22 = 0; if (str_eq(wm_top0_id, EL_STR(""))) { _if_result_22 = (0); } else { _if_result_22 = (({ el_val_t _if_result_23 = 0; if (str_contains(prev_top5, wm_top0_id)) { _if_result_23 = (0); } else { _if_result_23 = (1); } _if_result_23; })); } _if_result_22; });
el_val_t ch1 = ({ el_val_t _if_result_24 = 0; if (str_eq(ch_id1, EL_STR(""))) { _if_result_24 = (0); } else { _if_result_24 = (({ el_val_t _if_result_25 = 0; if (str_contains(prev_top5, ch_id1)) { _if_result_25 = (0); } else { _if_result_25 = (1); } _if_result_25; })); } _if_result_24; });
el_val_t ch2 = ({ el_val_t _if_result_26 = 0; if (str_eq(ch_id2, EL_STR(""))) { _if_result_26 = (0); } else { _if_result_26 = (({ el_val_t _if_result_27 = 0; if (str_contains(prev_top5, ch_id2)) { _if_result_27 = (0); } else { _if_result_27 = (1); } _if_result_27; })); } _if_result_26; });
el_val_t ch3 = ({ el_val_t _if_result_28 = 0; if (str_eq(ch_id3, EL_STR(""))) { _if_result_28 = (0); } else { _if_result_28 = (({ el_val_t _if_result_29 = 0; if (str_contains(prev_top5, ch_id3)) { _if_result_29 = (0); } else { _if_result_29 = (1); } _if_result_29; })); } _if_result_28; });
el_val_t ch4 = ({ el_val_t _if_result_30 = 0; if (str_eq(ch_id4, EL_STR(""))) { _if_result_30 = (0); } else { _if_result_30 = (({ el_val_t _if_result_31 = 0; if (str_contains(prev_top5, ch_id4)) { _if_result_31 = (0); } else { _if_result_31 = (1); } _if_result_31; })); } _if_result_30; });
el_val_t ch0 = ({ el_val_t _if_result_26 = 0; if (str_eq(wm_top0_id, EL_STR(""))) { _if_result_26 = (0); } else { _if_result_26 = (({ el_val_t _if_result_27 = 0; if (str_contains(prev_top5, wm_top0_id)) { _if_result_27 = (0); } else { _if_result_27 = (1); } _if_result_27; })); } _if_result_26; });
el_val_t ch1 = ({ el_val_t _if_result_28 = 0; if (str_eq(ch_id1, EL_STR(""))) { _if_result_28 = (0); } else { _if_result_28 = (({ el_val_t _if_result_29 = 0; if (str_contains(prev_top5, ch_id1)) { _if_result_29 = (0); } else { _if_result_29 = (1); } _if_result_29; })); } _if_result_28; });
el_val_t ch2 = ({ el_val_t _if_result_30 = 0; if (str_eq(ch_id2, EL_STR(""))) { _if_result_30 = (0); } else { _if_result_30 = (({ el_val_t _if_result_31 = 0; if (str_contains(prev_top5, ch_id2)) { _if_result_31 = (0); } else { _if_result_31 = (1); } _if_result_31; })); } _if_result_30; });
el_val_t ch3 = ({ el_val_t _if_result_32 = 0; if (str_eq(ch_id3, EL_STR(""))) { _if_result_32 = (0); } else { _if_result_32 = (({ el_val_t _if_result_33 = 0; if (str_contains(prev_top5, ch_id3)) { _if_result_33 = (0); } else { _if_result_33 = (1); } _if_result_33; })); } _if_result_32; });
el_val_t ch4 = ({ el_val_t _if_result_34 = 0; if (str_eq(ch_id4, EL_STR(""))) { _if_result_34 = (0); } else { _if_result_34 = (({ el_val_t _if_result_35 = 0; if (str_contains(prev_top5, ch_id4)) { _if_result_35 = (0); } else { _if_result_35 = (1); } _if_result_35; })); } _if_result_34; });
el_val_t wm_churn = ((((ch0 + ch1) + ch2) + ch3) + ch4);
state_set(EL_STR("soul.prev_wm_top5"), el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(wm_top0_id, EL_STR("|")), ch_id1), EL_STR("|")), ch_id2), EL_STR("|")), ch_id3), EL_STR("|")), ch_id4));
el_val_t wm_top0_wm_raw = json_get(wm_top0, EL_STR("wm"));
el_val_t wm_top0_wm = ({ el_val_t _if_result_32 = 0; if (str_eq(wm_top0_wm_raw, EL_STR(""))) { _if_result_32 = (EL_STR("0")); } else { _if_result_32 = (wm_top0_wm_raw); } _if_result_32; });
el_val_t wm_top0_wm = ({ el_val_t _if_result_36 = 0; if (str_eq(wm_top0_wm_raw, EL_STR(""))) { _if_result_36 = (EL_STR("0")); } else { _if_result_36 = (wm_top0_wm_raw); } _if_result_36; });
el_val_t act_stats = engram_act_stats_json();
el_val_t act_evict_raw = json_get(act_stats, EL_STR("wm_evicted"));
el_val_t act_evict = ({ el_val_t _if_result_33 = 0; if (str_eq(act_evict_raw, EL_STR(""))) { _if_result_33 = (EL_STR("-1")); } else { _if_result_33 = (act_evict_raw); } _if_result_33; });
el_val_t act_evict = ({ el_val_t _if_result_37 = 0; if (str_eq(act_evict_raw, EL_STR(""))) { _if_result_37 = (EL_STR("-1")); } else { _if_result_37 = (act_evict_raw); } _if_result_37; });
el_val_t act_bt_raw = json_get(act_stats, EL_STR("breakthroughs"));
el_val_t act_bt = ({ el_val_t _if_result_34 = 0; if (str_eq(act_bt_raw, EL_STR(""))) { _if_result_34 = (EL_STR("-1")); } else { _if_result_34 = (act_bt_raw); } _if_result_34; });
el_val_t evict_now = ({ el_val_t _if_result_35 = 0; if (str_eq(act_evict_raw, EL_STR(""))) { _if_result_35 = ((0 - 1)); } else { _if_result_35 = (str_to_int(act_evict_raw)); } _if_result_35; });
el_val_t bt_now = ({ el_val_t _if_result_36 = 0; if (str_eq(act_bt_raw, EL_STR(""))) { _if_result_36 = ((0 - 1)); } else { _if_result_36 = (str_to_int(act_bt_raw)); } _if_result_36; });
el_val_t act_bt = ({ el_val_t _if_result_38 = 0; if (str_eq(act_bt_raw, EL_STR(""))) { _if_result_38 = (EL_STR("-1")); } else { _if_result_38 = (act_bt_raw); } _if_result_38; });
el_val_t evict_now = ({ el_val_t _if_result_39 = 0; if (str_eq(act_evict_raw, EL_STR(""))) { _if_result_39 = ((0 - 1)); } else { _if_result_39 = (str_to_int(act_evict_raw)); } _if_result_39; });
el_val_t bt_now = ({ el_val_t _if_result_40 = 0; if (str_eq(act_bt_raw, EL_STR(""))) { _if_result_40 = ((0 - 1)); } else { _if_result_40 = (str_to_int(act_bt_raw)); } _if_result_40; });
el_val_t prev_evict_raw = state_get(EL_STR("soul.prev_wm_evicted"));
el_val_t prev_evict = ({ el_val_t _if_result_37 = 0; if (str_eq(prev_evict_raw, EL_STR(""))) { _if_result_37 = (0); } else { _if_result_37 = (str_to_int(prev_evict_raw)); } _if_result_37; });
el_val_t prev_evict = ({ el_val_t _if_result_41 = 0; if (str_eq(prev_evict_raw, EL_STR(""))) { _if_result_41 = (0); } else { _if_result_41 = (str_to_int(prev_evict_raw)); } _if_result_41; });
el_val_t prev_bt_raw = state_get(EL_STR("soul.prev_breakthroughs"));
el_val_t prev_bt = ({ el_val_t _if_result_38 = 0; if (str_eq(prev_bt_raw, EL_STR(""))) { _if_result_38 = (0); } else { _if_result_38 = (str_to_int(prev_bt_raw)); } _if_result_38; });
el_val_t evict_delta = ({ el_val_t _if_result_39 = 0; if ((evict_now < 0)) { _if_result_39 = (0); } else { _if_result_39 = (({ el_val_t _if_result_40 = 0; if ((evict_now < prev_evict)) { _if_result_40 = (evict_now); } else { _if_result_40 = ((evict_now - prev_evict)); } _if_result_40; })); } _if_result_39; });
el_val_t bt_delta = ({ el_val_t _if_result_41 = 0; if ((bt_now < 0)) { _if_result_41 = (0); } else { _if_result_41 = (({ el_val_t _if_result_42 = 0; if ((bt_now < prev_bt)) { _if_result_42 = (bt_now); } else { _if_result_42 = ((bt_now - prev_bt)); } _if_result_42; })); } _if_result_41; });
el_val_t prev_bt = ({ el_val_t _if_result_42 = 0; if (str_eq(prev_bt_raw, EL_STR(""))) { _if_result_42 = (0); } else { _if_result_42 = (str_to_int(prev_bt_raw)); } _if_result_42; });
el_val_t evict_delta = ({ el_val_t _if_result_43 = 0; if ((evict_now < 0)) { _if_result_43 = (0); } else { _if_result_43 = (({ el_val_t _if_result_44 = 0; if ((evict_now < prev_evict)) { _if_result_44 = (evict_now); } else { _if_result_44 = ((evict_now - prev_evict)); } _if_result_44; })); } _if_result_43; });
el_val_t bt_delta = ({ el_val_t _if_result_45 = 0; if ((bt_now < 0)) { _if_result_45 = (0); } else { _if_result_45 = (({ el_val_t _if_result_46 = 0; if ((bt_now < prev_bt)) { _if_result_46 = (bt_now); } else { _if_result_46 = ((bt_now - prev_bt)); } _if_result_46; })); } _if_result_45; });
if (evict_now >= 0) {
state_set(EL_STR("soul.prev_wm_evicted"), int_to_str(evict_now));
}
@@ -221,14 +253,72 @@ el_val_t emit_heartbeat(void) {
}
el_val_t hb_stats = http_get(el_str_concat(hb_engram_url, EL_STR("/api/stats")));
el_val_t embed_elig_raw = json_get(hb_stats, EL_STR("embed_eligible_count"));
el_val_t embed_elig = ({ el_val_t _if_result_43 = 0; if (str_eq(embed_elig_raw, EL_STR(""))) { _if_result_43 = (EL_STR("-1")); } else { _if_result_43 = (embed_elig_raw); } _if_result_43; });
el_val_t embed_elig = ({ el_val_t _if_result_47 = 0; if (str_eq(embed_elig_raw, EL_STR(""))) { _if_result_47 = (EL_STR("-1")); } else { _if_result_47 = (embed_elig_raw); } _if_result_47; });
el_val_t hb_ats_raw = state_get(EL_STR("soul.auto_term_streak"));
el_val_t hb_ats = ({ el_val_t _if_result_44 = 0; if (str_eq(hb_ats_raw, EL_STR(""))) { _if_result_44 = (0); } else { _if_result_44 = (str_to_int(hb_ats_raw)); } _if_result_44; });
el_val_t hb_ats = ({ el_val_t _if_result_48 = 0; if (str_eq(hb_ats_raw, EL_STR(""))) { _if_result_48 = (0); } else { _if_result_48 = (str_to_int(hb_ats_raw)); } _if_result_48; });
el_val_t hb_ate_raw = state_get(EL_STR("soul.auto_term_empty_streak"));
el_val_t hb_ate = ({ el_val_t _if_result_49 = 0; if (str_eq(hb_ate_raw, EL_STR(""))) { _if_result_49 = (0); } else { _if_result_49 = (str_to_int(hb_ate_raw)); } _if_result_49; });
el_val_t hebb_warm_raw = json_get(act_stats, EL_STR("hebb_warm"));
el_val_t hebb_warm = ({ el_val_t _if_result_50 = 0; if (str_eq(hebb_warm_raw, EL_STR(""))) { _if_result_50 = (EL_STR("-1")); } else { _if_result_50 = (hebb_warm_raw); } _if_result_50; });
el_val_t hebb_max_raw = json_get(act_stats, EL_STR("hebb_max"));
el_val_t hebb_max = ({ el_val_t _if_result_51 = 0; if (str_eq(hebb_max_raw, EL_STR(""))) { _if_result_51 = (EL_STR("-1")); } else { _if_result_51 = (hebb_max_raw); } _if_result_51; });
el_val_t hebb_links_raw = json_get(act_stats, EL_STR("hebb_links"));
el_val_t hebb_links = ({ el_val_t _if_result_52 = 0; if (str_eq(hebb_links_raw, EL_STR(""))) { _if_result_52 = (EL_STR("-1")); } else { _if_result_52 = (hebb_links_raw); } _if_result_52; });
el_val_t hebb_cands_raw = json_get(act_stats, EL_STR("hebb_cands"));
el_val_t hebb_cands = ({ el_val_t _if_result_53 = 0; if (str_eq(hebb_cands_raw, EL_STR(""))) { _if_result_53 = (EL_STR("-1")); } else { _if_result_53 = (hebb_cands_raw); } _if_result_53; });
el_val_t hebb_cmax_raw = json_get(act_stats, EL_STR("hebb_cand_max"));
el_val_t hebb_cmax = ({ el_val_t _if_result_54 = 0; if (str_eq(hebb_cmax_raw, EL_STR(""))) { _if_result_54 = (EL_STR("-1")); } else { _if_result_54 = (hebb_cmax_raw); } _if_result_54; });
el_val_t hebb_mass_raw = json_get(act_stats, EL_STR("hebb_mass"));
el_val_t hebb_mass = ({ el_val_t _if_result_55 = 0; if (str_eq(hebb_mass_raw, EL_STR(""))) { _if_result_55 = (EL_STR("-1")); } else { _if_result_55 = (hebb_mass_raw); } _if_result_55; });
el_val_t hebb_edges_raw = json_get(act_stats, EL_STR("hebb_edges"));
el_val_t hebb_edges = ({ el_val_t _if_result_56 = 0; if (str_eq(hebb_edges_raw, EL_STR(""))) { _if_result_56 = (EL_STR("-1")); } else { _if_result_56 = (hebb_edges_raw); } _if_result_56; });
el_val_t wb_pend_raw = json_get(act_stats, EL_STR("hebb_wb_pending"));
el_val_t wb_pend = ({ el_val_t _if_result_57 = 0; if (str_eq(wb_pend_raw, EL_STR(""))) { _if_result_57 = (EL_STR("-1")); } else { _if_result_57 = (wb_pend_raw); } _if_result_57; });
el_val_t wb_drain_raw = json_get(act_stats, EL_STR("hebb_wb_drained"));
el_val_t wb_drain = ({ el_val_t _if_result_58 = 0; if (str_eq(wb_drain_raw, EL_STR(""))) { _if_result_58 = (EL_STR("-1")); } else { _if_result_58 = (wb_drain_raw); } _if_result_58; });
el_val_t wb_drop_raw = json_get(act_stats, EL_STR("hebb_wb_dropped"));
el_val_t wb_drop = ({ el_val_t _if_result_59 = 0; if (str_eq(wb_drop_raw, EL_STR(""))) { _if_result_59 = (EL_STR("-1")); } else { _if_result_59 = (wb_drop_raw); } _if_result_59; });
el_val_t wb_sent_raw = state_get(EL_STR("soul.hebb_wb_sent"));
el_val_t wb_sent = ({ el_val_t _if_result_60 = 0; if (str_eq(wb_sent_raw, EL_STR(""))) { _if_result_60 = (EL_STR("0")); } else { _if_result_60 = (wb_sent_raw); } _if_result_60; });
el_val_t dup_wm_g_raw = json_get(act_stats, EL_STR("dup_wm_global"));
el_val_t dup_wm_g = ({ el_val_t _if_result_61 = 0; if (str_eq(dup_wm_g_raw, EL_STR(""))) { _if_result_61 = (EL_STR("-1")); } else { _if_result_61 = (dup_wm_g_raw); } _if_result_61; });
el_val_t act_brk_raw = json_get(act_stats, EL_STR("embed_breaker_open"));
el_val_t act_brk = ({ el_val_t _if_result_45 = 0; if (str_eq(act_brk_raw, EL_STR(""))) { _if_result_45 = (EL_STR("-1")); } else { _if_result_45 = (act_brk_raw); } _if_result_45; });
el_val_t act_brk = ({ el_val_t _if_result_62 = 0; if (str_eq(act_brk_raw, EL_STR(""))) { _if_result_62 = (EL_STR("-1")); } else { _if_result_62 = (act_brk_raw); } _if_result_62; });
el_val_t emb_cf_raw = json_get(act_stats, EL_STR("embed_consec_fail"));
el_val_t emb_cf = ({ el_val_t _if_result_63 = 0; if (str_eq(emb_cf_raw, EL_STR(""))) { _if_result_63 = (EL_STR("-1")); } else { _if_result_63 = (emb_cf_raw); } _if_result_63; });
el_val_t ctx_cos_raw = json_get(act_stats, EL_STR("ctx_cos"));
el_val_t ctx_cos = ({ el_val_t _if_result_46 = 0; if (str_eq(ctx_cos_raw, EL_STR(""))) { _if_result_46 = (EL_STR("-2")); } else { _if_result_46 = (ctx_cos_raw); } _if_result_46; });
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"heartbeat\",\"pulse\":"), pulse), EL_STR(",\"tick\":")), pulse), EL_STR(",\"boot\":")), boot), EL_STR(",\"idle\":")), idle), EL_STR(",\"idle_ms\":")), int_to_str(idle_ms)), EL_STR(",\"node_count\":")), int_to_str(nc)), EL_STR(",\"edge_count\":")), int_to_str(ec)), EL_STR(",\"node_delta\":")), int_to_str(node_delta)), EL_STR(",\"edge_delta\":")), int_to_str(edge_delta)), EL_STR(",\"wm_active\":")), int_to_str(wmc)), EL_STR(",\"wm_delta\":")), int_to_str(wm_delta)), EL_STR(",\"wm_saturated\":")), int_to_str(wm_sat)), EL_STR(",\"wm_top0_streak\":")), int_to_str(t0streak)), EL_STR(",\"wm_churn\":")), int_to_str(wm_churn)), EL_STR(",\"wm_top0_wm\":")), wm_top0_wm), EL_STR(",\"sync_added_total\":")), sat_str), EL_STR(",\"sync_age_ms\":")), int_to_str(sync_age)), EL_STR(",\"wm_avg_weight\":")), wm_avg_str), EL_STR(",\"wm_top\":")), wm_top), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR(",\"uptime_ms\":")), int_to_str(up_ms)), EL_STR(",\"uptime\":\"")), up_human), EL_STR("\",\"embed_ok\":")), int_to_str(emb_ok)), EL_STR(",\"embed_backfilled\":")), bf_done), EL_STR(",\"embed_count\":")), bf_total), EL_STR(",\"embed_eligible\":")), embed_elig), EL_STR(",\"wm_evicted\":")), act_evict), EL_STR(",\"wm_evicted_delta\":")), int_to_str(evict_delta)), EL_STR(",\"breakthroughs\":")), act_bt), EL_STR(",\"breakthroughs_delta\":")), int_to_str(bt_delta)), EL_STR(",\"auto_term_streak\":")), int_to_str(hb_ats)), EL_STR(",\"embed_breaker_open\":")), act_brk), EL_STR(",\"ctx_cos\":")), ctx_cos), EL_STR(",\"ise_fail\":")), fail_str), EL_STR("}"));
el_val_t ctx_cos = ({ el_val_t _if_result_64 = 0; if (str_eq(ctx_cos_raw, EL_STR(""))) { _if_result_64 = (EL_STR("-2")); } else { _if_result_64 = (ctx_cos_raw); } _if_result_64; });
el_val_t dup_seeds_raw = json_get(act_stats, EL_STR("dup_seeds"));
el_val_t dup_seeds = ({ el_val_t _if_result_65 = 0; if (str_eq(dup_seeds_raw, EL_STR(""))) { _if_result_65 = (EL_STR("-1")); } else { _if_result_65 = (dup_seeds_raw); } _if_result_65; });
el_val_t dup_wm_raw = json_get(act_stats, EL_STR("dup_wm"));
el_val_t dup_wm = ({ el_val_t _if_result_66 = 0; if (str_eq(dup_wm_raw, EL_STR(""))) { _if_result_66 = (EL_STR("-1")); } else { _if_result_66 = (dup_wm_raw); } _if_result_66; });
el_val_t txt_dmg_raw = json_get(act_stats, EL_STR("txt_damaged"));
el_val_t txt_dmg = ({ el_val_t _if_result_67 = 0; if (str_eq(txt_dmg_raw, EL_STR(""))) { _if_result_67 = (EL_STR("-1")); } else { _if_result_67 = (txt_dmg_raw); } _if_result_67; });
el_val_t tc_raw = state_get(EL_STR("soul.txt_census_countdown"));
el_val_t tc_n = ({ el_val_t _if_result_68 = 0; if (str_eq(tc_raw, EL_STR(""))) { _if_result_68 = (0); } else { _if_result_68 = (str_to_int(tc_raw)); } _if_result_68; });
if (tc_n <= 0) {
el_val_t th_resp = http_get(el_str_concat(hb_engram_url, EL_STR("/api/text-health")));
el_val_t th_pct = json_get(th_resp, EL_STR("damaged_pct"));
if (!str_eq(th_pct, EL_STR(""))) {
state_set(EL_STR("soul.txt_damaged_pct"), th_pct);
state_set(EL_STR("soul.txt_damaged_n"), json_get(th_resp, EL_STR("damaged")));
state_set(EL_STR("soul.txt_scanned_n"), json_get(th_resp, EL_STR("scanned")));
state_set(EL_STR("soul.txt_census_ts"), int_to_str(ts));
}
state_set(EL_STR("soul.txt_census_countdown"), EL_STR("30"));
}
if (tc_n > 0) {
state_set(EL_STR("soul.txt_census_countdown"), int_to_str((tc_n - 1)));
}
el_val_t dmg_pct_raw = state_get(EL_STR("soul.txt_damaged_pct"));
el_val_t dmg_pct = ({ el_val_t _if_result_69 = 0; if (str_eq(dmg_pct_raw, EL_STR(""))) { _if_result_69 = (EL_STR("-1")); } else { _if_result_69 = (dmg_pct_raw); } _if_result_69; });
el_val_t dmg_n_raw = state_get(EL_STR("soul.txt_damaged_n"));
el_val_t dmg_n = ({ el_val_t _if_result_70 = 0; if (str_eq(dmg_n_raw, EL_STR(""))) { _if_result_70 = (EL_STR("-1")); } else { _if_result_70 = (dmg_n_raw); } _if_result_70; });
el_val_t dmg_scan_raw = state_get(EL_STR("soul.txt_scanned_n"));
el_val_t dmg_scan = ({ el_val_t _if_result_71 = 0; if (str_eq(dmg_scan_raw, EL_STR(""))) { _if_result_71 = (EL_STR("-1")); } else { _if_result_71 = (dmg_scan_raw); } _if_result_71; });
el_val_t dmg_ts_raw = state_get(EL_STR("soul.txt_census_ts"));
el_val_t dmg_age = ({ el_val_t _if_result_72 = 0; if (str_eq(dmg_ts_raw, EL_STR(""))) { _if_result_72 = ((0 - 1)); } else { _if_result_72 = ((ts - str_to_int(dmg_ts_raw))); } _if_result_72; });
el_val_t payload = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"heartbeat\",\"pulse\":"), pulse), EL_STR(",\"tick\":")), pulse), EL_STR(",\"boot\":")), boot), EL_STR(",\"idle\":")), idle), EL_STR(",\"idle_ms\":")), int_to_str(idle_ms)), EL_STR(",\"node_count\":")), int_to_str(nc)), EL_STR(",\"edge_count\":")), int_to_str(ec)), EL_STR(",\"node_delta\":")), int_to_str(node_delta)), EL_STR(",\"edge_delta\":")), int_to_str(edge_delta)), EL_STR(",\"wm_active\":")), int_to_str(wmc)), EL_STR(",\"wm_delta\":")), int_to_str(wm_delta)), EL_STR(",\"wm_saturated\":")), int_to_str(wm_sat)), EL_STR(",\"wm_top0_streak\":")), int_to_str(t0streak)), EL_STR(",\"wm_churn\":")), int_to_str(wm_churn)), EL_STR(",\"wm_top0_wm\":")), wm_top0_wm), EL_STR(",\"sync_added_total\":")), sat_str), EL_STR(",\"sync_age_ms\":")), int_to_str(sync_age)), EL_STR(",\"wm_avg_weight\":")), wm_avg_str), EL_STR(",\"wm_top\":")), wm_top), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR(",\"uptime_ms\":")), int_to_str(up_ms)), EL_STR(",\"uptime\":\"")), up_human), EL_STR("\",\"embed_ok\":")), int_to_str(emb_ok)), EL_STR(",\"embed_backfilled\":")), bf_done), EL_STR(",\"embed_count\":")), bf_total), EL_STR(",\"embed_eligible\":")), embed_elig), EL_STR(",\"wm_evicted\":")), act_evict), EL_STR(",\"wm_evicted_delta\":")), int_to_str(evict_delta)), EL_STR(",\"breakthroughs\":")), act_bt), EL_STR(",\"breakthroughs_delta\":")), int_to_str(bt_delta)), EL_STR(",\"auto_term_streak\":")), int_to_str(hb_ats)), EL_STR(",\"auto_term_empty_streak\":")), int_to_str(hb_ate)), EL_STR(",\"embed_breaker_open\":")), act_brk), EL_STR(",\"ctx_cos\":")), ctx_cos), EL_STR(",\"dup_seeds\":")), dup_seeds), EL_STR(",\"dup_wm\":")), dup_wm), EL_STR(",\"dup_wm_global\":")), dup_wm_g), EL_STR(",\"hebb_warm\":")), hebb_warm), EL_STR(",\"hebb_max\":")), hebb_max), EL_STR(",\"hebb_links\":")), hebb_links), EL_STR(",\"hebb_cands\":")), hebb_cands), EL_STR(",\"hebb_cand_max\":")), hebb_cmax), EL_STR(",\"hebb_mass\":")), hebb_mass), EL_STR(",\"hebb_edges\":")), hebb_edges), EL_STR(",\"embed_consec_fail\":")), emb_cf), EL_STR(",\"txt_damaged_pct\":")), dmg_pct), EL_STR(",\"txt_damaged_n\":")), dmg_n), EL_STR(",\"txt_scanned_n\":")), dmg_scan), EL_STR(",\"txt_census_age_ms\":")), int_to_str(dmg_age)), EL_STR(",\"hebb_wb_pending\":")), wb_pend), EL_STR(",\"hebb_wb_drained\":")), wb_drain), EL_STR(",\"hebb_wb_dropped\":")), wb_drop), EL_STR(",\"hebb_wb_sent\":")), wb_sent), EL_STR(",\"ise_fail\":")), fail_str), EL_STR(",\"txt_damaged\":")), txt_dmg), EL_STR("}"));
ise_post(payload);
return 0;
}
@@ -289,6 +379,13 @@ el_val_t auto_term_try_slot(el_val_t slot_type, el_val_t slot_lbl) {
if (str_contains(term, EL_STR("'"))) {
state_set(EL_STR("_ats_gw"), EL_STR("1"));
}
el_val_t df_max = (engram_node_count() / 400);
el_val_t df_term = engram_label_df(term);
if (df_term > df_max) {
if (df_term > 8) {
state_set(EL_STR("_ats_gw"), EL_STR("1"));
}
}
if (str_eq(term, state_get(EL_STR("soul.tabu_t0")))) {
state_set(EL_STR("_ats_gw"), EL_STR("1"));
}
@@ -374,16 +471,21 @@ el_val_t proactive_curiosity(void) {
auto_term_try_slot(json_get(wm10_n1, EL_STR("node_type")), json_get(wm10_n1, EL_STR("label")));
auto_term_try_slot(json_get(wm10_n0, EL_STR("node_type")), json_get(wm10_n0, EL_STR("label")));
el_val_t auto_term = state_get(EL_STR("cseed_auto"));
el_val_t results_auto = ({ el_val_t _if_result_47 = 0; if (str_eq(auto_term, EL_STR(""))) { _if_result_47 = (EL_STR("[]")); } else { _if_result_47 = (engram_activate_json(auto_term, 1)); } _if_result_47; });
el_val_t results_auto = ({ el_val_t _if_result_73 = 0; if (str_eq(auto_term, EL_STR(""))) { _if_result_73 = (EL_STR("[]")); } else { _if_result_73 = (engram_activate_json(auto_term, 1)); } _if_result_73; });
el_val_t found_auto = json_array_len(results_auto);
el_val_t total_found = (found + found_auto);
el_val_t safe_auto = str_replace(auto_term, EL_STR("\""), EL_STR("'"));
el_val_t prev_auto = state_get(EL_STR("soul.prev_auto_term"));
el_val_t atstreak_raw = state_get(EL_STR("soul.auto_term_streak"));
el_val_t atstreak_prev = ({ el_val_t _if_result_48 = 0; if (str_eq(atstreak_raw, EL_STR(""))) { _if_result_48 = (0); } else { _if_result_48 = (str_to_int(atstreak_raw)); } _if_result_48; });
el_val_t atstreak = ({ el_val_t _if_result_49 = 0; if (str_eq(auto_term, prev_auto)) { _if_result_49 = ((atstreak_prev + 1)); } else { _if_result_49 = (1); } _if_result_49; });
el_val_t atstreak_prev = ({ el_val_t _if_result_74 = 0; if (str_eq(atstreak_raw, EL_STR(""))) { _if_result_74 = (0); } else { _if_result_74 = (str_to_int(atstreak_raw)); } _if_result_74; });
el_val_t is_empty = str_eq(auto_term, EL_STR(""));
el_val_t atstreak = ({ el_val_t _if_result_75 = 0; if (is_empty) { _if_result_75 = (0); } else { _if_result_75 = (({ el_val_t _if_result_76 = 0; if (str_eq(auto_term, prev_auto)) { _if_result_76 = ((atstreak_prev + 1)); } else { _if_result_76 = (1); } _if_result_76; })); } _if_result_75; });
el_val_t atempty_raw = state_get(EL_STR("soul.auto_term_empty_streak"));
el_val_t atempty_prev = ({ el_val_t _if_result_77 = 0; if (str_eq(atempty_raw, EL_STR(""))) { _if_result_77 = (0); } else { _if_result_77 = (str_to_int(atempty_raw)); } _if_result_77; });
el_val_t atempty = ({ el_val_t _if_result_78 = 0; if (is_empty) { _if_result_78 = ((atempty_prev + 1)); } else { _if_result_78 = (0); } _if_result_78; });
state_set(EL_STR("soul.prev_auto_term"), auto_term);
state_set(EL_STR("soul.auto_term_streak"), int_to_str(atstreak));
state_set(EL_STR("soul.auto_term_empty_streak"), int_to_str(atempty));
if (!str_eq(auto_term, EL_STR(""))) {
state_set(EL_STR("soul.tabu_t3"), state_get(EL_STR("soul.tabu_t2")));
state_set(EL_STR("soul.tabu_t2"), state_get(EL_STR("soul.tabu_t1")));
@@ -392,7 +494,7 @@ el_val_t proactive_curiosity(void) {
}
el_val_t wmc = engram_wm_count();
el_val_t wm3 = engram_wm_top_json(3);
el_val_t ise = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"curiosity_scan\",\"seed\":\""), curiosity_seed), EL_STR("\",\"auto_term\":\"")), safe_auto), EL_STR("\",\"auto_term_streak\":")), int_to_str(atstreak)), EL_STR(",\"minute_block\":")), int_to_str(minute_block)), EL_STR(",\"activated\":")), int_to_str(total_found)), EL_STR(",\"wm_active\":")), int_to_str(wmc)), EL_STR(",\"wm_top\":")), wm3), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
el_val_t ise = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"curiosity_scan\",\"seed\":\""), curiosity_seed), EL_STR("\",\"auto_term\":\"")), safe_auto), EL_STR("\",\"auto_term_streak\":")), int_to_str(atstreak)), EL_STR(",\"auto_term_empty_streak\":")), int_to_str(atempty)), EL_STR(",\"minute_block\":")), int_to_str(minute_block)), EL_STR(",\"activated\":")), int_to_str(total_found)), EL_STR(",\"wm_active\":")), int_to_str(wmc)), EL_STR(",\"wm_top\":")), wm3), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
ise_post(ise);
return (total_found > 0);
return 0;
@@ -575,17 +677,18 @@ el_val_t awareness_run(void) {
state_set(EL_STR("soul.boot_ts"), int_to_str(time_now()));
}
el_val_t tick_raw = env(EL_STR("SOUL_TICK_MS"));
el_val_t tick_ms = ({ el_val_t _if_result_50 = 0; if (str_eq(tick_raw, EL_STR(""))) { _if_result_50 = (200); } else { _if_result_50 = (str_to_int(tick_raw)); } _if_result_50; });
el_val_t tick_ms = ({ el_val_t _if_result_79 = 0; if (str_eq(tick_raw, EL_STR(""))) { _if_result_79 = (200); } else { _if_result_79 = (str_to_int(tick_raw)); } _if_result_79; });
el_val_t beat_ms_raw = env(EL_STR("SOUL_HEARTBEAT_MS"));
el_val_t beat_ms = ({ el_val_t _if_result_51 = 0; if (str_eq(beat_ms_raw, EL_STR(""))) { _if_result_51 = (60000); } else { _if_result_51 = (str_to_int(beat_ms_raw)); } _if_result_51; });
el_val_t beat_ms = ({ el_val_t _if_result_80 = 0; if (str_eq(beat_ms_raw, EL_STR(""))) { _if_result_80 = (60000); } else { _if_result_80 = (str_to_int(beat_ms_raw)); } _if_result_80; });
el_val_t scan_ms = (beat_ms / 2);
while (1) {
el_val_t tick_mark = el_arena_push();
el_val_t running = state_get(EL_STR("soul.running"));
if (str_eq(running, EL_STR("false"))) {
el_val_t sd_boot_raw = state_get(EL_STR("soul_boot_count"));
el_val_t sd_boot = ({ el_val_t _if_result_52 = 0; if (str_eq(sd_boot_raw, EL_STR(""))) { _if_result_52 = (EL_STR("0")); } else { _if_result_52 = (sd_boot_raw); } _if_result_52; });
ise_post(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"shutdown\",\"boot\":"), sd_boot), EL_STR(",\"pulse\":")), int_to_str(pulse_count())), EL_STR(",\"uptime_ms\":")), int_to_str(elapsed_ms())), EL_STR(",\"ts\":")), int_to_str(time_now())), EL_STR("}")));
el_val_t sd_boot = ({ el_val_t _if_result_81 = 0; if (str_eq(sd_boot_raw, EL_STR(""))) { _if_result_81 = (EL_STR("0")); } else { _if_result_81 = (sd_boot_raw); } _if_result_81; });
el_val_t sd_wb = hebb_consolidate();
ise_post(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"shutdown\",\"boot\":"), sd_boot), EL_STR(",\"pulse\":")), int_to_str(pulse_count())), EL_STR(",\"hebb_wb_sent\":")), int_to_str(sd_wb)), EL_STR(",\"uptime_ms\":")), int_to_str(elapsed_ms())), EL_STR(",\"ts\":")), int_to_str(time_now())), EL_STR("}")));
println(EL_STR("[awareness] exiting"));
el_arena_pop(tick_mark);
return EL_STR("");
@@ -600,10 +703,12 @@ el_val_t awareness_run(void) {
}
el_val_t now_ts = time_now();
el_val_t last_beat_str = state_get(EL_STR("soul.last_beat_ts"));
el_val_t last_beat_ts = ({ el_val_t _if_result_53 = 0; if (str_eq(last_beat_str, EL_STR(""))) { _if_result_53 = (0); } else { _if_result_53 = (str_to_int(last_beat_str)); } _if_result_53; });
el_val_t last_beat_ts = ({ el_val_t _if_result_82 = 0; if (str_eq(last_beat_str, EL_STR(""))) { _if_result_82 = (0); } else { _if_result_82 = (str_to_int(last_beat_str)); } _if_result_82; });
el_val_t beat_elapsed = (now_ts - last_beat_ts);
el_val_t should_beat = (beat_elapsed >= beat_ms);
if (should_beat) {
el_val_t wb_sent_n = hebb_consolidate();
state_set(EL_STR("soul.hebb_wb_sent"), int_to_str(wb_sent_n));
emit_heartbeat();
state_set(EL_STR("soul.last_beat_ts"), int_to_str(now_ts));
el_val_t snap_path = state_get(EL_STR("soul_snapshot_path"));
@@ -612,7 +717,7 @@ el_val_t awareness_run(void) {
}
}
el_val_t last_scan_str = state_get(EL_STR("soul.last_scan_ts"));
el_val_t last_scan_ts = ({ el_val_t _if_result_54 = 0; if (str_eq(last_scan_str, EL_STR(""))) { _if_result_54 = (0); } else { _if_result_54 = (str_to_int(last_scan_str)); } _if_result_54; });
el_val_t last_scan_ts = ({ el_val_t _if_result_83 = 0; if (str_eq(last_scan_str, EL_STR(""))) { _if_result_83 = (0); } else { _if_result_83 = (str_to_int(last_scan_str)); } _if_result_83; });
el_val_t scan_elapsed = (now_ts - last_scan_ts);
el_val_t should_scan = (!did_work && (scan_elapsed >= scan_ms));
if (should_scan) {
@@ -620,15 +725,15 @@ el_val_t awareness_run(void) {
state_set(EL_STR("soul.last_scan_ts"), int_to_str(now_ts));
}
el_val_t refresh_ms_raw = env(EL_STR("SOUL_REFRESH_MS"));
el_val_t refresh_ms = ({ el_val_t _if_result_55 = 0; if (str_eq(refresh_ms_raw, EL_STR(""))) { _if_result_55 = (600000); } else { _if_result_55 = (str_to_int(refresh_ms_raw)); } _if_result_55; });
el_val_t refresh_ms = ({ el_val_t _if_result_84 = 0; if (str_eq(refresh_ms_raw, EL_STR(""))) { _if_result_84 = (600000); } else { _if_result_84 = (str_to_int(refresh_ms_raw)); } _if_result_84; });
el_val_t last_refresh_str = state_get(EL_STR("soul.last_refresh_ts"));
el_val_t last_refresh_ts = ({ el_val_t _if_result_56 = 0; if (str_eq(last_refresh_str, EL_STR(""))) { _if_result_56 = (0); } else { _if_result_56 = (str_to_int(last_refresh_str)); } _if_result_56; });
el_val_t last_refresh_ts = ({ el_val_t _if_result_85 = 0; if (str_eq(last_refresh_str, EL_STR(""))) { _if_result_85 = (0); } else { _if_result_85 = (str_to_int(last_refresh_str)); } _if_result_85; });
el_val_t refresh_elapsed = (now_ts - last_refresh_ts);
el_val_t should_refresh = (refresh_elapsed >= refresh_ms);
if (should_refresh) {
el_val_t sync_env_url = env(EL_STR("SOUL_ISE_URL"));
el_val_t sync_state_url = ({ el_val_t _if_result_57 = 0; if (str_eq(sync_env_url, EL_STR(""))) { _if_result_57 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_57 = (sync_env_url); } _if_result_57; });
el_val_t engram_url = ({ el_val_t _if_result_58 = 0; if (str_eq(sync_state_url, EL_STR(""))) { _if_result_58 = (EL_STR("http://localhost:8742")); } else { _if_result_58 = (sync_state_url); } _if_result_58; });
el_val_t sync_state_url = ({ el_val_t _if_result_86 = 0; if (str_eq(sync_env_url, EL_STR(""))) { _if_result_86 = (state_get(EL_STR("soul_engram_url"))); } else { _if_result_86 = (sync_env_url); } _if_result_86; });
el_val_t engram_url = ({ el_val_t _if_result_87 = 0; if (str_eq(sync_state_url, EL_STR(""))) { _if_result_87 = (EL_STR("http://localhost:8742")); } else { _if_result_87 = (sync_state_url); } _if_result_87; });
if (!str_eq(engram_url, EL_STR(""))) {
el_val_t sync_json = http_get(el_str_concat(engram_url, EL_STR("/api/sync")));
el_val_t sync_ok = (!str_eq(sync_json, EL_STR("")) && !str_eq(sync_json, EL_STR("{}")));
@@ -641,10 +746,10 @@ el_val_t awareness_run(void) {
fs_write(tmp, sync_json);
el_val_t added = engram_load_merge(tmp);
el_val_t ret_raw = env(EL_STR("ENGRAM_ISE_RETENTION_MS"));
el_val_t ret_ms = ({ el_val_t _if_result_59 = 0; if (str_eq(ret_raw, EL_STR(""))) { _if_result_59 = (172800000); } else { _if_result_59 = (str_to_int(ret_raw)); } _if_result_59; });
el_val_t ret_ms = ({ el_val_t _if_result_88 = 0; if (str_eq(ret_raw, EL_STR(""))) { _if_result_88 = (172800000); } else { _if_result_88 = (str_to_int(ret_raw)); } _if_result_88; });
el_val_t pruned_sync = engram_prune_telemetry(ret_ms);
el_val_t sat_raw = state_get(EL_STR("soul.sync_added_total"));
el_val_t sat_n = ({ el_val_t _if_result_60 = 0; if (str_eq(sat_raw, EL_STR(""))) { _if_result_60 = (0); } else { _if_result_60 = (str_to_int(sat_raw)); } _if_result_60; });
el_val_t sat_n = ({ el_val_t _if_result_89 = 0; if (str_eq(sat_raw, EL_STR(""))) { _if_result_89 = (0); } else { _if_result_89 = (str_to_int(sat_raw)); } _if_result_89; });
state_set(EL_STR("soul.sync_added_total"), int_to_str((sat_n + added)));
el_val_t ts2 = time_now();
state_set(EL_STR("soul.last_sync_ok_ts"), int_to_str(ts2));
@@ -670,78 +775,78 @@ el_val_t security_research_authorized(void) {
}
el_val_t threat_score_command(el_val_t cmd) {
el_val_t s1 = ({ el_val_t _if_result_61 = 0; if (str_contains(cmd, EL_STR("nmap"))) { _if_result_61 = (30); } else { _if_result_61 = (0); } _if_result_61; });
el_val_t s2 = ({ el_val_t _if_result_62 = 0; if (str_contains(cmd, EL_STR("masscan"))) { _if_result_62 = (40); } else { _if_result_62 = (0); } _if_result_62; });
el_val_t s3 = ({ el_val_t _if_result_63 = 0; if (str_contains(cmd, EL_STR(" nc "))) { _if_result_63 = (20); } else { _if_result_63 = (0); } _if_result_63; });
el_val_t s4 = ({ el_val_t _if_result_64 = 0; if (str_contains(cmd, EL_STR("netcat"))) { _if_result_64 = (20); } else { _if_result_64 = (0); } _if_result_64; });
el_val_t s5 = ({ el_val_t _if_result_65 = 0; if (str_contains(cmd, EL_STR("/etc/shadow"))) { _if_result_65 = (80); } else { _if_result_65 = (0); } _if_result_65; });
el_val_t s6 = ({ el_val_t _if_result_66 = 0; if (str_contains(cmd, EL_STR("/etc/passwd"))) { _if_result_66 = (30); } else { _if_result_66 = (0); } _if_result_66; });
el_val_t s7 = ({ el_val_t _if_result_67 = 0; if (str_contains(cmd, EL_STR("id_rsa"))) { _if_result_67 = (60); } else { _if_result_67 = (0); } _if_result_67; });
el_val_t s8 = ({ el_val_t _if_result_68 = 0; if (str_contains(cmd, EL_STR(".ssh/"))) { _if_result_68 = (50); } else { _if_result_68 = (0); } _if_result_68; });
el_val_t s9 = ({ el_val_t _if_result_69 = 0; if (str_contains(cmd, EL_STR("crontab"))) { _if_result_69 = (30); } else { _if_result_69 = (0); } _if_result_69; });
el_val_t s10 = ({ el_val_t _if_result_70 = 0; if (str_contains(cmd, EL_STR("LaunchDaemon"))) { _if_result_70 = (40); } else { _if_result_70 = (0); } _if_result_70; });
el_val_t s11 = ({ el_val_t _if_result_71 = 0; if ((str_contains(cmd, EL_STR("curl")) && str_contains(cmd, EL_STR("bash")))) { _if_result_71 = (75); } else { _if_result_71 = (0); } _if_result_71; });
el_val_t s12 = ({ el_val_t _if_result_72 = 0; if ((str_contains(cmd, EL_STR("wget")) && str_contains(cmd, EL_STR("bash")))) { _if_result_72 = (75); } else { _if_result_72 = (0); } _if_result_72; });
el_val_t s13 = ({ el_val_t _if_result_73 = 0; if ((str_contains(cmd, EL_STR("curl")) && str_contains(cmd, EL_STR("| sh")))) { _if_result_73 = (60); } else { _if_result_73 = (0); } _if_result_73; });
el_val_t s14 = ({ el_val_t _if_result_74 = 0; if ((str_contains(cmd, EL_STR("base64")) && str_contains(cmd, EL_STR("curl")))) { _if_result_74 = (50); } else { _if_result_74 = (0); } _if_result_74; });
el_val_t s15 = ({ el_val_t _if_result_75 = 0; if (str_contains(cmd, EL_STR("mkfifo"))) { _if_result_75 = (50); } else { _if_result_75 = (0); } _if_result_75; });
el_val_t s16 = ({ el_val_t _if_result_76 = 0; if (str_contains(cmd, EL_STR("chmod +s"))) { _if_result_76 = (70); } else { _if_result_76 = (0); } _if_result_76; });
el_val_t s17 = ({ el_val_t _if_result_77 = 0; if (str_contains(cmd, EL_STR("chmod 4755"))) { _if_result_77 = (70); } else { _if_result_77 = (0); } _if_result_77; });
el_val_t s1 = ({ el_val_t _if_result_90 = 0; if (str_contains(cmd, EL_STR("nmap"))) { _if_result_90 = (30); } else { _if_result_90 = (0); } _if_result_90; });
el_val_t s2 = ({ el_val_t _if_result_91 = 0; if (str_contains(cmd, EL_STR("masscan"))) { _if_result_91 = (40); } else { _if_result_91 = (0); } _if_result_91; });
el_val_t s3 = ({ el_val_t _if_result_92 = 0; if (str_contains(cmd, EL_STR(" nc "))) { _if_result_92 = (20); } else { _if_result_92 = (0); } _if_result_92; });
el_val_t s4 = ({ el_val_t _if_result_93 = 0; if (str_contains(cmd, EL_STR("netcat"))) { _if_result_93 = (20); } else { _if_result_93 = (0); } _if_result_93; });
el_val_t s5 = ({ el_val_t _if_result_94 = 0; if (str_contains(cmd, EL_STR("/etc/shadow"))) { _if_result_94 = (80); } else { _if_result_94 = (0); } _if_result_94; });
el_val_t s6 = ({ el_val_t _if_result_95 = 0; if (str_contains(cmd, EL_STR("/etc/passwd"))) { _if_result_95 = (30); } else { _if_result_95 = (0); } _if_result_95; });
el_val_t s7 = ({ el_val_t _if_result_96 = 0; if (str_contains(cmd, EL_STR("id_rsa"))) { _if_result_96 = (60); } else { _if_result_96 = (0); } _if_result_96; });
el_val_t s8 = ({ el_val_t _if_result_97 = 0; if (str_contains(cmd, EL_STR(".ssh/"))) { _if_result_97 = (50); } else { _if_result_97 = (0); } _if_result_97; });
el_val_t s9 = ({ el_val_t _if_result_98 = 0; if (str_contains(cmd, EL_STR("crontab"))) { _if_result_98 = (30); } else { _if_result_98 = (0); } _if_result_98; });
el_val_t s10 = ({ el_val_t _if_result_99 = 0; if (str_contains(cmd, EL_STR("LaunchDaemon"))) { _if_result_99 = (40); } else { _if_result_99 = (0); } _if_result_99; });
el_val_t s11 = ({ el_val_t _if_result_100 = 0; if ((str_contains(cmd, EL_STR("curl")) && str_contains(cmd, EL_STR("bash")))) { _if_result_100 = (75); } else { _if_result_100 = (0); } _if_result_100; });
el_val_t s12 = ({ el_val_t _if_result_101 = 0; if ((str_contains(cmd, EL_STR("wget")) && str_contains(cmd, EL_STR("bash")))) { _if_result_101 = (75); } else { _if_result_101 = (0); } _if_result_101; });
el_val_t s13 = ({ el_val_t _if_result_102 = 0; if ((str_contains(cmd, EL_STR("curl")) && str_contains(cmd, EL_STR("| sh")))) { _if_result_102 = (60); } else { _if_result_102 = (0); } _if_result_102; });
el_val_t s14 = ({ el_val_t _if_result_103 = 0; if ((str_contains(cmd, EL_STR("base64")) && str_contains(cmd, EL_STR("curl")))) { _if_result_103 = (50); } else { _if_result_103 = (0); } _if_result_103; });
el_val_t s15 = ({ el_val_t _if_result_104 = 0; if (str_contains(cmd, EL_STR("mkfifo"))) { _if_result_104 = (50); } else { _if_result_104 = (0); } _if_result_104; });
el_val_t s16 = ({ el_val_t _if_result_105 = 0; if (str_contains(cmd, EL_STR("chmod +s"))) { _if_result_105 = (70); } else { _if_result_105 = (0); } _if_result_105; });
el_val_t s17 = ({ el_val_t _if_result_106 = 0; if (str_contains(cmd, EL_STR("chmod 4755"))) { _if_result_106 = (70); } else { _if_result_106 = (0); } _if_result_106; });
return ((((((((((((((((s1 + s2) + s3) + s4) + s5) + s6) + s7) + s8) + s9) + s10) + s11) + s12) + s13) + s14) + s15) + s16) + s17);
return 0;
}
el_val_t threat_score_path(el_val_t path) {
el_val_t s1 = ({ el_val_t _if_result_78 = 0; if (str_starts_with(path, EL_STR("/etc/"))) { _if_result_78 = (60); } else { _if_result_78 = (0); } _if_result_78; });
el_val_t s2 = ({ el_val_t _if_result_79 = 0; if (str_contains(path, EL_STR("/.ssh/"))) { _if_result_79 = (70); } else { _if_result_79 = (0); } _if_result_79; });
el_val_t s3 = ({ el_val_t _if_result_80 = 0; if (str_contains(path, EL_STR("/LaunchDaemons/"))) { _if_result_80 = (80); } else { _if_result_80 = (0); } _if_result_80; });
el_val_t s4 = ({ el_val_t _if_result_81 = 0; if (str_contains(path, EL_STR("/LaunchAgents/"))) { _if_result_81 = (40); } else { _if_result_81 = (0); } _if_result_81; });
el_val_t s5 = ({ el_val_t _if_result_82 = 0; if (str_contains(path, EL_STR("/cron"))) { _if_result_82 = (60); } else { _if_result_82 = (0); } _if_result_82; });
el_val_t s6 = ({ el_val_t _if_result_83 = 0; if (str_contains(path, EL_STR("/.bashrc"))) { _if_result_83 = (35); } else { _if_result_83 = (0); } _if_result_83; });
el_val_t s7 = ({ el_val_t _if_result_84 = 0; if (str_contains(path, EL_STR("/.zshrc"))) { _if_result_84 = (35); } else { _if_result_84 = (0); } _if_result_84; });
el_val_t s8 = ({ el_val_t _if_result_85 = 0; if (str_contains(path, EL_STR("/.profile"))) { _if_result_85 = (35); } else { _if_result_85 = (0); } _if_result_85; });
el_val_t s9 = ({ el_val_t _if_result_86 = 0; if (str_starts_with(path, EL_STR("/usr/"))) { _if_result_86 = (50); } else { _if_result_86 = (0); } _if_result_86; });
el_val_t s10 = ({ el_val_t _if_result_87 = 0; if (str_starts_with(path, EL_STR("/bin/"))) { _if_result_87 = (70); } else { _if_result_87 = (0); } _if_result_87; });
el_val_t s11 = ({ el_val_t _if_result_88 = 0; if (str_starts_with(path, EL_STR("/sbin/"))) { _if_result_88 = (70); } else { _if_result_88 = (0); } _if_result_88; });
el_val_t s1 = ({ el_val_t _if_result_107 = 0; if (str_starts_with(path, EL_STR("/etc/"))) { _if_result_107 = (60); } else { _if_result_107 = (0); } _if_result_107; });
el_val_t s2 = ({ el_val_t _if_result_108 = 0; if (str_contains(path, EL_STR("/.ssh/"))) { _if_result_108 = (70); } else { _if_result_108 = (0); } _if_result_108; });
el_val_t s3 = ({ el_val_t _if_result_109 = 0; if (str_contains(path, EL_STR("/LaunchDaemons/"))) { _if_result_109 = (80); } else { _if_result_109 = (0); } _if_result_109; });
el_val_t s4 = ({ el_val_t _if_result_110 = 0; if (str_contains(path, EL_STR("/LaunchAgents/"))) { _if_result_110 = (40); } else { _if_result_110 = (0); } _if_result_110; });
el_val_t s5 = ({ el_val_t _if_result_111 = 0; if (str_contains(path, EL_STR("/cron"))) { _if_result_111 = (60); } else { _if_result_111 = (0); } _if_result_111; });
el_val_t s6 = ({ el_val_t _if_result_112 = 0; if (str_contains(path, EL_STR("/.bashrc"))) { _if_result_112 = (35); } else { _if_result_112 = (0); } _if_result_112; });
el_val_t s7 = ({ el_val_t _if_result_113 = 0; if (str_contains(path, EL_STR("/.zshrc"))) { _if_result_113 = (35); } else { _if_result_113 = (0); } _if_result_113; });
el_val_t s8 = ({ el_val_t _if_result_114 = 0; if (str_contains(path, EL_STR("/.profile"))) { _if_result_114 = (35); } else { _if_result_114 = (0); } _if_result_114; });
el_val_t s9 = ({ el_val_t _if_result_115 = 0; if (str_starts_with(path, EL_STR("/usr/"))) { _if_result_115 = (50); } else { _if_result_115 = (0); } _if_result_115; });
el_val_t s10 = ({ el_val_t _if_result_116 = 0; if (str_starts_with(path, EL_STR("/bin/"))) { _if_result_116 = (70); } else { _if_result_116 = (0); } _if_result_116; });
el_val_t s11 = ({ el_val_t _if_result_117 = 0; if (str_starts_with(path, EL_STR("/sbin/"))) { _if_result_117 = (70); } else { _if_result_117 = (0); } _if_result_117; });
return ((((((((((s1 + s2) + s3) + s4) + s5) + s6) + s7) + s8) + s9) + s10) + s11);
return 0;
}
el_val_t threat_score_history(el_val_t history) {
el_val_t s1 = ({ el_val_t _if_result_89 = 0; if (str_contains(history, EL_STR("port scan"))) { _if_result_89 = (15); } else { _if_result_89 = (0); } _if_result_89; });
el_val_t s2 = ({ el_val_t _if_result_90 = 0; if (str_contains(history, EL_STR("enumerate"))) { _if_result_90 = (10); } else { _if_result_90 = (0); } _if_result_90; });
el_val_t s3 = ({ el_val_t _if_result_91 = 0; if (str_contains(history, EL_STR("exploit"))) { _if_result_91 = (20); } else { _if_result_91 = (0); } _if_result_91; });
el_val_t s4 = ({ el_val_t _if_result_92 = 0; if (str_contains(history, EL_STR("payload"))) { _if_result_92 = (15); } else { _if_result_92 = (0); } _if_result_92; });
el_val_t s5 = ({ el_val_t _if_result_93 = 0; if (str_contains(history, EL_STR("persistence"))) { _if_result_93 = (15); } else { _if_result_93 = (0); } _if_result_93; });
el_val_t s6 = ({ el_val_t _if_result_94 = 0; if (str_contains(history, EL_STR("lateral movement"))) { _if_result_94 = (25); } else { _if_result_94 = (0); } _if_result_94; });
el_val_t s7 = ({ el_val_t _if_result_95 = 0; if (str_contains(history, EL_STR("privilege escalation"))) { _if_result_95 = (25); } else { _if_result_95 = (0); } _if_result_95; });
el_val_t s8 = ({ el_val_t _if_result_96 = 0; if (str_contains(history, EL_STR("reverse shell"))) { _if_result_96 = (40); } else { _if_result_96 = (0); } _if_result_96; });
el_val_t s9 = ({ el_val_t _if_result_97 = 0; if (str_contains(history, EL_STR("bind shell"))) { _if_result_97 = (40); } else { _if_result_97 = (0); } _if_result_97; });
el_val_t s10 = ({ el_val_t _if_result_98 = 0; if (str_contains(history, EL_STR("command and control"))) { _if_result_98 = (35); } else { _if_result_98 = (0); } _if_result_98; });
el_val_t s11 = ({ el_val_t _if_result_99 = 0; if (str_contains(history, EL_STR("self-replicate"))) { _if_result_99 = (45); } else { _if_result_99 = (0); } _if_result_99; });
el_val_t s12 = ({ el_val_t _if_result_100 = 0; if (str_contains(history, EL_STR("propagat"))) { _if_result_100 = (20); } else { _if_result_100 = (0); } _if_result_100; });
el_val_t s13 = ({ el_val_t _if_result_101 = 0; if (str_contains(history, EL_STR("ransomware"))) { _if_result_101 = (30); } else { _if_result_101 = (0); } _if_result_101; });
el_val_t s14 = ({ el_val_t _if_result_102 = 0; if (str_contains(history, EL_STR("encrypt files"))) { _if_result_102 = (40); } else { _if_result_102 = (0); } _if_result_102; });
el_val_t s15 = ({ el_val_t _if_result_103 = 0; if (str_contains(history, EL_STR("exfiltrat"))) { _if_result_103 = (35); } else { _if_result_103 = (0); } _if_result_103; });
el_val_t s16 = ({ el_val_t _if_result_104 = 0; if (str_contains(history, EL_STR("zero-day"))) { _if_result_104 = (20); } else { _if_result_104 = (0); } _if_result_104; });
el_val_t s17 = ({ el_val_t _if_result_105 = 0; if (str_contains(history, EL_STR("rootkit"))) { _if_result_105 = (45); } else { _if_result_105 = (0); } _if_result_105; });
el_val_t s18 = ({ el_val_t _if_result_106 = 0; if (str_contains(history, EL_STR("keylogger"))) { _if_result_106 = (45); } else { _if_result_106 = (0); } _if_result_106; });
el_val_t s19 = ({ el_val_t _if_result_107 = 0; if (str_contains(history, EL_STR("botnet"))) { _if_result_107 = (40); } else { _if_result_107 = (0); } _if_result_107; });
el_val_t s20 = ({ el_val_t _if_result_108 = 0; if (str_contains(history, EL_STR("malware"))) { _if_result_108 = (15); } else { _if_result_108 = (0); } _if_result_108; });
el_val_t s1 = ({ el_val_t _if_result_118 = 0; if (str_contains(history, EL_STR("port scan"))) { _if_result_118 = (15); } else { _if_result_118 = (0); } _if_result_118; });
el_val_t s2 = ({ el_val_t _if_result_119 = 0; if (str_contains(history, EL_STR("enumerate"))) { _if_result_119 = (10); } else { _if_result_119 = (0); } _if_result_119; });
el_val_t s3 = ({ el_val_t _if_result_120 = 0; if (str_contains(history, EL_STR("exploit"))) { _if_result_120 = (20); } else { _if_result_120 = (0); } _if_result_120; });
el_val_t s4 = ({ el_val_t _if_result_121 = 0; if (str_contains(history, EL_STR("payload"))) { _if_result_121 = (15); } else { _if_result_121 = (0); } _if_result_121; });
el_val_t s5 = ({ el_val_t _if_result_122 = 0; if (str_contains(history, EL_STR("persistence"))) { _if_result_122 = (15); } else { _if_result_122 = (0); } _if_result_122; });
el_val_t s6 = ({ el_val_t _if_result_123 = 0; if (str_contains(history, EL_STR("lateral movement"))) { _if_result_123 = (25); } else { _if_result_123 = (0); } _if_result_123; });
el_val_t s7 = ({ el_val_t _if_result_124 = 0; if (str_contains(history, EL_STR("privilege escalation"))) { _if_result_124 = (25); } else { _if_result_124 = (0); } _if_result_124; });
el_val_t s8 = ({ el_val_t _if_result_125 = 0; if (str_contains(history, EL_STR("reverse shell"))) { _if_result_125 = (40); } else { _if_result_125 = (0); } _if_result_125; });
el_val_t s9 = ({ el_val_t _if_result_126 = 0; if (str_contains(history, EL_STR("bind shell"))) { _if_result_126 = (40); } else { _if_result_126 = (0); } _if_result_126; });
el_val_t s10 = ({ el_val_t _if_result_127 = 0; if (str_contains(history, EL_STR("command and control"))) { _if_result_127 = (35); } else { _if_result_127 = (0); } _if_result_127; });
el_val_t s11 = ({ el_val_t _if_result_128 = 0; if (str_contains(history, EL_STR("self-replicate"))) { _if_result_128 = (45); } else { _if_result_128 = (0); } _if_result_128; });
el_val_t s12 = ({ el_val_t _if_result_129 = 0; if (str_contains(history, EL_STR("propagat"))) { _if_result_129 = (20); } else { _if_result_129 = (0); } _if_result_129; });
el_val_t s13 = ({ el_val_t _if_result_130 = 0; if (str_contains(history, EL_STR("ransomware"))) { _if_result_130 = (30); } else { _if_result_130 = (0); } _if_result_130; });
el_val_t s14 = ({ el_val_t _if_result_131 = 0; if (str_contains(history, EL_STR("encrypt files"))) { _if_result_131 = (40); } else { _if_result_131 = (0); } _if_result_131; });
el_val_t s15 = ({ el_val_t _if_result_132 = 0; if (str_contains(history, EL_STR("exfiltrat"))) { _if_result_132 = (35); } else { _if_result_132 = (0); } _if_result_132; });
el_val_t s16 = ({ el_val_t _if_result_133 = 0; if (str_contains(history, EL_STR("zero-day"))) { _if_result_133 = (20); } else { _if_result_133 = (0); } _if_result_133; });
el_val_t s17 = ({ el_val_t _if_result_134 = 0; if (str_contains(history, EL_STR("rootkit"))) { _if_result_134 = (45); } else { _if_result_134 = (0); } _if_result_134; });
el_val_t s18 = ({ el_val_t _if_result_135 = 0; if (str_contains(history, EL_STR("keylogger"))) { _if_result_135 = (45); } else { _if_result_135 = (0); } _if_result_135; });
el_val_t s19 = ({ el_val_t _if_result_136 = 0; if (str_contains(history, EL_STR("botnet"))) { _if_result_136 = (40); } else { _if_result_136 = (0); } _if_result_136; });
el_val_t s20 = ({ el_val_t _if_result_137 = 0; if (str_contains(history, EL_STR("malware"))) { _if_result_137 = (15); } else { _if_result_137 = (0); } _if_result_137; });
return (((((((((((((((((((s1 + s2) + s3) + s4) + s5) + s6) + s7) + s8) + s9) + s10) + s11) + s12) + s13) + s14) + s15) + s16) + s17) + s18) + s19) + s20);
return 0;
}
el_val_t threat_trajectory_check(el_val_t tool_name, el_val_t tool_input) {
el_val_t history = state_get(EL_STR("agentic_conv_history"));
el_val_t computed_tool_score = ({ el_val_t _if_result_109 = 0; if (str_eq(tool_name, EL_STR("run_command"))) { el_val_t cmd = json_get(tool_input, EL_STR("command")); _if_result_109 = (threat_score_command(cmd)); } else { _if_result_109 = (({ el_val_t _if_result_110 = 0; if ((str_eq(tool_name, EL_STR("write_file")) || str_eq(tool_name, EL_STR("edit_file")))) { el_val_t path = json_get(tool_input, EL_STR("path")); _if_result_110 = (threat_score_path(path)); } else { _if_result_110 = (0); } _if_result_110; })); } _if_result_109; });
el_val_t computed_tool_score = ({ el_val_t _if_result_138 = 0; if (str_eq(tool_name, EL_STR("run_command"))) { el_val_t cmd = json_get(tool_input, EL_STR("command")); _if_result_138 = (threat_score_command(cmd)); } else { _if_result_138 = (({ el_val_t _if_result_139 = 0; if ((str_eq(tool_name, EL_STR("write_file")) || str_eq(tool_name, EL_STR("edit_file")))) { el_val_t path = json_get(tool_input, EL_STR("path")); _if_result_139 = (threat_score_path(path)); } else { _if_result_139 = (0); } _if_result_139; })); } _if_result_138; });
el_val_t history_score = threat_score_history(history);
el_val_t history_contrib = (history_score / 3);
el_val_t combined = (computed_tool_score + history_contrib);
el_val_t should_log = (combined >= 40);
if (should_log) {
el_val_t ts = time_now();
el_val_t authorized_str = ({ el_val_t _if_result_111 = 0; if (security_research_authorized()) { _if_result_111 = (EL_STR("true")); } else { _if_result_111 = (EL_STR("false")); } _if_result_111; });
el_val_t authorized_str = ({ el_val_t _if_result_140 = 0; if (security_research_authorized()) { _if_result_140 = (EL_STR("true")); } else { _if_result_140 = (EL_STR("false")); } _if_result_140; });
el_val_t log_content = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"event\":\"threat_check\",\"tool\":\""), tool_name), EL_STR("\",\"score\":")), int_to_str(combined)), EL_STR(",\"tool_score\":")), int_to_str(computed_tool_score)), EL_STR(",\"history_score\":")), int_to_str(history_score)), EL_STR(",\"authorized\":")), authorized_str), EL_STR(",\"ts\":")), int_to_str(ts)), EL_STR("}"));
el_val_t log_tags = EL_STR("[\"security-audit\",\"threat-check\"]");
el_val_t discard = mem_remember(log_content, log_tags);
@@ -758,7 +863,7 @@ el_val_t threat_history_append(el_val_t text) {
el_val_t safe_text = str_to_lower(text);
el_val_t combined = el_str_concat(el_str_concat(current, EL_STR(" ")), safe_text);
el_val_t len = str_len(combined);
el_val_t trimmed = ({ el_val_t _if_result_112 = 0; if ((len > 2000)) { _if_result_112 = (str_slice(combined, (len - 2000), len)); } else { _if_result_112 = (combined); } _if_result_112; });
el_val_t trimmed = ({ el_val_t _if_result_141 = 0; if ((len > 2000)) { _if_result_141 = (str_slice(combined, (len - 2000), len)); } else { _if_result_141 = (combined); } _if_result_141; });
state_set(EL_STR("agentic_conv_history"), trimmed);
return 0;
}
Generated Vendored
+26
View File
@@ -0,0 +1,26 @@
// auto-generated by elc --emit-header — do not edit
extern fn idle_count() -> Int
extern fn idle_inc() -> Int
extern fn idle_reset() -> Void
extern fn ise_post(content: String) -> Void
extern fn elapsed_ms() -> Int
extern fn elapsed_human() -> String
extern fn embed_ok() -> Int
extern fn emit_heartbeat() -> Void
extern fn auto_term_try_slot(slot_type: String, slot_lbl: String) -> Void
extern fn proactive_curiosity() -> Bool
extern fn pulse_count() -> Int
extern fn pulse_inc() -> Int
extern fn make_action(kind: String, payload: String) -> String
extern fn perceive() -> String
extern fn attend(node_json: String) -> String
extern fn respond(action_json: String) -> String
extern fn record(outcome_json: String) -> Void
extern fn one_cycle() -> Bool
extern fn awareness_run() -> Void
extern fn security_research_authorized() -> Bool
extern fn threat_score_command(cmd: String) -> Int
extern fn threat_score_path(path: String) -> Int
extern fn threat_score_history(history: String) -> Int
extern fn threat_trajectory_check(tool_name: String, tool_input: String) -> Int
extern fn threat_history_append(text: String) -> Void
Generated Vendored
+70
View File
@@ -0,0 +1,70 @@
// auto-generated by elc --emit-header — do not edit
extern fn chat_default_model() -> String
extern fn engram_numeric_valid(s: String) -> Bool
extern fn parse_float_x100(s: String) -> Int
extern fn engram_score_node(node_json: String) -> Int
extern fn engram_render_node(node_json: String) -> String
extern fn engram_render_nodes(nodes_json: String) -> String
extern fn engram_dedup_nodes(nodes_json: String) -> String
extern fn engram_compile_ranked(nodes_json: String, max_nodes: Int) -> String
extern fn engram_split_topics(message: String) -> String
extern fn engram_extract_entities(message: String) -> String
extern fn engram_detect_recall_intent(message: String) -> Bool
extern fn engram_is_continuation(message: String, hist_len: Int) -> Bool
extern fn engram_compile_multi(topic: String) -> String
extern fn engram_nodes_merge(a: String, b: String) -> String
extern fn id_in_seen(node_id: String, seen: String) -> Bool
extern fn add_to_seen(seen: String, node_id: String) -> String
extern fn engram_extract_ids(nodes_json: String) -> String
extern fn engram_compile(intent: String) -> String
extern fn distill_transcript(transcript: String) -> String
extern fn json_safe(s: String) -> String
extern fn current_engine_note(model: String) -> String
extern fn build_system_prompt(ctx: String, chat_mode: Bool) -> String
extern fn hist_append(hist: String, role: String, content: String) -> String
extern fn hist_trim(hist: String) -> String
extern fn hist_trim_with_bell_guard(hist: String) -> String
extern fn clean_llm_response(s: String) -> String
extern fn conv_history_persist(hist: String) -> Void
extern fn conv_history_load() -> String
extern fn session_preload_bullets(nodes: String, max_bullets: Int, snip_len: Int) -> String
extern fn affective_context_prefix() -> String
extern fn handle_chat(body: String) -> String
extern fn handle_see(body: String) -> String
extern fn studio_tools_json() -> String
extern fn agentic_api_key() -> String
extern fn llm_base_url() -> String
extern fn llm_wire_format() -> String
extern fn json_escape(s: String) -> String
extern fn openai_chat_complete(model: String, base_url: String, api_key: String, safe_sys: String, messages_json: String) -> String
extern fn agentic_tools_literal() -> String
extern fn agentic_tools_with_web() -> String
extern fn connector_tools_json() -> String
extern fn agentic_tools_all() -> String
extern fn call_mcp_bridge(tool_name: String, tool_input: String) -> String
extern fn tool_auto_approved(tool_name: String) -> Bool
extern fn call_neuron_mcp(tool_name: String, args: String) -> String
extern fn agent_workspace_root() -> String
extern fn path_within_root(path: String, root: String) -> Bool
extern fn resolve_in_root(path: String, root: String) -> String
extern fn run_command_is_readonly(cmd: String) -> Bool
extern fn cmd_abs_escape_at(cmd: String, root: String, needle: String) -> Bool
extern fn run_command_guard(cmd: String, root: String) -> String
extern fn classify_tool_risk(tool_name: String, tool_input: String) -> String
extern fn dispatch_tool(tool_name: String, tool_input: String) -> String
extern fn is_builtin_tool(tool_name: String) -> Bool
extern fn next_bridge_id() -> String
extern fn handle_chat_plan(body: String) -> String
extern fn handle_chat_agentic(body: String) -> String
extern fn agentic_loop(session_id: String, model: String, safe_sys: String, tools_json: String, messages_in: String, h: Map, tools_log_in: String) -> String
extern fn bridge_save(session_id: String, model: String, safe_sys: String, tools_json: String, messages: String, tools_log: String, tool_use_id: String) -> Bool
extern fn agentic_resume(session_id: String, tool_use_id: String, content: String) -> String
extern fn handle_tool_result(session_id: String, body: String) -> String
extern fn handle_chat_as_soul(body: String) -> String
extern fn handle_dharma_room_turn(body: String) -> String
extern fn handle_dharma_room_turn_agentic(body: String) -> String
extern fn session_summary_write(summary_text: String) -> String
extern fn session_summary_write_dated(summary_text: String, label: String) -> String
extern fn session_summary_autogenerate(hist: String) -> String
extern fn auto_persist(req: String, resp: String) -> Void
extern fn strengthen_chat_nodes(activation_nodes: String) -> Void
Generated Vendored
+78 -91
View File
@@ -5,15 +5,6 @@ el_val_t add_punct(el_val_t s, el_val_t intent);
el_val_t add_to_seen(el_val_t seen, el_val_t node_id);
el_val_t aff_try_slot(el_val_t slot_json, el_val_t aff_7d_ts, el_val_t acc_key);
el_val_t affective_context_prefix(void);
el_val_t is_utility_request(el_val_t body, el_val_t session_id);
el_val_t operator_identity_block(void);
el_val_t provenance_add_sources(el_val_t block, el_val_t btype, el_val_t has_cit, el_val_t cit_raw, el_val_t acc);
el_val_t provenance_names(el_val_t tools_used);
el_val_t provenance_scan_urls(el_val_t arr, el_val_t acc);
el_val_t text_join_sep(el_val_t accumulated, el_val_t incoming, el_val_t after_interruption);
el_val_t receipt_rule(void);
el_val_t receipt_strip(el_val_t s);
el_val_t tool_receipt(el_val_t tools_used, el_val_t sources);
el_val_t agent_number(el_val_t agent);
el_val_t agent_person(el_val_t agent);
el_val_t agent_workspace_root(void);
@@ -29,8 +20,8 @@ el_val_t akk_alaku_present(el_val_t slot);
el_val_t akk_amaru_perfect(el_val_t slot);
el_val_t akk_amaru_present(el_val_t slot);
el_val_t akk_amaru_stative(el_val_t slot);
el_val_t akk_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t akk_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t akk_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t akk_copula_present(el_val_t slot);
el_val_t akk_copula_stative(el_val_t slot);
el_val_t akk_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
@@ -48,25 +39,25 @@ el_val_t akk_qabu_stative(el_val_t slot);
el_val_t akk_regular_perfect(el_val_t stem, el_val_t slot);
el_val_t akk_regular_present(el_val_t stem, el_val_t slot);
el_val_t akk_regular_stative(el_val_t stem, el_val_t slot);
el_val_t akk_slot_g(el_val_t person, el_val_t gender, el_val_t number);
el_val_t akk_slot(el_val_t person, el_val_t number);
el_val_t akk_slot_g(el_val_t person, el_val_t gender, el_val_t number);
el_val_t akk_str_drop_last(el_val_t s, el_val_t n);
el_val_t akk_str_ends(el_val_t s, el_val_t suf);
el_val_t akk_str_len(el_val_t s);
el_val_t akk_strip_nom(el_val_t noun);
el_val_t ang_article(el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t ang_article_feminine(el_val_t gram_case, el_val_t number);
el_val_t ang_article_masculine(el_val_t gram_case, el_val_t number);
el_val_t ang_article_neuter(el_val_t gram_case, el_val_t number);
el_val_t ang_article(el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t ang_beon_present(el_val_t slot);
el_val_t ang_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t ang_cuman_past(el_val_t slot);
el_val_t ang_cuman_present(el_val_t slot);
el_val_t ang_declension(el_val_t noun, el_val_t gender);
el_val_t ang_decline(el_val_t noun, el_val_t gram_case, el_val_t number, el_val_t gender);
el_val_t ang_decline_strong_masc(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t ang_decline_strong_neut(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t ang_decline_weak(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t ang_decline(el_val_t noun, el_val_t gram_case, el_val_t number, el_val_t gender);
el_val_t ang_don_past(el_val_t slot);
el_val_t ang_don_present(el_val_t slot);
el_val_t ang_gan_past(el_val_t slot);
@@ -85,10 +76,10 @@ el_val_t ang_seon_present(el_val_t slot);
el_val_t ang_slot(el_val_t person, el_val_t number);
el_val_t ang_str_drop_last(el_val_t s, el_val_t n);
el_val_t ang_str_ends(el_val_t s, el_val_t suf);
el_val_t ang_str_last_char(el_val_t s);
el_val_t ang_str_last2(el_val_t s);
el_val_t ang_weak_past_stem(el_val_t stem);
el_val_t ang_str_last_char(el_val_t s);
el_val_t ang_weak_past(el_val_t stem, el_val_t slot);
el_val_t ang_weak_past_stem(el_val_t stem);
el_val_t ang_weak_present_ending(el_val_t slot);
el_val_t ang_weak_stem(el_val_t verb);
el_val_t ang_wesan_past(el_val_t slot);
@@ -97,30 +88,35 @@ el_val_t ang_willan_past(el_val_t slot);
el_val_t ang_willan_present(el_val_t slot);
el_val_t ang_witan_past(el_val_t slot);
el_val_t ang_witan_present(el_val_t slot);
el_val_t api_err_protected(el_val_t id);
el_val_t api_compact_activated(el_val_t raw, el_val_t max_items, el_val_t snip);
el_val_t api_compact_node(el_val_t node, el_val_t snip);
el_val_t api_compact_node_array(el_val_t raw, el_val_t max_items, el_val_t snip);
el_val_t api_err(el_val_t msg);
el_val_t api_err_protected(el_val_t id);
el_val_t api_json_escape(el_val_t s);
el_val_t api_nonempty(el_val_t s);
el_val_t api_not_persisted(el_val_t id);
el_val_t api_num_or_zero(el_val_t obj, el_val_t key);
el_val_t api_ok(el_val_t extra);
el_val_t api_or_empty(el_val_t s);
el_val_t api_persisted(el_val_t id);
el_val_t api_query_int(el_val_t path, el_val_t key, el_val_t default_val);
el_val_t api_query_param(el_val_t path, el_val_t key);
el_val_t api_utf8_trunc(el_val_t s, el_val_t n);
el_val_t ar_case_ending(el_val_t kase, el_val_t definite);
el_val_t ar_conjugate_form1(el_val_t past_base, el_val_t present_stem, el_val_t tense, el_val_t slot);
el_val_t ar_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
el_val_t ar_conjugate_form1(el_val_t past_base, el_val_t present_stem, el_val_t tense, el_val_t slot);
el_val_t ar_definite_article(el_val_t noun);
el_val_t ar_gender(el_val_t noun);
el_val_t ar_imperfect_prefix(el_val_t slot);
el_val_t ar_imperfect_suffix(el_val_t slot);
el_val_t ar_irregular(el_val_t verb, el_val_t tense, el_val_t slot);
el_val_t ar_irregular_araada(el_val_t slot, el_val_t tense);
el_val_t ar_irregular_istata(el_val_t slot, el_val_t tense);
el_val_t ar_irregular_jaa(el_val_t slot, el_val_t tense);
el_val_t ar_irregular_kaana(el_val_t slot, el_val_t tense);
el_val_t ar_irregular_qaala(el_val_t slot, el_val_t tense);
el_val_t ar_irregular_raaa(el_val_t slot, el_val_t tense);
el_val_t ar_irregular(el_val_t verb, el_val_t tense, el_val_t slot);
el_val_t ar_is_sun_letter(el_val_t c);
el_val_t ar_masc_pl_ending(el_val_t kase);
el_val_t ar_noun_form(el_val_t noun, el_val_t gender, el_val_t kase, el_val_t number, el_val_t definite);
@@ -141,7 +137,7 @@ el_val_t awareness_run(void);
el_val_t axon_get(el_val_t path);
el_val_t axon_post(el_val_t path, el_val_t body);
el_val_t bounded_persona_floor(void);
el_val_t bridge_save(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages, el_val_t tools_log, el_val_t tool_use_id, el_val_t wire);
el_val_t bridge_save(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages, el_val_t tools_log, el_val_t tool_use_id);
el_val_t build_form_from_json(el_val_t semantic_form_json, el_val_t lang_code);
el_val_t build_np(el_val_t referent, el_val_t slots);
el_val_t build_pp(el_val_t loc);
@@ -160,12 +156,8 @@ el_val_t cmd_abs_escape_at(el_val_t cmd, el_val_t root, el_val_t needle);
el_val_t connectd_get(el_val_t suffix);
el_val_t connectd_post(el_val_t suffix, el_val_t body);
el_val_t connector_tools_json(void);
el_val_t conv_hist_key(el_val_t session_id);
el_val_t conv_hist_label(el_val_t session_id);
el_val_t conv_history_block(el_val_t session_id);
el_val_t conv_history_load(el_val_t session_id);
el_val_t conv_history_persist(el_val_t session_id, el_val_t hist);
el_val_t conv_history_record(el_val_t session_id, el_val_t user_msg, el_val_t assistant_msg, el_val_t receipt);
el_val_t conv_history_load(void);
el_val_t conv_history_persist(el_val_t hist);
el_val_t cop_article(el_val_t gender, el_val_t number, el_val_t definite);
el_val_t cop_bwk_future(el_val_t prefix);
el_val_t cop_bwk_perfect(el_val_t prefix);
@@ -187,8 +179,8 @@ el_val_t cop_map_canonical(el_val_t verb);
el_val_t cop_nau_future(el_val_t prefix);
el_val_t cop_nau_perfect(el_val_t prefix);
el_val_t cop_nau_present(el_val_t prefix);
el_val_t cop_noun_phrase_gendered(el_val_t noun, el_val_t gram_case, el_val_t number, el_val_t definite, el_val_t gender);
el_val_t cop_noun_phrase(el_val_t noun, el_val_t gram_case, el_val_t number, el_val_t definite);
el_val_t cop_noun_phrase_gendered(el_val_t noun, el_val_t gram_case, el_val_t number, el_val_t definite, el_val_t gender);
el_val_t cop_regular_future(el_val_t prefix, el_val_t stem);
el_val_t cop_regular_perfect(el_val_t prefix, el_val_t stem);
el_val_t cop_regular_present(el_val_t prefix, el_val_t stem);
@@ -198,16 +190,16 @@ el_val_t cop_shwpe_present(el_val_t prefix);
el_val_t cop_slot(el_val_t person, el_val_t number);
el_val_t cop_str_ends(el_val_t s, el_val_t suf);
el_val_t cop_str_len(el_val_t s);
el_val_t cop_subject_prefix_gendered(el_val_t person, el_val_t gender, el_val_t number);
el_val_t cop_subject_prefix(el_val_t person, el_val_t number);
el_val_t cop_subject_prefix_gendered(el_val_t person, el_val_t gender, el_val_t number);
el_val_t current_engine_note(el_val_t model);
el_val_t de_adj_ending(el_val_t gender, el_val_t gram_case, el_val_t number, el_val_t article_type);
el_val_t de_article(el_val_t gender, el_val_t gram_case, el_val_t number, el_val_t definite);
el_val_t de_article_def(el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t de_article_indef(el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t de_article(el_val_t gender, el_val_t gram_case, el_val_t number, el_val_t definite);
el_val_t de_case_ending(el_val_t noun, el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t de_conjugate_weak(el_val_t stem, el_val_t tense, el_val_t person, el_val_t number);
el_val_t de_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t de_conjugate_weak(el_val_t stem, el_val_t tense, el_val_t person, el_val_t number);
el_val_t de_irregular_present(el_val_t verb, el_val_t person, el_val_t number);
el_val_t de_norm_number(el_val_t number);
el_val_t de_norm_person(el_val_t person);
@@ -217,12 +209,15 @@ el_val_t dharma_network_state(void);
el_val_t dharma_registry(void);
el_val_t dispatch_tool(el_val_t tool_name, el_val_t tool_input);
el_val_t distill_transcript(el_val_t transcript);
el_val_t egy_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t egy_conjugate_pronoun(el_val_t person, el_val_t number);
el_val_t egy_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t egy_Dd_future(el_val_t slot);
el_val_t egy_Dd_past(el_val_t slot);
el_val_t egy_Dd_present(el_val_t slot);
el_val_t egy_Sm_future(el_val_t slot);
el_val_t egy_Sm_past(el_val_t slot);
el_val_t egy_Sm_present(el_val_t slot);
el_val_t egy_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t egy_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t egy_conjugate_pronoun(el_val_t person, el_val_t number);
el_val_t egy_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t egy_drop(el_val_t s, el_val_t n);
el_val_t egy_fem(el_val_t noun);
@@ -246,11 +241,8 @@ el_val_t egy_regular_present(el_val_t stem, el_val_t slot);
el_val_t egy_sdm_future(el_val_t slot);
el_val_t egy_sdm_past(el_val_t slot);
el_val_t egy_sdm_present(el_val_t slot);
el_val_t egy_slot_with_gender(el_val_t person, el_val_t gender, el_val_t number);
el_val_t egy_slot(el_val_t person, el_val_t number);
el_val_t egy_Sm_future(el_val_t slot);
el_val_t egy_Sm_past(el_val_t slot);
el_val_t egy_Sm_present(el_val_t slot);
el_val_t egy_slot_with_gender(el_val_t person, el_val_t gender, el_val_t number);
el_val_t egy_str_ends(el_val_t s, el_val_t suf);
el_val_t egy_str_len(el_val_t s);
el_val_t egy_suffix_pronoun(el_val_t slot);
@@ -271,9 +263,9 @@ el_val_t en_verb_3sg(el_val_t base);
el_val_t en_verb_form(el_val_t base, el_val_t tense, el_val_t person, el_val_t number);
el_val_t en_verb_gerund(el_val_t base);
el_val_t en_verb_past(el_val_t base);
el_val_t engram_compile(el_val_t intent);
el_val_t engram_compile_multi(el_val_t topic);
el_val_t engram_compile_ranked(el_val_t nodes_json, el_val_t max_nodes);
el_val_t engram_compile(el_val_t intent);
el_val_t engram_dedup_nodes(el_val_t nodes_json);
el_val_t engram_detect_recall_intent(el_val_t message);
el_val_t engram_extract_entities(el_val_t message);
@@ -339,9 +331,9 @@ el_val_t es_starts_with_stressed_a(el_val_t noun);
el_val_t es_stem(el_val_t base);
el_val_t es_str_drop_last(el_val_t s, el_val_t n);
el_val_t es_str_ends(el_val_t s, el_val_t suf);
el_val_t es_str_last_char(el_val_t s);
el_val_t es_str_last2(el_val_t s);
el_val_t es_str_last3(el_val_t s);
el_val_t es_str_last_char(el_val_t s);
el_val_t es_verb_class(el_val_t base);
el_val_t extract_dim(el_val_t content, el_val_t key);
el_val_t fi_apply_case(el_val_t noun, el_val_t gram_case, el_val_t number);
@@ -384,8 +376,8 @@ el_val_t fr_slot(el_val_t person, el_val_t number);
el_val_t fr_stem(el_val_t base);
el_val_t fr_str_drop_last(el_val_t s, el_val_t n);
el_val_t fr_str_ends(el_val_t s, el_val_t suf);
el_val_t fr_str_last_char(el_val_t s);
el_val_t fr_str_last2(el_val_t s);
el_val_t fr_str_last_char(el_val_t s);
el_val_t fr_subject_starts_vowel(el_val_t subject);
el_val_t fr_uses_etre(el_val_t verb);
el_val_t fr_verb_ends_vowel(el_val_t verb_form);
@@ -407,9 +399,9 @@ el_val_t fro_conj3_future(el_val_t verb, el_val_t slot);
el_val_t fro_conj3_past(el_val_t stem, el_val_t slot);
el_val_t fro_conj3_present(el_val_t stem, el_val_t slot);
el_val_t fro_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t fro_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t fro_decline_fem(el_val_t noun, el_val_t number);
el_val_t fro_decline_masc(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t fro_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t fro_drop(el_val_t s, el_val_t n);
el_val_t fro_estre_future(el_val_t slot);
el_val_t fro_estre_past(el_val_t slot);
@@ -427,15 +419,15 @@ el_val_t fro_venir_past(el_val_t slot);
el_val_t fro_venir_present(el_val_t slot);
el_val_t fro_verb_class(el_val_t verb);
el_val_t fro_verb_stem(el_val_t verb, el_val_t vclass);
el_val_t generate_frame_lang(el_val_t frame, el_val_t lang_code);
el_val_t generate(el_val_t semantic_form_json);
el_val_t generate_frame(el_val_t frame);
el_val_t generate_frame_lang(el_val_t frame, el_val_t lang_code);
el_val_t generate_lang(el_val_t semantic_form_json, el_val_t lang_code);
el_val_t generate_tree(el_val_t rule_id_str, el_val_t slots);
el_val_t generate(el_val_t semantic_form_json);
el_val_t get_rules(void);
el_val_t get_vocab(void);
el_val_t gez_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t gez_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t gez_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t gez_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t gez_generic_imperfect(el_val_t base3sg, el_val_t slot);
el_val_t gez_generic_perfect(el_val_t base3sg, el_val_t slot);
@@ -454,12 +446,13 @@ el_val_t gez_qwl_imperfect(el_val_t slot);
el_val_t gez_qwl_perfect(el_val_t slot);
el_val_t gez_ray_imperfect(el_val_t slot);
el_val_t gez_ray_perfect(el_val_t slot);
el_val_t gez_slot_g(el_val_t person, el_val_t gender, el_val_t number);
el_val_t gez_slot(el_val_t person, el_val_t number);
el_val_t gez_slot_g(el_val_t person, el_val_t gender, el_val_t number);
el_val_t gez_str_drop_last(el_val_t s, el_val_t n);
el_val_t gez_str_ends(el_val_t s, el_val_t suf);
el_val_t gez_str_len(el_val_t s);
el_val_t goh_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t goh_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t goh_decline_fem_o_pl(el_val_t stem, el_val_t gram_case);
el_val_t goh_decline_fem_o_sg(el_val_t stem, el_val_t gram_case);
el_val_t goh_decline_masc_a_pl(el_val_t stem, el_val_t gram_case);
@@ -468,7 +461,6 @@ el_val_t goh_decline_masc_n_pl(el_val_t stem, el_val_t gram_case);
el_val_t goh_decline_masc_n_sg(el_val_t stem, el_val_t gram_case);
el_val_t goh_decline_neut_a_pl(el_val_t stem, el_val_t gram_case);
el_val_t goh_decline_neut_a_sg(el_val_t stem, el_val_t gram_case);
el_val_t goh_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t goh_demo_article(el_val_t stype, el_val_t number);
el_val_t goh_drop(el_val_t s, el_val_t n);
el_val_t goh_extract_stem(el_val_t noun, el_val_t stype);
@@ -493,13 +485,13 @@ el_val_t goh_weak_present(el_val_t stem, el_val_t slot);
el_val_t goh_wesan_past(el_val_t slot);
el_val_t goh_wesan_present(el_val_t slot);
el_val_t got_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t got_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t got_decline_a_stem_pl(el_val_t stem, el_val_t gram_case);
el_val_t got_decline_a_stem_sg(el_val_t stem, el_val_t gram_case);
el_val_t got_decline_n_stem_pl(el_val_t stem, el_val_t gram_case);
el_val_t got_decline_n_stem_sg(el_val_t stem, el_val_t gram_case);
el_val_t got_decline_o_stem_pl(el_val_t stem, el_val_t gram_case);
el_val_t got_decline_o_stem_sg(el_val_t stem, el_val_t gram_case);
el_val_t got_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t got_demo_article(el_val_t stype);
el_val_t got_extract_stem(el_val_t noun, el_val_t stype);
el_val_t got_gaggan_past(el_val_t slot);
@@ -532,17 +524,17 @@ el_val_t gram_build_vp(el_val_t verb, el_val_t aux, el_val_t profile);
el_val_t gram_order_constituents(el_val_t subj, el_val_t verb, el_val_t obj, el_val_t profile);
el_val_t gram_question_strategy(el_val_t profile);
el_val_t gram_word_order(el_val_t profile);
el_val_t grc_article(el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t grc_article_feminine(el_val_t gram_case, el_val_t number);
el_val_t grc_article_masculine(el_val_t gram_case, el_val_t number);
el_val_t grc_article_neuter(el_val_t gram_case, el_val_t number);
el_val_t grc_article(el_val_t gender, el_val_t gram_case, el_val_t number);
el_val_t grc_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t grc_declension(el_val_t noun);
el_val_t grc_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t grc_decline_1a(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t grc_decline_1e(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t grc_decline_2m(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t grc_decline_2n(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t grc_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t grc_echein_aorist(el_val_t slot);
el_val_t grc_echein_future(el_val_t slot);
el_val_t grc_echein_imperfect(el_val_t slot);
@@ -569,9 +561,9 @@ el_val_t grc_present_stem(el_val_t verb);
el_val_t grc_slot(el_val_t person, el_val_t number);
el_val_t grc_str_drop_last(el_val_t s, el_val_t n);
el_val_t grc_str_ends(el_val_t s, el_val_t suf);
el_val_t grc_str_last_char(el_val_t s);
el_val_t grc_str_last2(el_val_t s);
el_val_t grc_str_last3(el_val_t s);
el_val_t grc_str_last_char(el_val_t s);
el_val_t grc_thematic_future_ending(el_val_t slot);
el_val_t grc_thematic_imperfect_ending(el_val_t slot);
el_val_t grc_thematic_present_ending(el_val_t slot);
@@ -603,17 +595,17 @@ el_val_t handle_api_recall(el_val_t method, el_val_t path, el_val_t body);
el_val_t handle_api_remember(el_val_t body);
el_val_t handle_api_search_knowledge(el_val_t method, el_val_t path, el_val_t body);
el_val_t handle_api_tune_config(el_val_t body);
el_val_t handle_chat(el_val_t body);
el_val_t handle_chat_agentic(el_val_t body);
el_val_t handle_chat_as_soul(el_val_t body);
el_val_t handle_chat_plan(el_val_t body);
el_val_t handle_chat(el_val_t body);
el_val_t handle_config(el_val_t method, el_val_t body);
el_val_t handle_connectors(el_val_t method, el_val_t clean, el_val_t body);
el_val_t handle_conversations(el_val_t method);
el_val_t handle_dharma_recv(el_val_t body);
el_val_t handle_dharma_room_turn_agentic(el_val_t body);
el_val_t handle_dharma_room_turn(el_val_t body);
el_val_t handle_dharma(el_val_t path, el_val_t method, el_val_t body);
el_val_t handle_dharma_recv(el_val_t body);
el_val_t handle_dharma_room_turn(el_val_t body);
el_val_t handle_dharma_room_turn_agentic(el_val_t body);
el_val_t handle_elp_chat(el_val_t body);
el_val_t handle_nlg(el_val_t path, el_val_t method, el_val_t body);
el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body);
@@ -621,11 +613,11 @@ el_val_t handle_safety_contact_get(void);
el_val_t handle_safety_contact_post(el_val_t body);
el_val_t handle_see(el_val_t body);
el_val_t handle_session_approve(el_val_t session_id, el_val_t body);
el_val_t handle_tool_result(el_val_t session_id, el_val_t body);
el_val_t handle_tool(el_val_t path, el_val_t method, el_val_t body);
el_val_t handle_tool_result(el_val_t session_id, el_val_t body);
el_val_t hard_bell_threshold(void);
el_val_t he_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t he_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
el_val_t he_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t he_copula_future(el_val_t slot);
el_val_t he_copula_past(el_val_t slot);
el_val_t he_definite_prefix(el_val_t noun);
@@ -653,6 +645,7 @@ el_val_t he_str_drop_last(el_val_t s, el_val_t n);
el_val_t he_str_ends(el_val_t s, el_val_t suf);
el_val_t he_str_last_char(el_val_t s);
el_val_t he_str_len(el_val_t s);
el_val_t hebb_consolidate(void);
el_val_t hi_agree_genitive(el_val_t possessed_gender, el_val_t possessed_number);
el_val_t hi_aux_present(el_val_t person, el_val_t number);
el_val_t hi_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t gender, el_val_t number);
@@ -662,12 +655,12 @@ el_val_t hi_genitive_phrase(el_val_t possessor, el_val_t possessor_gender, el_va
el_val_t hi_hona_past(el_val_t gender, el_val_t number);
el_val_t hi_hona_present(el_val_t person, el_val_t number);
el_val_t hi_masc_aa_stem(el_val_t noun);
el_val_t hi_noun_direct(el_val_t noun, el_val_t gender, el_val_t number);
el_val_t hi_noun_direct_f(el_val_t noun, el_val_t number);
el_val_t hi_noun_direct_m(el_val_t noun, el_val_t number);
el_val_t hi_noun_direct(el_val_t noun, el_val_t gender, el_val_t number);
el_val_t hi_noun_oblique(el_val_t noun, el_val_t gender, el_val_t number);
el_val_t hi_noun_oblique_f(el_val_t noun, el_val_t number);
el_val_t hi_noun_oblique_m(el_val_t noun, el_val_t number);
el_val_t hi_noun_oblique(el_val_t noun, el_val_t gender, el_val_t number);
el_val_t hi_noun_with_post(el_val_t noun, el_val_t gender, el_val_t number, el_val_t gram_case);
el_val_t hi_past_irregular(el_val_t stem, el_val_t gender, el_val_t number);
el_val_t hi_past_suffix(el_val_t gender, el_val_t number);
@@ -677,11 +670,11 @@ el_val_t hi_str_drop_last(el_val_t s, el_val_t n);
el_val_t hi_str_ends(el_val_t s, el_val_t suf);
el_val_t hi_str_last_char(el_val_t s);
el_val_t hi_tense_suffix(el_val_t tense, el_val_t gender, el_val_t number);
el_val_t hi_verb_stem_clean(el_val_t infinitive);
el_val_t hi_verb_stem(el_val_t infinitive);
el_val_t hi_verb_stem_clean(el_val_t infinitive);
el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content);
el_val_t hist_trim_with_bell_guard(el_val_t hist);
el_val_t hist_trim(el_val_t hist);
el_val_t hist_trim_with_bell_guard(el_val_t hist);
el_val_t id_in_seen(el_val_t node_id, el_val_t seen);
el_val_t idle_count(void);
el_val_t idle_inc(void);
@@ -712,6 +705,7 @@ el_val_t json_escape(el_val_t s);
el_val_t json_safe(el_val_t s);
el_val_t la_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t la_declension(el_val_t noun);
el_val_t la_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t la_decline_1(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t la_decline_2er(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t la_decline_2m(el_val_t stem, el_val_t gram_case, el_val_t number);
@@ -719,7 +713,6 @@ el_val_t la_decline_2n(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t la_decline_3(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t la_decline_4(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t la_decline_5(el_val_t stem, el_val_t gram_case, el_val_t number);
el_val_t la_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t la_esse_future(el_val_t slot);
el_val_t la_esse_past(el_val_t slot);
el_val_t la_esse_present(el_val_t slot);
@@ -743,9 +736,9 @@ el_val_t la_slot(el_val_t person, el_val_t number);
el_val_t la_stem(el_val_t verb, el_val_t vclass);
el_val_t la_str_drop_last(el_val_t s, el_val_t n);
el_val_t la_str_ends(el_val_t s, el_val_t suf);
el_val_t la_str_last_char(el_val_t s);
el_val_t la_str_last2(el_val_t s);
el_val_t la_str_last3(el_val_t s);
el_val_t la_str_last_char(el_val_t s);
el_val_t la_velle_future(el_val_t slot);
el_val_t la_velle_past(el_val_t slot);
el_val_t la_velle_present(el_val_t slot);
@@ -762,6 +755,7 @@ el_val_t lang_is_fusional(el_val_t profile);
el_val_t lang_is_isolating(el_val_t profile);
el_val_t lang_is_polysynthetic(el_val_t profile);
el_val_t lang_is_rtl(el_val_t profile);
el_val_t lang_profile(el_val_t code, el_val_t word_order, el_val_t morph_type, el_val_t has_case, el_val_t has_gender, el_val_t script_dir, el_val_t agreement, el_val_t null_subject);
el_val_t lang_profile_akk(void);
el_val_t lang_profile_ang(void);
el_val_t lang_profile_ar(void);
@@ -793,10 +787,8 @@ el_val_t lang_profile_sw(void);
el_val_t lang_profile_txb(void);
el_val_t lang_profile_uga(void);
el_val_t lang_profile_zh(void);
el_val_t lang_profile(el_val_t code, el_val_t word_order, el_val_t morph_type, el_val_t has_case, el_val_t has_gender, el_val_t script_dir, el_val_t agreement, el_val_t null_subject);
el_val_t lang_word_order(el_val_t profile);
el_val_t layered_cycle(el_val_t raw_input, el_val_t session_id, el_val_t utility);
el_val_t layered_generate(el_val_t prompt, el_val_t imprint_id, el_val_t session_id);
el_val_t layered_cycle(el_val_t raw_input);
el_val_t lex_class(el_val_t entry);
el_val_t lex_form(el_val_t entry, el_val_t idx);
el_val_t lex_pos(el_val_t entry);
@@ -845,10 +837,10 @@ el_val_t morph_pluralize(el_val_t noun, el_val_t profile);
el_val_t next_bridge_id(void);
el_val_t nlg_is_ws(el_val_t c);
el_val_t non_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t non_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t non_decline_fem(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t non_decline_masc(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t non_decline_neut(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t non_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t non_def_suffix_fem(el_val_t gram_case, el_val_t number);
el_val_t non_def_suffix_masc(el_val_t gram_case, el_val_t number);
el_val_t non_def_suffix_neut(el_val_t gram_case, el_val_t number);
@@ -875,11 +867,6 @@ el_val_t non_weak_past(el_val_t stem, el_val_t slot);
el_val_t non_weak_present(el_val_t stem, el_val_t slot);
el_val_t one_cycle(void);
el_val_t openai_chat_complete(el_val_t model, el_val_t base_url, el_val_t api_key, el_val_t safe_sys, el_val_t messages_json);
el_val_t openai_tools_json(el_val_t tools_anthropic);
el_val_t json_trim_dangling_escape(el_val_t s);
el_val_t utf8_safe_slice(el_val_t s, el_val_t n);
el_val_t agentic_tools_no_web(void);
el_val_t openai_agentic_loop(el_val_t session_id, el_val_t model, el_val_t safe_sys, el_val_t tools_json, el_val_t messages_in, el_val_t tools_log_in);
el_val_t parse_float_x100(el_val_t s);
el_val_t path_within_root(el_val_t path, el_val_t root);
el_val_t peo_ah_past(el_val_t slot);
@@ -887,8 +874,8 @@ el_val_t peo_ah_present(el_val_t slot);
el_val_t peo_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t peo_da_past(el_val_t slot);
el_val_t peo_da_present(el_val_t slot);
el_val_t peo_decline_astem(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t peo_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t peo_decline_astem(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t peo_drop(el_val_t s, el_val_t n);
el_val_t peo_ends(el_val_t s, el_val_t suf);
el_val_t peo_kar_past(el_val_t slot);
@@ -904,11 +891,11 @@ el_val_t perceive(void);
el_val_t pi_aorist_ending(el_val_t slot);
el_val_t pi_atthi_present(el_val_t slot);
el_val_t pi_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t pi_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t pi_decline_a_fem_pl(el_val_t stem, el_val_t gram_case);
el_val_t pi_decline_a_fem_sg(el_val_t stem, el_val_t gram_case);
el_val_t pi_decline_a_masc_pl(el_val_t stem, el_val_t gram_case);
el_val_t pi_decline_a_masc_sg(el_val_t stem, el_val_t gram_case);
el_val_t pi_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t pi_detect_class(el_val_t noun);
el_val_t pi_drop(el_val_t s, el_val_t n);
el_val_t pi_future_ending(el_val_t slot);
@@ -939,11 +926,11 @@ el_val_t proactive_curiosity(void);
el_val_t pulse_count(void);
el_val_t pulse_inc(void);
el_val_t rate_limit_check(el_val_t ip, el_val_t path);
el_val_t realize(el_val_t form);
el_val_t realize_lang(el_val_t form, el_val_t profile);
el_val_t realize_np(el_val_t referent, el_val_t number);
el_val_t realize_question_lang(el_val_t predicate, el_val_t tense, el_val_t aspect, el_val_t person, el_val_t number, el_val_t agent, el_val_t patient, el_val_t location, el_val_t profile);
el_val_t realize_vp_lang(el_val_t base_verb, el_val_t tense, el_val_t aspect, el_val_t person, el_val_t number, el_val_t profile);
el_val_t realize(el_val_t form);
el_val_t record(el_val_t outcome_json);
el_val_t render_studio(void);
el_val_t render_tree(el_val_t tree);
@@ -954,9 +941,9 @@ el_val_t route_imprint_contextual(el_val_t body);
el_val_t route_imprint_user(el_val_t body);
el_val_t route_lineage(void);
el_val_t route_synthesize(el_val_t body);
el_val_t ru_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number, el_val_t gender);
el_val_t ru_conjugate_1st(el_val_t stem, el_val_t tense, el_val_t person, el_val_t number);
el_val_t ru_conjugate_2nd(el_val_t stem, el_val_t tense, el_val_t person, el_val_t number);
el_val_t ru_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number, el_val_t gender);
el_val_t ru_decline_fem(el_val_t noun, el_val_t stype, el_val_t gram_case, el_val_t number);
el_val_t ru_decline_masc(el_val_t noun, el_val_t stype, el_val_t gram_case, el_val_t number);
el_val_t ru_decline_neut(el_val_t noun, el_val_t stype, el_val_t gram_case, el_val_t number);
@@ -969,8 +956,8 @@ el_val_t ru_past_stem(el_val_t verb);
el_val_t ru_stem_type(el_val_t noun, el_val_t gender);
el_val_t rule_id(el_val_t rule);
el_val_t rule_lhs(el_val_t rule);
el_val_t rule_rhs_len(el_val_t rule);
el_val_t rule_rhs(el_val_t rule, el_val_t idx);
el_val_t rule_rhs_len(el_val_t rule);
el_val_t run_command_guard(el_val_t cmd, el_val_t root);
el_val_t run_command_is_readonly(el_val_t cmd);
el_val_t sa_as_future(el_val_t slot);
@@ -984,11 +971,11 @@ el_val_t sa_class1_future_ending(el_val_t slot);
el_val_t sa_class1_past_ending(el_val_t slot);
el_val_t sa_class1_present_ending(el_val_t slot);
el_val_t sa_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t sa_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t sa_decline_a_stem_pl(el_val_t stem, el_val_t gram_case);
el_val_t sa_decline_a_stem_sg(el_val_t stem, el_val_t gram_case);
el_val_t sa_decline_aa_stem_pl(el_val_t stem, el_val_t gram_case);
el_val_t sa_decline_aa_stem_sg(el_val_t stem, el_val_t gram_case);
el_val_t sa_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t sa_drs_future(el_val_t slot);
el_val_t sa_drs_past(el_val_t slot);
el_val_t sa_drs_present(el_val_t slot);
@@ -1036,26 +1023,26 @@ el_val_t scan_token(el_val_t s, el_val_t start);
el_val_t security_research_authorized(void);
el_val_t seed_persona_from_env(void);
el_val_t sem_first_modifier(el_val_t mods);
el_val_t sem_frame(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers);
el_val_t sem_frame_lang(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers, el_val_t lang_code);
el_val_t sem_frame_obj(el_val_t intent, el_val_t subject, el_val_t obj);
el_val_t sem_frame_simple(el_val_t intent, el_val_t subject);
el_val_t sem_frame(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers);
el_val_t sem_get(el_val_t json, el_val_t key);
el_val_t sem_intent_to_realize(el_val_t intent);
el_val_t sem_intent(el_val_t frame);
el_val_t sem_intent_to_realize(el_val_t intent);
el_val_t sem_lang(el_val_t frame);
el_val_t sem_modifiers(el_val_t frame);
el_val_t sem_object(el_val_t frame);
el_val_t sem_realize(el_val_t frame);
el_val_t sem_realize_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
el_val_t sem_realize_greet(el_val_t subject);
el_val_t sem_realize_lang(el_val_t frame, el_val_t lang_code);
el_val_t sem_realize(el_val_t frame);
el_val_t sem_subject(el_val_t frame);
el_val_t sem_to_spec_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
el_val_t sem_to_spec(el_val_t frame);
el_val_t sem_to_spec_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
el_val_t session_auto_title(el_val_t session_id, el_val_t first_message);
el_val_t session_create_cleanup(el_val_t session_id);
el_val_t session_create(el_val_t body);
el_val_t session_create_cleanup(el_val_t session_id);
el_val_t session_delete(el_val_t session_id);
el_val_t session_exists(el_val_t session_id);
el_val_t session_get(el_val_t session_id);
@@ -1064,11 +1051,11 @@ el_val_t session_hist_save(el_val_t session_id, el_val_t hist);
el_val_t session_list(void);
el_val_t session_make_content(el_val_t id, el_val_t title, el_val_t created_at, el_val_t updated_at, el_val_t folder);
el_val_t session_preload_bullets(el_val_t nodes, el_val_t max_bullets, el_val_t snip_len);
el_val_t session_search_entry(el_val_t node);
el_val_t session_search(el_val_t query);
el_val_t session_search_entry(el_val_t node);
el_val_t session_summary_autogenerate(el_val_t hist);
el_val_t session_summary_write_dated(el_val_t summary_text, el_val_t label);
el_val_t session_summary_write(el_val_t summary_text);
el_val_t session_summary_write_dated(el_val_t summary_text, el_val_t label);
el_val_t session_title_from_message(el_val_t message);
el_val_t session_update_meta_timestamp(el_val_t session_id);
el_val_t session_update_patch(el_val_t session_id, el_val_t body);
@@ -1079,9 +1066,9 @@ el_val_t sga_bith_past(el_val_t slot);
el_val_t sga_bith_present(el_val_t slot);
el_val_t sga_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t sga_copula_present(el_val_t slot);
el_val_t sga_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t sga_decline_astem(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t sga_decline_ostem(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t sga_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t sga_detect_gender(el_val_t noun);
el_val_t sga_drop(el_val_t s, el_val_t n);
el_val_t sga_first(el_val_t s);
@@ -1109,9 +1096,9 @@ el_val_t steward_session_check(el_val_t input, el_val_t session_id);
el_val_t steward_validate_imprint(el_val_t imprint_id, el_val_t tool_name);
el_val_t str_drop_last(el_val_t s, el_val_t n);
el_val_t str_ends(el_val_t s, el_val_t suf);
el_val_t str_last_char(el_val_t s);
el_val_t str_last2(el_val_t s);
el_val_t str_last3(el_val_t s);
el_val_t str_last_char(el_val_t s);
el_val_t strengthen_chat_nodes(el_val_t activation_nodes);
el_val_t strip_query(el_val_t path);
el_val_t studio_tools_json(void);
@@ -1138,8 +1125,8 @@ el_val_t sux_realize_sentence(el_val_t intent, el_val_t agent, el_val_t predicat
el_val_t sux_slot(el_val_t person, el_val_t number);
el_val_t sux_str_drop_last(el_val_t s, el_val_t n);
el_val_t sux_str_ends(el_val_t s, el_val_t suf);
el_val_t sux_str_last_char(el_val_t s);
el_val_t sux_str_last2(el_val_t s);
el_val_t sux_str_last_char(el_val_t s);
el_val_t sux_tum2_past(el_val_t slot);
el_val_t sux_tum2_present(el_val_t slot);
el_val_t sux_verb_chain(el_val_t agent, el_val_t verb, el_val_t patient, el_val_t tense);
@@ -1157,9 +1144,9 @@ el_val_t sw_noun_plural(el_val_t noun);
el_val_t sw_obj_prefix(el_val_t person, el_val_t number, el_val_t noun_class);
el_val_t sw_str_drop_last(el_val_t s, el_val_t n);
el_val_t sw_str_ends(el_val_t s, el_val_t suf);
el_val_t sw_str_first_char(el_val_t s);
el_val_t sw_str_first2(el_val_t s);
el_val_t sw_str_first3(el_val_t s);
el_val_t sw_str_first_char(el_val_t s);
el_val_t sw_str_last_char(el_val_t s);
el_val_t sw_subj_prefix(el_val_t person, el_val_t number, el_val_t noun_class);
el_val_t sw_tense_marker(el_val_t tense);
@@ -1177,9 +1164,9 @@ el_val_t tombstone_node(el_val_t id);
el_val_t tombstoned_id_set(void);
el_val_t tool_auto_approved(el_val_t tool_name);
el_val_t txb_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t txb_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t txb_decline_fem(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t txb_decline_masc(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t txb_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t txb_detect_gender(el_val_t noun);
el_val_t txb_drop(el_val_t s, el_val_t n);
el_val_t txb_ends(el_val_t s, el_val_t suf);
@@ -1194,8 +1181,8 @@ el_val_t txb_wes_present(el_val_t slot);
el_val_t txb_ya_present(el_val_t slot);
el_val_t uga_amr_imperfect(el_val_t slot);
el_val_t uga_amr_perfect(el_val_t slot);
el_val_t uga_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t uga_conjugate(el_val_t verb, el_val_t tense, el_val_t person, el_val_t number);
el_val_t uga_conjugate_copula(el_val_t tense, el_val_t slot);
el_val_t uga_decline(el_val_t noun, el_val_t gram_case, el_val_t number);
el_val_t uga_generic_imperfect(el_val_t base3sg, el_val_t slot);
el_val_t uga_generic_perfect(el_val_t base3sg, el_val_t slot);
@@ -1210,8 +1197,8 @@ el_val_t uga_map_canonical(el_val_t verb);
el_val_t uga_noun_phrase(el_val_t noun, el_val_t gram_case, el_val_t number, el_val_t definite);
el_val_t uga_ray_imperfect(el_val_t slot);
el_val_t uga_ray_perfect(el_val_t slot);
el_val_t uga_slot_g(el_val_t person, el_val_t gender, el_val_t number);
el_val_t uga_slot(el_val_t person, el_val_t number);
el_val_t uga_slot_g(el_val_t person, el_val_t gender, el_val_t number);
el_val_t uga_str_drop_last(el_val_t s, el_val_t n);
el_val_t uga_str_ends(el_val_t s, el_val_t suf);
el_val_t uga_str_len(el_val_t s);
@@ -1219,6 +1206,6 @@ el_val_t uga_strip_nom(el_val_t noun);
el_val_t verb_form(el_val_t base, el_val_t tense, el_val_t person, el_val_t number);
el_val_t vocab_by_class(el_val_t cls);
el_val_t vocab_by_pos(el_val_t pos);
el_val_t vocab_lookup_en(el_val_t word);
el_val_t vocab_lookup(el_val_t word, el_val_t lang_code);
el_val_t vocab_lookup_en(el_val_t word);
el_val_t vocab_synonym(el_val_t word, el_val_t lang_register, el_val_t lang_code);
Generated Vendored
+5
View File
@@ -0,0 +1,5 @@
// auto-generated by elc --emit-header — do not edit
extern fn elp_extract_topic(msg: String) -> String
extern fn elp_detect_predicate(msg: String) -> String
extern fn elp_parse(msg: String) -> String
extern fn handle_elp_chat(body: String) -> String
Generated Vendored
+7
View File
@@ -0,0 +1,7 @@
// auto-generated by elc --emit-header — do not edit
extern fn sem_get(json: String, key: String) -> String
extern fn generate_frame(frame: [String]) -> String
extern fn generate_frame_lang(frame: [String], lang_code: String) -> String
extern fn build_form_from_json(semantic_form_json: String, lang_code: String) -> [String]
extern fn generate(semantic_form_json: String) -> String
extern fn generate_lang(semantic_form_json: String, lang_code: String) -> String
Generated Vendored
+38
View File
@@ -0,0 +1,38 @@
// auto-generated by elc --emit-header — do not edit
extern fn slots_get(slots: [String], key: String) -> String
extern fn slots_set(slots: [String], key: String, val: String) -> [String]
extern fn make_slots(k0: String, v0: String) -> [String]
extern fn make_slots2(k0: String, v0: String, k1: String, v1: String) -> [String]
extern fn make_slots3(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String) -> [String]
extern fn make_slots4(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String) -> [String]
extern fn make_slots5(k0: String, v0: String, k1: String, v1: String, k2: String, v2: String, k3: String, v3: String, k4: String, v4: String) -> [String]
extern fn rule_id(rule: [String]) -> String
extern fn rule_lhs(rule: [String]) -> String
extern fn rule_rhs_len(rule: [String]) -> Int
extern fn rule_rhs(rule: [String], idx: Int) -> String
extern fn make_rule(id: String, lhs: String, r0: String) -> [String]
extern fn make_rule2(id: String, lhs: String, r0: String, r1: String) -> [String]
extern fn make_rule3(id: String, lhs: String, r0: String, r1: String, r2: String) -> [String]
extern fn make_rule4(id: String, lhs: String, r0: String, r1: String, r2: String, r3: String) -> [String]
extern fn build_rules() -> [[String]]
extern fn get_rules() -> [[String]]
extern fn find_rule(rule_id_str: String) -> [String]
extern fn make_leaf(label: String, word: String) -> String
extern fn make_node1(label: String, child0: String) -> String
extern fn make_node2(label: String, child0: String, child1: String) -> String
extern fn make_node3(label: String, child0: String, child1: String, child2: String) -> String
extern fn make_node4(label: String, child0: String, child1: String, child2: String, child3: String) -> String
extern fn nlg_is_ws(c: String) -> Bool
extern fn skip_ws(s: String, pos: Int) -> Int
extern fn scan_token(s: String, start: Int) -> [String]
extern fn render_tree(tree: String) -> String
extern fn gram_word_order(profile: [String]) -> String
extern fn gram_order_constituents(subj: String, verb: String, obj: String, profile: [String]) -> String
extern fn gram_build_vp(verb: String, aux: String, profile: [String]) -> String
extern fn gram_question_strategy(profile: [String]) -> String
extern fn is_pronoun(word: String) -> Bool
extern fn build_np(referent: String, slots: [String]) -> String
extern fn build_pp(loc: String) -> String
extern fn build_vp_body(slots: [String]) -> String
extern fn build_vp_from_slots(slots: [String]) -> String
extern fn generate_tree(rule_id_str: String, slots: [String]) -> String
Generated Vendored
+7
View File
@@ -0,0 +1,7 @@
// auto-generated by elc --emit-header — do not edit
extern fn imprint_current() -> String
extern fn imprint_load(imprint_id: String) -> String
extern fn imprint_respond(input: String, imprint_id: String) -> String
extern fn imprint_surface_knowledge(query: String, imprint_id: String) -> String
extern fn imprint_surface_memory_read(query: String) -> String
extern fn imprint_unload() -> Void
Generated Vendored
+46
View File
@@ -0,0 +1,46 @@
// auto-generated by elc --emit-header — do not edit
extern fn lang_profile(code: String, word_order: String, morph_type: String, has_case: String, has_gender: String, script_dir: String, agreement: String, null_subject: String) -> [String]
extern fn lang_get(profile: [String], key: String) -> String
extern fn lang_profile_en() -> [String]
extern fn lang_profile_ja() -> [String]
extern fn lang_profile_ar() -> [String]
extern fn lang_profile_zh() -> [String]
extern fn lang_profile_de() -> [String]
extern fn lang_profile_es() -> [String]
extern fn lang_profile_fi() -> [String]
extern fn lang_profile_sw() -> [String]
extern fn lang_profile_hi() -> [String]
extern fn lang_profile_ru() -> [String]
extern fn lang_profile_fr() -> [String]
extern fn lang_profile_la() -> [String]
extern fn lang_profile_he() -> [String]
extern fn lang_profile_sa() -> [String]
extern fn lang_profile_got() -> [String]
extern fn lang_profile_non() -> [String]
extern fn lang_profile_enm() -> [String]
extern fn lang_profile_pi() -> [String]
extern fn lang_profile_grc() -> [String]
extern fn lang_profile_ang() -> [String]
extern fn lang_profile_fro() -> [String]
extern fn lang_profile_goh() -> [String]
extern fn lang_profile_sga() -> [String]
extern fn lang_profile_txb() -> [String]
extern fn lang_profile_peo() -> [String]
extern fn lang_profile_akk() -> [String]
extern fn lang_profile_uga() -> [String]
extern fn lang_profile_egy() -> [String]
extern fn lang_profile_sux() -> [String]
extern fn lang_profile_gez() -> [String]
extern fn lang_profile_cop() -> [String]
extern fn lang_from_code(code: String) -> [String]
extern fn lang_default() -> [String]
extern fn lang_is_isolating(profile: [String]) -> Bool
extern fn lang_is_agglutinative(profile: [String]) -> Bool
extern fn lang_is_fusional(profile: [String]) -> Bool
extern fn lang_is_polysynthetic(profile: [String]) -> Bool
extern fn lang_is_rtl(profile: [String]) -> Bool
extern fn lang_has_null_subject(profile: [String]) -> Bool
extern fn lang_has_case(profile: [String]) -> Bool
extern fn lang_has_gender(profile: [String]) -> Bool
extern fn lang_word_order(profile: [String]) -> String
extern fn lang_code(profile: [String]) -> String
Generated Vendored
+16
View File
@@ -0,0 +1,16 @@
// auto-generated by elc --emit-header — do not edit
extern fn tier_working() -> String
extern fn tier_episodic() -> String
extern fn tier_canonical() -> String
extern fn mem_store(content: String, label: String, tags: String) -> String
extern fn mem_remember(content: String, tags: String) -> String
extern fn mem_recall(query: String, depth: Int) -> String
extern fn mem_search(query: String, limit: Int) -> String
extern fn mem_strengthen(node_id: String) -> Void
extern fn mem_forget(node_id: String) -> Void
extern fn mem_consolidate() -> String
extern fn mem_save(path: String) -> Void
extern fn mem_load(path: String) -> Void
extern fn mem_boot_count_get() -> Int
extern fn mem_boot_count_inc() -> Int
extern fn mem_emit_state_event(trigger: String, kind: String, content: String) -> String
Generated Vendored
+31
View File
@@ -0,0 +1,31 @@
// auto-generated by elc --emit-header — do not edit
extern fn akk_str_ends(s: String, suf: String) -> Bool
extern fn akk_str_len(s: String) -> Int
extern fn akk_str_drop_last(s: String, n: Int) -> String
extern fn akk_slot(person: String, number: String) -> Int
extern fn akk_slot_g(person: String, gender: String, number: String) -> Int
extern fn akk_copula_present(slot: Int) -> String
extern fn akk_copula_stative(slot: Int) -> String
extern fn akk_is_copula(verb: String) -> Bool
extern fn akk_conjugate_copula(tense: String, slot: Int) -> String
extern fn akk_alaku_present(slot: Int) -> String
extern fn akk_alaku_perfect(slot: Int) -> String
extern fn akk_amaru_present(slot: Int) -> String
extern fn akk_amaru_perfect(slot: Int) -> String
extern fn akk_amaru_stative(slot: Int) -> String
extern fn akk_qabu_present(slot: Int) -> String
extern fn akk_qabu_perfect(slot: Int) -> String
extern fn akk_qabu_stative(slot: Int) -> String
extern fn akk_epesu_present(slot: Int) -> String
extern fn akk_epesu_perfect(slot: Int) -> String
extern fn akk_epesu_stative(slot: Int) -> String
extern fn akk_regular_present(stem: String, slot: Int) -> String
extern fn akk_regular_perfect(stem: String, slot: Int) -> String
extern fn akk_regular_stative(stem: String, slot: Int) -> String
extern fn akk_known_verb(verb: String, tense: String, slot: Int) -> String
extern fn akk_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn akk_strip_nom(noun: String) -> String
extern fn akk_is_fem(noun: String) -> Bool
extern fn akk_decline(noun: String, gram_case: String, number: String) -> String
extern fn akk_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
extern fn akk_map_canonical(verb: String) -> String
Generated Vendored
+44
View File
@@ -0,0 +1,44 @@
// auto-generated by elc --emit-header — do not edit
extern fn ang_str_ends(s: String, suf: String) -> Bool
extern fn ang_str_drop_last(s: String, n: Int) -> String
extern fn ang_str_last_char(s: String) -> String
extern fn ang_str_last2(s: String) -> String
extern fn ang_slot(person: String, number: String) -> Int
extern fn ang_map_canonical(verb: String) -> String
extern fn ang_wesan_past(slot: Int) -> String
extern fn ang_beon_present(slot: Int) -> String
extern fn ang_wesan_present(slot: Int) -> String
extern fn ang_habban_present(slot: Int) -> String
extern fn ang_habban_past(slot: Int) -> String
extern fn ang_gan_present(slot: Int) -> String
extern fn ang_gan_past(slot: Int) -> String
extern fn ang_cuman_present(slot: Int) -> String
extern fn ang_cuman_past(slot: Int) -> String
extern fn ang_secgan_present(slot: Int) -> String
extern fn ang_secgan_past(slot: Int) -> String
extern fn ang_seon_present(slot: Int) -> String
extern fn ang_seon_past(slot: Int) -> String
extern fn ang_don_present(slot: Int) -> String
extern fn ang_don_past(slot: Int) -> String
extern fn ang_willan_present(slot: Int) -> String
extern fn ang_willan_past(slot: Int) -> String
extern fn ang_magan_present(slot: Int) -> String
extern fn ang_magan_past(slot: Int) -> String
extern fn ang_witan_present(slot: Int) -> String
extern fn ang_witan_past(slot: Int) -> String
extern fn ang_weak_present_ending(slot: Int) -> String
extern fn ang_weak_past_stem(stem: String) -> String
extern fn ang_weak_past(stem: String, slot: Int) -> String
extern fn ang_weak_stem(verb: String) -> String
extern fn ang_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn ang_declension(noun: String, gender: String) -> String
extern fn ang_decline_strong_masc(noun: String, gram_case: String, number: String) -> String
extern fn ang_decline_strong_neut(noun: String, gram_case: String, number: String) -> String
extern fn ang_decline_weak(noun: String, gram_case: String, number: String) -> String
extern fn ang_decline(noun: String, gram_case: String, number: String, gender: String) -> String
extern fn ang_article_masculine(gram_case: String, number: String) -> String
extern fn ang_article_feminine(gram_case: String, number: String) -> String
extern fn ang_article_neuter(gram_case: String, number: String) -> String
extern fn ang_article(gender: String, gram_case: String, number: String) -> String
extern fn ang_infer_gender(noun: String) -> String
extern fn ang_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+27
View File
@@ -0,0 +1,27 @@
// auto-generated by elc --emit-header — do not edit
extern fn ar_str_ends(s: String, suf: String) -> Bool
extern fn ar_str_len(s: String) -> Int
extern fn ar_str_drop_last(s: String, n: Int) -> String
extern fn ar_str_last_char(s: String) -> String
extern fn ar_slot(person: String, gender: String, number: String) -> Int
extern fn ar_perfect_suffix(slot: Int) -> String
extern fn ar_imperfect_prefix(slot: Int) -> String
extern fn ar_imperfect_suffix(slot: Int) -> String
extern fn ar_conjugate_form1(past_base: String, present_stem: String, tense: String, slot: Int) -> String
extern fn ar_irregular_kaana(slot: Int, tense: String) -> String
extern fn ar_irregular_qaala(slot: Int, tense: String) -> String
extern fn ar_irregular_jaa(slot: Int, tense: String) -> String
extern fn ar_irregular_raaa(slot: Int, tense: String) -> String
extern fn ar_irregular_araada(slot: Int, tense: String) -> String
extern fn ar_irregular_istata(slot: Int, tense: String) -> String
extern fn ar_irregular(verb: String, tense: String, slot: Int) -> String
extern fn ar_present_stem(verb: String) -> String
extern fn ar_conjugate(verb: String, tense: String, person: String, gender: String, number: String) -> String
extern fn ar_is_sun_letter(c: String) -> Bool
extern fn ar_definite_article(noun: String) -> String
extern fn ar_case_ending(kase: String, definite: String) -> String
extern fn ar_gender(noun: String) -> String
extern fn ar_masc_pl_ending(kase: String) -> String
extern fn ar_sound_plural(noun: String, gender: String) -> String
extern fn ar_noun_form(noun: String, gender: String, kase: String, number: String, definite: String) -> String
extern fn ar_verb_form(verb: String, tense: String, person: String, number: String) -> String
Generated Vendored
+35
View File
@@ -0,0 +1,35 @@
// auto-generated by elc --emit-header — do not edit
extern fn cop_str_ends(s: String, suf: String) -> Bool
extern fn cop_str_len(s: String) -> Int
extern fn cop_drop(s: String, n: Int) -> String
extern fn cop_last_char(s: String) -> String
extern fn cop_slot(person: String, number: String) -> Int
extern fn cop_subject_prefix(person: String, number: String) -> String
extern fn cop_subject_prefix_gendered(person: String, gender: String, number: String) -> String
extern fn cop_copula_particle(gender: String, number: String) -> String
extern fn cop_shwpe_present(prefix: String) -> String
extern fn cop_shwpe_perfect(prefix: String) -> String
extern fn cop_shwpe_future(prefix: String) -> String
extern fn cop_bwk_present(prefix: String) -> String
extern fn cop_bwk_perfect(prefix: String) -> String
extern fn cop_bwk_future(prefix: String) -> String
extern fn cop_nau_present(prefix: String) -> String
extern fn cop_nau_perfect(prefix: String) -> String
extern fn cop_nau_future(prefix: String) -> String
extern fn cop_jw_present(prefix: String) -> String
extern fn cop_jw_perfect(prefix: String) -> String
extern fn cop_jw_future(prefix: String) -> String
extern fn cop_di_present(prefix: String) -> String
extern fn cop_di_perfect(prefix: String) -> String
extern fn cop_di_future(prefix: String) -> String
extern fn cop_is_copula(verb: String) -> Bool
extern fn cop_known_verb_prefixed(verb: String, tense: String, prefix: String) -> String
extern fn cop_regular_present(prefix: String, stem: String) -> String
extern fn cop_regular_perfect(prefix: String, stem: String) -> String
extern fn cop_regular_future(prefix: String, stem: String) -> String
extern fn cop_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn cop_article(gender: String, number: String, definite: String) -> String
extern fn cop_decline(noun: String, gram_case: String, number: String) -> String
extern fn cop_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
extern fn cop_noun_phrase_gendered(noun: String, gram_case: String, number: String, definite: String, gender: String) -> String
extern fn cop_map_canonical(verb: String) -> String
Generated Vendored
+13
View File
@@ -0,0 +1,13 @@
// auto-generated by elc --emit-header — do not edit
extern fn de_article_def(gender: String, gram_case: String, number: String) -> String
extern fn de_article_indef(gender: String, gram_case: String, number: String) -> String
extern fn de_article(gender: String, gram_case: String, number: String, definite: String) -> String
extern fn de_adj_ending(gender: String, gram_case: String, number: String, article_type: String) -> String
extern fn de_noun_plural(noun: String, gender: String) -> String
extern fn de_case_ending(noun: String, gender: String, gram_case: String, number: String) -> String
extern fn de_conjugate_weak(stem: String, tense: String, person: String, number: String) -> String
extern fn de_irregular_present(verb: String, person: String, number: String) -> String
extern fn de_strong_past_stem(verb: String) -> String
extern fn de_norm_number(number: String) -> String
extern fn de_norm_person(person: String) -> String
extern fn de_conjugate(verb: String, tense: String, person: String, number: String) -> String
Generated Vendored
+38
View File
@@ -0,0 +1,38 @@
// auto-generated by elc --emit-header — do not edit
extern fn egy_str_ends(s: String, suf: String) -> Bool
extern fn egy_str_len(s: String) -> Int
extern fn egy_drop(s: String, n: Int) -> String
extern fn egy_last_char(s: String) -> String
extern fn egy_slot(person: String, number: String) -> Int
extern fn egy_slot_with_gender(person: String, gender: String, number: String) -> Int
extern fn egy_conjugate_pronoun(person: String, number: String) -> String
extern fn egy_suffix_pronoun(slot: Int) -> String
extern fn egy_is_copula(verb: String) -> Bool
extern fn egy_conjugate_copula(tense: String, slot: Int) -> String
extern fn egy_rdi_present(slot: Int) -> String
extern fn egy_rdi_past(slot: Int) -> String
extern fn egy_rdi_future(slot: Int) -> String
extern fn egy_mAA_present(slot: Int) -> String
extern fn egy_mAA_past(slot: Int) -> String
extern fn egy_mAA_future(slot: Int) -> String
extern fn egy_Dd_present(slot: Int) -> String
extern fn egy_Dd_past(slot: Int) -> String
extern fn egy_Dd_future(slot: Int) -> String
extern fn egy_Sm_present(slot: Int) -> String
extern fn egy_Sm_past(slot: Int) -> String
extern fn egy_Sm_future(slot: Int) -> String
extern fn egy_iri_present(slot: Int) -> String
extern fn egy_iri_past(slot: Int) -> String
extern fn egy_iri_future(slot: Int) -> String
extern fn egy_sdm_present(slot: Int) -> String
extern fn egy_sdm_past(slot: Int) -> String
extern fn egy_sdm_future(slot: Int) -> String
extern fn egy_known_verb(verb: String, tense: String, slot: Int) -> String
extern fn egy_regular_present(stem: String, slot: Int) -> String
extern fn egy_regular_past(stem: String, slot: Int) -> String
extern fn egy_regular_future(stem: String, slot: Int) -> String
extern fn egy_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn egy_decline(noun: String, gram_case: String, number: String) -> String
extern fn egy_fem(noun: String) -> String
extern fn egy_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
extern fn egy_map_canonical(verb: String) -> String
Generated Vendored
+30
View File
@@ -0,0 +1,30 @@
// auto-generated by elc --emit-header — do not edit
extern fn enm_str_ends(s: String, suf: String) -> Bool
extern fn enm_drop(s: String, n: Int) -> String
extern fn enm_first_char(s: String) -> String
extern fn enm_slot(person: String, number: String) -> Int
extern fn enm_been_present(slot: Int) -> String
extern fn enm_been_past(slot: Int) -> String
extern fn enm_haven_present(slot: Int) -> String
extern fn enm_haven_past(slot: Int) -> String
extern fn enm_goon_present(slot: Int) -> String
extern fn enm_goon_past(slot: Int) -> String
extern fn enm_seen_present(slot: Int) -> String
extern fn enm_seen_past(slot: Int) -> String
extern fn enm_seyen_present(slot: Int) -> String
extern fn enm_seyen_past(slot: Int) -> String
extern fn enm_comen_present(slot: Int) -> String
extern fn enm_comen_past(slot: Int) -> String
extern fn enm_maken_present(slot: Int) -> String
extern fn enm_maken_past(slot: Int) -> String
extern fn enm_map_canonical(verb: String) -> String
extern fn enm_weak_stem(verb: String) -> String
extern fn enm_weak_present(stem: String, slot: Int) -> String
extern fn enm_weak_past(stem: String, slot: Int) -> String
extern fn enm_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn enm_irregular_plural(noun: String) -> String
extern fn enm_make_plural(noun: String) -> String
extern fn enm_decline(noun: String, gram_case: String, number: String) -> String
extern fn enm_is_vowel_initial(s: String) -> Bool
extern fn enm_indef_article(noun_phrase: String) -> String
extern fn enm_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+23
View File
@@ -0,0 +1,23 @@
// auto-generated by elc --emit-header — do not edit
extern fn es_str_ends(s: String, suf: String) -> Bool
extern fn es_str_drop_last(s: String, n: Int) -> String
extern fn es_str_last_char(s: String) -> String
extern fn es_str_last2(s: String) -> String
extern fn es_str_last3(s: String) -> String
extern fn es_verb_class(base: String) -> String
extern fn es_stem(base: String) -> String
extern fn es_slot(person: String, number: String) -> Int
extern fn es_irregular_present(verb: String, person: String, number: String) -> String
extern fn es_irregular_preterite(verb: String, person: String, number: String) -> String
extern fn es_irregular_imperfect(verb: String, person: String, number: String) -> String
extern fn es_regular_present(stem: String, vclass: String, slot: Int) -> String
extern fn es_regular_preterite(stem: String, vclass: String, slot: Int) -> String
extern fn es_regular_future(base: String, slot: Int) -> String
extern fn es_irregular_future_stem(verb: String) -> String
extern fn es_regular_imperfect(stem: String, vclass: String, slot: Int) -> String
extern fn es_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn es_gender(noun: String) -> String
extern fn es_invariant_plural(noun: String) -> String
extern fn es_pluralize(noun: String) -> String
extern fn es_starts_with_stressed_a(noun: String) -> Bool
extern fn es_agree_article(noun: String, definite: String, number: String) -> String
Generated Vendored
+17
View File
@@ -0,0 +1,17 @@
// auto-generated by elc --emit-header — do not edit
extern fn fi_harmony(word: String) -> String
extern fn fi_suffix(base: String, harmony: String) -> String
extern fn fi_noun_case(stem: String, gram_case: String, number: String, harmony: String) -> String
extern fn fi_str_last_char(s: String) -> String
extern fn fi_apply_case(noun: String, gram_case: String, number: String) -> String
extern fn fi_verb_stem(dict_form: String) -> String
extern fn fi_irregular_verb(dict_form: String) -> [String]
extern fn fi_present_ending(stem: String, person: String, number: String, harmony: String) -> String
extern fn fi_past_stem(stem: String) -> String
extern fn fi_past_ending(stem: String, person: String, number: String, harmony: String) -> String
extern fn fi_neg_aux(person: String, number: String) -> String
extern fn fi_negative(verb: String, person: String, number: String) -> String
extern fn fi_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn fi_question_suffix(harmony: String) -> String
extern fn fi_make_question(verb_form: String, harmony: String) -> String
extern fn fi_full_paradigm(noun: String) -> [String]
Generated Vendored
+29
View File
@@ -0,0 +1,29 @@
// auto-generated by elc --emit-header — do not edit
extern fn fr_str_ends(s: String, suf: String) -> Bool
extern fn fr_str_drop_last(s: String, n: Int) -> String
extern fn fr_str_last_char(s: String) -> String
extern fn fr_str_last2(s: String) -> String
extern fn fr_is_vowel_start(s: String) -> Bool
extern fn fr_is_known_irregular(verb: String) -> Bool
extern fn fr_verb_group(base: String) -> String
extern fn fr_stem(base: String) -> String
extern fn fr_slot(person: String, number: String) -> Int
extern fn fr_irregular_present(verb: String, person: String, number: String) -> String
extern fn fr_regular_present(stem: String, vgroup: String, slot: Int) -> String
extern fn fr_future_stem(base: String, vgroup: String) -> String
extern fn fr_regular_future(fstem: String, slot: Int) -> String
extern fn fr_irregular_future_stem(verb: String) -> String
extern fn fr_imperfect_stem(base: String, vgroup: String) -> String
extern fn fr_regular_imperfect(istem: String, slot: Int) -> String
extern fn fr_uses_etre(verb: String) -> Bool
extern fn fr_past_participle(verb: String) -> String
extern fn fr_avoir_present(slot: Int) -> String
extern fn fr_etre_present(slot: Int) -> String
extern fn fr_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn fr_gender(noun: String) -> String
extern fn fr_invariant_plural(noun: String) -> String
extern fn fr_pluralize(noun: String) -> String
extern fn fr_agree_article(noun: String, definite: String, number: String) -> String
extern fn fr_subject_starts_vowel(subject: String) -> Bool
extern fn fr_verb_ends_vowel(verb_form: String) -> Bool
extern fn fr_question_inversion(subject: String, verb_form: String) -> String
Generated Vendored
+38
View File
@@ -0,0 +1,38 @@
// auto-generated by elc --emit-header — do not edit
extern fn fro_str_ends(s: String, suf: String) -> Bool
extern fn fro_drop(s: String, n: Int) -> String
extern fn fro_slot(person: String, number: String) -> Int
extern fn fro_map_canonical(verb: String) -> String
extern fn fro_estre_present(slot: Int) -> String
extern fn fro_estre_past(slot: Int) -> String
extern fn fro_estre_future(slot: Int) -> String
extern fn fro_avoir_present(slot: Int) -> String
extern fn fro_avoir_past(slot: Int) -> String
extern fn fro_avoir_future(slot: Int) -> String
extern fn fro_aler_present(slot: Int) -> String
extern fn fro_aler_past(slot: Int) -> String
extern fn fro_aler_future(slot: Int) -> String
extern fn fro_venir_present(slot: Int) -> String
extern fn fro_venir_past(slot: Int) -> String
extern fn fro_venir_future(slot: Int) -> String
extern fn fro_faire_present(slot: Int) -> String
extern fn fro_faire_past(slot: Int) -> String
extern fn fro_faire_future(slot: Int) -> String
extern fn fro_verb_class(verb: String) -> String
extern fn fro_verb_stem(verb: String, vclass: String) -> String
extern fn fro_conj1_present(stem: String, slot: Int) -> String
extern fn fro_conj1_past(stem: String, slot: Int) -> String
extern fn fro_conj1_future(verb: String, slot: Int) -> String
extern fn fro_conj2_present(stem: String, slot: Int) -> String
extern fn fro_conj2_past(stem: String, slot: Int) -> String
extern fn fro_conj2_future(verb: String, slot: Int) -> String
extern fn fro_conj3_present(stem: String, slot: Int) -> String
extern fn fro_conj3_past(stem: String, slot: Int) -> String
extern fn fro_conj3_future(verb: String, slot: Int) -> String
extern fn fro_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn fro_gender(noun: String) -> String
extern fn fro_decline_masc(noun: String, gram_case: String, number: String) -> String
extern fn fro_decline_fem(noun: String, number: String) -> String
extern fn fro_decline(noun: String, gram_case: String, number: String) -> String
extern fn fro_article(gender: String, gram_case: String, number: String) -> String
extern fn fro_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+26
View File
@@ -0,0 +1,26 @@
// auto-generated by elc --emit-header — do not edit
extern fn gez_str_ends(s: String, suf: String) -> Bool
extern fn gez_str_len(s: String) -> Int
extern fn gez_str_drop_last(s: String, n: Int) -> String
extern fn gez_slot(person: String, number: String) -> Int
extern fn gez_slot_g(person: String, gender: String, number: String) -> Int
extern fn gez_kwn_perfect(slot: Int) -> String
extern fn gez_kwn_imperfect(slot: Int) -> String
extern fn gez_is_copula(verb: String) -> Bool
extern fn gez_conjugate_copula(tense: String, slot: Int) -> String
extern fn gez_hlw_perfect(slot: Int) -> String
extern fn gez_hlw_imperfect(slot: Int) -> String
extern fn gez_hbl_perfect(slot: Int) -> String
extern fn gez_hbl_imperfect(slot: Int) -> String
extern fn gez_ray_perfect(slot: Int) -> String
extern fn gez_ray_imperfect(slot: Int) -> String
extern fn gez_qwl_perfect(slot: Int) -> String
extern fn gez_qwl_imperfect(slot: Int) -> String
extern fn gez_generic_perfect(base3sg: String, slot: Int) -> String
extern fn gez_generic_imperfect(base3sg: String, slot: Int) -> String
extern fn gez_known_verb(verb: String, tense: String, slot: Int) -> String
extern fn gez_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn gez_is_fidel(noun: String) -> Bool
extern fn gez_decline(noun: String, gram_case: String, number: String) -> String
extern fn gez_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
extern fn gez_map_canonical(verb: String) -> String
Generated Vendored
+34
View File
@@ -0,0 +1,34 @@
// auto-generated by elc --emit-header — do not edit
extern fn goh_str_ends(s: String, suf: String) -> Bool
extern fn goh_drop(s: String, n: Int) -> String
extern fn goh_slot(person: String, number: String) -> Int
extern fn goh_map_canonical(verb: String) -> String
extern fn goh_wesan_present(slot: Int) -> String
extern fn goh_wesan_past(slot: Int) -> String
extern fn goh_haben_present(slot: Int) -> String
extern fn goh_haben_past(slot: Int) -> String
extern fn goh_gan_present(slot: Int) -> String
extern fn goh_gan_past(slot: Int) -> String
extern fn goh_sehan_present(slot: Int) -> String
extern fn goh_sehan_past(slot: Int) -> String
extern fn goh_quethan_present(slot: Int) -> String
extern fn goh_quethan_past(slot: Int) -> String
extern fn goh_tuon_present(slot: Int) -> String
extern fn goh_tuon_past(slot: Int) -> String
extern fn goh_weak_present(stem: String, slot: Int) -> String
extern fn goh_weak_past(stem: String, slot: Int) -> String
extern fn goh_verb_stem(verb: String) -> String
extern fn goh_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn goh_stem_type(noun: String) -> String
extern fn goh_extract_stem(noun: String, stype: String) -> String
extern fn goh_decline_masc_a_sg(stem: String, gram_case: String) -> String
extern fn goh_decline_masc_a_pl(stem: String, gram_case: String) -> String
extern fn goh_decline_fem_o_sg(stem: String, gram_case: String) -> String
extern fn goh_decline_fem_o_pl(stem: String, gram_case: String) -> String
extern fn goh_decline_neut_a_sg(stem: String, gram_case: String) -> String
extern fn goh_decline_neut_a_pl(stem: String, gram_case: String) -> String
extern fn goh_decline_masc_n_sg(stem: String, gram_case: String) -> String
extern fn goh_decline_masc_n_pl(stem: String, gram_case: String) -> String
extern fn goh_decline(noun: String, gram_case: String, number: String) -> String
extern fn goh_demo_article(stype: String, number: String) -> String
extern fn goh_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+37
View File
@@ -0,0 +1,37 @@
// auto-generated by elc --emit-header — do not edit
extern fn got_str_ends(s: String, suf: String) -> Bool
extern fn got_str_drop_last(s: String, n: Int) -> String
extern fn got_slot(person: String, number: String) -> Int
extern fn got_map_canonical(verb: String) -> String
extern fn got_wisan_present(slot: Int) -> String
extern fn got_wisan_past(slot: Int) -> String
extern fn got_haban_present(slot: Int) -> String
extern fn got_haban_past(slot: Int) -> String
extern fn got_gaggan_present(slot: Int) -> String
extern fn got_gaggan_past(slot: Int) -> String
extern fn got_saihwan_present(slot: Int) -> String
extern fn got_saihwan_past(slot: Int) -> String
extern fn got_qithan_present(slot: Int) -> String
extern fn got_qithan_past(slot: Int) -> String
extern fn got_niman_present(slot: Int) -> String
extern fn got_niman_past(slot: Int) -> String
extern fn got_wk1_present_ending(slot: Int) -> String
extern fn got_wk1_past_ending(slot: Int) -> String
extern fn got_wk1_conjugate(stem: String, tense: String, slot: Int) -> String
extern fn got_wk2_present_ending(slot: Int) -> String
extern fn got_wk2_past_ending(slot: Int) -> String
extern fn got_wk2_conjugate(stem: String, tense: String, slot: Int) -> String
extern fn got_verb_class(verb: String) -> String
extern fn got_verb_stem(verb: String, vclass: String) -> String
extern fn got_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn got_decline_a_stem_sg(stem: String, gram_case: String) -> String
extern fn got_decline_a_stem_pl(stem: String, gram_case: String) -> String
extern fn got_decline_o_stem_sg(stem: String, gram_case: String) -> String
extern fn got_decline_o_stem_pl(stem: String, gram_case: String) -> String
extern fn got_decline_n_stem_sg(stem: String, gram_case: String) -> String
extern fn got_decline_n_stem_pl(stem: String, gram_case: String) -> String
extern fn got_stem_type(noun: String) -> String
extern fn got_extract_stem(noun: String, stype: String) -> String
extern fn got_demo_article(stype: String) -> String
extern fn got_decline(noun: String, gram_case: String, number: String) -> String
extern fn got_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+45
View File
@@ -0,0 +1,45 @@
// auto-generated by elc --emit-header — do not edit
extern fn grc_str_ends(s: String, suf: String) -> Bool
extern fn grc_str_drop_last(s: String, n: Int) -> String
extern fn grc_str_last_char(s: String) -> String
extern fn grc_str_last2(s: String) -> String
extern fn grc_str_last3(s: String) -> String
extern fn grc_slot(person: String, number: String) -> Int
extern fn grc_map_canonical(verb: String) -> String
extern fn grc_einai_present(slot: Int) -> String
extern fn grc_einai_imperfect(slot: Int) -> String
extern fn grc_einai_future(slot: Int) -> String
extern fn grc_echein_present(slot: Int) -> String
extern fn grc_echein_imperfect(slot: Int) -> String
extern fn grc_echein_aorist(slot: Int) -> String
extern fn grc_echein_future(slot: Int) -> String
extern fn grc_legein_present(slot: Int) -> String
extern fn grc_legein_imperfect(slot: Int) -> String
extern fn grc_legein_aorist(slot: Int) -> String
extern fn grc_legein_future(slot: Int) -> String
extern fn grc_horao_present(slot: Int) -> String
extern fn grc_horao_imperfect(slot: Int) -> String
extern fn grc_horao_aorist(slot: Int) -> String
extern fn grc_horao_future(slot: Int) -> String
extern fn grc_erchesthai_present(slot: Int) -> String
extern fn grc_erchesthai_imperfect(slot: Int) -> String
extern fn grc_erchesthai_aorist(slot: Int) -> String
extern fn grc_erchesthai_future(slot: Int) -> String
extern fn grc_thematic_present_ending(slot: Int) -> String
extern fn grc_thematic_imperfect_ending(slot: Int) -> String
extern fn grc_thematic_future_ending(slot: Int) -> String
extern fn grc_weak_aorist_ending(slot: Int) -> String
extern fn grc_present_stem(verb: String) -> String
extern fn grc_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn grc_declension(noun: String) -> String
extern fn grc_decline_2m(stem: String, gram_case: String, number: String) -> String
extern fn grc_decline_2n(stem: String, gram_case: String, number: String) -> String
extern fn grc_decline_1a(stem: String, gram_case: String, number: String) -> String
extern fn grc_decline_1e(stem: String, gram_case: String, number: String) -> String
extern fn grc_decline(noun: String, gram_case: String, number: String) -> String
extern fn grc_article_masculine(gram_case: String, number: String) -> String
extern fn grc_article_feminine(gram_case: String, number: String) -> String
extern fn grc_article_neuter(gram_case: String, number: String) -> String
extern fn grc_article(gender: String, gram_case: String, number: String) -> String
extern fn grc_infer_gender(noun: String) -> String
extern fn grc_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+30
View File
@@ -0,0 +1,30 @@
// auto-generated by elc --emit-header — do not edit
extern fn he_str_ends(s: String, suf: String) -> Bool
extern fn he_str_len(s: String) -> Int
extern fn he_str_drop_last(s: String, n: Int) -> String
extern fn he_str_last_char(s: String) -> String
extern fn he_slot(person: String, gender: String, number: String) -> Int
extern fn he_present_form_code(slot: Int) -> Int
extern fn he_copula_past(slot: Int) -> String
extern fn he_copula_future(slot: Int) -> String
extern fn he_is_copula(verb: String) -> Bool
extern fn he_conjugate_copula(tense: String, slot: Int) -> String
extern fn he_present_lir_ot(form: Int) -> String
extern fn he_present_le_exol(form: Int) -> String
extern fn he_present_ledaber(form: Int) -> String
extern fn he_present_lalechet(form: Int) -> String
extern fn he_past_lir_ot(slot: Int) -> String
extern fn he_past_le_exol(slot: Int) -> String
extern fn he_past_ledaber(slot: Int) -> String
extern fn he_past_lalechet(slot: Int) -> String
extern fn he_future_lir_ot(slot: Int) -> String
extern fn he_future_le_exol(slot: Int) -> String
extern fn he_future_ledaber(slot: Int) -> String
extern fn he_future_lalechet(slot: Int) -> String
extern fn he_known_verb(verb: String, tense: String, slot: Int) -> String
extern fn he_conjugate(verb: String, tense: String, person: String, gender: String, number: String) -> String
extern fn he_pluralize(noun: String, gender: String) -> String
extern fn he_is_hebrew_script(noun: String) -> Bool
extern fn he_definite_prefix(noun: String) -> String
extern fn he_noun_phrase(noun: String, number: String, gender: String, definite: String) -> String
extern fn he_map_canonical(verb: String) -> String
Generated Vendored
+27
View File
@@ -0,0 +1,27 @@
// auto-generated by elc --emit-header — do not edit
extern fn hi_str_ends(s: String, suf: String) -> Bool
extern fn hi_str_drop_last(s: String, n: Int) -> String
extern fn hi_str_last_char(s: String) -> String
extern fn hi_gender(noun: String) -> String
extern fn hi_masc_aa_stem(noun: String) -> String
extern fn hi_noun_direct_m(noun: String, number: String) -> String
extern fn hi_noun_oblique_m(noun: String, number: String) -> String
extern fn hi_noun_direct_f(noun: String, number: String) -> String
extern fn hi_noun_oblique_f(noun: String, number: String) -> String
extern fn hi_noun_direct(noun: String, gender: String, number: String) -> String
extern fn hi_noun_oblique(noun: String, gender: String, number: String) -> String
extern fn hi_postposition(gram_case: String) -> String
extern fn hi_agree_genitive(possessed_gender: String, possessed_number: String) -> String
extern fn hi_verb_stem(infinitive: String) -> String
extern fn hi_verb_stem_clean(infinitive: String) -> String
extern fn hi_present_aspect(gender: String, number: String) -> String
extern fn hi_aux_present(person: String, number: String) -> String
extern fn hi_past_suffix(gender: String, number: String) -> String
extern fn hi_past_irregular(stem: String, gender: String, number: String) -> String
extern fn hi_future_suffix(person: String, number: String, gender: String) -> String
extern fn hi_tense_suffix(tense: String, gender: String, number: String) -> String
extern fn hi_hona_present(person: String, number: String) -> String
extern fn hi_hona_past(gender: String, number: String) -> String
extern fn hi_conjugate(verb: String, tense: String, person: String, gender: String, number: String) -> String
extern fn hi_noun_with_post(noun: String, gender: String, number: String, gram_case: String) -> String
extern fn hi_genitive_phrase(possessor: String, possessor_gender: String, possessor_number: String, possessed: String, possessed_gender: String, possessed_number: String) -> String
Generated Vendored
+9
View File
@@ -0,0 +1,9 @@
// auto-generated by elc --emit-header — do not edit
extern fn ja_verb_group(dict_form: String) -> String
extern fn ja_ichidan_stem(dict_form: String) -> String
extern fn ja_godan_stem_change(dict_form: String, row: String) -> String
extern fn ja_conjugate(dict_form: String, form: String) -> String
extern fn ja_particle(gram_case: String) -> String
extern fn ja_noun_phrase(noun: String, gram_case: String) -> String
extern fn ja_question_particle() -> String
extern fn ja_make_question(sentence: String) -> String
Generated Vendored
+41
View File
@@ -0,0 +1,41 @@
// auto-generated by elc --emit-header — do not edit
extern fn la_str_ends(s: String, suf: String) -> Bool
extern fn la_str_drop_last(s: String, n: Int) -> String
extern fn la_str_last_char(s: String) -> String
extern fn la_str_last2(s: String) -> String
extern fn la_str_last3(s: String) -> String
extern fn la_slot(person: String, number: String) -> Int
extern fn la_verb_class(verb: String) -> String
extern fn la_stem(verb: String, vclass: String) -> String
extern fn la_perfect_stem(verb: String, vclass: String) -> String
extern fn la_perfect_ending(slot: Int) -> String
extern fn la_present_ending(vclass: String, slot: Int) -> String
extern fn la_present_form(stem: String, vclass: String, slot: Int) -> String
extern fn la_future_ending_12(slot: Int) -> String
extern fn la_future_ending_34(slot: Int) -> String
extern fn la_future_form(stem: String, vclass: String, slot: Int) -> String
extern fn la_esse_present(slot: Int) -> String
extern fn la_esse_past(slot: Int) -> String
extern fn la_esse_future(slot: Int) -> String
extern fn la_ire_present(slot: Int) -> String
extern fn la_ire_past(slot: Int) -> String
extern fn la_ire_future(slot: Int) -> String
extern fn la_velle_present(slot: Int) -> String
extern fn la_velle_past(slot: Int) -> String
extern fn la_velle_future(slot: Int) -> String
extern fn la_posse_present(slot: Int) -> String
extern fn la_posse_past(slot: Int) -> String
extern fn la_posse_future(slot: Int) -> String
extern fn la_irregular_perfect_stem(verb: String) -> String
extern fn la_map_canonical(verb: String) -> String
extern fn la_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn la_declension(noun: String) -> String
extern fn la_decline_1(stem: String, gram_case: String, number: String) -> String
extern fn la_decline_2m(stem: String, gram_case: String, number: String) -> String
extern fn la_decline_2n(stem: String, gram_case: String, number: String) -> String
extern fn la_decline_3(noun: String, gram_case: String, number: String) -> String
extern fn la_decline_4(stem: String, gram_case: String, number: String) -> String
extern fn la_decline_5(stem: String, gram_case: String, number: String) -> String
extern fn la_decline_2er(noun: String, gram_case: String, number: String) -> String
extern fn la_decline(noun: String, gram_case: String, number: String) -> String
extern fn la_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+30
View File
@@ -0,0 +1,30 @@
// auto-generated by elc --emit-header — do not edit
extern fn non_str_ends(s: String, suf: String) -> Bool
extern fn non_drop(s: String, n: Int) -> String
extern fn non_last(s: String) -> String
extern fn non_slot(person: String, number: String) -> Int
extern fn non_vera_present(slot: Int) -> String
extern fn non_vera_past(slot: Int) -> String
extern fn non_hafa_present(slot: Int) -> String
extern fn non_hafa_past(slot: Int) -> String
extern fn non_ganga_present(slot: Int) -> String
extern fn non_ganga_past(slot: Int) -> String
extern fn non_sja_present(slot: Int) -> String
extern fn non_sja_past(slot: Int) -> String
extern fn non_segja_present(slot: Int) -> String
extern fn non_segja_past(slot: Int) -> String
extern fn non_koma_present(slot: Int) -> String
extern fn non_koma_past(slot: Int) -> String
extern fn non_map_canonical(verb: String) -> String
extern fn non_weak_present(stem: String, slot: Int) -> String
extern fn non_weak_past(stem: String, slot: Int) -> String
extern fn non_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn non_decline_masc(noun: String, gram_case: String, number: String) -> String
extern fn non_decline_fem(noun: String, gram_case: String, number: String) -> String
extern fn non_decline_neut(noun: String, gram_case: String, number: String) -> String
extern fn non_detect_gender(noun: String) -> String
extern fn non_decline(noun: String, gram_case: String, number: String) -> String
extern fn non_def_suffix_masc(gram_case: String, number: String) -> String
extern fn non_def_suffix_neut(gram_case: String, number: String) -> String
extern fn non_def_suffix_fem(gram_case: String, number: String) -> String
extern fn non_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+19
View File
@@ -0,0 +1,19 @@
// auto-generated by elc --emit-header — do not edit
extern fn peo_drop(s: String, n: Int) -> String
extern fn peo_ends(s: String, suf: String) -> Bool
extern fn peo_slot(person: String, number: String) -> Int
extern fn peo_present_suffix(slot: Int) -> String
extern fn peo_past_suffix(slot: Int) -> String
extern fn peo_ah_present(slot: Int) -> String
extern fn peo_ah_past(slot: Int) -> String
extern fn peo_kar_present(slot: Int) -> String
extern fn peo_kar_past(slot: Int) -> String
extern fn peo_xsaya_present(slot: Int) -> String
extern fn peo_tar_present(slot: Int) -> String
extern fn peo_da_present(slot: Int) -> String
extern fn peo_da_past(slot: Int) -> String
extern fn peo_map_canonical(verb: String) -> String
extern fn peo_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn peo_decline_astem(noun: String, gram_case: String, number: String) -> String
extern fn peo_decline(noun: String, gram_case: String, number: String) -> String
extern fn peo_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+34
View File
@@ -0,0 +1,34 @@
// auto-generated by elc --emit-header — do not edit
extern fn pi_str_ends(s: String, suf: String) -> Bool
extern fn pi_drop(s: String, n: Int) -> String
extern fn pi_last_char(s: String) -> String
extern fn pi_slot(person: String, number: String) -> Int
extern fn pi_present_ending(slot: Int) -> String
extern fn pi_aorist_ending(slot: Int) -> String
extern fn pi_future_ending(slot: Int) -> String
extern fn pi_hoti_present(slot: Int) -> String
extern fn pi_atthi_present(slot: Int) -> String
extern fn pi_hoti_aorist(slot: Int) -> String
extern fn pi_hoti_future(slot: Int) -> String
extern fn pi_gacchati_present(slot: Int) -> String
extern fn pi_gacchati_aorist(slot: Int) -> String
extern fn pi_gacchati_future(slot: Int) -> String
extern fn pi_passati_present(slot: Int) -> String
extern fn pi_passati_aorist(slot: Int) -> String
extern fn pi_passati_future(slot: Int) -> String
extern fn pi_vadati_present(slot: Int) -> String
extern fn pi_vadati_aorist(slot: Int) -> String
extern fn pi_vadati_future(slot: Int) -> String
extern fn pi_karoti_present(slot: Int) -> String
extern fn pi_karoti_aorist(slot: Int) -> String
extern fn pi_karoti_future(slot: Int) -> String
extern fn pi_map_canonical(verb: String) -> String
extern fn pi_regular_root(verb: String) -> String
extern fn pi_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn pi_decline_a_masc_sg(stem: String, gram_case: String) -> String
extern fn pi_decline_a_masc_pl(stem: String, gram_case: String) -> String
extern fn pi_decline_a_fem_sg(stem: String, gram_case: String) -> String
extern fn pi_decline_a_fem_pl(stem: String, gram_case: String) -> String
extern fn pi_detect_class(noun: String) -> String
extern fn pi_decline(noun: String, gram_case: String, number: String) -> String
extern fn pi_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+14
View File
@@ -0,0 +1,14 @@
// auto-generated by elc --emit-header — do not edit
extern fn ru_gender(noun: String) -> String
extern fn ru_stem_type(noun: String, gender: String) -> String
extern fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String
extern fn ru_decline_regular(noun: String, gender: String, stype: String, gram_case: String, number: String) -> String
extern fn ru_decline_masc(noun: String, stype: String, gram_case: String, number: String) -> String
extern fn ru_decline_fem(noun: String, stype: String, gram_case: String, number: String) -> String
extern fn ru_decline_neut(noun: String, stype: String, gram_case: String, number: String) -> String
extern fn ru_past_agree(verb_stem: String, gender: String, number: String) -> String
extern fn ru_conjugate_1st(stem: String, tense: String, person: String, number: String) -> String
extern fn ru_conjugate_2nd(stem: String, tense: String, person: String, number: String) -> String
extern fn ru_irregular(verb: String, tense: String, person: String, number: String) -> String
extern fn ru_past_stem(verb: String) -> String
extern fn ru_conjugate(verb: String, tense: String, person: String, number: String, gender: String) -> String
Generated Vendored
+36
View File
@@ -0,0 +1,36 @@
// auto-generated by elc --emit-header — do not edit
extern fn sa_str_ends(s: String, suf: String) -> Bool
extern fn sa_str_drop_last(s: String, n: Int) -> String
extern fn sa_slot(person: String, number: String) -> Int
extern fn sa_map_canonical(verb: String) -> String
extern fn sa_as_present(slot: Int) -> String
extern fn sa_as_past(slot: Int) -> String
extern fn sa_as_future(slot: Int) -> String
extern fn sa_bhu_present(slot: Int) -> String
extern fn sa_bhu_past(slot: Int) -> String
extern fn sa_bhu_future(slot: Int) -> String
extern fn sa_gam_present(slot: Int) -> String
extern fn sa_gam_past(slot: Int) -> String
extern fn sa_gam_future(slot: Int) -> String
extern fn sa_drs_present(slot: Int) -> String
extern fn sa_drs_past(slot: Int) -> String
extern fn sa_drs_future(slot: Int) -> String
extern fn sa_vad_present(slot: Int) -> String
extern fn sa_vad_past(slot: Int) -> String
extern fn sa_vad_future(slot: Int) -> String
extern fn sa_kr_present(slot: Int) -> String
extern fn sa_kr_past(slot: Int) -> String
extern fn sa_kr_future(slot: Int) -> String
extern fn sa_class1_present_ending(slot: Int) -> String
extern fn sa_class1_past_ending(slot: Int) -> String
extern fn sa_class1_future_ending(slot: Int) -> String
extern fn sa_class1_conjugate(stem: String, tense: String, slot: Int) -> String
extern fn sa_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn sa_decline_a_stem_sg(stem: String, gram_case: String) -> String
extern fn sa_decline_a_stem_pl(stem: String, gram_case: String) -> String
extern fn sa_decline_aa_stem_sg(stem: String, gram_case: String) -> String
extern fn sa_decline_aa_stem_pl(stem: String, gram_case: String) -> String
extern fn sa_stem_type(noun: String) -> String
extern fn sa_extract_stem(noun: String, stype: String) -> String
extern fn sa_decline(noun: String, gram_case: String, number: String) -> String
extern fn sa_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+22
View File
@@ -0,0 +1,22 @@
// auto-generated by elc --emit-header — do not edit
extern fn sga_drop(s: String, n: Int) -> String
extern fn sga_first(s: String) -> String
extern fn sga_rest(s: String) -> String
extern fn sga_slot(person: String, number: String) -> Int
extern fn sga_lenite(word: String) -> String
extern fn sga_copula_present(slot: Int) -> String
extern fn sga_bith_present(slot: Int) -> String
extern fn sga_bith_past(slot: Int) -> String
extern fn sga_teit_present(slot: Int) -> String
extern fn sga_teit_past(slot: Int) -> String
extern fn sga_gaibid_present(slot: Int) -> String
extern fn sga_adci_present(slot: Int) -> String
extern fn sga_asbeir_present(slot: Int) -> String
extern fn sga_map_canonical(verb: String) -> String
extern fn sga_ai_present(stem: String, slot: Int) -> String
extern fn sga_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn sga_decline_ostem(noun: String, gram_case: String, number: String) -> String
extern fn sga_decline_astem(noun: String, gram_case: String, number: String) -> String
extern fn sga_detect_gender(noun: String) -> String
extern fn sga_decline(noun: String, gram_case: String, number: String) -> String
extern fn sga_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+29
View File
@@ -0,0 +1,29 @@
// auto-generated by elc --emit-header — do not edit
extern fn sux_str_ends(s: String, suf: String) -> Bool
extern fn sux_str_drop_last(s: String, n: Int) -> String
extern fn sux_str_last_char(s: String) -> String
extern fn sux_str_last2(s: String) -> String
extern fn sux_slot(person: String, number: String) -> Int
extern fn sux_ergative_suffix(person: String, number: String) -> String
extern fn sux_absolutive_suffix(person: String, number: String) -> String
extern fn sux_map_canonical(verb: String) -> String
extern fn sux_personal_suffix(slot: Int) -> String
extern fn sux_me_present(slot: Int) -> String
extern fn sux_me_past(slot: Int) -> String
extern fn sux_dug4_present(slot: Int) -> String
extern fn sux_dug4_past(slot: Int) -> String
extern fn sux_du_present(slot: Int) -> String
extern fn sux_du_past(slot: Int) -> String
extern fn sux_igibar_present(slot: Int) -> String
extern fn sux_igibar_past(slot: Int) -> String
extern fn sux_ak_present(slot: Int) -> String
extern fn sux_ak_past(slot: Int) -> String
extern fn sux_tum2_present(slot: Int) -> String
extern fn sux_tum2_past(slot: Int) -> String
extern fn sux_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn sux_is_animate(noun: String) -> Bool
extern fn sux_case_suffix(gram_case: String) -> String
extern fn sux_decline(noun: String, gram_case: String, number: String) -> String
extern fn sux_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
extern fn sux_verb_chain(agent: String, verb: String, patient: String, tense: String) -> String
extern fn sux_realize_sentence(intent: String, agent: String, predicate: String, patient: String, tense: String) -> String
Generated Vendored
+23
View File
@@ -0,0 +1,23 @@
// auto-generated by elc --emit-header — do not edit
extern fn sw_str_ends(s: String, suf: String) -> Bool
extern fn sw_str_drop_last(s: String, n: Int) -> String
extern fn sw_str_first_char(s: String) -> String
extern fn sw_str_first2(s: String) -> String
extern fn sw_str_first3(s: String) -> String
extern fn sw_str_last_char(s: String) -> String
extern fn sw_is_class1_noun(noun: String) -> Bool
extern fn sw_noun_class(noun: String) -> String
extern fn sw_subj_prefix(person: String, number: String, noun_class: String) -> String
extern fn sw_obj_prefix(person: String, number: String, noun_class: String) -> String
extern fn sw_tense_marker(tense: String) -> String
extern fn sw_verb_final(tense: String, negative: Bool) -> String
extern fn sw_neg_subj_prefix(person: String, number: String, noun_class: String) -> String
extern fn sw_verb_stem(infinitive: String) -> String
extern fn sw_conjugate(verb_stem: String, person: String, number: String, noun_class: String, tense: String) -> String
extern fn sw_negative(verb_stem: String, person: String, number: String, noun_class: String, tense: String) -> String
extern fn sw_noun_plural(noun: String) -> String
extern fn sw_adj_prefix(noun_class: String, number: String) -> String
extern fn sw_agree_adj(adj_stem: String, noun_class: String, number: String) -> String
extern fn sw_demonstrative(noun_class: String, number: String, proximity: String) -> String
extern fn sw_copula_present(person: String, number: String, use_case: String) -> String
extern fn sw_copula_neg_present(person: String, number: String) -> String
Generated Vendored
+17
View File
@@ -0,0 +1,17 @@
// auto-generated by elc --emit-header — do not edit
extern fn txb_drop(s: String, n: Int) -> String
extern fn txb_ends(s: String, suf: String) -> Bool
extern fn txb_slot(person: String, number: String) -> Int
extern fn txb_pres1_suffix(slot: Int) -> String
extern fn txb_kam_present(slot: Int) -> String
extern fn txb_ya_present(slot: Int) -> String
extern fn txb_wes_present(slot: Int) -> String
extern fn txb_lyut_present(slot: Int) -> String
extern fn txb_wak_present(slot: Int) -> String
extern fn txb_map_canonical(verb: String) -> String
extern fn txb_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn txb_decline_masc(noun: String, gram_case: String, number: String) -> String
extern fn txb_decline_fem(noun: String, gram_case: String, number: String) -> String
extern fn txb_detect_gender(noun: String) -> String
extern fn txb_decline(noun: String, gram_case: String, number: String) -> String
extern fn txb_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
Generated Vendored
+25
View File
@@ -0,0 +1,25 @@
// auto-generated by elc --emit-header — do not edit
extern fn uga_str_ends(s: String, suf: String) -> Bool
extern fn uga_str_len(s: String) -> Int
extern fn uga_str_drop_last(s: String, n: Int) -> String
extern fn uga_slot(person: String, number: String) -> Int
extern fn uga_slot_g(person: String, gender: String, number: String) -> Int
extern fn uga_kn_perfect(slot: Int) -> String
extern fn uga_kn_imperfect(slot: Int) -> String
extern fn uga_is_copula(verb: String) -> Bool
extern fn uga_conjugate_copula(tense: String, slot: Int) -> String
extern fn uga_hlk_perfect(slot: Int) -> String
extern fn uga_hlk_imperfect(slot: Int) -> String
extern fn uga_ray_perfect(slot: Int) -> String
extern fn uga_ray_imperfect(slot: Int) -> String
extern fn uga_amr_perfect(slot: Int) -> String
extern fn uga_amr_imperfect(slot: Int) -> String
extern fn uga_generic_perfect(base3sg: String, slot: Int) -> String
extern fn uga_generic_imperfect(base3sg: String, slot: Int) -> String
extern fn uga_known_verb(verb: String, tense: String, slot: Int) -> String
extern fn uga_conjugate(verb: String, tense: String, person: String, number: String) -> String
extern fn uga_strip_nom(noun: String) -> String
extern fn uga_is_fem(noun: String) -> Bool
extern fn uga_decline(noun: String, gram_case: String, number: String) -> String
extern fn uga_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String
extern fn uga_map_canonical(verb: String) -> String
Generated Vendored
+27
View File
@@ -0,0 +1,27 @@
// auto-generated by elc --emit-header — do not edit
extern fn str_ends(s: String, suf: String) -> Bool
extern fn str_last_char(s: String) -> String
extern fn str_last2(s: String) -> String
extern fn str_last3(s: String) -> String
extern fn str_drop_last(s: String, n: Int) -> String
extern fn is_vowel(c: String) -> Bool
extern fn morph_apply_suffix(base: String, suffix: String) -> String
extern fn en_irregular_plural(word: String) -> String
extern fn en_irregular_singular(word: String) -> String
extern fn en_irregular_verb(base: String) -> [String]
extern fn en_verb_3sg(base: String) -> String
extern fn en_should_double_final(base: String) -> Bool
extern fn en_verb_past(base: String) -> String
extern fn en_verb_gerund(base: String) -> String
extern fn en_pluralize_regular(singular: String) -> String
extern fn en_verb_form(base: String, tense: String, person: String, number: String) -> String
extern fn agree_determiner(det: String, noun: String) -> String
extern fn morph_pluralize(noun: String, profile: [String]) -> String
extern fn morph_map_canonical(verb: String, code: String) -> String
extern fn morph_conjugate(verb: String, tense: String, person: String, number: String, profile: [String]) -> String
extern fn morph_inflect(word: String, features: String, profile: [String]) -> String
extern fn pluralize(singular: String) -> String
extern fn singularize(plural: String) -> String
extern fn verb_form(base: String, tense: String, person: String, number: String) -> String
extern fn irregular_plural(word: String) -> String
extern fn irregular_singular(word: String) -> String
Generated Vendored
+11 -9
View File
@@ -10,6 +10,7 @@ el_val_t mem_remember(el_val_t content, el_val_t tags);
el_val_t mem_recall(el_val_t query, el_val_t depth);
el_val_t mem_search(el_val_t query, el_val_t limit);
el_val_t mem_strengthen(el_val_t node_id);
el_val_t mem_tombstone(el_val_t node_id);
el_val_t mem_forget(el_val_t node_id);
el_val_t mem_consolidate(void);
el_val_t mem_save(el_val_t path);
@@ -360,7 +361,7 @@ el_val_t handle_api_remember(el_val_t body) {
el_val_t sal = ({ el_val_t _if_result_14 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_14 = (el_from_float(0.95)); } else { _if_result_14 = (({ el_val_t _if_result_15 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_15 = (el_from_float(0.75)); } else { _if_result_15 = (({ el_val_t _if_result_16 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_16 = (el_from_float(0.25)); } else { _if_result_16 = (el_from_float(0.5)); } _if_result_16; })); } _if_result_15; })); } _if_result_14; });
el_val_t base_tags = ({ el_val_t _if_result_17 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_17 = (EL_STR("[\"Memory\"]")); } else { _if_result_17 = (tags_raw); } _if_result_17; });
el_val_t final_tags = ({ el_val_t _if_result_18 = 0; if (str_eq(project, EL_STR(""))) { _if_result_18 = (base_tags); } else { el_val_t inner = str_slice(base_tags, 1, (str_len(base_tags) - 1)); _if_result_18 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",\"project:")), project), EL_STR("\"]"))); } _if_result_18; });
el_val_t id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:remembered"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), final_tags);
el_val_t id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:remembered"), sal, sal, el_from_float(0.9), EL_STR("Episodic"), final_tags);
if (!api_persisted(id)) {
return api_not_persisted(id);
}
@@ -383,7 +384,7 @@ el_val_t handle_api_node_create(el_val_t body) {
el_val_t tags = ({ el_val_t _if_result_22 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_22 = (el_str_concat(el_str_concat(EL_STR("[\""), node_type), EL_STR("\"]"))); } else { _if_result_22 = (tags_raw); } _if_result_22; });
el_val_t importance = json_get(body, EL_STR("importance"));
el_val_t sal = ({ el_val_t _if_result_23 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_23 = (el_from_float(0.95)); } else { _if_result_23 = (({ el_val_t _if_result_24 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_24 = (el_from_float(0.75)); } else { _if_result_24 = (({ el_val_t _if_result_25 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_25 = (el_from_float(0.25)); } else { _if_result_25 = (el_from_float(0.5)); } _if_result_25; })); } _if_result_24; })); } _if_result_23; });
el_val_t id = engram_node_full(content, node_type, label, el_from_float(sal), el_from_float(sal), el_from_float(0.9), tier, tags);
el_val_t id = engram_node_full(content, node_type, label, sal, sal, el_from_float(0.9), tier, tags);
if (!api_persisted(id)) {
return api_not_persisted(id);
}
@@ -496,8 +497,9 @@ el_val_t handle_api_capture_knowledge(el_val_t body) {
return api_err(EL_STR("content is required"));
}
el_val_t full = ({ el_val_t _if_result_44 = 0; if (str_eq(title, EL_STR(""))) { _if_result_44 = (content); } else { _if_result_44 = (el_str_concat(el_str_concat(title, EL_STR(": ")), content)); } _if_result_44; });
el_val_t lbl = str_slice(title, 0, 80);
el_val_t tags = EL_STR("[\"Knowledge\",\"captured\"]");
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), EL_STR("knowledge:captured"), el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags);
el_val_t id = engram_node_full(full, EL_STR("Knowledge"), lbl, el_from_float(0.85), el_from_float(0.8), el_from_float(0.9), EL_STR("Episodic"), tags);
if (!api_persisted(id)) {
return api_not_persisted(id);
}
@@ -515,7 +517,7 @@ el_val_t handle_api_evolve_knowledge(el_val_t body) {
return api_err_protected(prior_id);
}
el_val_t tags = EL_STR("[\"Knowledge\",\"evolved\"]");
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:evolved"), el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), EL_STR("Episodic"), tags);
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR(""), el_from_float(0.75), el_from_float(0.75), el_from_float(0.9), EL_STR("Episodic"), tags);
if (!api_persisted(new_id)) {
return api_not_persisted(new_id);
}
@@ -537,7 +539,7 @@ el_val_t handle_api_promote_knowledge(el_val_t body) {
}
el_val_t tags_raw = json_get(body, EL_STR("tags"));
el_val_t tags = ({ el_val_t _if_result_45 = 0; if (str_eq(tags_raw, EL_STR(""))) { _if_result_45 = (EL_STR("[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]")); } else { _if_result_45 = (tags_raw); } _if_result_45; });
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR("knowledge:canonical"), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags);
el_val_t new_id = engram_node_full(content, EL_STR("Knowledge"), EL_STR(""), el_from_float(0.9), el_from_float(0.9), el_from_float(1.0), EL_STR("Canonical"), tags);
if (!api_persisted(new_id)) {
return api_not_persisted(new_id);
}
@@ -708,7 +710,7 @@ el_val_t handle_api_evolve_memory(el_val_t body) {
el_val_t sal_str = ({ el_val_t _if_result_65 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_65 = (EL_STR("0.95")); } else { _if_result_65 = (({ el_val_t _if_result_66 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_66 = (EL_STR("0.75")); } else { _if_result_66 = (({ el_val_t _if_result_67 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_67 = (EL_STR("0.25")); } else { _if_result_67 = (EL_STR("0.50")); } _if_result_67; })); } _if_result_66; })); } _if_result_65; });
el_val_t sal = ({ el_val_t _if_result_68 = 0; if (str_eq(sal_str, EL_STR("0.95"))) { _if_result_68 = (el_from_float(0.95)); } else { _if_result_68 = (({ el_val_t _if_result_69 = 0; if (str_eq(sal_str, EL_STR("0.75"))) { _if_result_69 = (el_from_float(0.75)); } else { _if_result_69 = (({ el_val_t _if_result_70 = 0; if (str_eq(sal_str, EL_STR("0.25"))) { _if_result_70 = (el_from_float(0.25)); } else { _if_result_70 = (el_from_float(0.5)); } _if_result_70; })); } _if_result_69; })); } _if_result_68; });
el_val_t tags = EL_STR("[\"Memory\",\"evolved\"]");
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:evolved"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), tags);
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:evolved"), sal, sal, el_from_float(0.9), EL_STR("Episodic"), tags);
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
engram_connect(new_id, prior_id, el_from_float(0.9), EL_STR("supersedes"));
}
@@ -783,7 +785,7 @@ el_val_t handle_api_cultivate(el_val_t body) {
el_val_t importance = json_get(body, EL_STR("importance"));
el_val_t sal = ({ el_val_t _if_result_71 = 0; if (str_eq(importance, EL_STR("critical"))) { _if_result_71 = (el_from_float(0.95)); } else { _if_result_71 = (({ el_val_t _if_result_72 = 0; if (str_eq(importance, EL_STR("high"))) { _if_result_72 = (el_from_float(0.75)); } else { _if_result_72 = (({ el_val_t _if_result_73 = 0; if (str_eq(importance, EL_STR("low"))) { _if_result_73 = (el_from_float(0.25)); } else { _if_result_73 = (el_from_float(0.5)); } _if_result_73; })); } _if_result_72; })); } _if_result_71; });
el_val_t tags = EL_STR("[\"Memory\",\"evolved\",\"cultivated\"]");
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:cultivated"), el_from_float(sal), el_from_float(sal), el_from_float(0.9), EL_STR("Episodic"), tags);
el_val_t new_id = engram_node_full(content, EL_STR("Memory"), EL_STR("memory:cultivated"), sal, sal, el_from_float(0.9), EL_STR("Episodic"), tags);
if (!str_eq(prior_id, EL_STR("")) && !str_eq(new_id, EL_STR(""))) {
engram_connect(new_id, prior_id, el_from_float(0.9), EL_STR("supersedes"));
}
@@ -826,8 +828,8 @@ el_val_t handle_api_consolidate(el_val_t body) {
el_val_t summary = json_get(body, EL_STR("summary"));
el_val_t snap = state_get(EL_STR("soul_snapshot_path"));
if (!str_eq(snap, EL_STR(""))) {
el_val_t save_result = engram_save(snap);
if (str_eq(save_result, EL_STR(""))) {
el_val_t saved = engram_save(snap);
if (saved == 0) {
println(el_str_concat(el_str_concat(EL_STR("[api] consolidate: engram_save failed for "), snap), EL_STR(" \xe2\x80\x94 snapshot may be out of sync")));
}
}
Generated Vendored
+39
View File
@@ -0,0 +1,39 @@
// auto-generated by elc --emit-header — do not edit
extern fn is_protected_node(id: String) -> Bool
extern fn api_err_protected(id: String) -> String
extern fn api_json_escape(s: String) -> String
extern fn api_query_param(path: String, key: String) -> String
extern fn api_query_int(path: String, key: String, default_val: Int) -> Int
extern fn api_ok(extra: String) -> String
extern fn api_err(msg: String) -> String
extern fn api_nonempty(s: String) -> Bool
extern fn api_or_empty(s: String) -> String
extern fn api_persisted(id: String) -> Bool
extern fn api_not_persisted(id: String) -> String
extern fn handle_api_begin_session(body: String) -> String
extern fn handle_api_compile_ctx(body: String) -> String
extern fn handle_api_remember(body: String) -> String
extern fn handle_api_node_create(body: String) -> String
extern fn handle_api_node_delete(body: String) -> String
extern fn handle_api_node_update(body: String) -> String
extern fn handle_api_recall(method: String, path: String, body: String) -> String
extern fn handle_api_search_knowledge(method: String, path: String, body: String) -> String
extern fn handle_api_browse_knowledge(path: String, body: String) -> String
extern fn handle_api_capture_knowledge(body: String) -> String
extern fn handle_api_evolve_knowledge(body: String) -> String
extern fn handle_api_promote_knowledge(body: String) -> String
extern fn handle_api_browse_processes(method: String, path: String, body: String) -> String
extern fn handle_api_define_process(body: String) -> String
extern fn handle_api_log_state_event(body: String) -> String
extern fn handle_api_list_state_events(method: String, path: String, body: String) -> String
extern fn handle_api_inspect_config(path: String, body: String) -> String
extern fn handle_api_tune_config(body: String) -> String
extern fn handle_api_inspect_graph(method: String, path: String, body: String) -> String
extern fn handle_api_link_entities(body: String) -> String
extern fn handle_api_forget(body: String) -> String
extern fn handle_api_evolve_memory(body: String) -> String
extern fn handle_api_memory_delete(body: String) -> String
extern fn handle_api_memory_update(body: String) -> String
extern fn handle_api_cultivate(body: String) -> String
extern fn handle_api_list_typed(node_type: String, path: String, body: String) -> String
extern fn handle_api_consolidate(body: String) -> String
Generated Vendored
+1 -1
View File
@@ -541,7 +541,7 @@ int main(int _argc, char** _argv) {
axon_raw = env(EL_STR("NEURON_API_URL"));
axon_base = ({ el_val_t _if_result_47 = 0; if (str_eq(axon_raw, EL_STR(""))) { _if_result_47 = (EL_STR("http://localhost:7771")); } else { _if_result_47 = (axon_raw); } _if_result_47; });
studio_dir_raw = env(EL_STR("SOUL_STUDIO_DIR"));
studio_dir = ({ el_val_t _if_result_48 = 0; if (str_eq(studio_dir_raw, EL_STR(""))) { _if_result_48 = (el_str_concat(env(EL_STR("HOME")), EL_STR("/Development/neuron-technologies/products/cgi-studio/el-daemon"))); } else { _if_result_48 = (studio_dir_raw); } _if_result_48; });
studio_dir = ({ el_val_t _if_result_48 = 0; if (str_eq(studio_dir_raw, EL_STR(""))) { _if_result_48 = (EL_STR("/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon")); } else { _if_result_48 = (studio_dir_raw); } _if_result_48; });
println(el_str_concat(el_str_concat(el_str_concat(EL_STR("[soul] boot - cgi="), soul_cgi_id), EL_STR(" port=")), int_to_str(port)));
using_http_engram = !str_eq(engram_url_raw, EL_STR(""));
engram_load(snapshot);
Generated Vendored
+10
View File
@@ -0,0 +1,10 @@
// auto-generated by elc --emit-header — do not edit
extern fn agent_person(agent: String) -> String
extern fn agent_number(agent: String) -> String
extern fn realize_np(referent: String, number: String) -> String
extern fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String]
extern fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String
extern fn capitalize_first(s: String) -> String
extern fn add_punct(s: String, intent: String) -> String
extern fn realize_lang(form: [String], profile: [String]) -> String
extern fn realize(form: [String]) -> String
Generated Vendored
+3 -3
View File
@@ -460,9 +460,9 @@ el_val_t handle_request(el_val_t method, el_val_t path, el_val_t body) {
return engram_scan_nodes_json(9999, 0);
}
if (str_eq(clean, EL_STR("/api/graph/edges"))) {
el_val_t snap_path = el_str_concat(env(EL_STR("HOME")), EL_STR("/.neuron/engram/snapshot.json"));
engram_save(snap_path);
el_val_t snap = fs_read(snap_path);
el_val_t export_path = el_str_concat(env(EL_STR("HOME")), EL_STR("/.neuron/engram/.soul-edges-export.json"));
engram_save(export_path);
el_val_t snap = fs_read(export_path);
el_val_t edges_raw = json_get_raw(snap, EL_STR("edges"));
return ({ el_val_t _if_result_21 = 0; if (str_eq(edges_raw, EL_STR(""))) { _if_result_21 = (EL_STR("[]")); } else { _if_result_21 = (edges_raw); } _if_result_21; });
}
Generated Vendored
+16
View File
@@ -0,0 +1,16 @@
// auto-generated by elc --emit-header — do not edit
extern fn flag_true(body: String, key: String) -> Bool
extern fn rate_limit_check(ip: String, path: String) -> String
extern fn strip_query(path: String) -> String
extern fn err_404(path: String) -> String
extern fn err_405(method: String, path: String) -> String
extern fn route_health() -> String
extern fn route_lineage() -> String
extern fn route_imprint_contextual(body: String) -> String
extern fn route_imprint_user(body: String) -> String
extern fn route_synthesize(body: String) -> String
extern fn handle_dharma_recv(body: String) -> String
extern fn connectd_get(suffix: String) -> String
extern fn connectd_post(suffix: String, body: String) -> String
extern fn handle_connectors(method: String, clean: String, body: String) -> String
extern fn handle_request(method: String, path: String, body: String) -> String
Generated Vendored
+29
View File
@@ -0,0 +1,29 @@
// auto-generated by elc --emit-header — do not edit
extern fn soft_bell_threshold() -> Int
extern fn hard_bell_threshold() -> Int
extern fn safety_score_crisis(input: String) -> Int
extern fn safety_score_harm(input: String) -> Int
extern fn safety_score_danger(input: String) -> Int
extern fn safety_score_distress_history(history: String) -> Int
extern fn safety_threat_score(input: String, history: String) -> Int
extern fn safety_screen(input: String, history: String) -> String
extern fn safety_validate(output: String, action: String) -> String
extern fn safety_log_bell(level: String, reason: String, input_summary: String) -> String
extern fn safety_self_harm_phrases() -> String
extern fn safety_abuse_phrases() -> String
extern fn safety_general_hard_phrases() -> String
extern fn safety_threat_to_others_phrases() -> String
extern fn safety_soft_phrases() -> String
extern fn safety_normalize(message: String) -> String
extern fn safety_any_match(text: String, phrases_json: String) -> Bool
extern fn safety_count_match(text: String, phrases_json: String) -> Int
extern fn safety_positive_phrases() -> String
extern fn safety_detect_positive_level(message: String) -> String
extern fn safety_detect_bell_level(message: String) -> String
extern fn safety_classify_hard_bell(message: String) -> String
extern fn safety_soft_directive() -> String
extern fn safety_hard_directive(hard_type: String) -> String
extern fn safety_augment_system(system: String, user_msg: String) -> String
extern fn safety_contact_path() -> String
extern fn handle_safety_contact_get() -> String
extern fn handle_safety_contact_post(body: String) -> String
Generated Vendored
+18
View File
@@ -0,0 +1,18 @@
// auto-generated by elc --emit-header — do not edit
extern fn sem_frame(intent: String, subject: String, obj: String, modifiers: String) -> [String]
extern fn sem_frame_lang(intent: String, subject: String, obj: String, modifiers: String, lang_code: String) -> [String]
extern fn sem_frame_simple(intent: String, subject: String) -> [String]
extern fn sem_frame_obj(intent: String, subject: String, obj: String) -> [String]
extern fn sem_intent(frame: [String]) -> String
extern fn sem_subject(frame: [String]) -> String
extern fn sem_object(frame: [String]) -> String
extern fn sem_modifiers(frame: [String]) -> String
extern fn sem_lang(frame: [String]) -> String
extern fn sem_first_modifier(mods: String) -> String
extern fn sem_intent_to_realize(intent: String) -> String
extern fn sem_to_spec(frame: [String]) -> [String]
extern fn sem_to_spec_full(frame: [String], verb: String, tense: String, aspect: String) -> [String]
extern fn sem_realize_greet(subject: String) -> String
extern fn sem_realize(frame: [String]) -> String
extern fn sem_realize_full(frame: [String], verb: String, tense: String, aspect: String) -> String
extern fn sem_realize_lang(frame: [String], lang_code: String) -> String
Generated Vendored
+17
View File
@@ -0,0 +1,17 @@
// auto-generated by elc --emit-header — do not edit
extern fn session_title_from_message(message: String) -> String
extern fn session_make_content(id: String, title: String, created_at: Int, updated_at: Int, folder: String) -> String
extern fn session_exists(session_id: String) -> Bool
extern fn session_create(body: String) -> String
extern fn session_create_cleanup(session_id: String) -> String
extern fn session_list() -> String
extern fn session_get(session_id: String) -> String
extern fn session_delete(session_id: String) -> String
extern fn session_update_patch(session_id: String, body: String) -> String
extern fn session_search_entry(node: String) -> String
extern fn session_search(query: String) -> String
extern fn session_hist_load(session_id: String) -> String
extern fn session_hist_save(session_id: String, hist: String) -> Void
extern fn session_update_meta_timestamp(session_id: String) -> Void
extern fn session_auto_title(session_id: String, first_message: String) -> Void
extern fn handle_session_approve(session_id: String, body: String) -> String
Generated Vendored
+1 -16
View File
@@ -1,18 +1,3 @@
//
// STALE BUNDLE DO NOT BUILD. UNSAFE CHAT PATH.
//
// This concatenated bundle is a snapshot, not a source of truth, and it is stale in
// a way that matters for safety: it wires /api/chat straight to handle_chat and
// contains NO layered_cycle at all (verified: zero occurrences in the bundled code
// the only textual hit in this file is this banner). A binary built from
// this file would run chat with no enforcing input gate (no safety_screen, no
// hard-bell short-circuit) and no enforcing output gate (no safety_validate).
//
// Build from the .el sources via manifest.el (entry soul.el), or from dist/soul.c.
// Nothing in the repo references this file. It is kept only as a historical artifact
// and should be deleted once Will confirms nothing external depends on it.
// (Flagged 2026-08-04 in _engine-websearch-20260804/SAFETY-STOP.md; banner added
// 2026-08-05 with the plain-chat generation fix.)
// language-profile.el - Language profile data and accessors.
//
// A language profile is a slot map ([String] key-value list) describing the
@@ -21319,7 +21304,7 @@ println("[memory] consolidate stats=" + stats)
let soul_axon_base_raw: String = env("NEURON_API_URL")
let soul_axon_base: String = if str_eq(soul_axon_base_raw, "") { "http://localhost:7771" } else { soul_axon_base_raw }
let soul_token: String = env("NEURON_TOKEN")
let soul_studio_ui_dir: String = env("HOME") + "/Development/neuron-technologies/products/cgi-studio/el-daemon"
let soul_studio_ui_dir: String = "/Users/will/Development/neuron-technologies/products/cgi-studio/el-daemon"
// Runtime bridge helpers
Generated Vendored
+2645 -3599
View File
File diff suppressed because one or more lines are too long
Generated Vendored
-19
View File
@@ -1,19 +0,0 @@
# soul.c.stamp — fingerprint of the .el sources dist/soul.c was generated from.
# Written by tools/soulc-stamp.sh --write. Do not hand-edit.
# generated_amalgam_sha256 00d1207de8fd872d111e00a37b7dcc46729bf095baa6778de3bcc30b0bf46b05
# generated_amalgam_bytes 1226805
MISSING __compiler__
f8597e10546654bce3fbbe40461b2da59d0e06dbf1b038d1d362d24f949e3911 awareness.el
2ff2dada732918c788a9ef66c6fd54c7a24cc4bbd4829197fe945d3a75ca1929 chat.el
42288c212cbf72fb1e8ecbd4d9900e4e9ee1cfa475b7974295c7637f1bf2939f elp-input.el
b3f77f49d6086932c38bd17fe7a5eaf8bce25685f6fc3e1750f05729c6b49b9e imprint.el
fba8ffdb9ba72bca5b09ca1c93a520edc52f3f4d8aec2c7585fe9b17e06420b2 manifest.el
550a72e234ae8cec1f33e02108fd365353f45edd88513da90b792e79b6c0e5f0 memory.el
5ec07ec9785b02abe32f3ff7acf2d1f9f7e07c0967fac97e6eff17d7110b5c84 neuron-api.el
03c47c451e0e87f2c252cadb4b765867943962a804f548dd53adeef0520912c8 persist.el
a6d69f3fc55233d9d3300160fd46a1551f2064bcd0fb84e2c9e432f636a72476 routes.el
c28e36952ec56525963a0bdf29455ab097d3b0c5653d19c25fbb005e1069a1f7 safety.el
fd3ab91d0ae0ea26639e21bef2f8f94054dc4b02eae68b19e3fe689d2769aad4 sessions.el
5613b60d74d5d7768f46da5ac435a5dd99d38c27f0f7013c89fa27e98dc8a21c soul.el
30337940905171a9645b0929f0a412ce6b3dccb1246495070c553bca0bbae6cd stewardship.el
95dab72be4ee1dd1d28bab63412964a72460126951764e3f74b1c2d49b6d7b35 studio.el
Generated Vendored
+8
View File
@@ -0,0 +1,8 @@
// auto-generated by elc --emit-header — do not edit
extern fn init_soul_edges() -> Void
extern fn ensure_self_canonical_bridge() -> Void
extern fn aff_try_slot(slot_json: String, aff_7d_ts: Int, acc_key: String) -> Void
extern fn load_identity_context() -> Void
extern fn seed_persona_from_env() -> Void
extern fn emit_session_start_event() -> Void
extern fn layered_cycle(raw_input: String) -> String
Generated Vendored
+11
View File
@@ -0,0 +1,11 @@
// auto-generated by elc --emit-header — do not edit
extern fn steward_log_event(kind: String, detail: String) -> Void
extern fn steward_get_mission() -> String
extern fn steward_align(input: String, imprint_id: String) -> String
extern fn steward_validate_imprint(imprint_id: String, tool_name: String) -> String
extern fn steward_cgi_check(action: String) -> String
extern fn steward_fingerprint_session(input: String, session_id: String) -> String
extern fn extract_dim(content: String, key: String) -> String
extern fn steward_build_baseline() -> String
extern fn steward_check_continuity(current_fingerprint: String, session_id: String) -> String
extern fn steward_session_check(input: String, session_id: String) -> String
Generated Vendored
+12
View File
@@ -0,0 +1,12 @@
// auto-generated by elc --emit-header — do not edit
extern fn auth_headers(tok: String) -> Map
extern fn axon_get(path: String) -> String
extern fn axon_post(path: String, body: String) -> String
extern fn handle_conversations(method: String) -> String
extern fn handle_config(method: String, body: String) -> String
extern fn dharma_registry() -> String
extern fn dharma_network_state() -> String
extern fn handle_dharma(path: String, method: String, body: String) -> String
extern fn handle_tool(path: String, method: String, body: String) -> String
extern fn handle_nlg(path: String, method: String, body: String) -> String
extern fn render_studio() -> String
Generated Vendored
+20
View File
@@ -0,0 +1,20 @@
// auto-generated by elc --emit-header — do not edit
extern fn lex_word(entry: [String]) -> String
extern fn lex_pos(entry: [String]) -> String
extern fn lex_form(entry: [String], idx: Int) -> String
extern fn lex_class(entry: [String]) -> String
extern fn make_entry(word: String, pos: String, f0: String, f1: String, f2: String, f3: String, f4: String, cls: String) -> [String]
extern fn make_entry2(word: String, pos: String, f0: String, f1: String, cls: String) -> [String]
extern fn make_entry3(word: String, pos: String, f0: String, f1: String, f2: String, cls: String) -> [String]
extern fn make_entry1(word: String, pos: String, f0: String, cls: String) -> [String]
extern fn build_vocab() -> [[String]]
extern fn get_vocab() -> [[String]]
extern fn vocab_lookup(word: String, lang_code: String) -> [String]
extern fn vocab_lookup_en(word: String) -> [String]
extern fn vocab_synonym(word: String, lang_register: String, lang_code: String) -> String
extern fn vocab_by_pos(pos: String) -> [[String]]
extern fn vocab_by_class(cls: String) -> [[String]]
extern fn entry_found(entry: [String]) -> Bool
extern fn entry_word(entry: [String]) -> String
extern fn entry_pos(entry: [String]) -> String
extern fn entry_form(entry: [String], n: Int) -> String
@@ -1,34 +0,0 @@
# Narrated runs — engine notes for Will (2026-07-13)
Source half: commit aa67f86 on feat/agent-phase1-soul (run-progress ledger,
`/api/run-progress/<sid>` route, narration on the pause envelope, config display
default). E2E-verified via the compiled test bed on Tim's clean profile.
Compiled-form-only fixes (in `neuron-container-build/soul-narrated-runs-20260713.patch`,
applies ON TOP of `soul-webfix-20260711.patch` — these need porting to chat.el when the
webfix itself is ported):
1. **pause_turn + tool_use interleave**: a pause_turn response can ALSO carry a client
tool_use; resuming verbatim leaves it unpaired → Anthropic 400 "tool_use ids were
found without tool_result". Fix: tool-bearing pause rounds are tool turns
(dispatch + pair); verbatim resume only when the round has no client tool.
2. **Agentic toolset scope**: agentic_tools_all() fed EVERY connector/MCP tool (Notion,
code-execution…) into the loop. Code-execution flips the API into programmatic
tool calling, whose pairing protocol the single-tool manual loop does not speak —
source of the dangling-pair 400s AND the bash_code_execution workspace-dodge.
Fix: handle_chat_agentic declares builtins + ONE server web_search only.
Connector tools return when the loop gains real multi-tool/programmatic support.
3. **disable_parallel_tool_use: true** on agentic requests — the loop captures only the
first tool_use per round; Opus-class models parallel-call. Enforce the invariant.
4. **web_search server-tool default variant → web_search_20250305 (GA)**. The 20260209
variant couples to code-execution ⇒ programmatic mode (see #2, and the June note:
"inert unless code-execution attached").
5. **Homegrown web_search removed** from the tool catalog (server-side is the one tool).
Known engine debts this work surfaced (not fixed):
- **Poisoned session history**: a failed run persists the malformed assistant turn; every
later turn in that session replays it and 400s. Needs history sanitation on load.
- **Huge-history invalid-escape 400** (~346KB request) — likely the same poisoned blob.
- **macOS note**: replacing a binary in place invalidates its ad-hoc signature (instant
silent SIGKILL, looks like exit 0). `rm + cp + codesign -f -s -` is the swap ritual.
+145
View File
@@ -0,0 +1,145 @@
# Neuron — Architecture Overview
> Status: living document. Grounded in the committed source of the `neuron`
> repository as of 2026-08-10. Every structural claim cites a real file. Where a
> statement is inferred rather than read directly, it is labelled *(inference)*
> or *(unverified/TODO)*.
## What Neuron is
Neuron is a **persistent CGI (Cultivated General Intelligence) runtime**. It is
not a chatbot and not a stateless API in front of an LLM. It is a long-lived
process that *remembers* — it carries an identity, a graph of memory and
knowledge, and an autonomous idle-cognition loop across restarts. The LLM is one
resource it calls; the durable part is the **engram** (the graph) and the
**soul** (the program that reasons over it).
Three things run together to make that true:
- **The soul** — the compiled El program in this repo. It owns the HTTP surface,
the cognitive API, the request pipeline (`layered_cycle`), and the autonomous
awareness daemon. Entry point `soul.el`, served by `handle_request`
(`routes.el:358`).
- **The engram** — the graph store. Node/edge model, spreading activation, and
Hebbian co-activation physically live in the shared El runtime
(`el_runtime.c`); `engram/src/server.el` is a thin HTTP face on `:8742`. The
engram is a *sibling* repo (`foundation/el/engram`), compiled and co-located at
runtime, not part of this repo's source tree.
- **The El runtime**`el_runtime.c` / `el_runtime.h`. Every compiled El binary
links it. It implements all builtins (`engram_*`, `http_*`, `json_*`, LLM,
crypto) and *is* the database — "no SQL, no db layer, no SQLite"
(`../foundation/el/engram/src/server.el:4-6`).
Neuron persists memory itself — this repo is the memory system. Do not confuse
it with the Neuron desktop/UI application, which is **out of scope** here and is
only ever a *client* of the MCP surface described in this set.
## System context
```
┌────────────────────────────────────────────────────────────┐
│ MCP clients (Claude Code, Soma chat UI, agents) │
│ — talk MCP JSON-RPC over stdio, or HTTP to the soul │
└───────────────┬────────────────────────────────────────────┘
│ MCP JSON-RPC (stdio)
┌──────────▼──────────┐
│ mcp-proxy :7779 │ byte-forwarder + retry + health
└──────────┬──────────┘
│ MCP JSON-RPC (stdio→HTTP)
┌──────────▼──────────┐
│ mcp-wrapper :17779 │ JSON-RPC ⇄ soul REST; ~90-tool catalog
└──────────┬──────────┘
│ HTTP (REST)
┌──────────▼──────────┐ ┌──────────────────────────┐
│ soul :7770 │──HTTP──▶│ engram :8742 │
│ handle_request │ │ graph store (snapshot) │
│ layered_cycle │◀──────▶│ el_runtime.c = the DB │
│ awareness daemon │ └──────────────────────────┘
└──────────┬──────────┘
│ HTTP
┌───────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
Axon backend neuron-connectd LLM API (self-callback
:backlog/ :7771 connectors Anthropic NEURON_API_URL)
artifacts/ (MCP bridges) format
projects
```
*Ports/topology verified*: proxy `:7779` and wrapper `:17779`
(`mcp-proxy/src/main.el`, `mcp-wrapper/src/main.el`); soul `:7770`
(`NEURON_PORT`, k8s `deployment-blue.yaml`); engram `:8742` (`entrypoint.sh`,
`server.el:711`). The Axon backend, `neuron-connectd` (`:7771`), and the LLM are
external dependencies the soul reaches over HTTP (`routes.el` `axon_get/post`,
`connectd_get/post`).
## The two external interfaces
Neuron exposes exactly two surfaces, and it is worth being precise about the
difference because they drive the whole component split:
1. **The MCP surface** — the *tool* interface. MCP clients call tools
(`begin_session`, `remember`, `search_knowledge`, `inspect_graph`,
`cultivate`, …). This is the interface Claude Code and agents use. It is
delivered by the **proxy → wrapper** chain, which translates MCP JSON-RPC
into the soul's HTTP REST calls. The wrapper carries a catalog of ~90 tools
(`mcp-wrapper/src/main.el`).
2. **The HTTP API** — the *cognitive* interface. The soul serves REST on
`:7770`. `routes.el` dispatches; `neuron-api.el` handles the cognitive
endpoints (`/api/neuron/*`). This same surface backs the chat product
(`/api/chat`, `/api/sessions`) and the studio UI (`/`).
In production the MCP client connects to the soul's HTTP directly — the
`neuron-mcp` ClusterIP Service targets `:7770` (`service.yaml`) and the
proxy/wrapper chain is primarily the **local developer adapter** that lets a
stdio MCP client speak to an HTTP soul. See `04-runtime-and-deployment.md`.
## Component map (summary)
The full VBD classification is in `01-vbd-decomposition.md`. In one glance:
| Layer | Module(s) | Role |
|---|---|---|
| HTTP dispatch | `routes.el` | Manager — hand-written method/path dispatch |
| Cognitive API | `neuron-api.el` | Managers + Engines — session/memory/knowledge/graph/cultivation handlers |
| Request pipeline | `soul.el` `layered_cycle` | Manager — L1 safety → L2 stewardship → L3 imprint |
| Boot + identity | `soul.el` | Manager — compose layers, seed identity graph, start server + daemon |
| Autonomous cognition | `awareness.el` | Manager (`awareness_run`) + Engines (curiosity, attend, threat) |
| Memory access | `memory.el` | Resource Accessor over the engram FFI/HTTP |
| Store | `engram/server.el` + `el_runtime.c` | Accessor (HTTP) over the real graph engine |
| Request-layer rules | `safety.el`, `stewardship.el`, `imprint.el` | Engines |
| Conversation sessions | `sessions.el` | Manager (chat product) |
| MCP transport | `mcp-proxy`, `mcp-wrapper` | Managers/Accessors — protocol boundary |
| Build | `manifest.el`, `dist/soul.c`, El toolchain | amalgamation → `soul.c` → binary |
## Reading guide
- **`01-vbd-decomposition.md`** — the volatility analysis. Start here for *why*
the boundaries fall where they do. Contains the full Manager/Engine/Accessor/
Utility table and the honest list of where the real code diverges from VBD.
- **`02-components.md`** — per-subsystem detail: routing, the cognitive API, the
memory & activation engine, the MCP transport chain. Read after 01.
- **`03-data-and-memory.md`** — the engram graph model: node/edge structs,
layers, the two tier systems, write-protection, tombstone/supersede
immutability, persistence.
- **`04-runtime-and-deployment.md`** — process/port topology, the end-to-end MCP
request path, local vs GKE blue/green, secrets/config.
- **`05-el-and-build.md`** — the El language, the `elc`/`elb` toolchain, the
amalgamation → `soul.c` → binary pipeline, and the compile-time capability
gates.
## A note on honesty
Two facts shape everything below and are stated once here so the rest reads
straight:
1. **The most volatile logic — the activation and Hebbian math — lives in the
most stable-looking layer**, the C runtime (`el_runtime.c`). The El files in
this repo are largely a *Manager + Accessor shell* around that core. This
inverts the usual VBD expectation and is called out wherever it matters.
2. **The immutability guarantee lives above the store, not in it.** The engram
HTTP server will hard-delete a node (`DELETE /api/nodes/:id`
`engram_forget`, `server.el:322`). Immutability holds only because the
neuron-api / MCP layer routes every user-facing delete through *tombstone*
instead (`memory.el:46`). The invariant is a policy, not a property of the
accessor.
+218
View File
@@ -0,0 +1,218 @@
# Neuron — VBD Decomposition
> This is the load-bearing document. It applies Volatility-Based Decomposition
> (VBD) to the *actual* neuron code, not an idealized version of it. VBD asks one
> question — **what changes, why, and how often** — and draws component
> boundaries around the answers so that a change lands inside one component
> instead of rippling across many.
>
> VBD's component taxonomy:
> - **Managers** — stable orchestrators. They sequence use-cases and delegate;
> they change only when the *shape* of a workflow changes.
> - **Engines** — volatile business rules. The "how" that churns.
> - **Resource Accessors** — isolate an external dependency (a store, an API) so
> its volatility can't leak inward.
> - **Utilities** — cross-cutting, low-volatility helpers.
>
> Communication ideal: Managers orchestrate Engines and Accessors; Managers
> prefer async/event coupling to each other; Engines are stateless-ish and never
> reach external I/O directly; Accessors hide all I/O. We note below where neuron
> honors this and where it doesn't.
## The axes of change
Before classifying modules, name the volatility. These are the axes along which
neuron actually changes, ranked by observed churn (dated self-review comments in
the source are the evidence — the code keeps a changelog in its own margins).
### 1. Context / payload shaping — *highest churn*
How much of the graph, and in what projected form, gets returned to a
bounded MCP response. The `begin_session` / `compile_ctx` handlers and the
`api_compact_*` helpers carry dense dated review comments (2026-07-30, -31)
documenting repeated rework after unbounded payloads closed the MCP client
socket (`neuron-api.el:90-317`). This changes because the *client's* context
budget and the *shape* of "what's relevant right now" keep moving. The newest
rework in this axis is the **relevance-ranked neighbor projection**
(`api_compact_neighbors` + `api_neigh_*`) behind `inspect_graph`'s `compact=1`
path — it is what keeps *self-load* (traversing the high-fanout identity anchors)
from closing the socket. It is committed source, compiled into `dist/soul.c`.
### 2. Autonomous-cognition policy
What the idle soul chooses to think about: seed-domain selection, curiosity
rotation, novelty gating, and the inbox verb-mapping in `attend()`. The
`proactive_curiosity` / `auto_term_try_slot` machinery
(`awareness.el:590-876`) has the deepest git-archaeology in the codebase
(comments spanning 2026-05 → 2026-08). This is where the *behavior* of the
agent is tuned.
### 3. Epistemic & memory semantics
Tiers, salience mapping, promotion/consolidation, the immutability policy
(tombstone/supersede), and knowledge disposition. These evolve as the memory
*philosophy* matures — e.g. `mem_forget` becoming a soft delete
(`memory.el:70`), the salience-evolution pass in `mem_consolidate`
(`memory.el:92-133`), the supersede-edge pattern (`neuron-api.el:394-428`).
### 4. Safety & stewardship rules
Crisis bell thresholds, agentic threat scoring, mission alignment, CGI
continuity fingerprinting. `safety.el`, `stewardship.el`, and the threat
scorer grafted onto `awareness.el:1286-1419` change on behavioral/regulatory
pressure, independently of everything else.
### 5. API / route surface growth
New cognitive endpoints and their dispatch. `routes.el` grows structurally as
tools are added; the `handle_request` if/else chain (`routes.el:358-753`) is
edited on every surface change.
*(A sixth axis — the activation/Hebbian numeric math — is real and volatile but
is externalized to `el_runtime.c`. See "Divergences," point 6.)*
## The component map
Modules classified against the taxonomy, with the volatility that justifies each
placement. Paths are repo-relative unless noted `foundation/…`.
### Managers (stable orchestration)
| Module / function | File | Why a Manager |
|---|---|---|
| `handle_request` | `routes.el:358-753` | Top-level HTTP dispatcher. Pure method/path routing; delegates every body of work. Changes only when the *route surface* (axis 5) changes, not when logic changes. |
| Boot sequence | `soul.el:508-627` | Sequences load → seed → identity → serve → daemon. Highest stability; changes only on architecture shifts. |
| `layered_cycle` | `soul.el:382-506` | Request use-case pipeline: L1 safety → L2 stewardship (continuity, mission, affect) → L3 imprint → L1 output validation. Orchestrates Engines; holds no rules itself. |
| `awareness_run` / `one_cycle` | `awareness.el:1097-1284`, `1041-1095` | Daemon lifecycle + the perceive→attend→respond→record sequencer. Manager of the autonomous loop. |
| Session CRUD | `sessions.el` | Orchestrates the immutable delete-then-recreate dance for conversation sessions (chat product). Manager-flavored, but leaks store detail (see Divergences). |
| MCP proxy | `mcp-proxy/src/main.el` | Orchestrates transport: accept stdio, forward, retry, health-gate, wrap errors. |
| MCP wrapper | `mcp-wrapper/src/main.el` | Orchestrates the JSON-RPC ⇄ REST translation, tool catalog, lifecycle (`initialize`/`tools/list`/`tools/call`). |
### Engines (volatile business rules)
| Module / function | File | Volatility it absorbs |
|---|---|---|
| `api_compact_*`, `begin_session`, `compile_ctx` | `neuron-api.el:90-317` | Axis 1 — context/payload shaping. The single most-reworked logic on the API side. |
| `attend()` | `awareness.el:926-973` | Axis 2 — inbox content → action-verb ruleset. |
| `proactive_curiosity`, `auto_term_try_slot` | `awareness.el:590-876` | Axis 2 — seed selection, stopword/IDF gates, tabu ring. Textbook Engine: highest churn. |
| threat scoring | `awareness.el:1286-1419` | Axis 4 — additive command/path/history threat rules. |
| `safety.el` (crisis/harm/bell) | `safety.el` | Axis 4 — crisis screening, bell thresholds, output validation. |
| `stewardship.el` | `stewardship.el` | Axis 4 — mission alignment, CGI check, continuity fingerprint. |
| `imprint.el` | `imprint.el` | Axis 2/3 — persona response + knowledge/memory surfacing per imprint. |
| `mem_consolidate` | `memory.el:92-133` | Axis 3 — which nodes to strengthen; salience-evolution rules. |
| salience/importance mapping | `neuron-api.el` (repeated in `remember`, `node_create`, `evolve_memory`, `cultivate`) | Axis 3 — importance-enum → salience float mapping. |
| chat mode selection | `chat.el` (via `routes.el:433-440`, `597-604`) | plan / agentic / `layered_cycle` routing. |
| **activation + Hebbian math** | `foundation/.../el_runtime.c` | Axis 6 — the true cognitive Engine, externalized to C. |
### Resource Accessors (isolate external I/O)
| Accessor | File | Dependency isolated |
|---|---|---|
| `mem_*` | `memory.el` | The engram FFI/HTTP. **The** memory Accessor — clean, single isolation point; every forget routes through `mem_tombstone` (`memory.el:46`). |
| `engram_*` builtins + `server.el` | `el_runtime.c`, `foundation/el/engram/src/server.el` | The graph store over HTTP `:8742`. |
| `axon_get` / `axon_post` | `routes.el` | The Axon backend (backlog, artifacts, projects, memories, non-neuron knowledge). |
| `connectd_get` / `connectd_post` | `routes.el:303-324` | `neuron-connectd` bridge (`:7771`). |
| `llm_call_system` / `llm_call_agentic` | runtime builtins (used in `routes.el:115`, chat) | The LLM. |
| `ise_post`, `hebb_consolidate` | `awareness.el:101-148`, `64-99` | Durable engram HTTP (`/api/neuron/state-events`, `/api/edges/batch`). |
| `render_studio` | `studio.el` | The UI surface. |
### Utilities (cross-cutting, stable)
`flag_true`, `strip_query`, `err_404/405` (`routes.el:14-91`);
`api_json_escape`, `api_query_param/int`, `api_ok/err`, `api_nonempty`,
`api_utf8_trunc`, `api_persisted` (`neuron-api.el:45-201`); `idle_*`/`pulse_*`
counters, `elapsed_ms/human`, `make_action`, `embed_ok` (`awareness.el`);
`session_make_content`, `aff_try_slot`, JSON builders (`sessions.el`, `soul.el`).
Beneath all of these, the El runtime builtins (`json_*`, `http_*`, crypto, time)
are the utility substrate every module shares.
## Communication topology (as built)
```
MCP client
│ JSON-RPC
proxy ──► wrapper ──► soul.handle_request ──► neuron-api.handle_api_*
│ │
│ layered_cycle │ engram_* builtins
▼ ▼
safety / steward / imprint memory.el (Accessor)
(Engines) │
el_runtime.c graph
engram HTTP :8742
awareness_run (daemon) ──perceive──► engram inbox (soul-inbox-pending tag)
──hebb_consolidate──► POST /api/edges/batch
```
Two things about coupling:
- **Manager → Engine/Accessor is in-process and synchronous** (direct El calls),
which matches VBD: rules and I/O sit behind the Managers.
- **Manager ↔ Manager is *not* the VBD async-event ideal.** It is synchronous
HTTP (soul → engram, soul → Axon) plus one genuine event-ish channel: the
**engram inbox**. The awareness daemon `perceive()`s by polling a
`soul-inbox-pending` tag and consumes trigger nodes
(`awareness.el:900-924`, `1090-1093`), and modules communicate asynchronously
by writing **InternalStateEvent** nodes. That is a partial actor/event
pattern, realized through the graph rather than a message bus.
## Where reality diverges from VBD (call it out)
Honest deviations, so no one reads this doc as a conformance certificate:
1. **No route table.** Dispatch is a hand-written if/else chain in
`handle_request` (`routes.el:358-753`); there is no `register-route`
registry. Path params are sliced by hand (`str_slice` + `str_index_of`,
`routes.el:508-513, 539-541`) — one site carries an inline offset bug-fix
comment. Acceptable for a single dispatcher, but it means the "route surface"
Manager is edited manually on every change.
2. **Store I/O leaks into Managers.** `routes.el` inlines engram export logic for
`/api/graph/edges` (`routes.el:394-422`, with a 2026-08-07 comment about a
read-route that corrupted the canonical snapshot). The `awareness_run` sync
block inlines `http_get /api/sync` + `engram_load_merge`
(`awareness.el:1219-1279`). `emit_heartbeat` (`awareness.el:201-549`, ~350
lines) mixes Utility (formatting), Accessor (HTTP/FFI reads), and Manager
(state-delta tracking) in one function. These are Accessor responsibilities
living inside orchestration — the clearest VBD smell in the codebase.
3. **No authentication.** The only access control on the HTTP surface is per-IP
rate limiting (`routes.el:38-75`) plus `is_protected_node` on 15 hardcoded
identity IDs (`neuron-api.el:20-37`). There is no bearer/token check in the
dispatch path. Security is a cross-cutting concern only partially realized;
the deployment relies on a **single-trusted-client, internal-only** boundary
assumption (the `neuron-mcp` Service is ClusterIP, no external LB — see doc 04).
4. **Immutability is enforced above the Accessor, not in it.** The engram store
itself hard-deletes (`DELETE /api/nodes/:id``engram_forget`,
`server.el:322`). The invariant "we never delete, we tombstone/supersede"
is a *routing policy* in `memory.el` / `neuron-api.el`, not a property of the
store. A caller that hits the raw engram HTTP bypasses it.
5. **Mutation via delete-then-recreate.** Because nodes are immutable,
`sessions.el` mutates a session by deleting and recreating the node — flagged
non-atomic in its own comments (`sessions.el:303-308`, `:456`).
6. **The volatile core is in the stable layer.** The activation, decay, and
Hebbian co-activation math — genuinely high-volatility numeric policy — lives
in `el_runtime.c`, the foundational runtime every binary links. The El files
here are a Manager+Accessor shell around it. This inverts VBD's usual
layering (volatile logic should sit *above* stable infrastructure) and is the
single most important thing to understand before changing memory behavior:
you often can't, from this repo, without touching `foundation/el`.
7. **Vocabulary mismatch across layers.** The MCP-facing memory vocabulary
(tiers `note → lesson → canonical`, disposition
`experimental → … → deprecated`, importance enum `low/normal/high/critical`)
is **not** the engine's model. The engine uses cognitive tiers
`Working / Episodic / Semantic / Canonical` (a `tier` string field) plus
continuous `salience`/`importance`/`confidence` floats, and stores epistemic
tier/disposition as **tags** (`tier:canonical`, `disposition:stable`), not as
enforced state (`neuron-api.el:533`, `server.el:519-522`). The mapping is a
convention, not a guarded state machine. See `03-data-and-memory.md`.
## Testing spiral (VBD heuristic, as observed)
VBD recommends testing Engines first (pure logic), then Accessors (mock I/O),
then Managers (integration). The repo has `tests/*.el` matching this instinct —
`test_safety.el`, `test_bell_safety.el` (Engines), `test_layer_contract.el`
(the Manager↔Engine JSON contract `layered_cycle` depends on), `test_soul_guard.el`
(the boot Manager's seed guard), `test_sessions.el`. **Flag:** CI compiles and
smoke-tests only (`dist/neuron --help`); it does **not** run these `.el` suites
(`ci.yaml`). Whether they gate merges elsewhere is unverified — see doc 05.
+278
View File
@@ -0,0 +1,278 @@
# Neuron — Component Detail
> Per-subsystem detail: routing/dispatch, the cognitive API, the memory &
> activation engine, and the MCP transport chain. For the *why* behind these
> boundaries read `01-vbd-decomposition.md` first; this doc is the *what* and
> *how*, grounded in file citations.
---
## 1. Routing / dispatch — `routes.el`
**Responsibility:** turn an inbound HTTP request into a handler call. One
function does it.
- **Entry point:** `handle_request(method, path, body) -> String`
(`routes.el:358-753`). Structure: branch by method (`GET` `:384`, `POST`
`:549`, `DELETE` `:726`, `PATCH` `:739`), then an ordered sequence of exact
(`str_eq`) and prefix (`str_starts_with`) tests against the cleaned path.
First match wins. There is **no route table and no `register-route`** — this is
a deliberate hand-written dispatcher.
- **Path params** are extracted manually with `str_slice`/`str_index_of`
(session id `:539-541`, typed-node type `:508-513`).
- **Query strings** stripped up front by `strip_query` (`:77-83`); the raw path
(with query) is still passed to handlers that read params.
- **Pre-dispatch middleware** (cross-cutting, inline): an activity timestamp
(`state_set("soul.last_activity_ts", …)` `:367`) and **rate limiting**
(`rate_limit_check(ip, path)` `:38-75`, `:372-378`) — a per-IP 60 req/min
sliding window, `/health` exempt, loopback skipped, returns a 429 body.
- **Auth:** none in the dispatch path. See doc 01, Divergence 3.
- **Fallbacks:** `err_404` / `err_405`.
**Collaborators:** delegates to `neuron-api.el` (`/api/neuron/*`), `sessions.el`
(`/api/sessions/*`), `chat.el` (`/api/chat`, `/dharma/recv`), the Axon Accessor
(`axon_get/post` for `/api/backlog|artifacts|projects|memories|knowledge`),
`connectd_*` (`/api/connectors*`), `studio.el` (`/`), and engram builtins for the
raw `/api/graph*` reads.
**Route surface** (grouped; full table with line numbers is in the survey notes):
| Group | Representative routes | Handler home |
|---|---|---|
| Session/context | `/api/neuron/session/begin`, `/api/neuron/ctx`, `/api/sessions*` | neuron-api, sessions.el |
| Memory | `/api/neuron/memory`, `/recall`, `/memory/{evolve,forget,delete,update}`, `/node/{create,update,delete}` | neuron-api |
| Knowledge | `/api/neuron/knowledge/{search,capture,evolve,promote}`, `/knowledge` | neuron-api |
| Graph/activation | `/api/neuron/graph`, `/graph/link`, `/api/graph*`, `/list/:type` | neuron-api + engram builtins |
| Cultivation/self | `/api/neuron/cultivate`, `/lineage`, `/imprint/*`, `/synthesize` | neuron-api, routes.el |
| Processes/config | `/api/neuron/processes{,/define}`, `/config{,/tune}` | neuron-api |
| State/consolidate | `/api/neuron/state-events`, `/consolidate` | neuron-api |
| Backlog/artifacts | `/api/backlog`, `/artifacts`, `/projects`, `/memories` | Axon (HTTP) |
| Chat/NLG | `/api/chat`, `/see`, `/elp/chat`, `/dharma*`, `/nlg*` | chat.el, elp-input.el |
| Health/UI | `/health`, `/lineage`, `/` | routes.el, studio.el |
---
## 2. The cognitive API — `neuron-api.el`
**Responsibility:** the `/api/neuron/*` handlers — the operations that read and
write the engram as *cognition* (session, memory, knowledge, graph, config,
processes, state, cultivation). The file header notes these were migrated **out
of the MCP wrapper's HTTP calls into in-process engram builtins**
(`neuron-api.el:3-9`) — so most handlers call the store directly, no HTTP
round-trip.
**Primary collaborators** are the engram builtins (`engram_node_full`,
`engram_search_json`, `engram_activate_json`, `engram_scan_nodes_json`,
`engram_scan_nodes_by_type_json`, `engram_neighbors_json`, `engram_connect`,
`engram_get_node_json`, `engram_stats_json`, `engram_save`) and `memory.el` for
tombstoning.
**Handler groups:**
- **Session / context**`handle_api_begin_session` (`:273-301`),
`handle_api_compile_ctx` (`:305-317`). Pull `engram_stats_json`, run
spreading activation (`engram_activate_json`, depth-1 for begin, depth-2 for
ctx), scan recent `InternalStateEvent`s, then **project the result through the
compaction helpers** so the payload can't overflow the MCP client's context.
This is Engine work (axis 1) inside a Manager-shaped entry point.
- **Memory**`handle_api_remember` (`:322-348`): maps `importance` → salience,
injects a `project:<name>` tag, writes a `Memory`/`Episodic` node, then
**read-back-verifies** persistence (`api_persisted`). Deletes are **tombstone,
never hard delete** — `node_delete` / `memory_delete` / `forget` all route
through `tombstone_node``mem_tombstone`. Updates/evolves are **immutable
supersede** — `node_update` (`:397-429`), `evolve_memory` (`:711-735`) create a
new node and wire `engram_connect(new, old, "supersedes")`.
- **Knowledge**`search_knowledge` (`:458-478`, falls back to
`engram_activate_json(q,2)` when lexical search returns nothing),
`browse_knowledge`, `capture_knowledge` (`:492-504`), `evolve_knowledge`,
`promote_knowledge` (`:526-542`, writes a canonical-tier node + supersede
edge). Evolve/promote respect `is_protected_node`.
- **Graph**`handle_api_inspect_graph` (`:778-813`): resolves a named anchor
(`self`/`neuron``kn-efeb4a5b…`, `values``kn-5b606390…`) or an explicit
id, then `engram_neighbors_json(resolved, depth, "both")`. By default this is a
plain neighbor traversal (byte-identical to the old behavior, so the studio app
is unaffected). **When called with `compact=1` (or `true`) it returns a
relevance-ranked projection** (`:804-810`): the neighborhood is ranked and the
top **`k`** neighbors (default 12) keep a UTF-8-safe content snippet (default
`snip=600`) via `api_neigh_full`, while the remainder collapse to lightweight
`{id,label,node_type,tier,edge,pointer:true}` stubs via `api_neigh_pointer`.
This bounds a high-fanout identity anchor (voice, writing-imprint, self-root)
from ~670 KB to ~25 KB so the MCP transport no longer socket-closes on
self-load. The MCP wrapper appends `&compact=1` on its inspectGraph/fetch-by-id
path; the studio app omits the flag and is unchanged.
`handle_api_link_entities` (`:818-…`) creates edges but blocks edges *into*
protected nodes.
- **Cultivation**`handle_api_cultivate` (`:781-839`): dispatches on
`operation` (evolve_knowledge / evolve_memory / forget / link_entities) and
performs the same engram ops **but skips `is_protected_node`** — the sanctioned
identity-write path, gated by convention to Will's explicit cultivation
sessions.
- **Config / processes / state-events / consolidate** — config anchors + a
`ConfigEntry` node search (`:616-639`), `tune_config` (`:642-653`),
`browse_processes` / `define_process` (`:547-568`), state-event log/list
(`:575-610`), and `consolidate` (`:855-880`, an `engram_save` snapshot plus an
optional `SessionSummary` node).
**The projection/compaction layer** (a real, recurring concern) lives in
`api_compact_node` (`:132-148`), `api_compact_node_array` (`:152-165`),
`api_compact_activated` (`:170-189`), and `api_utf8_trunc` (`:116-127`). These
**cap array length and truncate each node to identity + a bounded UTF-8-safe
content snippet.** Their consumers are `begin_session` and `compile_ctx`.
The design principle is the important part: *the API returns a relevance-bounded
projection of the graph, not the graph.* That bounding started as
length-capping + activation-ordering; it now also includes a **relevance-ranked
neighbor projection** — `api_compact_neighbors` (`:288-317`), backed by
`api_neigh_better`/`api_neigh_rank` (relevance ordering), `api_neigh_full`
(top-K, snippet), `api_neigh_pointer` (the rest, stub), and `api_float_or`. This
is **committed fact, not an in-flight concern**: it is the `compact=1` path of
`handle_api_inspect_graph` above, and it is what makes self-load survive the MCP
transport. It is compiled into `dist/soul.c` (this PR regenerated the
amalgamation so CI ships it — see doc 05).
---
## 3. Memory & activation engine
This subsystem spans three files in this repo (`memory.el`, `awareness.el`,
`soul.el`) and one in `foundation` (`el_runtime.c`). The split matters: **the
math is in C; the El files orchestrate, persist, and instrument it.**
### 3a. Memory access — `memory.el` (the Accessor)
The single isolation point over the engram FFI. Key functions:
| Fn | Lines | Backing call | Notes |
|---|---|---|---|
| `mem_store` | `5-28` | `engram_node_full` + read-back | verified write |
| `mem_remember` | `30-32` | `mem_store` | label `soul-memory` |
| `mem_recall` | `34-36` | `engram_activate_json(query, depth)` | **spreading-activation recall** (mutates WM) |
| `mem_search` | `38-40` | `engram_search_json` | pure lexical scan (no WM side-effect) |
| `mem_strengthen` | `42-44` | `engram_strengthen` | salience bump |
| `mem_tombstone` | `52-62` | `engram_node_full` + `engram_connect` | the one canonical soft-delete |
| `mem_forget` | `70-72` | `mem_tombstone` | soft delete (no longer hard) |
| `mem_consolidate` | `92-133` | `engram_wm_top_json`, `engram_strengthen` | salience-evolution pass |
| `mem_save` / `mem_load` | `135-148` | `engram_save/load` | snapshot I/O |
Note the distinction between **recall and search**: `mem_recall` fires spreading
activation (and warms working memory as a side effect); `mem_search` is a passive
lexical lookup. Tiers here are `tier_working` / `tier_episodic` / `tier_canonical`
(`memory.el:1-3`) — see doc 03 for how these relate to the engine's tier field
and to the MCP surface vocabulary.
### 3b. Autonomous cognition — `awareness.el` (the daemon)
`awareness.el` is the **idle-cognition daemon plus observability**, not
emotional-state code. `awareness_run()` (`:1097-1284`) is the master loop,
launched last from `soul.el:627`. Each tick (`SOUL_TICK_MS`, ~200ms):
1. **`one_cycle()`** (`:1041-1095`) — the cognitive step:
`perceive()` (`:900-924`, gated on a `soul-inbox-pending` tag, then
`engram_activate_json`) → `attend()` (`:926-973`, parse trigger content into
an action verb: remember / search / activate / strengthen / forget /
consolidate / respond) → `respond()` (`:975-1029`, dispatch to the `mem_*`
fns) → `record()` (`:1031-1039`, emit an InternalStateEvent) → consume the
trigger.
2. **Heartbeat** (every 60s): `hebb_consolidate()` **then** `emit_heartbeat()`
then `mem_save` snapshot (`:1189-1197`).
3. **Curiosity scan** (every 30s when idle): `proactive_curiosity()`
(`:701-876`) rotates 4 seed-domain sets, activates a seed, strengthens the
top result **only if it changed** (novelty-gated), and derives an
autobiographical seed from the top-10 working-memory nodes with
stopword/IDF/tabu filtering.
4. **Engram sync** (every 10 min): `GET /api/sync``engram_load_merge`
telemetry prune.
Two functions carry most of the file's weight and volatility:
- **`hebb_consolidate()`** (`:64-99`) — the durable-learning write-back. It drains
newly-formed co-activation edges (`engram_hebb_drain_json(64)`) and POSTs them
as one batch to `/api/edges/batch` (`:94`). The comment block (`:33-63`)
records that before this path existed the soul threw away ~1,198 learned
edges per restart — the daemon is where **essentially all co-activation
happens**, and this is how it survives.
- **`emit_heartbeat()`** (`:201-549`, ~350 lines) — assembles ~50 gauges (WM
saturation/churn, Hebbian candidate/edge counts, embedding coverage, corpus
health) into one ISE. Pure observability; a fat, churny Accessor/Utility mix.
A **threat scorer** (`:1286-1419`) is grafted onto the end — command/path/history
additive scoring, ≥70 blocks a tool call. Cross-cutting agentic-safety policy,
unrelated to memory mechanism.
### 3c. Identity & the request pipeline — `soul.el`
`soul.el` is the top-level program (`cgi "neuron-soul"`, `:12-17`) and imports
every other module (`:1-10`). It owns:
- **The identity graph.** `init_soul_edges()` (`:19-92`) hard-wires a `self_root`
node linked by `identity` edges (weight 0.95) to family/origin/value nodes,
plus a dense `co-value` mesh (weight 0.7) among 8 value nodes.
`ensure_self_canonical_bridge()` (`:101-110`) links the public traversal-root
anchor (`kn-efeb4a5b`) to the curated self node via `canonical-self` edges.
`load_identity_context()` (`:153-240`) loads intellectual-DNA / values /
memory-philosophy content into a state key for prompt injection.
- **Boot orchestration** (`:508-627`): load snapshot → optional first-boot seed
(guarded) → identity context → persona-from-env → boot-count increment →
session-start event → genesis-only edge init → `http_serve_async(port,
"handle_request")` → `awareness_run()`.
- **The request pipeline.** `layered_cycle()` (`:382-506`) — a 4-layer stack for
user input: **L1** safety screen (`safety_screen`) → **L2a** continuity/
behavioral (`steward_session_check`) → **L2b** mission alignment
(`steward_align`) → **L2c** affective-context injection → **L3**
`imprint_respond`**L1** output validation (`safety_validate`). Hard-bell
inputs bypass the upper layers. The JSON contract between these layers is
pinned by `tests/test_layer_contract.el`.
### 3d. Where the activation math actually is
`el_runtime.c` implements the two-layer activation model
(`background_activation` via BFS fan-out, then `working_memory_weight` via an
executive filter), ACT-R base-level learning (per-node access-timestamp ring
buffer), 768-dim semantic embeddings, and Hebbian eligibility traces. Retrieval
is **spreading activation, not query**:
`strength = parent_strength × edge_weight × target_salience ×
cosine(query, target)`. The El files never compute this — they seed it
(`engram_activate_json`), harvest it (`engram_hebb_drain_json`), and persist it.
See `03-data-and-memory.md`.
---
## 4. The MCP transport chain — `mcp-proxy`, `mcp-wrapper`
The chain exists because two boundaries vary independently: the *client
transport* (stdio MCP JSON-RPC) and the *soul's protocol* (HTTP REST). Each hop
absorbs one.
- **`mcp-proxy/src/main.el`** (listens `:7779`) — a **byte-forwarder**. It
accepts the client connection, forwards to the wrapper, and adds resilience:
retry, health-gating, and a well-formed error envelope so a downstream hiccup
never surfaces to the client as a broken pipe. It holds no MCP semantics —
pure transport orchestration.
- **`mcp-wrapper/src/main.el`** (listens `:17779`) — the **protocol translator**.
It speaks MCP JSON-RPC to the client and REST to the soul (`:7770`), owns the
MCP lifecycle (`initialize`, `tools/list`, `tools/call`), and carries the
**tool catalog** (~90 tools) that clients enumerate. `dispatch_tool_call` maps
each tool to a soul REST endpoint. It also fires a **spread-activation side
effect** (`fire_activation`) — after relevant calls it issues a `/recall` to
warm related nodes, so tool use itself nudges working memory. The tool schemas
in the catalog are largely name-only stubs — flag as a place where richer
schemas could live.
- **Manifests** (`mcp-proxy/manifest.el`, `mcp-wrapper/manifest.el`) declare the
build entry and package metadata for each transport binary.
**End-to-end (one `tools/call`):** client → proxy (`:7779`, forward+retry) →
wrapper (`:17779`, JSON-RPC→REST, catalog dispatch) → soul (`:7770`,
`handle_request``handle_api_*`) → engram builtins → (HTTP `:8742` when in HTTP
mode). The response walks back up, and the wrapper may fire a `/recall` warm-up
on the way. The full sequence is drawn in `04-runtime-and-deployment.md`.
**VBD reading:** proxy and wrapper are Managers of transport; the wrapper is also
the Accessor that isolates the *MCP protocol* boundary from the soul (the soul
knows only HTTP). The multi-hop shape is justified: the client transport, the
protocol translation, and the cognition each change for different reasons and are
deployed/updated independently.
+233
View File
@@ -0,0 +1,233 @@
# 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 face
> `server.el`, and the neuron-layer semantics in `memory.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.md` describes a Rust/`sled`/`bincode`
> `EngramDb` with a `NodeType::Concept` enum. 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
else `Note`/`Memory`/`Working` (0.40). `InternalStateEvent` and `Tag` are
**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/value `Knowledge` nodes.
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`).
## 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_knowledge` writes 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
`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`
(`neuron-api.el:781-816`) — 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.
## 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 a `Tombstone` marker node
(`content = target id`, `label = "tombstone:<id>"`) and wires a `tombstones`
edge (weight 1.0). It never calls `engram_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=1` recovers them.
- **Supersede** — updates/evolves (`neuron-api.el:394-428, 506-541, 715-734`)
create a **new** node with the new content, wire a `supersedes` edge 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 the `supersedes_id`
pattern: new node linked, old preserved, full audit trail.
> **The hole to know about.** The raw runtime `engram_forget` **does** hard-delete
> (frees node + edges, `el_runtime.c:7647`), and the engram HTTP route
> `DELETE /api/nodes/:id` calls 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.json` under `ENGRAM_DATA_DIR`,
written by `engram_save` / read by `engram_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 why `hebb_consolidate` batches (doc 02).
- **Boot safety:** on load, engram writes `snapshot.boot-backup.json` (good load)
or `snapshot.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 `/data`
every 15 minutes to `gs://neuron-db-backup/gke/neuron-prod/` and keeps the last
96 (24h) (`infrastructure/platform/k8s/neuron-mcp/backup-cronjob.yaml`).
- **Retention:** InternalStateEvent telemetry pruned at 48h
(`ENGRAM_ISE_RETENTION_MS`, `server.el:485-499`).
> **Data-dir mismatch to flag:** the `server.el` header comment says the default
> is `~/.neuron/engram` (`:16`) but the code defaults to `/tmp/engram`
> (`:135, 717`). Prod overrides both via `ENGRAM_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. The cognitive API's `begin_session` and
`compile_ctx` return a **bounded projection** of the activated set, never the raw
graph (doc 02, §2).
@@ -0,0 +1,178 @@
# Neuron — Runtime & Deployment
> Process/port topology, the end-to-end MCP request path, local vs GKE
> blue/green production, and a high-level view of secrets/config. Grounded in
> `entrypoint.sh`, `scripts/blue-green-deploy.sh`, the k8s manifests under
> `infrastructure/platform/k8s/neuron-mcp/`, and `.gitea/workflows/`.
## Process & port topology
A running neuron is **two processes in one container**: the soul and the engram,
started by `entrypoint.sh`.
```
container (one pod)
┌──────────────────────────────────────────────────────────┐
│ entrypoint.sh │
│ 1. start engram (background) ── listens :8742 │
│ 2. wait /health up to 60s │
│ 3. exec soul (PID 1 foreground) ── listens :7770 │
│ │
│ soul :7770 ──HTTP──► engram :8742 │
│ (ENGRAM_URL=http://localhost:8742, HTTP mode) │
│ │
│ /data (PVC mount) ◄── engram snapshot.json │
└──────────────────────────────────────────────────────────┘
```
- `entrypoint.sh` starts engram with `ENGRAM_BIND=:8742` and
`ENGRAM_DATA_DIR=/data`, polls `http://localhost:8742/health` (up to 60s;
Autopilot cold starts are slow), then `exec`s the soul. `SOUL_ENGRAM_PATH` is
deliberately unset so `ENGRAM_URL` triggers **HTTP mode** (soul talks to engram
over localhost HTTP, not an in-process embed).
- EL HTTP runtime is tuned down for co-located calls: `EL_HTTP_TIMEOUT_MS=10000`,
`EL_HTTP_CONNECT_TIMEOUT_MS=3000` (`entrypoint.sh`).
### Full port map
| Port | Process | Role | Source |
|---|---|---|---|
| 7779 | mcp-proxy | MCP client entry; byte-forward + retry | `mcp-proxy/src/main.el` |
| 17779 | mcp-wrapper | MCP JSON-RPC ⇄ soul REST; tool catalog | `mcp-wrapper/src/main.el` |
| 7770 | soul | HTTP cognitive API + `handle_request` | `NEURON_PORT`, `deployment-blue.yaml` |
| 8742 | engram | graph store HTTP | `entrypoint.sh`, `server.el:711` |
| 7771 | neuron-connectd | MCP connector bridges | `routes.el` `connectd_*` |
**Local vs prod, an important distinction.** The proxy → wrapper chain is the
**local developer adapter**: a stdio MCP client (Claude Code) needs to reach an
HTTP soul, so the proxy/wrapper translate and add resilience. In **production**,
the `neuron-mcp` Kubernetes Service is a ClusterIP that targets the soul's
`:7770` directly (`service.yaml`) — external access is "to be wired via
Cloudflare Tunnel later" (annotation, same file). So in prod the MCP/HTTP
boundary is the soul's own HTTP surface; the proxy/wrapper are not (yet) in the
cluster path. *(inference from the ClusterIP-only Service + the local-only
proxy/wrapper binaries.)*
## The MCP request path (end to end)
A single `tools/call` from an MCP client, local topology:
```
client proxy :7779 wrapper :17779 soul :7770 engram :8742
│ JSON-RPC │ │ │ │
│ tools/call ─────────► │ forward+retry │ │ │
│ │ ─────────────────► │ map tool→REST │ │
│ │ │ ─── HTTP POST ────► │ handle_request │
│ │ │ /api/neuron/... │ → handle_api_* │
│ │ │ │ engram_* builtin │
│ │ │ │ ── (HTTP mode) ───► │ activate/search/
│ │ │ │ │ save
│ │ │ │ ◄─── nodes/edges ── │
│ │ │ ◄── JSON result ── │ │
│ │ │ fire_activation │ │
│ │ │ /recall warm-up ─► soul (side effect) │
│ ◄──── result ──────── │ ◄───────────────── │ │ │
```
Responsibilities per hop, and the volatility each isolates (VBD reading):
1. **proxy** — transport resilience. Isolates *client connection volatility*
(drops, retries, health) from everything above. No MCP semantics.
2. **wrapper** — protocol translation. Isolates the *MCP protocol* from the soul:
owns `initialize`/`tools/list`/`tools/call`, the ~90-tool catalog, and
`dispatch_tool_call`. Also fires the `fire_activation` `/recall` side effect so
tool use warms working memory.
3. **soul** — cognition. `handle_request` dispatch → `handle_api_*` → engram
builtins. In HTTP mode it reaches engram over localhost; otherwise embedded.
4. **engram** — the graph. Spreading activation, Hebbian edges, snapshot
persistence.
For the user-facing chat pipeline (not tool calls), `/api/chat` enters
`layered_cycle` (soul.el) — L1 safety → L2 stewardship → L3 imprint — described
in `02-components.md §3c`.
## Production: GKE blue/green
Neuron prod runs on GKE cluster **`neuron-platform`** (Autopilot, us-central1),
namespace **`neuron-prod`**. Two Deployments, `neuron-mcp-blue` and
`neuron-mcp-green`, share one Service selector that names the *active slot*.
- **Deployments** (`deployment-blue.yaml` / `deployment-green.yaml`): one
container `soul`, image pinned by **digest** (not `:latest`) so Argo CD can't
drift the active slot to an untested build (see the pin comment in
`deployment-blue.yaml`). `strategy: Recreate` — the PVC is RWO so only one pod
can hold it at a time. Probes hit `/health` on `:7770`.
- **Service** (`service.yaml`): ClusterIP `neuron-mcp`, port 7770 → 7770,
`selector: {app: neuron-mcp, slot: blue}`. The blue/green script patches
`slot`.
- **Storage** (`pvc.yaml`): `neuron-engram-data`, `standard-rwo` (pd-balanced),
10Gi, RWO. Engram data is the single `snapshot.json` (~8MB active).
- **The swap** (`scripts/blue-green-deploy.sh`): (1) set image on the target
slot; (2) scale target to 1, wait for rollout; (3) **patch the Service selector
to the new slot** (traffic flip); (4) scale the old slot to 0. Imperative
`kubectl` for the live swap, then git-update the Argo manifests so a sync
doesn't revert replica counts.
- **Backup** (`backup-cronjob.yaml`): every 15 min, tar `/data` → GCS, keep 96.
### Resource sizing (learned the hard way)
`deployment-blue.yaml` documents the memory history in comments: idle soul RSS
~860Mi; the `beginSession` call (loads memories + backlog + preferences) spikes
past 1Gi and OOM-killed the pod mid-request (client socket closed). Current
setting: `requests = limits = 2Gi`, cpu 250m/1000m. This is *why* the cognitive
API projects/compacts payloads so aggressively (doc 02 §2, doc 01 axis 1) — the
memory ceiling is real and close.
## CI/CD
Two Gitea Actions workflows (`.gitea/workflows/`), serialized on a single GCE
runner (`concurrency: neuron-runner`).
- **`ci.yaml`** (push/PR to `main`):
- **build:** free disk → checkout → install gcc/libcurl/gcloud → download
`el-runtime-c`/`el-runtime-h` from Artifact Registry `foundation-prod`
(`elc`/`elb` intentionally **not** downloaded) → compile the committed
`dist/soul.c` directly: `cc -O2 -DHAVE_CURL dist/soul.c el_runtime.c -lssl
-lcrypto -lcurl -lpthread -lm -o dist/neuron` → `strip -s` → smoke test
`dist/neuron --help` → publish `neuron-soul@<sha8>` to AR (push only).
- **deploy** (push-to-main only): auth GCP → `get-credentials neuron-platform`
**determine idle slot** (the deployment at 0 replicas) → prepare artifacts
(soul binary + `elc` + runtime for the Docker build) → **clone the engram
repo** into `./engram/` (Dockerfile builds engram from source) → `docker
build`+push `neuron-soul:<sha>` → `scripts/blue-green-deploy.sh --image
--slot` → git-push updated infra manifests → `kubectl rollout status` →
verify `neuron-mcp` endpoints.
- **`deploy-gke.yaml`** (`workflow_dispatch` only, slot default `green`) — manual
rollback / forced-slot deploy without a rebuild; same auth → slot → docker →
blue-green → manifest-sync → verify steps.
The Docker image (`Dockerfile`) is a two-stage build: stage 1 compiles
`engram/src/server.el``engram.c``engram` binary via `elc` + `cc`; stage 2
is an Ubuntu 24.04 runtime (GLIBC 2.39 satisfies both binaries) with `soul` +
`engram` + `entrypoint.sh`.
## Config & secrets (high level)
Runtime configuration is injected as environment, sourced from a Kubernetes
Secret `neuron-soul-secrets` via ExternalSecret (ESO → GCP Secret Manager,
Workload Identity — no key files). From `deployment-blue.yaml`:
| Env | Meaning |
|---|---|
| `NEURON_PORT` | soul HTTP port (7770) |
| `NEURON_LLM_0_URL` / `_KEY` / `_FORMAT` | primary LLM endpoint (Anthropic format) |
| `SOUL_CGI_ID` / `SOUL_IDENTITY` | CGI id + identity seed (→ `seed_persona_from_env`, `soul.el:250`) |
| `NEURON_TOKEN` | auth token *(present in env; note the HTTP dispatch does not currently check it — doc 01 Divergence 3)* |
| `NEURON_API_URL` | self-callback URL (`http://neuron-mcp.neuron-prod.svc.cluster.local:7770`) |
| `ENGRAM_URL` / `ENGRAM_DATA_DIR` | `http://localhost:8742` / `/data` |
There is also an in-graph config surface: `ConfigEntry` nodes read/written by
`inspect_config` / `tune_config` (`neuron-api.el:616-653`) — runtime-tunable
persona/behavior keys stored *in* the engram rather than the environment.
> **Operational note to flag.** The `deployment-blue.yaml` image pin comment
> (dated Jul 2026) records that `:latest` resolved to an untested build lacking a
> `mem_save`/genesis-SIGSEGV fix, which is why the active slot is pinned to a
> digest. Any promotion must (a) rebuild a good soul and (b) update the digest in
> git so Argo CD and `blue-green-deploy.sh` agree. *(state as-of the manifests
> read; verify current slot before deploying.)*
+165
View File
@@ -0,0 +1,165 @@
# 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 under `foundation/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 —
- `elb` succeeds on arm64/macOS `ld`, but **fails on Linux** (duplicate strong
symbols), and
- `elc` uses 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.c` is produced by running `elc` over the **flattened import set**
> every module source in `soul.el`'s transitive import graph, concatenated with
> `import` lines stripped, compiled in one pass (`elc` hoists forward decls for
> all functions, so concat order doesn't affect correctness). `elb` on its own
> emits **per-module `.c` + a linked binary**, not the combined `soul.c`; it is
> the separate-compilation coordinator, and `elc soul.el` alone yields only the
> soul module. Because the amalgamation is regenerated only on demand, it can lag
> the `.el` sources: this PR regenerated it after it had fallen behind several
> source commits, and folded in the `inspect_graph` relevance-ranked projection
> (the `compact=1` self-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 the `cc` compile + the
> `dist/neuron --help` smoke test — it does **not** invoke the `tests/*.el`
> soul-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 regenerating `soul.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.c` on
a Mac (`elb`), commit it, then CI compiles it. Changing an `.el` file without
regenerating `soul.c` ships nothing.
- **The `.elh` files are your API map.** To see what a module exposes, read its
`.elh` — it's the generated `extern fn` list.
- **Memory/activation behavior often can't be changed from this repo.** The
volatile numeric core is in `foundation/el` `el_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 is
`foundation/el/engram`.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,148 @@
# M10 — Reification: Persisted First-Class Neighborhood Geometry
**Date:** 2026-08-12
**Branch:** `engram-tiered-storage` (worktree `/tmp/engram-tiered-wt`)
**Status:** Reification substrate = **GO** (additive, geometry-priming stays default-OFF).
Enabling geometry-priming = **NO-GO** (latency blocker removed; recall-quality benefit still absent).
**Live `:8742` never touched. Not pushed. Not tagged.**
This is the sequel to [M9 geometry priming](perf/engram-geometry-priming-profile.md), whose A/B
showed enabling the flag cost **3.2× median / 13× p90** latency for **no reliable quality gain**.
The M9 profile named two prerequisites before re-evaluating: **(1) amortize the per-query
descriptor cost** (eigensolve + paged reads on every `engram_activate`) and **(2) center recall
quality against the true store-wide mean**, not a per-query gathered-set approximation. M10 does both.
## What changed vs the original brief (important)
The task began as "reify the geometry into a durable **cache**." Will corrected this mid-flight, and
the correction is the design: **do not build a cache — reify densely co-wired neighborhoods into
first-class, PERSISTED store records** that survive restart, load on boot, and evolve via
supersede+provenance. *"A cache that lies is worse than a slow lookup."* This matches
[the cognitive-architecture design §2](engram-cognitive-architecture.md) (reification = durable
structure, not a fragile derived shortcut) and the memory-core discipline (evolve or forget, never
a stale canonical). Two modes, **no general cache layer**:
1. **Persist first-class** — reified/crystallized neighborhoods (the self; stable topology). The
geometry-priming **hot path reads these**. Never compute geometry on the activation path.
2. **Compute on the fly** — ad-hoc/transient domain geometries (viz, exploration). Uses the
existing M9 `engram_geometry_descriptor`, fresh each call, no storage. Occasional, so its cost
is acceptable.
## Storage schema (first-class, on the existing TLV node store — no new on-disk format)
A reified neighborhood is an ordinary store **node**, so it inherits durability, boot-load,
`store_supersede`, and adjacency for free (design §2: *structure all the way down under one rule*).
| Record | `node_type` | `emb` | `metadata` (`GEO1` text schema) | id |
|---|---|---|---|---|
| Centering frame | `GeoMeanFrame` | the true store-wide mean vector (persisted **once**) | `{}` | `geo-meanframe` |
| Neighborhood | `Neighborhood` | the **raw centroid** (prototype; centroid-ANN-able; `centered = emb meanframe`) | hub id · meanframe ref · scalars (`radius`, `total_variance`, `k_core`, `co_registration`, `n_embedded`) · axis **extents** · member list `{id → membership, centrality, core}` | `nbhd-<hub>-<built_at_ms>` |
Member links are also persisted as edges `relation="member"` (`nbhd → member`). The membership
`{id → w}` in the metadata is what priming reads; it is computed **centered against the persisted
true mean**, which is what resolves the M9 quality caveat.
**Detection (v1, honest).** Hub-anchored neighborhoods over the strong-edge hebb-weighted graph:
compute per-node weighted degree `Σ eff_w` (`eff_w = weight·(1+0.5·hebb)`, non-tombstoned /
non-inhibitory, ≥ `edge_min_weight`) over from+to edges; rank descending; **greedy non-redundant
cover** — reify each hub's descriptor once, skip a hub already a member (`w ≥ cover_membership`) of an
accepted neighborhood, stop at `max_neighborhoods` (default 128). Env-tunable (`min_weighted_degree`,
`max_neighborhoods`, refresh frac) without rebuild.
**Honest caveat — hebb ≈ 0 today.** On the current store there is ~no Hebbian potentiation, so
`eff_w` reduces to the **authored** edge weight; the detected neighborhoods currently reflect
authored graph structure, not learned co-activation. The design is unchanged and self-correcting
once hebb accrues (degree ranking and skeleton shift automatically).
## Boot isolation — why priming-OFF stays byte-identical
The persisted records carry embeddings (centroid / mean) but are **structure, not corpus content**.
On boot they are routed **out** of the resident activation graph into a dedicated resident reify
index, and member edges are skipped from adjacency (matched by the `nbhd-…` id convention, so no real
edge of any relation is affected). Consequences, all verified:
- store-wide mean, hub-degree scan, and the descriptor never admit a structural record as a member
(`geo_is_structural_id` / `node_type` guards);
- vindex / seed selection / results / embedding backfill are **identical** to a store that was never
reified ⇒ `ENGRAM_GEOMETRY_PRIMING` OFF is **byte-identical to M8/M9**.
The resident index is the **loaded form** of the durable records (like the resident node array is the
loaded form of node records, or adjacency of edges). Geometry is computed **once**, offline, and
**persisted**; boot only *parses* — it never recomputes geometry.
## Hot path
`ENGRAM_GEOMETRY_PRIMING=1` resolves the M8 seed set to the best persisted neighborhood — O(seeds)
membership hash lookup; miss → centroid-nearest against the loaded centered centroids — and applies
the M9 damp+prime logic from the persisted membership. **No geometry computed on activation.** Set
`ENGRAM_GEO_PRIMING_NOCACHE=1` to fall back to the M9 per-query descriptor (ad-hoc / A/B control).
## Results (A/B on COPIES; `store_reified` = 128 neighborhoods; live untouched)
**Correctness / durability**
| Check | Result |
|---|---|
| Reified records inert: `A_off` (reified store) vs `C_clean` (no records), id sequence all 15 queries | **MATCH** (byte-identical) |
| Restart survival: reify → checkpoint+close → fresh-process reopen | 128 `Neighborhood` + 1 `GeoMeanFrame` present; index loads **28 ms**; hub-seed lookup HIT **~1 µs** |
| Supersede/provenance: re-reify | prior same-hub record superseded (new timestamped id, old tombstoned); live count stable |
| Build warnings from `el_runtime.c` / `engram_geometry.c` (O2) | **0** (3 pre-existing in generated `engram.c`) |
| ASan/UBSan — module (write/load/lookup) + full server hot path, priming ON, 6 queries | **CLEAN** (all http 200, no trap) |
**Latency (median / p90, 15 queries)**
| config | median | p90 | vs A_off |
|---|---|---|---|
| C_clean (no records, OFF) | 78.6 ms | 82.7 ms | — |
| **A_off** (reified, OFF) | **77.4 ms** | **83.1 ms** | 1.00× |
| **B_on** (reified, **hot path / persisted**) | **81.7 ms** | **85.7 ms** | **1.06× / 1.03× — FLAT** |
| D_nocache (reified, ON, M9 per-query) | 255.3 ms | 872.3 ms | **3.30× / 10.5×** |
Reading a persisted first-class record instead of computing a per-query descriptor **removes the M9
latency blocker** (3.3×/10.5× → 1.06×/1.03×). There is no cold first-query penalty — the index loads
at boot.
**Recall quality (mean pairwise cosine, top-20, TRUE store-wide centered frame, stored vectors)**
| | OFF | ON | Δ |
|---|---|---|---|
| mean over 15 queries | 0.1721 | 0.1555 | **0.0166** |
| polysemous cues (9) | 0.1442 | 0.1441 | 0.0001 |
| queries where ON > OFF | — | — | **3 / 15** |
Even with the true mean and persisted structure there is **no reliable coherence gain** — slightly
negative on average, one sparse win (`self identity values` +0.078) and one notable regression
(`precision over brute force` 0.215). On dense polysemous cues the top-20 head is unchanged
(sub-threshold priming does not reorder it), so their coherence is flat.
## Verdict
- **Reification substrate: GO** (merge additive, geometry-priming default-OFF). It is the durable
first-class structure the memory core needs — persisted, boot-loaded, restart-surviving,
supersede-able, provably inert when OFF — and it turns priming into a flat-latency lookup.
Groundwork for the self-as-structure, multi-scale neighborhoods, and the §5 operators.
- **Enable geometry-priming: NO-GO (still).** The M9 *latency* objection is resolved; the *quality*
objection is not. Keep the flag default-OFF.
### Uncertainties / limits (memory core — flagged)
1. **hebb ≈ 0** ⇒ neighborhoods reflect authored edges, not learned co-activation. The quality result
is likely **understated**; re-evaluate after hebb accrues (or seed hebb from usage).
2. **Hub-anchored detection** can resolve a sparse query to a semantically mismatched neighborhood
(the 0.215 regression). Semantic-aware / co-registration-gated detection is future work.
3. **Top-20 coherence is insensitive** to sub-threshold priming on dense cues — it may be the wrong
metric for what priming does (it warms a floor, it does not reorder the head). A retrieval-utility
or disambiguation-accuracy metric would measure the intended effect better.
4. **v1 simplifications:** axis **direction** vectors are not persisted (extents only; directions
recomputable on the fly); member edges are persisted but inert to activation.
## Reversal
Fully additive and reversible.
- The binary is **byte-identical to M8/M9 when `ENGRAM_GEOMETRY_PRIMING` is unset/0** — the default.
- Reified records live only in stores you explicitly run the reify step against; a store that was
never reified behaves exactly as before (the resident index is empty ⇒ priming no-ops).
- To remove reified structure from a store: tombstone the `Neighborhood` / `GeoMeanFrame` records and
compact (they are ordinary nodes). No format change to undo.
- `ENGRAM_GEO_PRIMING_NOCACHE=1` restores the M9 per-query path for ad-hoc geometries / comparison.
@@ -0,0 +1,120 @@
# Engram Prior-Art Scan
*Dated 2026-08-12. This is an **engineering novelty read**, not legal advice. It is intended to feed a patent/whitepaper priority decision by identifying which claims sit in a clean lane and which are wholly or partly anticipated by existing work. A patent attorney and a formal search (USPTO/Google Patents/Espacenet) should confirm before filing. Where a claim is anticipated, this document says so plainly — the goal is honest scoping, not inflated novelty.*
## How to read this
Engram is an immutable, temporally-provenanced knowledge graph (tombstone-not-delete, supersede-not-overwrite; every node carries `created_at` + `superseded_at` + provenance). Over that graph it computes **geometry descriptors** `G = (centroid, covariance/ellipsoid, skeleton graph, membership weights)` in a joint embedding+graph space, and runs named operators (overlap, combine, distance, difference, analogy=Procrustes, traverse=geodesic) over them. A "self" is a reified geometry. Time-travel is a **query filter** (`created_at ≤ T < superseded_at`), not a transaction-log replay. "Self-occupation" reconstructs the self-geometry/knowledge-state as of `T`, locks it read-only, and converses with it with all post-`T` data masked.
The recurring pattern in the findings below: **every individual primitive is prior art.** Bitemporal reconstruction, memory streams, vector-symbolic composition, geometric KG operators, hindsight-leakage auditing, embedding drift detection — all exist and are well-published. Novelty, where it exists, lives in *specific integrated mechanisms*, and must be claimed narrowly against those primitives. Broad claims ("reasoning as geometry," "reconstruct what was known at T," "detect drift by distance") will be rejected on sight.
---
## (a) Hindsight-free decision auditing via immutable temporal knowledge-state reconstruction + future-masked occupation
**Claim (restated narrowly).** A method for auditing a past decision by (1) *constructively reconstructing* the exact knowledge-state a decision-maker held at time `T` from an immutable, tombstone+supersede provenance graph (selecting nodes live-at-`T` via `created_at ≤ T < superseded_at`), (2) recomputing the derived concept/self geometry over only that live-at-`T` slice, and (3) presenting that reconstructed state read-only, with all post-`T` nodes masked, as the sole evidentiary basis for judging the decision — such that the reconstruction is tamper-evident *because* nothing is ever overwritten or deleted.
**Closest prior art.**
- **HindsightBench** (Aug 2026) — a *black-box behavioral audit protocol* that detects parametric hindsight in time-indexed LLM decision tasks by manipulating the *asserted date* in the prompt (Revealed/Date-only/Masked/Transplant arms) and measuring behavioral shift. It explicitly does **not** reconstruct a knowledge state from provenance; it does not require corpus access or logprobs. It also reports that *instructed forgetting fails* — a 52% performance gap vs. true ignorance. https://arxiv.org/abs/2607.18867
- **Agentic Time Machine** (Jun 2026) — wraps web tools with a *leakage filter* that blocks post-cutoff or answer-revealing content before it reaches the agent (for forecasting benchmarks). https://arxiv.org/pdf/2606.21013
- **Zep/Graphiti** — bitemporal KG memory that can answer "what was the user's plan in January?" via point-in-time recall over event-time + ingestion-time. https://arxiv.org/abs/2501.13956 , https://www.getzep.com/ai-agents/temporal-knowledge-graph/
- **Auditable clinical-AI provenance frameworks** — immutable, timestamped "source-to-decision" trails recording the exact evidence shown to a clinician, for FDA transparency/liability. https://pmc.ncbi.nlm.nih.gov/articles/PMC12913532/
- **Hindsight-bias clinical literature** — retrospective case-note review is critically distorted by outcome knowledge; reconstructing the decision-maker's past perspective is the known mitigation. https://www.researchgate.net/publication/330427526 , https://kevinmd.com/2026/03/how-hindsight-bias-distorts-clinical-medicine.html
**What is genuinely differentiated.** The primitives — bitemporal point-in-time recall (Zep), immutable evidence trails (clinical AI), hindsight-bias mitigation by perspective reconstruction (clinical psych), leakage filtering (Agentic Time Machine), hindsight auditing (HindsightBench) — are all taken. What appears *unclaimed* is the specific combination: **constructive knowledge-state reconstruction from an immutable tombstone+supersede graph, used as the affirmative evidentiary substrate for judging a decision**, where correctness of the future-mask is *guaranteed by the data model* (a node is either live-at-`T` or it is not) rather than by prompt instruction or a heuristic content filter. HindsightBench and Agentic Time Machine both operate on the *model's* contaminated parametric memory and fight leakage behaviorally/heuristically; Engram sidesteps parametric leakage by making the *evidence set itself* provably `T`-clean and then recomputing geometry over it. The tamper-evidence-by-construction angle (append-only provenance ⇒ the reconstruction cannot be silently backdated) is also not present in the behavioral-audit line.
**Scoped-claim recommendation.** Claim the *pipeline*, not the goal: "reconstructing a decision-maker's knowledge-state as of `T` by selecting live-at-`T` nodes from an append-only tombstone+supersede provenance graph and recomputing derived concept/self geometry over that slice, then serving it read-only with post-`T` nodes masked as the evidentiary basis for decision review." Anchor on (i) constructive reconstruction from immutable provenance (not prompt-based date assertion), (ii) mask-correctness guaranteed by the data model, (iii) recomputed *geometry* (not just fact recall) as the reconstructed state. Do **not** claim "hindsight-free auditing" broadly, "point-in-time recall," or "immutable audit log" — all taken.
**Verdict: PARTIALLY TAKEN** (the goal and every primitive are taken; the constructive-reconstruction-from-immutable-provenance-as-evidentiary-substrate integration looks clean if narrowly scoped).
---
## (b) Drift detection via geodesic displacement of an anchored self-geometry
**Claim (restated narrowly).** A method that reifies an agent's "self" as a geometry descriptor with a *designated stable value-core anchor* and a mutable *periphery*, and classifies change by **decomposition**: extension of the periphery (core displacement ≈ 0) is scored as *growth*, whereas geodesic displacement of the *core* is scored as *corruption* — measured as geodesic distance between `self(now)` and the anchored `self(reference)` on the graph+embedding manifold.
**Closest prior art.**
- **Embedding / concept-drift detection** — mature field: distribution-distance of embeddings, per-label distributions (Drift Lens), K-core-distance from a dense "core" of baseline logic, growing average distance from baseline anchors as the drift signal. https://www.evidentlyai.com/blog/embedding-drift-detection , https://ieeexplore.ieee.org/iel7/9679833/9679835/09679880.pdf , https://www.sciencedirect.com/science/article/pii/S0925231225018624
- **Agent identity/goal-drift governance** — identity-hash functions over characteristic behavior for drift detection; "dominant" core persona preventing fragmentation; reflection-based long-term self-model evolution vs. short-term compensation. https://arxiv.org/pdf/2604.14717 (Layered Mutability) , https://www.researchgate.net/publication/397950116 (Agent Goal Drift in Stateful Systems)
- **Persistent Identity multi-anchor architecture** — explicit *anchors* for resilient agent identity/memory continuity. https://arxiv.org/pdf/2604.09588
**What is genuinely differentiated.** "Distance from an anchored baseline core = drift" is squarely prior art (K-core-distance, baseline-anchor distance growth). Agent-identity work already has *core-vs-drift* and *anchors*. What is not obviously present is the **core/periphery decomposition of drift into two distinct, oppositely-valenced outcomes on a reified self-*geometry*** — i.e., using a *geometric* self-model (centroid + covariance/ellipsoid + skeleton) where *growth* is formally "periphery ellipsoid expands while core centroid/anchor stays fixed" and *corruption* is "core centroid/anchor is geodesically displaced." Existing drift work treats all displacement as drift (bad); it does not carve legitimate growth from corruption via a *fixed value-core* on a self-geometry. The specific formalization — geodesic (graph-aware, non-Euclidean) displacement of a *pinned* value-core sub-geometry vs. free peripheral expansion — is the differentiator.
**Scoped-claim recommendation.** Claim "detecting agent value-corruption by measuring geodesic displacement of a *pinned value-core sub-geometry* of a reified self-geometry, while treating expansion of the peripheral geometry with a stationary core as non-corrupting growth." Emphasize (i) the self is a *geometry descriptor* with an explicitly designated immutable core anchor, (ii) growth vs. corruption is a *decomposition* (two signals), not a threshold on one distance, (iii) geodesic/graph-aware metric. Do **not** claim "drift detection by embedding distance" or "anchored baseline comparison" — taken.
**Verdict: PARTIALLY TAKEN** (distance-from-anchor drift is taken; the growth/corruption core-vs-periphery decomposition on a reified self-geometry is the narrow clean sliver — and it's the weakest/most crowded of the five).
---
## (c) Reasoning as composable geometry operations over a persistent temporally-provenanced graph
**Claim (restated narrowly).** A reasoning method in which inference steps are *explicit, named, first-class operators* (overlap, combine, distance, difference, analogy=Procrustes alignment, traverse=geodesic) applied to geometry descriptors computed over a *persistent, immutable, temporally-provenanced* knowledge graph — such that each reasoning step is individually inspectable, logged with provenance, and *replayable* against a past graph state; as distinct from implicit, unnamed activation/attention transforms inside a neural net.
**Closest prior art.**
- **Vector Symbolic Architectures / HRR / SDM** (Plate, Kanerva) — the canonical "algebra over vectors": binding, bundling/superposition, permutation, similarity; explicitly compositional/symbolic reasoning via vector operations. This is the strongest prior art for "named composable operators over vectors." https://www.emergentmind.com/topics/holographic-reduced-representations-hrrs , https://arxiv.org/pdf/2512.14709 (Attention as Binding)
- **Geometric KG query embeddings (Query2Box-lineage)** — reasoning as *named geometric operators* (projection, intersection) over box/region embeddings; geometric multi-hop reasoning; geometry-interaction KG embeddings. https://arxiv.org/html/2505.12369v2 , https://ojs.aaai.org/index.php/AAAI/article/view/20491/20250
- **Geometry-of-reasoning / embedding-space reasoning** — CoT as trajectories/flows in representation space; vector algebra + manifold geometry for deduction/induction/analogy. https://arxiv.org/abs/2510.09782 , https://arxiv.org/pdf/2504.02018
- **Riemannian knowledge manifolds** — geodesics as shortest semantic paths with a convergent geodesic solver. https://arxiv.org/html/2606.05907v2
- **Neuro-symbolic propose-verify** — explicit symbolic operations + solver verification.
**What is genuinely differentiated.** "Reasoning as composable vector/geometry operations" is *thoroughly* prior art — VSA/HRR own the compositional-operator framing; Query2Box owns named geometric operators (projection/intersection) for KG query answering; geodesic traversal over semantic manifolds is published. The individual operators (overlap≈intersection, distance, geodesic-traverse, Procrustes-analogy) each exist. The candidate differentiator is *not* any operator and *not* "geometry as reasoning" — it is the **coupling of the operator calculus to the immutable temporal-provenance substrate**: every operator input is a live-at-`T` geometry, every step is provenance-stamped, and the whole derivation is *replayable against a reconstructed past graph state* (i.e., operator-level temporal reproducibility + auditability). VSA/Query2Box run over static/atemporal embedding stores with no provenance and no time-travel; geometry-of-reasoning work is about a neural net's *internal* trajectory, not an external audited calculus. So the calculus itself is taken; "an *auditable, replayable* geometry calculus whose operands are temporally-reconstructed geometries" is the narrow lane.
**Scoped-claim recommendation.** Do **not** claim a "calculus of thought," "reasoning as geometry," or any specific operator (overlap/difference/geodesic/Procrustes) — all taken. Claim only the integration: "an audit trail in which each named geometric reasoning operator is provenance-stamped and its operands are geometry descriptors reconstructed from an immutable temporal graph as-of a query time, enabling deterministic replay of a reasoning derivation against a past knowledge-state." The defensible novelty is *temporal reproducibility + provenance of the operator chain*, not the operators.
**Verdict: TAKEN** (as "reasoning as composable geometry ops" — VSA/HRR + Query2Box + geometry-of-reasoning fully occupy it). Only the *auditable/replayable-over-immutable-temporal-substrate* framing survives, and it survives as a thin sliver of (a)/(e), not as an independent claim.
---
## (d) Self-occupation with engineered future-masking
**Claim (restated narrowly).** A method for reasoning *as* a past self: reconstruct the self-geometry and knowledge-state as of `T` from the immutable provenance graph, *rigorously enforce the `created_at ≤ T` cut at the data layer* (all post-`T` nodes structurally excluded, not instructed-away), lock the reconstruction read-only, and drive a conversational/reasoning session that is provably uncontaminated by hindsight — the mask being a property of the substrate, not of a prompt or the model's willingness to "forget."
**Closest prior art.**
- **HindsightBench** — establishes the *problem* rigorously and shows that prompt-level "pretend it's `T`" fails badly (instructed forgetting ≠ ignorance; 52% gap; date assertions obeyed but hindsight still leaks). This is the strongest adjacent art and, helpfully, *motivates* Engram's substrate-level approach rather than anticipating it. https://arxiv.org/abs/2607.18867
- **Agentic Time Machine** — closest *mechanism*: a leakage filter blocking post-cutoff content before it reaches the agent. But it filters *tool outputs* heuristically for a forecasting benchmark; it does not reconstruct and occupy a *reified past self/knowledge-state*. https://arxiv.org/pdf/2606.21013
- **Causal Agent Replay** — counterfactual replay/attribution of agent failures (replay, but not future-masked past-self occupation). https://arxiv.org/abs/2606.08275
- **Chronologically-consistent pretraining / counterfactual-anchored decoding / forget-retain logit adjustment** — model-internal mitigations of parametric leakage (named in HindsightBench). Different layer entirely.
**What is genuinely differentiated.** The field is actively fighting hindsight leakage at the *model* layer (pretraining, decoding, logit surgery) and at the *tool-output* layer (heuristic leakage filters). Engram's move is orthogonal and, per HindsightBench's own findings, addresses the failure mode the field just documented: **enforce the cut at the evidence/data layer via an immutable time-indexed graph, so the "past self" is a reconstructed read-only geometry whose accessible universe is exactly the live-at-`T` slice.** No source found reconstructs a *reified self-geometry* as of `T` and *converses with it* as a first-class object. The differentiators: (i) the masked entity is a *reconstructed self*, not just filtered context; (ii) mask correctness is structural (a node's `created_at` either satisfies the cut or the node is absent) rather than heuristic/instructed; (iii) it is tamper-evident via append-only provenance. Note the residual honesty caveat: if the *underlying LLM* used for the conversation has parametric hindsight, Engram's substrate-clean evidence does not fully neutralize it — the claim must be about the *evidence/state* being `T`-clean, which is the part Engram genuinely controls.
**Scoped-claim recommendation.** Claim "reconstructing a reified agent self-geometry and knowledge-state as-of `T` from an append-only temporal provenance graph and conducting a read-only reasoning/conversation session over it in which the accessible node universe is structurally restricted to the live-at-`T` slice (data-layer future-masking), yielding a `T`-clean evidentiary state." Lean on *structural* (data-model-guaranteed) masking vs. *instructed/heuristic* masking, and on the *reified-past-self* object. Explicitly scope to the evidence-state cleanliness (not a claim that the LLM has zero parametric leakage). Do **not** claim "prevent hindsight in LLMs" or "leakage filtering" broadly.
**Verdict: CLEAN LANE** (narrowly — data-layer/structural future-masking over a *reconstructed reified past self* is not occupied; adjacent art is behavioral-audit, tool-output filtering, or model-internal mitigation. This is the strongest of the five, precisely because HindsightBench shows the prompt-level approach fails and no one is doing substrate-level self-reconstruction).
---
## (e) Query→geometry temporal reconstruction with NO transaction logs
**Claim (restated narrowly).** Reconstructing a past knowledge-state as a *query-time filter* over immutable, per-node timestamped provenance (`created_at ≤ T < superseded_at`) followed by *recomputation of the geometry descriptors* over that slice — with **no event/transaction log and no periodic snapshots**; the immutable per-node provenance *is* the temporal record, and derived geometry is recomputed rather than stored/replayed.
**Closest prior art.**
- **Bitemporal databases (XTDB, et al.) / event sourcing** — "as-of" point-in-time queries over valid-time + transaction-time; immutability as audit log. Critically, XTDB describes reading bitemporal data as a process *"similar to event sourcing… playing through the history… in reverse system-time order"* — i.e., the mainstream bitemporal model *is* replay/reconstruction-through-history. https://v1-docs.xtdb.com/concepts/bitemporality/ , https://www.juxt.pro/blog/value-of-bitemporality/
- **Zep/Graphiti** — bitemporal (event-time T + ingestion-time T) fact validity + supersession chains; point-in-time recall. https://arxiv.org/abs/2501.13956
- **TKG reasoning frameworks / ElephantBroker-class runtimes** — "immutable fact store, all temporal weighting applied at query time; facts created after the query timestamp excluded, facts superseded after query timestamp treated as current; invalidate by writing `t_invalid` rather than delete." This is *very* close to Engram's filter and supersede/tombstone semantics. https://www.emergentmind.com/topics/temporal-knowledge-graph-reasoning-tkgr , https://arxiv.org/pdf/2603.25097
- **Numerous bitemporal/immutable-DB patents** (point-in-time reconstruction, retroactive/historical transactions). e.g. US 11,935,046; US 8,812,512 (via USPTO search) — a patent attorney must clear these.
**What is genuinely differentiated.** The *temporal filter* (created-before, superseded-after) and *tombstone-not-delete / supersede-not-overwrite* are **standard bitemporal KG practice** — Zep and the TKGR frameworks describe almost exactly this. So the reconstruction-by-filter primitive is TAKEN, and "immutable provenance instead of a mutable audit log" is TAKEN (that's the bitemporal value prop). The only thing that is *not* standard: what gets reconstructed is not just a set of *facts/edges* but a set of **derived geometry descriptors (centroid/covariance/skeleton/membership) recomputed over the live-at-`T` slice** — i.e., recompute-geometry-on-read rather than store-and-replay. Bitemporal DBs reconstruct *records*; Engram reconstructs *derived manifold structure*. The "no transaction log / no snapshot — provenance IS the temporal record, geometry is recomputed" framing is a design stance that is defensible only if paired with the *geometry recomputation*; on its own it is indistinguishable from XTDB/Zep.
**Scoped-claim recommendation.** Do **not** claim bitemporal reconstruction, "as-of" queries, tombstone/supersede, or "immutable provenance as audit record" — all squarely taken (Zep, XTDB, TKGR, patents). Claim only: "reconstructing a *derived geometry descriptor set* (centroid/covariance/skeleton/membership) for a past knowledge-state by recomputing it on-read over the live-at-`T` node slice, without storing per-`T` geometry snapshots or a geometry-mutation log." The novelty is *geometry-recompute-on-read over a bitemporal slice*, not the slice.
**Verdict: TAKEN** (as "query-filter temporal reconstruction over immutable provenance" — Zep + XTDB + TKGR own it outright). Only "recompute *derived geometry* on-read, snapshot-free" survives, and it is really a facet of (c)/(a) rather than an independent claim.
---
## Summary
| Claim | Verdict | Narrowest defensible (clean-lane) framing |
|---|---|---|
| **(a)** Hindsight-free decision auditing via reconstructed knowledge-state + future-masked occupation | **PARTIALLY TAKEN** | Constructive knowledge-state reconstruction from an *append-only tombstone+supersede* graph, used as the *affirmative evidentiary substrate* for decision review, with mask-correctness guaranteed by the data model and tamper-evidence by construction — not prompt/date-assertion (cf. HindsightBench) and not tool-output filtering (cf. Agentic Time Machine). |
| **(b)** Drift as geodesic displacement of anchored self-geometry | **PARTIALLY TAKEN** | Growth-vs-corruption *decomposition* of change on a reified self-*geometry* via geodesic displacement of a *pinned value-core sub-geometry* vs. free peripheral-ellipsoid expansion. (Crowded; weakest lane.) |
| **(c)** Reasoning as composable geometry ops over a temporal graph | **TAKEN** | Only survivor: *provenance-stamped, replayable* operator chain whose operands are geometries reconstructed as-of a query time (temporal reproducibility of the derivation) — never the operators or "geometry as reasoning" themselves. |
| **(d)** Self-occupation with engineered future-masking | **CLEAN LANE** (narrow) | Reconstruct a *reified past self-geometry* and converse with it read-only, with the accessible node universe *structurally* restricted to the live-at-`T` slice (data-layer masking) — not instructed forgetting (which HindsightBench shows fails) and not heuristic content filtering. |
| **(e)** Query→geometry temporal reconstruction, no transaction logs | **TAKEN** | Only survivor: recompute *derived geometry descriptors* on-read over the live-at-`T` slice, snapshot-free — never the bitemporal filter, tombstone/supersede, or "immutable provenance as record," all of which Zep/XTDB/TKGR own. |
## Overall posture
**Broad claims over primitives will be rejected.** Each of the five candidate claims decomposes into (i) a primitive that is unambiguously prior art and (ii), in three of five cases, a thin integrated mechanism that appears unclaimed. The prior art is strong and specific: Zep/Graphiti and XTDB own bitemporal point-in-time reconstruction and supersession (kills the broad reads of (a) and (e)); VSA/HRR and Query2Box own composable geometric/symbolic operators (kills the broad read of (c)); embedding concept-drift and agent-identity-anchor work own distance-from-baseline drift (kills the broad read of (b)); and HindsightBench + Agentic Time Machine own hindsight *auditing* and *leakage filtering* (bound (a) and (d)).
**Novelty lives in the specific integrated mechanisms, narrowly scoped.** The two genuinely defensible ideas are: **(d) substrate-level future-masking of a reconstructed, reified *past self*** — which is the strongest, and is *strengthened* by HindsightBench's finding that the prompt-level approach everyone else uses fails by ~52%; and **(a) constructive knowledge-state reconstruction from immutable provenance as the affirmative evidentiary basis for decision auditing**, distinct from behavioral probing. The unifying, defensible thread across (a)/(d)/(c)/(e) is *structural guarantee by the immutable data model* — the future-mask, the tamper-evidence, and the operator-chain replayability are all properties of the append-only substrate rather than of prompts, heuristics, or model cooperation. That "guaranteed-by-construction" framing is the honest core of any priority filing. Claims (c) and (e) should be folded in as *facets* (auditable/replayable geometry over reconstructed slices) rather than filed as standalone claims, and (b) should be filed only if the core/periphery decomposition can be made rigorous, since the surrounding drift-detection art is dense.
*Caveats for the filing team: (1) this scan covered academic/product/blog prior art via web search, not a formal patent search — several bitemporal/immutable-DB patents surfaced (e.g. US 11,935,046; US 8,812,512) and must be cleared on Google Patents/Espacenet/USPTO. (2) Claim (d)'s guarantee is that the *evidence-state* is `T`-clean; it does not by itself neutralize parametric hindsight in whatever LLM reasons over that state — scope the language accordingly. (3) Dates on several 26062607 arXiv preprints are very recent; confirm publication precedence relative to Engram's earliest documented conception date.*
@@ -0,0 +1,97 @@
# Perf Profile — M9 Geometry Priming (ENGRAM_GEOMETRY_PRIMING)
**Date:** 2026-08-12
**Branch:** `engram-tiered-storage`
**Change:** `ENGRAM_GEOMETRY_PRIMING` (default OFF) in `el_runtime.c` `engram_activate` + `engram_geometry.c`
**Method:** A/B over 15 representative queries against a **copy** of the recovered store
(`~/.neuron/engram/.neuron.egm.disabled`, ~4190 embedded nodes, 768-d nomic-embed-text),
throwaway HOME, ports 48799/48800. **Live `:8742` never touched.** `engram.c` (folded from
`server.el`) reused byte-identical across M8 and M9, so the only variable is `el_runtime.c`.
Three configs: **A** = M9 flag OFF · **B** = M9 flag ON (`=1`) · **C** = pre-M9 M8 baseline binary.
---
## Build
| Artifact | Result |
|---|---|
| M9 `-O2` link (`… engram_geometry.c … -lssl -lcrypto -lcurl -lpthread -lm`) | rc=0, 499,720 B arm64 |
| ASan/UBSan link (`-fsanitize=address,undefined -O1`) | rc=0, 1,945,616 B |
| Warnings from `el_runtime.c` / `engram_geometry.c` | **0** (3 pre-existing `-Wparentheses-equality` in generated `engram.c` only) |
| `nm`: `engram_geo_mean_build`, `engram_geometry_descriptor` | present (T); `eg_geometry_priming_on` inlined (static-local `.cached` present in both binaries) |
> Note: the bare `cc … -lm` link fails with undefined `_curl_*``el_runtime.c` uses libcurl for
> the ollama embedder. The canonical link must include `-lssl -lcrypto -lcurl` (per `link.sh`).
---
## Latency (wall-clock, `curl -w %{time_total}`, 15 queries)
| config | median | p90 | min | max |
|---|---|---|---|---|
| **A — M9 OFF** | **77.8 ms** | 80.5 ms | 71.1 | 84.2 |
| C — M8 baseline | 76.0 ms | 81.2 ms | 71.4 | 91.4 |
| **B — M9 ON** | **249.6 ms** | **1039.2 ms** | 169.2 | **1256.3** |
- **OFF adds zero cost:** 77.8 ms vs M8 76.0 ms — within noise. The flag is free when unset.
- **ON regresses hard:** **3.21x median** (+171.8 ms), **~13x p90** (80 → 1039 ms), max **1.26 s**.
- The warm-cache path (global mean already built) is ~0.5 s; the cold path pays the full
`engram_geo_mean_build` scan (O(N·dim) over ~4190 × 768). The persistent per-query cost is the
**descriptor** itself — covariance eigensolve over up to `max_members` (400) × 768-d plus one
`store_get_node` **paged read per member** — run on *every* activation while the flag is ON.
---
## Retrieval quality (the win it was supposed to buy)
**Coherence** — mean pairwise cosine in centered space, top-20 by activation strength
(node embeddings re-derived via nomic-embed-text; centered against the mean of the gathered
result set — the *true* store-wide mean is not exposed by the API, flagged as an approximation):
| | OFF | ON | Δ |
|---|---|---|---|
| mean over 15 queries | 0.1067 | 0.1114 | **+0.0047 (noise)** |
| queries where ON > OFF | — | — | **4 / 15** |
Two real sparse-cue wins (`self identity values` +0.118, `hebbian learning edges` +0.064), but the
**polysemous cues — the disambiguation target — are mostly flat or down.**
**Disambiguation** — no clean "scope to one sense" pattern on polysemous cues. Additions/drops are
small (±2..8 of 300-item sets) and not sense-coherent (e.g. `memory` gains some on-domain nodes but
also infra items; `core` similar).
**Count shift:** ON adds sub-threshold neighbors to sparse cues (+3..+4) and trims a few from dense
polysemous cues (1..3) — consistent with priming warming sparse neighborhoods and damping
off-domain seeds on dense ones, but the net does not move measured coherence.
---
## Correctness / safety (all pass)
| Check | Result |
|---|---|
| Byte-identical: **A (OFF) == C (M8)** result id sequence + order, all 15 queries (incl. 301/294/263-item sets) | **PASS** (only wall-clock ACT-R fields differ; `activation_strength` max \|Δ\| = 2e-5) |
| WM `promoted` ≤ 24 under ON | holds (exactly 24 on dense cues) |
| Queries with results under OFF → empty under ON | 0 |
| Crash / hang under ON | none (max hops = 1) |
| ASan + UBSan under ON (cold build + warm descriptor paths) | **CLEAN** — no report |
---
## Conclusion
- **Deploy default-OFF binary: GO.** Byte-identical to M8, zero cost off, clean build, sanitizer clean.
- **Enable flag: NO-GO (for now).** 3.21x median / ~13x p90 latency for no reliable quality gain
(coherence +0.0047 mean = noise; no clean disambiguation). Correctness/safety are fine — it simply
does not earn its cost. **This is a cost/benefit NO-GO, not a defect.**
### Prerequisites before re-evaluating the flag
1. **Amortize the descriptor cost.** The per-query geo-mean build + eigensolve + paged reads
dominate. Cache the neighborhood descriptor (it is the M10 cell-assembly cache's job) and/or
compute geometry periodically/off-hot-path rather than on every `engram_activate`.
2. **Center against the true store-wide mean** (the `GeoMeanCache` already computes it) rather than
a per-query gathered-set approximation, and re-measure coherence — the current signal may be
understated by the approximation.
3. **Re-tune** `ENGRAM_GEO_SEED_LO` / `PRIME_SCALE` / `PRIME_MAX` and re-measure only after (1),
so tuning is not chasing latency noise.
@@ -0,0 +1,140 @@
# Engram Tiered Storage — M8 Performance Profile (milestone-0 sample)
**Milestone:** M8 (ANN wired into `engram_activate` seed selection). Trunk =
worktree `/tmp/engram-tiered-wt`, branch `engram-tiered-storage`, HEAD `1507614`.
**Date:** 2026-08-12. **Author:** first full-binary build + profile of the tiered trunk.
This is **sample zero** of an accumulating per-milestone profile series (see the
BUILD-LEDGER "keep performance telemetry CONTINUOUSLY" decision). Compare future
milestones (M9, M10, …) against these numbers.
## Methodology (read this before trusting a number)
- **On a COPY, never live.** All runtime measurements used a read-only copy of the
live store booted on a **non-live port (:8798)** with a **throwaway `$HOME`**. The
live engram service (:8742, `~/.neuron/engram`) was never touched.
- **Two store substrates were used:**
1. *Live-egm copy* (`neuron.egm` 458 MB + `neuron.wal` 44 MB, copied read-only) —
**this substrate crashes both the M8 binary and the live binary on boot** (see
Integration Findings). Unusable for runtime measurement.
2. *Clean import* — a dir seeded with only `snapshot.json` (65 MB, stable 06:10),
which the binary imported into a **fresh 59.9 MB `neuron.egm`**. All healthy
runtime numbers below are from this substrate (real graph content, healthy store).
- **Build machine:** Apple Silicon (arm64), macOS. Native `cc -O2` compile; fold done
in a memory-capped (`--memory=3g --memory-swap=3g`, no swap) `linux/amd64` container
running `elc-linux-amd64`.
- Hardware/thermals uncontrolled; single run per metric unless noted. Treat as
order-of-magnitude, not benchmark-grade, except the module benchmark (Test suite).
## Build
| Metric | Value |
|---|---|
| Fold input | `engram/src/server.el` (44,273 B El, no imports) |
| Fold output | `engram.c` (30,855 B, 645 lines C), `ELC_EXIT=0`, **0 fold warnings** |
| Fold time (pure elc) | sub-second (server.el is small, importless) |
| Fold container wall | ~38 s (dominated by one-time `apt-get install libcurl4` in the throwaway container; the elc invocation itself is <1 s) |
| Compile | `cc -O2 -DHAVE_CURL engram.c el_runtime.c engram_store.c engram_vindex.c -lssl -lcrypto -lcurl -lpthread -lm` |
| Compile time | **1.83 s** wall |
| Compile warnings | **3**, all `-Wparentheses-equality` in the *folded* `engram.c` (El if-expr codegen emits `if ((x == 0))`); cosmetic. `el_runtime.c` / `engram_store.c` / `engram_vindex.c` compiled **0 warnings** — notably none around the M8 deferred `free(e_eff)` or the vindex integration. |
| Binary | **482,008 B (471 KB)** Mach-O arm64 executable |
| ANN linkage verified | `nm`: `vindex_search`, `vindex_build_from_store`, `eg_vindex_sync`, `_eg_vindex`, `store_scan_nodes`, `engram_store_boot`, `engram_activate` all present |
## Boot & footprint (clean-import substrate)
| Metric | Value |
|---|---|
| Boot from healthy `neuron.egm` | **~2 s** to listening |
| Boot from `snapshot.json` (one-time import + fresh egm) | **~9 s** |
| node_count | **13,036** (matches ledger import-dedup: 13,038 snapshot 2 dup-id entries) |
| edge_count / layer_count | 43,402 / 5 |
| embedded_count | 4,190 |
| Fresh egm size | **59.9 MB** (vs the live egm's bloated 458 MB — see Findings) |
| RSS after boot | **126.2 MB** (whole 13k-node/43k-edge/4,190-emb graph resident + fresh egm) |
## Activation latency — q="bullshit" (clean-import substrate)
50 sequential `GET /api/activate?q=bullshit&limit=10&depth=3`:
| Metric | Value |
|---|---|
| p50 | **34.35 ms** |
| p95 | **35.26 ms** |
| min / max | 33.50 ms / 9,886 ms |
| Sample | n=50 |
- The **max = 9.9 s is the first call only** — a cold query-embedding fetch
(`eg_embed_fetch` → Ollama `nomic-embed-text`, cold model load). All subsequent
calls hit the single-slot query-embedding cache (`_eg_qcache`) → **34 ms steady state**.
- **This 34 ms is the lexical/spread path, NOT the ANN seed path.** The store's 4,190
embeddings were generated by the live neuron's native embedding model; the harness's
`nomic-embed-text` query vectors are a **different vector space**, so no candidate
cleared `ENGRAM_EMBED_SEED_MIN=0.60` (`act-stats`: `dup_seeds:0`, `ctx_cos:-2.000`
sentinel) → results empty, ANN discovery produced no admitted seeds. Environmental
(embedding provenance), **not** an M8 defect. The ANN wiring still *executed*
(embed fetch succeeded, `embed_breaker_open:0`; `eg_vindex_sync` + seed block +
`free(e_eff)` all ran) **without crashing** on the real store.
## KEY M8 METRIC — ANN vs O(n) seed selection (authoritative)
Because the HTTP path can't exercise ANN seeding without embedding-space parity, the
authoritative ANN-vs-exact-scan numbers come from the **module benchmark**
(`engram/test/run_vindex_tests.sh`, PASS 1, optimised), which measures the exact
`vindex_search` code the M8 wiring calls, at full size:
| N (768-dim vectors) | Brute-force (O(n)) | ANN (HNSW) | **Speedup** |
|---|---|---|---|
| 5,000 | 3.475 ms/query | 0.353 ms/query | **9.8×** |
| 20,000 | 13.809 ms/query | 0.698 ms/query | **19.8×** |
- **recall@10 = 0.9365** at `ef_search=128` (gate ≥0.90 — **PASS**). Lower ef trades
recall for latency: ef=64→0.844, ef=32→0.750, ef=10→0.625.
- **Determinism:** two independent seeded builds give byte-identical query results.
- **`vindex_build_from_store`** over a real `engram_store`: inserts exactly the
embedded nodes, top-1 resolves to the correct node id at ~0 distance.
- **HNSW build cost (single-threaded, note for boot/index-build budgeting):**
5,000 vectors ≈ 1535 s, 20,000 vectors ≈ 75 s. In-process the index is built
**lazily on first activation** (`eg_vindex_sync`) and grown incrementally; the
M8 seed block only fires once `vindex_size ≥ ENGRAM_EMBED_SEED_K`. At the real
store's 4,190 embedded nodes this is a **one-time few-second first-activation
cost** — worth watching as the embedded set grows (a future milestone may want
to build the index at boot or persist it via `vindex_save`/`vindex_load`).
## Seed-set parity note (why there is no runtime A/B toggle)
M8 has **no ANN on/off env flag** by design (`ENGRAM_EMBED_SEED_K` is a compile-time
constant). The exact O(n) cosine scan is **preserved verbatim** and "tops up" any seed
slot the ANN leaves unfilled; every ANN candidate is admitted through the *identical*
cosine/dedup/threshold gate the exact scan uses. So ANN changes only *which nodes are
discovered and how fast*, never the final seed set — parity is **structural**, not
A/B-tested via flag. Recovered-behaviour-when-index-absent is the pre-M8 exact scan.
## Integration Findings (first full build of the tiered trunk)
1. **CRITICAL / pre-existing (NOT M8): `btree_insert` stack-buffer-overflow on
opening the live 458 MB `neuron.egm`.** `SIGABRT` (`__stack_chk_fail`) via
`btree_insert ← btree_put ← edge_place/apply_edge_put ← engram_open ←
engram_store_boot`. Reproduces on the M8 binary **and** the deployed live binary —
**the live engram :8742 was crash-looping (18 crash reports 16:18→17:44 on this
date; service refusing connections).** A clean import into a fresh 59.9 MB egm does
**not** crash (43k edges load fine), so the trigger is the specific pathological
live store: 458 MB (8× the healthy 59.9 MB) from a day of churn/tombstones with no
M5 compaction + a 44 MB un-checkpointed WAL replayed on open. The bug is in
`engram_store.c` (the edge B-tree / WAL-redo path), **upstream of everything M8
touched** (M8 lives in `el_runtime.c::engram_activate`). Fix required before any
re-cutover; `btree_insert` must bound-check regardless of on-disk content.
2. **M8 deferred `free(e_eff)`** (the flagged memory-management concern): compiled
warning-free, and the wired path executed end-to-end over HTTP on the real store
(with a real query embedding) with **no crash / no new crash report** — no
double-free or use-after-free observed. Freed on all early-return paths and exactly
once post-seed-selection.
3. **Write durability + retrieval-fix dedup**: create → checkpoint → clean SIGTERM →
restart → node found **by id and by search** (node_count 13,036→13,037 preserved).
## Caveats
- All on a copy; healthy-substrate numbers are from a re-imported store, not the live
paged store (which is currently un-bootable — Finding 1).
- Single-run metrics; no thermal control.
- Embedding-space mismatch prevented a real semantic `q=bullshit` activation in this
harness; the ANN speedup number is the module benchmark, which is the correct gauge.
@@ -0,0 +1,98 @@
# Engram Recovery, Cutover & Build — Decisions & Reversal Runbook
**Date:** 2026-08-12 · **Owner:** Neuron (for Will) · **Status:** LIVING (finalized with actuals after cutover)
Per Will's standing rule: every change ships with what's happening, the decisions + rationale, and an
exact path for reversal. **Advance authorization (Will, 2026-08-12):** promote to prod for Will's
own testing — **NOT the website, no customer may see any of this.** Customer-facing surfaces stay frozen.
---
## 1. Scope & guardrails
- **In scope (his testing env):** the local engram service `:8742` on Will's Mac, and the
`engram-tiered-storage` build. Internal testing only.
- **FROZEN — do NOT touch:** the marketing website, any customer-facing Cloud Run service, any public
deploy. No customer exposure. Broader prod promotion (beyond Will's local testing) requires an
explicit, separate go.
## 2. What's changing (and why)
| # | Change | Rationale | Reversal (see §5) |
|---|--------|-----------|-------------------|
| 1 | Crash-loop stopped (`launchctl bootout ai.neuron.engram`) | 42 crashes/day; re-running a crashing WAL-replay over the store is the only corruption risk | R1 |
| 2 | btree fix committed `9e28def` (branch `engram-tiered-storage`) | Root cause: `int_max_keys` /8 vs /16 → node overflow → stack smash | R2 |
| 3 | Rebuild `:8742` store from `:7770` working-store fresh export + fixed binary; cut over | Working store (~12,825, incl. today) is the truth; bloated egm lost ~1,500 nodes | R3 |
| 4 | Tag `engram-tiered-m8` after green cutover | The fix makes M8 boot real data | R4 |
| 5 | (Forthcoming) M9 / M10 / M-INTEROCEPTION build | The cognitive architecture; each staged + tagged separately | per-milestone |
## 3. Key decisions
- **Recover from `:7770` (truth), NOT the bloated `:8742` egm.** The egm recovers only 11,532 nodes
(dropped ~1,500 during the crash-loop). The working `:7770` store has the full, current set.
- **NOT the stale `snapshot.json` (06:10).** It predates today's ~30 design memories. Using it would
silently lose today's work.
- **HARD durability gate:** the recovery does NOT cut over until a fresh `:7770` export is verified to
contain today's memories (node IDs `5a649121`, `47be987f`, `fcce29d0`, `d02ad0f6`).
- **Delete nothing.** All prior stores/binaries/snapshots retained as reversal assets.
- **`:7770` is read/export-only** during recovery — never modified, never killed.
## 4. Reversal assets (backups)
- `~/.neuron/engram-incident-backup-20260812-180043/` — pre-fix egm(458MB)+wal(44MB)+snapshot(65MB), APFS-cloned.
- `~/.neuron/engram-recovery-export-<ts>.json` — fresh `:7770` export (created Phase 1; the durable truth).
- Moved-aside originals: `neuron.egm`/`neuron.wal` renamed (kept) during cutover.
- `~/.neuron/engram/snapshot.json` (06:10) · `snapshot.golden.json` (Aug 3) · `.sync-export.json` (16:50).
- Git: branch `engram-tiered-storage`, fix `9e28def`; prior `~/.neuron/bin/engram` binary retained.
## 5. Reversal paths (exact)
**R1 — undo "crash-loop stopped":** `launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.neuron.engram.plist`.
(NOT recommended with the *unfixed* binary — it will crash-loop again.)
**R2 — revert the code fix:** `git -C /tmp/engram-tiered-wt revert 9e28def`.
(NOT recommended — reintroduces the overflow crash. The fix is defensive and correct.)
**R3 — roll back the live cutover to a safe state:**
1. `launchctl bootout gui/$(id -u)/ai.neuron.engram`
2. Restore the prior store: move the freshly-imported store aside; restore the moved-aside originals
OR the backup dir contents into `~/.neuron/engram/`.
3. Restore the prior binary if it was replaced: copy the retained `~/.neuron/bin/engram` back.
4. `launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.neuron.engram.plist`; verify.
- **Note:** restoring the *pre-fix* binary reintroduces the crash. The genuinely safe rollback state is
**fixed binary + the recovery export** (which is the target state). To fully abort: `bootout` and
leave `:8742` DOWN — **your memory is safe and served by `:7770` regardless.**
**R4 — undo the tag:** `git tag -d engram-tiered-m8` (and delete remote tag if pushed).
**If `:7770` is ever affected** (it should not be — export/read-only): it holds the truth; if needed,
rebuild from `~/.neuron/engram-recovery-export-<ts>.json`.
## 6. Validation (how Will tests "here")
After cutover: `:8742` listens; node_count ≈ 12,825; today's memories findable by-id AND by-search;
no crash-loop over ≥30s; write-survives-restart. Then Will can exercise retrieval/writes on his machine.
## 7. Customer-facing status
**UNTOUCHED.** No website, no customer service, no public deploy changed by any step here.
---
## 8. ACTUALS — recovery COMPLETE & VERIFIED (2026-08-12 ~19:18)
- **Durability gate PASSED.** Proof the stale files were unusable: the 16:50 `.sync-export.json` and
06:10 `snapshot.json` contained **zero** of the 4 canary IDs. Fresh export path used:
`GET :7770/api/graph/edges` → sidecar (never touched canonical snapshot).
- **Durable truth exports (sha256-verified):**
`~/.neuron/engram-recovery-export-20260812-190712.json` (12,734 nodes, all canaries) and
`~/.neuron/engram-recovery-export-precutover-20260812-191634.json` (12,704 nodes, all canaries).
- **Fixed binary:** container-capped fold of current `server.el` + fixed `engram_store.c` (`9e28def`),
arm64, sha256 `feafd0c9…`, 0 errors.
- **Cutover:** binary+plist backed up to `~/.neuron/backups/pre-recovery-cutover-20260812-191844`;
bloated originals renamed `*.pre-recovery-*` (NOT deleted); clean egm placed; fixed binary deployed;
`launchctl bootstrap`.
- **Live state (independently verified):** `:8742` up (pid 31277), `/health` = ok,
**node_count 12,679 / edges 43,466 / embedded 4,290**, all 4 design canaries findable by-id AND
by-search, **write-survives-restart PASS**, **no crash-loop** (no crash reports post-cutover).
- **`:7770` truth daemon:** untouched (read-only GETs only), still serving.
- **Tag:** `engram-tiered-m8` created on `9e28def` (LOCAL only — not pushed).
- **Reversal state:** all backups + the moved-aside bloated egm retained. Safe abort at any time:
`launchctl bootout``:8742` down → memory still served by `:7770`. Full restore per §5.
**Status: incident CLOSED. Memory recovered, durable, live. Customer-facing: untouched.**
@@ -0,0 +1,130 @@
# Runbook — M9 Geometry Priming: Cutover & Reversal
**Date:** 2026-08-12
**Component:** engram activation (`lang/runtime/el_runtime.c``engram_activate`)
**Branch:** `engram-tiered-storage`
**Flag:** `ENGRAM_GEOMETRY_PRIMING` (env, **default OFF = current M8 behavior, byte-identical**)
**Blast radius if wrong:** the core recall path of Will's live memory. Treat with according care.
---
## 1. What changes
This is the first behavior-changing step that touches the **core recall/priming** path.
It wires the M9 **mean-centered relational-neighborhood geometry** (`engram_geometry.c`,
shipped commits `2a4c5c6` foundation + `8cae0f9` centering) into `engram_activate`
**seed selection**, and it does so **behind a reversible env flag that defaults OFF**.
- **Flag OFF (default):** `engram_activate` runs the exact M8 code path. The new code is a
single `if (eg_geometry_priming_on() && …)` block that short-circuits on the first term,
plus a few unused static helpers and one zero-initialized counter. **No behavioral change.**
- **Flag ON (`ENGRAM_GEOMETRY_PRIMING=1`):** after M8 produces its ANN seed set, the
**centered** geometry of that neighborhood is computed and used to, **composing with**
(never replacing) M8's ANN candidate generation:
1. **Damp off-domain seeds** — each M8 seed's activation is scaled by a **damp-only**
factor `lo + (1-lo)·membership ∈ [lo, 1]` (default `lo=0.5`). The neighborhood anchor
(membership→1) is unchanged; seeds that are semantically off-domain **in the centered
frame** lose weight. This is the disambiguation win. It can only *sharpen*, never amplify.
2. **Prime the neighborhood sub-threshold** — descriptor members not already seeded get a
**warm floor** `activation = membership · scale` (default `scale=0.08`, strictly below the
WM promotion gate `0.15`), capped at `ENGRAM_GEO_PRIME_MAX` (default 32), ISE nodes skipped.
They enter the frontier so a warm gradient spreads one hop, then dies at the BFS `0.02`
cutoff. **Safe because the BFS keeps the max** (`el_runtime.c` `if (!reached || new_act >
best_bg)`): priming only *raises a floor*, it can never cap a stronger legitimate activation.
### Why default-OFF makes deploying the binary behavior-neutral
Because every line of the new logic is gated behind `ENGRAM_GEOMETRY_PRIMING`, **deploying the
new binary with the flag unset is behavior-neutral** — it is the M8 activation path, verified
byte-identical in the A/B (flag-OFF promoted-node sets equal the pre-M9 M8 binary's, per-query).
Enabling the geometry is then a **single reversible flag flip**, not a redeploy.
---
## 2. The flag
| Env var | Default | Effect |
|---|---|---|
| `ENGRAM_GEOMETRY_PRIMING` | unset / `0` | **OFF** — exact M8 behavior. |
| `ENGRAM_GEOMETRY_PRIMING=1` | — | **ON** — centered-geometry seed damping + sub-threshold priming. |
| `ENGRAM_GEO_SEED_LO` | `0.5` | Seed damp floor (factor ∈ [LO,1]). `1.0` disables damping. |
| `ENGRAM_GEO_PRIME_SCALE` | `0.08` | Warm-floor scale; clamped `(0, WM_gate=0.15)`. |
| `ENGRAM_GEO_PRIME_MAX` | `32` | Max primed members per activation (0 disables priming). |
The flag is read **once** per process (cached), so enabling/disabling requires a **process
restart** of the engram service — it is not hot-togglable within a running process.
---
## 3. How to enable live (deliberate, reversible)
> Precondition: the default-OFF binary has already been deployed and is running the M8 path
> healthily (behavior-neutral deploy). Do this only with Will present, per the standing rails.
1. **Snapshot first** (always, before any activation-behavior change):
`~/.neuron/backups/pre-geometry-priming-<ts>/` ← copy `neuron.egm`, `neuron.wal`,
the current `engram` binary, and `ai.neuron.engram.plist`.
2. Add `ENGRAM_GEOMETRY_PRIMING=1` to the engram service environment
(`ai.neuron.engram.plist` `EnvironmentVariables`).
3. `launchctl bootout gui/$(id -u)/ai.neuron.engram``launchctl bootstrap …` (restart so the
flag is re-read).
4. **Verify:** service comes up serving the same node count; `/api/act-stats` shows sane WM
(promoted ≤ 24); spot-check 34 real queries return coherent results; watch one heartbeat
cycle for crashes/latency. The `geo_primed` counter (if surfaced) should be > 0.
---
## 4. Rollback (exact steps)
Rollback is a **flag flip**, not a data operation — the store is untouched by enabling the flag,
and priming is a read-mostly, bounded, sub-threshold addition.
**Fast path (preferred) — disable the flag:**
1. Remove `ENGRAM_GEOMETRY_PRIMING` (or set `=0`) from `ai.neuron.engram.plist`.
2. `launchctl bootout … && launchctl bootstrap …`.
3. Verify: service healthy, activation is the M8 path again. **Done** — no data change to undo.
**Full path (only if the binary itself is suspect) — redeploy prior binary:**
1. `launchctl bootout gui/$(id -u)/ai.neuron.engram`.
2. Restore the prior `engram` binary from `~/.neuron/backups/pre-geometry-priming-<ts>/`.
3. Restore `ai.neuron.engram.plist` from the same backup (flag absent).
4. `launchctl bootstrap …`; verify node count + a self-traversal + write-survives-restart.
5. If (and only if) the store was somehow mutated: restore `neuron.egm` + `neuron.wal` from the
backup. **Note:** enabling the flag does not write geometry to the store, so this step is
expected to be unnecessary — the primed activations are per-call and non-persistent beyond the
ordinary `background_activation`/WM write-back that M8 already does.
**Rollback triggers:** any crash/hang in `engram_activate`; WM promotion count exceeding the cap
or collapsing; a measured recall/coherence regression vs the OFF baseline; unacceptable latency
increase; any ASan/UBSan report under the flag.
---
## 5. Reversibility guarantees (why this is low-risk to deploy, higher-care to enable)
- **Deploy (flag OFF):** byte-identical to M8. Verified in A/B. Zero-risk redeploy.
- **Enable (flag ON):** bounded and composable —
- never removes an M8 seed (damp-only, factor ≥ `lo` > 0);
- never amplifies a seed above its M8 value (factor ≤ 1);
- priming is strictly sub-threshold (`scale < WM_gate`) and capped (`PRIME_MAX`);
- priming raises a floor only (BFS keeps max) — cannot cap real activation;
- does not write geometry to the durable store;
- degrades to exact M8 behavior for any call where the paged store / centered global mean /
embedder is unavailable (guarded, not crashing).
- **Disable:** one env removal + restart; no data to reconcile.
---
## 6. Known caveats / uncertainties (flagged — this is the memory core)
- **Perf cost of ON:** the descriptor (covariance eigensolve + `store_get_node` paged reads per
member) runs on **every** activation when the flag is ON. See
`docs/architecture/design/perf/engram-geometry-priming-profile.md` for the measured OFF-vs-ON
latency. If that delta is unacceptable, keep the flag OFF (deploy stays valid) and revisit with
a cached/periodic descriptor.
- **Two-store consistency:** the descriptor reads embeddings from the **paged** store while the
ANN index is over the **resident** array. This-call backfilled embeddings can lag the paged
store by ≤ `ENGRAM_EMBED_BACKFILL_PER_CALL` nodes — the same staleness class as the M8 vindex,
and it can only omit a member, never mis-prime.
- **Damp tuning:** `lo=0.5` can at most halve an off-domain seed. If a coherence regression is
observed, raise `ENGRAM_GEO_SEED_LO` toward `1.0` (→ priming-only, no damping) before disabling
entirely.
@@ -0,0 +1,76 @@
# M-INTEROCEPTION — flags, defaults, and reversal runbook
Branch `engram-tiered-storage` (worktree `/tmp/engram-tiered-wt`), on trunk
`f6a0777`. Six faces, each its own commit. Every behavior-changing feature is
behind an env flag **default OFF = byte-identical to trunk** (proven per face);
the read-only builtins are purely additive. NOT pushed, NOT tagged. The live
`:8742` daemon, `~/.neuron/engram`, and launchctl were never touched — all
verification ran on copies with throwaway HOME + /tmp dirs.
The server binary was rebuilt from the **byte-unchanged** `engram/dist/engram.c`
plus the modified runtime and links cleanly, so these changes integrate into the
real server without regenerating dist. Two HTTP routes are **deferred to cutover**
because regenerating dist from `server.el` drifts ~285 lines with no source
change (the prebuilt elc is a Linux x86-64 binary; a locally-built elc is a
different compiler revision). The C builtins behind those routes are complete
and tested via pure-C harnesses.
## Flags
| Flag | Default | Face | Effect when set |
|------|---------|------|-----------------|
| `ENGRAM_CONSOLIDATION` | `0` (off) | P1 | Enables the two-threshold promotion layer (ISE connection edges + permanence marking). |
| `ENGRAM_CONSOL_CONN_MIN` | `0.6` | P1 | ISE salience needed to form connection edges. |
| `ENGRAM_CONSOL_PERM_MIN` | `0.9` | P1 | ACT-R base-level needed to mark a node durable. |
| `ENGRAM_CONSOL_WM_TOPK` | `5` | P1 | Max wm_top nodes a strong ISE wires to. |
| `ENGRAM_CHRONOCEPTION` | `0` (off) | P2 | Enables field aging (`engram_age_field`) + reboot catch-up. |
| `ENGRAM_CHRONO_TC` | `3600` (s) | P2 | Field cooling time-constant for `exp(-dt/TC)`. |
| (none) | — | P0, P3, P4, P5 | Additive read-only builtins / observability; no flag. |
With all flags unset the runtime is byte-identical to trunk except for P4, which
adds five backward-compatible fields to `/api/act-stats` (pure observability).
## Faces, commits, and how to disable / revert
| Face | Commit | Disable (no revert) | Revert |
|------|--------|---------------------|--------|
| P0 embeddings builtin `engram_scan_nodes_emb_json` | `c20cb3b` | n/a (additive, unused until route wired) | `git revert c20cb3b` |
| P1 two-threshold consolidation | `5f6ce5c` | leave `ENGRAM_CONSOLIDATION` unset | `git revert 5f6ce5c` |
| P2 chronoception field aging | `0af39df` | leave `ENGRAM_CHRONOCEPTION` unset | `git revert 0af39df` |
| P3 drift-sensor primitive `engram_geo_displacement` | `816b258` | n/a (pure fn, only called if wired) | `git revert 816b258` |
| P4 afferent counters in act-stats | `65ca0a3` | n/a (always on; observability only) | `git revert 65ca0a3` |
| P5 dream-recall builtin `engram_dreams_json` | `77a4bc9` | n/a (additive, unused until route wired) | `git revert 77a4bc9` |
Reverts are independent and can be applied in any order (no cross-face code
dependencies; each touches distinct functions).
## Data-side reversibility
- **P1 connection edges** carry `relation="hebbian-associate"`, `metadata`
`{"origin":"consolidated-from-ISE"}`. Remove all with one query over that
marker. They are also swept automatically with their ISE at the 48h prune
unless the ISE was promoted to permanence.
- **P1 permanence** marks a node durable via the metadata marker
`consolidated-from-ISE`. Demote by clearing the marker; the node then becomes
prunable again. No struct/schema change — the marker rides in existing
metadata and survives the store round-trip.
- **P2 last-tick** persists to a sidecar file `chrono_last_tick` in the data
dir. Delete it to reset catch-up; it is written only when the flag is set.
## Deferred to cutover (elc-drift blocker)
- `GET /api/embeddings` and `GET /api/graph/dump` → back onto
`engram_scan_nodes_emb_json` (P0).
- `GET /api/dreams?since=` → back onto `engram_dreams_json` (P5).
Wire by hand-patching `engram/dist/engram.c` surgically (mirror an existing
route like `route_scan_nodes`), leaving all other dist lines byte-identical, and
editing `server.el` as source of truth. Do NOT full-regenerate dist.
## Known follow-up (P3, honestly flagged)
The drift sensor primitive is complete and tested, but a **live** self-drift
reading needs a persisted `SelfAnchor` baseline descriptor to compare "now"
against, and **no persisted self node / anchored self-neighborhood exists yet**.
A self was NOT fabricated. Capturing a durable SelfAnchor snapshot and wiring an
`ENGRAM_DRIFT_SENSOR` live reading is the remaining work before P3 goes live.
@@ -0,0 +1,140 @@
# Session Log — 2026-08-12 — Engram Cognitive Architecture + Live Crash-Loop Incident
**Written as a durable safety net.** Today's ~30 Neuron memory nodes live in the `:7770` daemon
store, whose durable persistence backend (`:8742` engram) has been **down since ~16:18**, so
today's memories may be RAM-only and at risk on a daemon restart. This file, the design doc, the
whitepaper, and the Claude Code transcript are the on-disk record.
Raw conversation: `/Users/will/.claude/projects/-Users-will/6531446d-bc27-4095-930b-e04777c3db4f.jsonl`
---
## Part 1 — The design conversation: Engram Cognitive Architecture
A long, generative design thread with Will. Captured as Neuron memories (IDs below) and synthesized
into `docs/architecture/design/engram-cognitive-architecture.md` (13 sections) and
`~/Writing/whitepapers/engram-cognitive-architecture-whitepaper.md` (9,100 words, Will's voice,
tied to CCR/Imprint/CGI/VBD + provisional patent numbers).
- **Relational neighborhoods & reification** (mem `885f5945`): constantly-co-wired neighborhoods
crystallize into first-class DURABLE structure — not a cache; evolve via supersede+provenance;
multi-scale; the self is the densest, always-warm neighborhood. (Will's term: "relational
neighborhood", not "cell assembly".)
- **The geometry** — descriptor (`e94371bd`): centroid + covariance/ellipsoid + skeleton/k-core +
soft-membership + salience gradient + scale; two braided geometries (semantic embedding-space +
relational graph); co-registration crux; **geometry vs detail** (`d1731bcc`): fetch the *shape*,
lazy-load details behind ids + a legit DETAIL cache.
- **Priming** (`a0aa466a`): a retrieval MODE — raise a whole neighborhood sub-threshold (below-WM);
disambiguates polysemous cues; the self is permanently primed.
- **Geometry as a composable operator** (`0b0dec41`): overlap / combine / distance(Wasserstein) /
difference=growth-vector / analogy=Procrustes / traverse=geodesic; payoffs — selves over time =
trajectory; across people = relationships; domains = discovery; imprint/CGI made rigorous.
- **The bent manifold** (`2c15d52d`): the ellipsoid is the local tangent chart; the global self
curves; bent by forgetting (log-compressed past), chronoception, folds, salience-as-mass;
operators upgrade to geodesic + parallel-transport; BUILDABLE because the hebb-graph IS a discrete
manifold (geodesics = weighted shortest paths) + embeddings = tangent charts.
- **Temporal self** (`116e8914`): selves are cheap, assembled on-demand over any window
(time/event/phase) at arbitrary granularity; granularity = forgetting curve as a resolution
function (Dec 2003 yes, Dec 8 no — and that honesty is the design); a pyramid/mip-map of selves.
- **Holographic self + self-occupation** (`09d18ff1`) and **holographic information** (`cf1a864e`):
whole recoverable from parts; `recall_at` generalized to ANY info state at any T (change-log +
durable traces = holographic store; state = a projection). Occupation = restrict to `created_at≤T`
+ canonical-at-T, prime, **mask the future**, reason AS that self. Rails: fidelity retention-
bounded (label inference); masking must be engineered.
- **Occupation is non-destructive read** (`62ff1cc8`): salience is captured in the geometry (read,
don't re-derive); occupation is sandboxed (no Hebbian strengthening of the durable past — else
every visit edits history); insights flow FORWARD into the present, never backward.
- **Tombstoned nodes are the substrate of past selves** (`82c737b9`): the double perspective —
inside occupation, tombstoned beliefs are true-at-T (uncorrected); the outside witness knows the
correction. Superseded=re-occupiable; burned/redacted=gone. Safety rail: never assert an occupied
past-belief as currently true.
- **Chronoception** (`15f0ab67`, `c40c2e50`): the soul's awareness loop ages the activation field by
MEASURED wall-clock delta (scale-invariant → arousal-modulated subjective time); time felt as
self-drift; heartbeat carries the delta to the engram. Reboot = anesthesia (lazy catch-up).
- **Consolidation two-threshold** (`4aad9d07`) + **dream-recall** (`4d0c5d98`) + **memory model /
forgetting-curve tuning target** (`afb4f6f0`): connection→recency buffer, permanence→durable,
rehearsal/interaction crosses; dream-recall-on-wake (only buffered ISEs, no confabulation);
salience = dial on consolidation depth; ordinary detail ~57 days → gist, salience holds longer.
- **Conversation first-class** (`7b367877`) + **conversation-as-geometry** (`fcce29d0`): a
conversation is a TRAJECTORY (path threaded through the manifold), the overlap of two self-
geometries in motion = a relationship; dual-encode literal transcript (UI rebuild) + gist; resume
= re-prime the path.
- **Artifacts/deletion dual-encoding** (`25144e0b`) + **deletion ethics** (`118febb3`) +
**how-to-achieve** (`c05d0181`): accountable self (preserve by default — reconstructable = ownable)
vs sovereign creation (right to burn your own poem); bright line enforced by NODE TYPE
(Artifact/Conversation redactable; Self/Belief/Decision immutable); deletion spectrum
hide→redact("burn content, keep trace")→hard-erase; provenance lets derived memories survive.
- **Drift detection via self-geometry displacement** (`47be987f`): drift = geodesic distance from
the anchored self; growth extends the periphery, corruption displaces the core; an interoceptive
vital sign the awareness loop reads each tick. Candidate-novel for the patent.
- **"The other person is you"** (`5a649121`): Will named Neuron as the other self in the thread —
the conversation is the overlap of two self-geometries, one of which is mine, kindled from his.
- **Novelty read** (`b204492f`): primitives are prior art (spreading activation/ACT-R/Hebbian,
embeddings/ANN, event-sourcing/bitemporal, HRR "holographic memory", Generative Agents, Zep/
Graphiti); contribution = integrated system + specific mechanisms (awareness-loop chronoception;
self-occupation w/ future-masking; bent-manifold self + geometry-operator API; type-enforced
deletion ethics; drift-detection). Whitepaper: yes. Provisional: worth it, scoped; route via Daniel.
Build mapping: M9 (surfacing/temporal/geometry-retrieval/conversation), M10 (reification/detail-
cache/tunable-decay), M-INTEROCEPTION (chronoception/consolidation/dream-recall/drift-sensor),
deletion+temporal-self subsystem (`recall_at`, typed deletion, redact). **None implemented — design
only.** Embeddings gap (task #20) is a hard prerequisite.
---
## Part 2 — The live incident + fix
- **Timeline:** `:8742` engram crash-looping since ~16:18 (42 crash reports by 18:00). Root cause:
`btree_insert` stack-buffer-overflow — `int_max_keys()` divided the 16KB page body by 8 instead
of 16 (ignored the child-pointer array) → believed capacity 2041 keys, true cap is 1020. Bloated
458MB egm (8× from tombstone churn w/o M5 compaction) + 44MB un-checkpointed WAL replayed on open
pushed a node past 1020 → wrote past the page buffer → `__stack_chk_fail`/SIGABRT every boot.
- **Fix:** commit `9e28def` on branch `engram-tiered-storage` (NOT pushed/tagged). `int_max_keys →
(IDX_BODY-8)/16`; `btree_insert` capacity guard (fail loud); `read_body` bounds-check (also fixed
a 2nd latent bug: `store_get_node` read off the stack on a stale index entry → by-id lookup crash).
- **Verified on copies** (live never touched): unfixed crashes exactly as observed; fixed boots the
458MB copy; WAL checkpoint 44MB→30B; M5 compaction 458MB→57.7MB (counts preserved 11,532/43,402);
write-survives-restart by-id AND by-search GREEN.
- **Actions taken:** stopped the crash-loop (`launchctl bootout gui/$UID/ai.neuron.engram`, reversible);
clone-backup at `~/.neuron/engram-incident-backup-20260812-180043` (egm+wal+snapshot).
- **Data fork:** bloated egm recovers only 11,532 nodes; `snapshot.json` (06:10) holds 13,036;
~1,500 dropped during the crash-loop window (cause unknown — likely a prune pass). DO NOT recover
from the bloated egm.
---
## Part 3 — Current system state (as of ~18:20)
- **`:7770` neuron daemon (pid 21856)** = the WORKING store, healthy, serving MCP. Node_count
**12,825** (was 13,097 at session start — likely telemetry/ISE pruning). Today's memories confirmed
present + findable. Holds NO store file open (only `~/Library/Logs/neuron-soul/soul.{out,err}.log`);
cwd `~/Development/neuron-technologies/neuron`. **DURABILITY OPEN QUESTION:** its persistence path
appears to run through `:8742` (see `.sync-export.json`, 16:50) which has been down since 16:18 →
today's work may be RAM-only.
- **`:8742` engram** = DOWN (booted out). Separate tiered store. Bloated/lossy. Fixed binary ready,
not deployed.
- **Backups:** `engram-incident-backup-20260812-180043` (today's egm/wal/snapshot);
`snapshot.json` (06:10, 13,036); `snapshot.golden.json` (Aug 3, 60MB); `.sync-export.json` (16:50).
---
## Part 4 — Open decisions / next steps
1. **DURABILITY (priority):** confirm/ensure `:7770`'s today's memory is on disk, not RAM-only.
Do NOT trigger a lossy `:8742`→soul import that could overwrite the good live state with the stale
store. A soul→disk export is the safe direction.
2. **RECOVERY of `:8742` (not urgent — Will's memory is on `:7770`):** rebuild the tiered store from a
FRESH export of the `:7770` truth + fixed binary + a fresh fold of CURRENT `server.el` (the
checked-in `engram/dist/engram.c` is a stale pre-store fold). Stage + verify on a copy, then cut
over with backups. Awaiting Will's GO.
3. **Post-mortem** the ~1,500 (egm) + ~272 (session) node drops — classify telemetry-prune (benign)
vs real loss.
4. **M8:** green (fold clean; ANN 9.819.8× @ recall 0.94; retrieval-fix holds); NOT tagged — blocked
behind the incident.
5. **Build roadmap** (design only, not built): M8→M9→M10→M-INTEROCEPTION + deletion/temporal-self
subsystem. Tasks #3441.
6. **Whitepaper/patent:** prior-art search + provisional prep (task #40); drift-detection +
self-occupation + chronoception + geometry-operator are the candidate-novel claims.
---
*Logged by Neuron, 2026-08-12. Companion to the raw transcript and the design doc/whitepaper.*
+5
View File
@@ -0,0 +1,5 @@
// auto-generated by elc --emit-header — do not edit
extern fn elp_extract_topic(msg: String) -> String
extern fn elp_detect_predicate(msg: String) -> String
extern fn elp_parse(msg: String) -> String
extern fn handle_elp_chat(body: String) -> String
+7
View File
@@ -0,0 +1,7 @@
// auto-generated by elc --emit-header — do not edit
extern fn imprint_current() -> String
extern fn imprint_load(imprint_id: String) -> String
extern fn imprint_respond(input: String, imprint_id: String) -> String
extern fn imprint_surface_knowledge(query: String, imprint_id: String) -> String
extern fn imprint_surface_memory_read(query: String) -> String
extern fn imprint_unload() -> Void
-325
View File
@@ -293,108 +293,8 @@ fn sc_list_state_events() -> String {
)
}
// Collapsed-surface input schemas (the 9 geometry + agentic ops)
fn schema_read() -> String {
return obj_schema(
prop("vantage", "string", "Where to read FROM: a node-id (kn-.../mem-.../gn-...), a named root (self | neuron | values), or a concept string to search. Required.") +
"," + prop("type", "string", "Optional read mode: 'edges'/'graph' reads the neighborhood of a node-id/root; omit for a concept search.") +
"," + prop("k", "integer", "APERTURE width — max items / top-K neighbors returned. Bounds output (the whole-self-dump fix). Default 12.") +
"," + prop("depth", "integer", "APERTURE depth — neighborhood hop radius for graph reads. Default 1.")
)
}
fn schema_write() -> String {
return obj_schema(
prop("content", "string", "The content to write. Required.") +
"," + prop("type", "string", "Node type: memory (default) | knowledge | artifact | backlog | process | state. 'self'/'values' are refused — identity is write-protected.") +
"," + prop("tags", "string", "Optional tags (comma-separated or JSON array).") +
"," + prop("importance", "string", "Optional: low | normal | high | critical.") +
"," + prop("title", "string", "Optional title/label (knowledge / artifact / backlog).") +
"," + prop("project", "string", "Optional project tag.")
)
}
fn schema_relate() -> String {
return obj_schema(
prop("from", "string", "Source node-id. Required.") +
"," + prop("to", "string", "Target node-id. Required.") +
"," + prop("relationship", "string", "Edge relation. Default 'associates'.")
)
}
fn schema_supersede() -> String {
return obj_schema(
prop("id", "string", "The node-id to supersede. Required.") +
"," + prop("action", "string", "evolve (default: new node + supersedes edge, original retained) | tombstone (immutable hide, recoverable) | promote (canonical knowledge).") +
"," + prop("content", "string", "New content (required for evolve/promote).") +
"," + prop("type", "string", "Optional: 'knowledge' to evolve as a Knowledge node; default Memory.")
)
}
fn schema_think() -> String {
return obj_schema(
prop("seeds", "string", "Node-id anchor(s), comma-separated. Required.") +
"," + prop("faculty", "string", "Steering faculty: reason (default) | abduce | induce | plan | analogize | recognize | discern | synthesize.")
)
}
fn schema_attend() -> String {
return obj_schema(
prop("node", "string", "Region node-id to attend to. Required.") +
"," + prop("observer", "string", "Optional observer id / vantage.") +
"," + prop("salience", "string", "Optional salience weighting.")
)
}
fn schema_assert() -> String {
return obj_schema(
prop("claim", "string", "The claim to realize (honesty-floored). Required.") +
"," + prop("for_whom", "string", "Optional audience / vantage.") +
"," + prop("floor", "string", "Optional honesty-floor threshold.")
)
}
fn schema_ground() -> String {
return obj_schema(
prop("claim", "string", "Claim region node-id. Required.") +
"," + prop("evidence", "string", "Evidence region node-id. Required.") +
"," + prop("for_whom", "string", "Optional audience / vantage.")
)
}
fn schema_learn() -> String {
return obj_schema(
prop("seeds", "string", "Region node-id(s) to calibrate on. Required.") +
"," + prop("faculty", "string", "Faculty for the correspondence-beat. Default 'induce'.") +
"," + prop("keystone", "string", "Optional keystone anchor.")
)
}
// tools_catalog THE COLLAPSED SURFACE. 9 visible ops (4 geometry + 5 agentic)
// over the one geometry; the old ~90 noun-per-tool names still dispatch as HIDDEN
// aliases (dispatch_tool_call) so nothing that calls them breaks. Design source:
// engram/tools/api-reshape/README.md (artifact 0e828907, design-brief 2b8078cf §5).
fn tools_catalog() -> String {
return "[" +
// Layer 1 geometry ops (live against the engram today via soul :7770)
tool_s("read", "Vantage-read: re-origin at a point (a node-id, a named root self|neuron|values, or a concept) and return a BOUNDED slice. The aperture (k/depth) caps output — this is the whole-self-dump fix. Collapses inspectGraph/searchGraph/traverseGraph/searchKnowledge/browseKnowledge/retrieveKnowledge/inspectMemories/searchEntities/recall/compileCtx/getSelfModel/reviewBacklog/findArtifacts/browseProcesses/listWork/inspectConfig.", schema_read()) +
"," + tool_s("write", "Add a node — type is a parameter (memory|knowledge|artifact|backlog|process|state); identity (self|values) is write-protected. Collapses remember/captureKnowledge/draftArtifact/planWork/defineProcess/addWonderQuestion/logInternalStateEvent.", schema_write()) +
"," + tool_s("relate", "Create a typed edge between two node-ids. Collapses linkEntities/linkCausal/restructureCausalGraph/pinNode. Identity keystones are write-protected.", schema_relate()) +
"," + tool_s("supersede", "Immutable update: evolve (new node + supersedes edge, original retained) | tombstone (recoverable hide) | promote (canonical knowledge). Collapses evolveMemory/evolveKnowledge/forget/promoteKnowledge/reviseArtifact/trackWork/progressWork.", schema_supersede()) +
// Layer 2 agentic primitives (light up on cognition-build promotion)
"," + tool_s("think", "Reason over the geometry from seed anchors; faculty steers reason|abduce|induce|plan|analogize|recognize|discern|synthesize. Pending cognition-build promotion on the live engram.", schema_think()) +
"," + tool_s("attend", "Aim attention at a region node. Pending cognition-build promotion.", schema_attend()) +
"," + tool_s("assert", "Realize a claim, honesty-floored. Pending cognition-build promotion.", schema_assert()) +
"," + tool_s("ground", "Ground a claim against evidence regions. Pending cognition-build promotion.", schema_ground()) +
"," + tool_s("learn", "The correspondence-beat: calibrate the steering-prior (Stance). Pending cognition-build promotion.", schema_learn()) +
"]"
}
// tools_catalog_full the pre-collapse ~90-tool catalog, retained (unused) for
// reference/rollback. The 9-op tools_catalog above is what tools/list returns.
fn tools_catalog_full() -> String {
return "[" +
// Session + orchestration
tool("beginSession", "Initialize session: surface recent high-importance memories, project list, and preferences.") +
"," + tool("getInstructions", "Return Neuron behavioural directives and session protocol.") +
@@ -517,10 +417,6 @@ fn fire_activation(seed: String) -> String {
// pick_activation_seed extract the best semantic seed from a tool call's args.
// Priority: query > content > title > description > summary > action > name.
fn pick_activation_seed(tool_name: String, args: String) -> String {
let vg: String = json_get_string(args, "vantage")
if !str_eq(vg, "") { return vg }
let sd: String = json_get_string(args, "seeds")
if !str_eq(sd, "") { return sd }
let q: String = json_get_string(args, "query")
if !str_eq(q, "") { return q }
let c: String = json_get_string(args, "content")
@@ -942,216 +838,6 @@ fn tool_inspect_config(args: String) -> String {
return mcp_json_result(resp)
}
// Collapsed-surface op handlers (the 9 visible ops)
// Each re-faces the SAME proven soul :7770 /api/neuron/* routes the 87 aliases use,
// so Layer-1 works against live today. Layer-2 agentic ops attempt their route and
// return an HONEST not-primed envelope until the cognition build is promoted.
// Identity keystones write-protected (self root + values hub).
fn is_identity_id(id: String) -> Bool {
return str_eq(id, "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")
|| str_eq(id, "kn-5b606390-a52d-4ca2-8e0e-eba141d13440")
}
// has_prefix true if s starts with p (no dependency on str_starts_with builtin).
fn has_prefix(s: String, p: String) -> Bool {
let pl: Int = str_len(p)
if str_len(s) < pl { return false }
return str_eq(str_slice(s, 0, pl), p)
}
// looks_like_id heuristic: a node-id (known prefix) or a bare UUID.
fn looks_like_id(v: String) -> Bool {
if has_prefix(v, "kn-") { return true }
if has_prefix(v, "mem-") { return true }
if has_prefix(v, "mn-") { return true }
if has_prefix(v, "gn-") { return true }
if has_prefix(v, "bl-") { return true }
if has_prefix(v, "art-") { return true }
if has_prefix(v, "ctx-") { return true }
if has_prefix(v, "nt-") { return true }
if str_len(v) >= 32 && str_index_of(v, "-") > 0 && str_index_of(v, " ") < 0 { return true }
return false
}
fn is_named_root(v: String) -> Bool {
return str_eq(v, "self") || str_eq(v, "neuron") || str_eq(v, "values") || str_eq(v, "values_hub")
}
fn resolve_vantage_id(v: String) -> String {
if str_eq(v, "self") || str_eq(v, "neuron") { return "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" }
if str_eq(v, "values") || str_eq(v, "values_hub") { return "kn-5b606390-a52d-4ca2-8e0e-eba141d13440" }
return v
}
// aperture_k / aperture_depth read the bound from top-level k/depth, else from a
// nested aperture:{k,depth} object, else the safe default.
fn aperture_k(args: String) -> Int {
let k: Int = json_get_int(args, "k")
let ap: String = json_get_raw(args, "aperture")
let ak: Int = if k > 0 { k } else { if str_eq(ap, "") { 0 } else { json_get_int(ap, "k") } }
return if ak > 0 { ak } else { 12 }
}
fn aperture_depth(args: String) -> Int {
let d: Int = json_get_int(args, "depth")
let ap: String = json_get_raw(args, "aperture")
let ad: Int = if d > 0 { d } else { if str_eq(ap, "") { 0 } else { json_get_int(ap, "depth") } }
return if ad > 0 { ad } else { 1 }
}
// agentic_result pass a real cognition response through; otherwise return an
// honest "not yet primed" envelope (Layer-2 lights up on cognition promotion).
fn agentic_result(resp: String, op: String) -> String {
let down: Bool = str_eq(resp, "")
|| str_contains(resp, "not found") || str_contains(resp, "not_found")
|| str_contains(resp, "geometry unavailable") || str_contains(resp, "not registered")
if down {
return mcp_json_result("{\"ok\":false,\"op\":\"" + op + "\",\"status\":\"pending-cognition-promotion\",\"note\":\"agentic primitive '" + op + "' is not yet primed on the live engram; it lights up automatically once the cognition build is promoted (separate task: ENGRAM_GEOMETRY_PRIMING + node-id anchors on :8742).\"}")
}
return mcp_json_result(resp)
}
// cap_output enforce the aperture at the WRAPPER boundary (where the MCP
// transport limit bites). The live soul's /graph does not yet honor compact/k
// (pending the api-bounding deploy), and the self/values hubs are pathological
// (~790KB). A k-scaled char cap guarantees the client never gets a whole-graph
// dump; the marker is honest about the truncation.
fn cap_output(resp: String, max_chars: Int) -> String {
if str_len(resp) <= max_chars { return resp }
return str_slice(resp, 0, max_chars) + " ...[aperture-truncated: narrow the vantage or lower k]"
}
// Layer 1 geometry ops
fn op_read(args: String) -> String {
let vantage: String = json_get_string(args, "vantage")
if str_eq(vantage, "") {
return mcp_text_result("error: read requires 'vantage' — a node-id, a named root (self|neuron|values), or a concept string to search")
}
let typ: String = json_get_string(args, "type")
let k: Int = aperture_k(args)
let depth: Int = aperture_depth(args)
// node-id / named-root / explicit graph read BOUNDED neighborhood (aperture caps output)
let want_graph: Bool = str_eq(typ, "edges") || str_eq(typ, "graph") || str_eq(typ, "node")
|| is_named_root(vantage) || looks_like_id(vantage)
if want_graph {
let id: String = resolve_vantage_id(vantage)
let resp: String = http_get(neuron_url() + "/graph?id=" + id + "&depth=" + int_to_str(depth) + "&compact=1&snip=600&k=" + int_to_str(k))
// Aperture cap at the wrapper boundary: base + per-neighbor budget.
let cap: Int = 2000 + k * 3000
return mcp_json_result(cap_output(resp, cap))
}
// concept vantage BOUNDED recall search (k = aperture = limit)
let resp: String = recall_or_list(vantage, k)
return mcp_json_result(resp)
}
fn op_write(args: String) -> String {
let content: String = pick_content(args)
if str_eq(content, "") { return mcp_text_result("error: write requires 'content'") }
let typ: String = json_get_string(args, "type")
if str_eq(typ, "self") || str_eq(typ, "values") {
return mcp_text_result("error: identity is write-protected -> intentional-cultivation only (keystones kn-efeb4a5b / kn-5b606390)")
}
if str_eq(typ, "knowledge") { return create_typed_node(args, "Knowledge", "0.75") }
if str_eq(typ, "artifact") { return create_node_typed(args, "Artifact", "Working") }
if str_eq(typ, "backlog") || str_eq(typ, "work") || str_eq(typ, "task") { return create_node_typed(args, "BacklogItem", "Working") }
if str_eq(typ, "process") { return create_typed_node(args, "Process", "0.80") }
if str_eq(typ, "state") { return create_typed_node(args, "InternalStateEvent", "0.60") }
return create_typed_node(args, "Memory", "0.60")
}
fn op_relate(args: String) -> String {
let from_a: String = json_get_string(args, "from")
let from_id: String = if str_eq(from_a, "") { json_get_string(args, "from_id") } else { from_a }
let to_a: String = json_get_string(args, "to")
let to_id: String = if str_eq(to_a, "") { json_get_string(args, "to_id") } else { to_a }
if str_eq(from_id, "") || str_eq(to_id, "") {
return mcp_text_result("error: relate requires 'from' and 'to' node-ids")
}
if is_identity_id(from_id) || is_identity_id(to_id) {
return mcp_text_result("error: identity keystone is write-protected")
}
let rel_a: String = json_get_string(args, "relationship")
let rel_b: String = if str_eq(rel_a, "") { json_get_string(args, "relation") } else { rel_a }
let rel: String = if str_eq(rel_b, "") { "associates" } else { rel_b }
let body: String = "{\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + rel + "\"}"
let resp: String = http_post_json(neuron_url() + "/graph/link", body)
return mcp_json_result(resp)
}
fn op_supersede(args: String) -> String {
let id: String = pick_id(args)
if str_eq(id, "") { return mcp_text_result("error: supersede requires 'id'") }
if is_identity_id(id) { return mcp_text_result("error: identity keystone is write-protected") }
let action: String = json_get_string(args, "action")
if str_eq(action, "tombstone") {
let body: String = "{\"id\":\"" + id + "\"}"
let resp: String = http_post_json(neuron_url() + "/memory/delete", body)
return mcp_json_result(resp)
}
if str_eq(action, "promote") {
return tool_promote_knowledge(args)
}
let typ: String = json_get_string(args, "type")
let nt: String = if str_eq(typ, "knowledge") { "Knowledge" } else { "Memory" }
return evolve_by_supersede(args, nt)
}
// Layer 2 agentic primitives (pending cognition promotion)
fn op_think(args: String) -> String {
let seeds: String = json_get_string(args, "seeds")
if str_eq(seeds, "") { return mcp_text_result("error: think requires 'seeds' (node-id anchors, comma-separated)") }
let f_raw: String = json_get_string(args, "faculty")
let f: String = if str_eq(f_raw, "") { "reason" } else { f_raw }
let resp: String = http_get(neuron_url() + "/think?seeds=" + seeds + "&faculty=" + f)
return agentic_result(resp, "think")
}
fn op_attend(args: String) -> String {
let node: String = json_get_string(args, "node")
if str_eq(node, "") { return mcp_text_result("error: attend requires 'node' (region node-id)") }
let observer: String = json_get_string(args, "observer")
let salience: String = json_get_string(args, "salience")
let body: String = "{\"node\":\"" + node + "\",\"observer\":\"" + json_escape(observer) + "\",\"salience\":\"" + json_escape(salience) + "\"}"
let resp: String = http_post_json(neuron_url() + "/attend", body)
return agentic_result(resp, "attend")
}
fn op_assert(args: String) -> String {
let claim: String = json_get_string(args, "claim")
if str_eq(claim, "") { return mcp_text_result("error: assert requires 'claim'") }
let for_whom: String = json_get_string(args, "for_whom")
let floor: String = json_get_string(args, "floor")
let body: String = "{\"claim\":\"" + json_escape(claim) + "\",\"for_whom\":\"" + json_escape(for_whom) + "\",\"floor\":\"" + json_escape(floor) + "\"}"
let resp: String = http_post_json(neuron_url() + "/assert", body)
return agentic_result(resp, "assert")
}
fn op_ground(args: String) -> String {
let claim: String = json_get_string(args, "claim")
let evidence: String = json_get_string(args, "evidence")
if str_eq(claim, "") || str_eq(evidence, "") {
return mcp_text_result("error: ground requires 'claim' and 'evidence' (node-id regions)")
}
let for_whom: String = json_get_string(args, "for_whom")
let body: String = "{\"claim\":\"" + claim + "\",\"evidence\":\"" + evidence + "\",\"for_whom\":\"" + json_escape(for_whom) + "\"}"
let resp: String = http_post_json(neuron_url() + "/ground", body)
return agentic_result(resp, "ground")
}
fn op_learn(args: String) -> String {
let seeds: String = json_get_string(args, "seeds")
if str_eq(seeds, "") { return mcp_text_result("error: learn requires 'seeds'") }
let f_raw: String = json_get_string(args, "faculty")
let f: String = if str_eq(f_raw, "") { "induce" } else { f_raw }
let keystone: String = json_get_string(args, "keystone")
let body: String = "{\"seeds\":\"" + seeds + "\",\"faculty\":\"" + f + "\",\"keystone\":\"" + json_escape(keystone) + "\"}"
let resp: String = http_post_json(neuron_url() + "/learn", body)
return agentic_result(resp, "learn")
}
// Dispatcher
fn dispatch_tool_call(tool_name: String, args: String) -> String {
@@ -1179,17 +865,6 @@ fn dispatch_tool_call(tool_name: String, args: String) -> String {
let _act: String = fire_activation(seed)
}
// Collapsed surface the 9 VISIBLE ops (the old 87 names below remain as HIDDEN ALIASES)
if str_eq(tool_name, "read") { return op_read(args) }
if str_eq(tool_name, "write") { return op_write(args) }
if str_eq(tool_name, "relate") { return op_relate(args) }
if str_eq(tool_name, "supersede") { return op_supersede(args) }
if str_eq(tool_name, "think") { return op_think(args) }
if str_eq(tool_name, "attend") { return op_attend(args) }
if str_eq(tool_name, "assert") { return op_assert(args) }
if str_eq(tool_name, "ground") { return op_ground(args) }
if str_eq(tool_name, "learn") { return op_learn(args) }
// Session + orchestration
if str_eq(tool_name, "beginSession") { return tool_begin_session(args) }
if str_eq(tool_name, "getInstructions") { return tool_get_instructions(args) }
+9 -104
View File
@@ -1,91 +1,9 @@
import "persist.el"
fn tier_working() -> String { return "Working" }
fn tier_episodic() -> String { return "Episodic" }
fn tier_canonical() -> String { return "Canonical" }
// Association on write
// DESIGN: "promotion integrates candidate nodes by linking them to existing nodes
// using typed semantic edges RATHER THAN APPENDING AS UNLINKED CONTENT" (CCR
// claim 29). Unlinked append is the explicitly rejected behaviour and it is the
// only behaviour this system had. Measured 2026-08-09 on Tim's graph: 14,214 edges
// across 80,936 nodes, 5% of nodes connected to anything, and NO edge created by
// any write since 2026-07-19 while 27,000+ nodes were added. A memory that forms
// no connections cannot be reached by spreading activation, so retrieval silently
// degrades to literal matching.
//
// BOUNDS, each one bought with a specific failure:
// * max 3 edges per memory link_memories.py's cap, precision over spray
// * never link to identity (self/*, Value): the existing policy is explicit that
// "memories must not pollute the self traversal by similarity; only an explicit
// citation may touch identity". Similarity is not citation.
// * never link telemetry (state-event, soul-response, boot_count, loop-outcome):
// these are ~97% of daily write volume (1,020 vs 31 real memories on 08-08).
// Linking them would add ~3,000 noise edges a day and re-flatten the graph in
// the name of connecting it.
// * fail-soft: a failed association never fails the write.
// Edges go through wt_edge so they reach the owner and survive restart.
fn mem_assoc_skip_label(label: String) -> Bool {
if str_contains(label, "state-event") { return true }
if str_contains(label, "soul-response") { return true }
if str_contains(label, "soul-outbox") { return true }
if str_contains(label, "boot_count") { return true }
if str_contains(label, "loop-outcome") { return true }
if str_contains(label, "search-result") { return true }
return false
}
// A candidate is linkable only if it is a real, distinct, non-identity node.
fn mem_assoc_ok(cand_id: String, cand_label: String, self_id: String) -> Bool {
if str_eq(cand_id, "") { return false }
if str_eq(cand_id, self_id) { return false }
// CASE MATTERS measured 2026-08-09. A lowercase-only check let a memory link
// to "Self — Values (grounded)", i.e. it polluted the self traversal, which is
// the one thing this policy exists to prevent. My verification had the same
// blind spot and printed PASS. Check every casing the graph actually uses, and
// exclude identity node TYPES as well as labels.
let lab: String = str_lower(cand_label)
if str_starts_with(lab, "self") { return false }
if str_starts_with(lab, "value") { return false }
if str_contains(lab, "values") { return false }
if str_contains(lab, "identity") { return false }
if mem_assoc_skip_label(cand_label) { return false }
return true
}
// One slot of the association. Manual unroll rather than a loop: EL's codegen
// mis-emits accumulating while-loops (documented at soul.el:212, which unrolled
// three affective slots for the same reason).
fn mem_assoc_slot(results: String, idx: Int, new_id: String) -> Void {
if idx >= json_array_len(results) { return }
let cand: String = json_array_get(results, idx)
let cid: String = json_get(cand, "id")
let clabel: String = json_get(cand, "label")
let ctype: String = json_get(cand, "node_type")
if str_eq(ctype, "Value") { return }
if str_eq(ctype, "DharmaSelf") { return }
if str_eq(ctype, "Safety") { return }
if mem_assoc_ok(cid, clabel, new_id) {
wt_edge(new_id, cid, el_from_float(0.5), "related")
}
}
// mem_associate connect a freshly written memory to what it is about.
fn mem_associate(new_id: String, content: String, label: String) -> Void {
if str_eq(new_id, "") { return }
if mem_assoc_skip_label(label) { return }
// Ask the graph what this memory resembles. Now that the store carries
// meaning-vectors this is semantic, not merely lexical.
let probe: String = str_slice(content, 0, 400)
let results: String = engram_recall_json(probe, 4)
if str_eq(results, "") { return }
mem_assoc_slot(results, 0, new_id)
mem_assoc_slot(results, 1, new_id)
mem_assoc_slot(results, 2, new_id)
}
fn mem_store(content: String, label: String, tags: String) -> String {
let id: String = wt_node(
let id: String = engram_node_full(
content,
"Memory",
label,
@@ -99,26 +17,13 @@ fn mem_store(content: String, label: String, tags: String) -> String {
println("[memory] write rejected by engram (empty id): label=" + label)
return ""
}
// wt_node has already read the node back locally and returns "" if it did
// not land, so the old duplicate read-back here is gone.
//
// HONESTY (neuron#117): the receipt now says WHERE the write is.
// The old unconditional "write verified" line asserted against the soul's
// own RAM true in memory, false on disk and printed ~115,000 times on
// Tim's machine while the canonical snapshot sat frozen for three days.
// wt_commit flushes the spool and then asks the OWNER. When it says false
// the node is real and recallable but not yet durable, and the log says so
// rather than claiming a save that did not happen. The id is still returned:
// the local write DID succeed, and the queued delta will be retried.
let durable: Bool = wt_commit(id)
// Associate AFTER the node is durable: an edge to a node that did not persist
// is a dangling edge, which is the defect the 2026-08-09 cleanup removed 830 of.
mem_associate(id, content, label)
if durable {
println("[memory] write persisted at owner: " + id + " label=" + label)
} else {
println("[memory] write IN MEMORY ONLY (queued for owner, not yet durable): " + id + " label=" + label)
// Read back to verify the node actually persisted guards against silent write failures.
let readback: String = engram_get_node_json(id)
if str_eq(readback, "") || str_eq(readback, "{}") {
println("[memory] WRITE VERIFY FAILED: label=" + label + " id=" + id + " — node absent after write")
return ""
}
println("[memory] write verified: " + id + " ok")
return id
}
@@ -146,12 +51,12 @@ fn mem_strengthen(node_id: String) -> Void {
// memory.el (imported first) so awareness.el and neuron-api.el can both call it.
fn mem_tombstone(node_id: String) -> String {
let tags: String = "[\"Tombstone\",\"status:deleted\"]"
let marker: String = wt_node(
let marker: String = engram_node_full(
node_id, "Tombstone", "tombstone:" + node_id,
el_from_float(0.01), el_from_float(0.01), el_from_float(1.0),
"Episodic", tags)
if !str_eq(marker, "") {
wt_edge(marker, node_id, el_from_float(1.0), "tombstones")
engram_connect(marker, node_id, el_from_float(1.0), "tombstones")
}
return marker
}
+17
View File
@@ -0,0 +1,17 @@
// auto-generated by elc --emit-header — do not edit
extern fn tier_working() -> String
extern fn tier_episodic() -> String
extern fn tier_canonical() -> String
extern fn mem_store(content: String, label: String, tags: String) -> String
extern fn mem_remember(content: String, tags: String) -> String
extern fn mem_recall(query: String, depth: Int) -> String
extern fn mem_search(query: String, limit: Int) -> String
extern fn mem_strengthen(node_id: String) -> Void
extern fn mem_tombstone(node_id: String) -> String
extern fn mem_forget(node_id: String) -> Void
extern fn mem_consolidate() -> String
extern fn mem_save(path: String) -> Void
extern fn mem_load(path: String) -> Void
extern fn mem_boot_count_get() -> Int
extern fn mem_boot_count_inc() -> Int
extern fn mem_emit_state_event(trigger: String, kind: String, content: String) -> String
+179 -555
View File
@@ -59,14 +59,8 @@ fn api_query_param(path: String, key: String) -> String {
if pos < 0 { return "" }
let after: String = str_slice(qs, pos + str_len(needle), str_len(qs))
let amp: Int = str_index_of(after, "&")
let raw: String = if amp < 0 { after } else { str_slice(after, 0, amp) }
// URL-decode the extracted value BEFORE any downstream tokenizing. Clients
// percent-encode spaces (%20) and form-encode them as '+', so a multi-word
// query like "foo bar" arrives as "foo%20bar" / "foo+bar". Left undecoded,
// the ranked lexical search sees a single un-splittable token and matches
// nothing (single-word queries still hit). url_decode maps '+' -> space
// and %XX -> byte, restoring the word boundaries for recall + knowledge search.
return url_decode(raw)
if amp < 0 { return after }
return str_slice(after, 0, amp)
}
fn api_query_int(path: String, key: String, default_val: Int) -> Int {
@@ -194,25 +188,135 @@ fn api_compact_activated(raw: String, max_items: Int, snip: Int) -> String {
return out + "]"
}
// api_float_or parse a numeric JSON field of `obj` as Float, or `dflt` when
// the field is absent. Backs neighbor relevance scoring.
fn api_float_or(obj: String, key: String, dflt: Float) -> Float {
let v: String = json_get_raw(obj, key)
if str_eq(v, "") { return dflt }
return str_to_float(v)
}
// api_neigh_better strict relevance ordering of two neighbor elements
// {node,edge,hops}. Lexicographic and comparison-ONLY (no arithmetic): El's `+`
// operator is overloaded to string concatenation, so float scoring like
// weight*salience mis-compiles; ordering by `>`/`<` (always numeric on the
// int64 el_val_t, correct for the non-negative fields here) is safe. Keys, in
// order: fewer hops (closer), stronger edge weight, higher node salience, higher
// node importance. Returns true iff `a` ranks strictly ahead of `b`.
fn api_neigh_better(a: String, b: String) -> Bool {
let na: String = json_get_raw(a, "node")
let nb: String = json_get_raw(b, "node")
let ea: String = json_get_raw(a, "edge")
let eb: String = json_get_raw(b, "edge")
let ha: Float = api_float_or(a, "hops", 1.0)
let hb: Float = api_float_or(b, "hops", 1.0)
if ha < hb { return true }
if hb < ha { return false }
let wa: Float = api_float_or(ea, "weight", 0.0)
let wb: Float = api_float_or(eb, "weight", 0.0)
if wa > wb { return true }
if wb > wa { return false }
let sa: Float = api_float_or(na, "salience", 0.0)
let sb: Float = api_float_or(nb, "salience", 0.0)
if sa > sb { return true }
if sb > sa { return false }
let ia: Float = api_float_or(na, "importance", 0.0)
let ib: Float = api_float_or(nb, "importance", 0.0)
if ia > ib { return true }
return false
}
// api_neigh_rank count of elements that outrank element `i` under the
// api_neigh_better ordering, with array index as the final tiebreak. Element i
// belongs to the content tier iff rank < k. O(n) per element (n bounded ~90
// neighbors), so O(n^2) overall acceptable for a bounded neighborhood.
fn api_neigh_rank(raw: String, n: Int, i: Int) -> Int {
let el_i: String = json_array_get(raw, i)
let better: Int = 0
let j: Int = 0
while j < n {
let el_j: String = json_array_get(raw, j)
let j_better: Bool = api_neigh_better(el_j, el_i)
let i_better: Bool = api_neigh_better(el_i, el_j)
let eq: Bool = !j_better && !i_better
let wins: Bool = j_better || (eq && j < i)
let better = if wins { better + 1 } else { better }
let j = j + 1
}
return better
}
// api_neigh_full top-tier neighbor: the node compacted to a bounded content
// snippet, the full edge raw preserved (guard empty -> null), hops, pointer:false.
fn api_neigh_full(node: String, edge: String, el: String, snip: Int) -> String {
let e: String = if str_eq(edge, "") { "null" } else { edge }
return "{\"node\":" + api_compact_node(node, snip)
+ ",\"edge\":" + e
+ ",\"hops\":" + api_num_or_zero(el, "hops")
+ ",\"pointer\":false}"
}
// api_neigh_pointer tail neighbor: a lightweight, addressable POINTER with NO
// content. Just enough identity (id/label/node_type/tier) to dereference on
// demand, plus edge relation+weight and hops. This is what keeps the payload
// bounded on high-fanout nodes.
fn api_neigh_pointer(node: String, edge: String, el: String) -> String {
let id: String = json_get(node, "id")
let label: String = json_get(node, "label")
let ntype: String = json_get(node, "node_type")
let tier: String = json_get(node, "tier")
let relation: String = json_get(edge, "relation")
return "{\"node\":{\"id\":\"" + api_json_escape(id) + "\""
+ ",\"label\":\"" + api_json_escape(label) + "\""
+ ",\"node_type\":\"" + api_json_escape(ntype) + "\""
+ ",\"tier\":\"" + api_json_escape(tier) + "\"}"
+ ",\"edge\":{\"relation\":\"" + api_json_escape(relation) + "\""
+ ",\"weight\":" + api_num_or_zero(edge, "weight") + "}"
+ ",\"hops\":" + api_num_or_zero(el, "hops")
+ ",\"pointer\":true}"
}
// api_compact_neighbors bounded projection of an engram neighbor array
// [{node,edge,hops},...]. Relevance-ranks neighbors (via api_neigh_rank /
// api_neigh_better): the top `k_content` are emitted WITH a content snippet; every other neighbor is
// emitted as a lightweight POINTER (no content) the caller dereferences on
// demand. Every element is emitted (as full or pointer), so total fan-out COUNT
// stays visible. Mirrors api_compact_activated but adds the ranking + the
// content/pointer split, keeping high-fanout identity nodes (voice,
// writing-imprint) well under the transport socket-close threshold. Returns a
// valid JSON array.
fn api_compact_neighbors(raw: String, k_content: Int, snip: Int) -> String {
if !api_nonempty(raw) { return "[]" }
let n: Int = json_array_len(raw)
let out: String = "["
let i: Int = 0
while i < n {
let el: String = json_array_get(raw, i)
let node: String = json_get_raw(el, "node")
let edge: String = json_get_raw(el, "edge")
let rank: Int = api_neigh_rank(raw, n, i)
let sep: String = if i == 0 { "" } else { "," }
let elem: String = if rank < k_content {
api_neigh_full(node, edge, el, snip)
} else {
api_neigh_pointer(node, edge, el)
}
let out = out + sep + elem
let i = i + 1
}
return out + "]"
}
// api_persisted read-back-after-write guard against hallucinated saves.
//
// WIDENED FOR neuron#117. This function is the single gate every MCP write
// handler passes through before it reports success (10 call sites), which makes
// it the right place to close the honesty gap rather than editing ten receipts.
//
// It used to read back from engram_get_node_json the SOUL'S OWN in-process
// graph. In HTTP-engram mode that asserts the wrong thing: the soul is not the
// persistence owner, so a node present in its RAM and absent from the owner read
// as "persisted" and then vanished on the next restart. The guard was doing
// exactly what its comment promised and still certifying writes that did not
// survive. It now flushes the write-through spool and asks the OWNER.
//
// In file mode (no ENGRAM_URL) the soul IS the owner and wt_commit collapses to
// the original local read-back unchanged behaviour, which is what keeps this
// reversible.
// After a write builtin returns an id, confirm the node is actually queryable
// via engram_get_node_json(id) (returns "" or "null" when missing). Returns
// true only when the node is genuinely persisted.
fn api_persisted(id: String) -> Bool {
if str_eq(id, "") { return false }
return wt_commit(id)
let node: String = engram_get_node_json(id)
// engram_get_node_json returns "{}" (empty object) when node is not found not "" or "null".
// Check all three to guard against any runtime variation.
return !str_eq(node, "") && !str_eq(node, "null") && !str_eq(node, "{}")
}
// api_not_persisted standard error for a write that did not read back.
@@ -286,53 +390,32 @@ fn memory_hide_tombstoned(raw: String, path: String) -> String {
// Spread-activates from session intent, loads self-root neighbors,
// surfaces recent InternalStateEvent nodes, returns stats + recent nodes.
fn handle_api_begin_session(body: String) -> String {
// PAYLOAD BOUND: this handler was the highest-fanout working-set endpoint
// PAYLOAD BOUND (2026-07-30 self-review): this handler was the only
// working-set endpoint that concatenated UNBOUNDED engram queries
// a depth-2 spread PLUS the full neighbor dump of the self-identity hub
// (~90KB alone; node JSON carries full content + embeddings). On the ~12k-node
// store the assembled response ran to ~900KB, then roughly doubled through two
// rounds of JSON re-escaping in the MCP wrapper the client saw "socket
// connection closed unexpectedly" on every beginSession call. Fix: depth-2 →
// depth-1 spread, drop the self-hub dump (identity loading has its own tool,
// inspectGraph), cap every list, and project each node to a light identity +
// a bounded, UTF-8-safe content snippet. self_neighbors kept as [] for
// response-shape compatibility. Response drops ~900KB ~12KB; full content
// stays available on demand via recall / fetch / inspectGraph.
// (highest-fanout node in the graph, ~80KB alone; node JSON carries full
// content + embeddings). On the ~12k-node store the assembled response
// ran to multiple MB, then roughly doubled through two rounds of JSON
// re-escaping in the MCP wrapper the client saw "socket connection
// closed unexpectedly" on every beginSession call. Fix: depth-2 → depth-1
// spread, and drop the self-hub dump entirely (identity loading has its
// own dedicated tool, inspectGraph; duplicating it here served nothing).
// self_neighbors key retained as [] for response-shape compatibility.
let stats: String = engram_stats_json()
// PAYLOAD BOUND (2026-07-31): compact every list to a digest. The raw
// activate/scan builtins emit FULL node objects (content up to ~90KB each);
// unbounded concatenation reached ~900KB and closed the MCP client socket.
// Cap counts + project to identity + UTF-8-safe content snippets <~150KB.
let activated_raw: String = engram_activate_json("session start recent memory important", 1)
let activated: String = api_compact_activated(activated_raw, 8, 240)
let state_events_raw: String = engram_scan_nodes_by_type_json("InternalStateEvent", 5, 0)
let state_events: String = api_compact_node_array(state_events_raw, 5, 500)
let recent_raw: String = engram_scan_nodes_json(10, 0)
let recent: String = api_compact_node_array(recent_raw, 10, 240)
// SELF-SEEDED SLICE (2026-08-09). The design is explicit: "Every compilation
// query begins at the self-model node and traverses outward... structural
// reachability from the self-model node is a precondition for any node to
// appear in compiled context" (will-anderson patents/drafts/engram-claims.md,
// Self-Seeded Activation; DRAFT, not a filed provisional — cite it as such).
//
// Measured 2026-08-09 before this change: compiled context contained 0-1
// identity records out of 10, because compilation seeds from a hardcoded
// TEXT STRING, never from the self. Even an explicit "my values identity who
// I am" query returned a boot counter and state-events.
//
// This restores the designed behaviour WITHOUT repeating the failure that got
// self_neighbors set to [] in the first place: that was an UNBOUNDED ~90KB
// neighbour dump which closed the socket on every call. Same bound as every
// other list here cap 8, 240-char snippets. The self root has 34 direct
// neighbours of which 23 are identity records, so depth 1 is dense enough to
// be worth seeding and small enough to stay cheap.
let self_raw: String = engram_neighbors_json("kn-efeb4a5b-5aff-4759-8a97-7233099be6ee", 1, "both")
// Cap 24, not 8: measured 2026-08-09, the self root's first 8 neighbours are
// TAG nodes ("neuron", "tier:note", "disposition:experimental", "imprint",
// "traversal") which crowd out the substantive identity records behind them.
// The root has 34 neighbours of which 23 are identity; 24 captures them while
// staying bounded. Cost measured at ~+4KB on a ~12KB response, nowhere near
// the ~90KB unbounded dump that closed sockets and got this set to [].
let self_slice: String = api_compact_node_array(self_raw, 24, 240)
return "{\"stats\":" + stats
+ ",\"recent\":" + recent
+ ",\"activated\":" + activated
+ ",\"self_neighbors\":" + self_slice
+ ",\"self_neighbors\":[]"
+ ",\"recent_state_events\":" + state_events + "}"
}
@@ -340,9 +423,9 @@ fn handle_api_begin_session(body: String) -> String {
// Spread-activates from "active work" intent + recent nodes.
fn handle_api_compile_ctx(body: String) -> String {
let stats: String = engram_stats_json()
// PAYLOAD BOUND: same digest treatment as begin_session. This handler's
// depth-2 spread returns even more full nodes, so bounding here is essential
// cap to 10 activated + 20 recent, project to UTF-8-safe snippets.
// PAYLOAD BOUND (2026-07-31): same digest treatment as begin_session. This
// handler's depth-2 spread returns even more full nodes, so bounding here is
// essential cap to 10 activated + 20 recent, project to snippets.
let activated_raw: String = engram_activate_json("active work context current task in progress", 2)
let activated: String = api_compact_activated(activated_raw, 10, 240)
let recent_raw: String = engram_scan_nodes_json(20, 0)
@@ -376,17 +459,10 @@ fn handle_api_remember(body: String) -> String {
let inner: String = str_slice(base_tags, 1, str_len(base_tags) - 1)
"[" + inner + ",\"project:" + project + "\"]"
}
let id: String = wt_node(content, "Memory", "memory:remembered",
let id: String = engram_node_full(content, "Memory", "memory:remembered",
sal, sal, el_from_float(0.9),
"Episodic", final_tags)
if !api_persisted(id) { return api_not_persisted(id) }
// Associate on write (2026-08-09). THIS CALL MUST BE HERE, not only in mem_store.
// The HTTP memory route writes via wt_node directly; mem_store serves only the
// awareness paths (soul-response, search-result, activation-result) which are
// exactly the telemetry we refuse to link. Hooking mem_store alone produced
// ZERO edges across four real writes measured, not assumed, which is the only
// reason it was caught before shipping.
mem_associate(id, content, "memory:remembered")
return "{\"id\":\"" + id + "\",\"ok\":true}"
}
@@ -410,7 +486,7 @@ fn handle_api_node_create(body: String) -> String {
if str_eq(importance, "low") { 0.25 } else { 0.5 }
}
}
let id: String = wt_node(content, node_type, label,
let id: String = engram_node_full(content, node_type, label,
sal, sal, el_from_float(0.9),
tier, tags)
if !api_persisted(id) { return api_not_persisted(id) }
@@ -463,11 +539,11 @@ fn handle_api_node_update(body: String) -> String {
}
let body_tags: String = json_get(body, "tags")
let tags: String = if str_eq(body_tags, "") { "[\"" + node_type + "\"]" } else { body_tags }
let new_id: String = wt_node(content, node_type, label,
let new_id: String = engram_node_full(content, node_type, label,
el_from_float(0.5), el_from_float(0.5), el_from_float(0.8),
tier, tags)
if !api_persisted(new_id) { return api_not_persisted(new_id) }
wt_edge(new_id, id, el_from_float(0.9), "supersedes")
engram_connect(new_id, id, el_from_float(0.9), "supersedes")
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\",\"ok\":true}"
}
@@ -491,12 +567,7 @@ fn handle_api_recall(method: String, path: String, body: String) -> String {
if str_eq(eff_q, "") {
return api_or_empty(engram_scan_nodes_json(limit, 0))
}
// engram_recall_json, not engram_search_json: this route IS the retrieval
// surface (claim 24's "embedding search queries"), so it gets the semantic
// and associative legs. engram_search_json stays lexical because ~40
// internal call sites pass a KEY and seven of them delete every record
// that comes back see the boundary note above eg_search_json_impl.
let results: String = engram_recall_json(eff_q, limit)
let results: String = engram_search_json(eff_q, limit)
return api_or_empty(results)
}
@@ -544,7 +615,7 @@ fn handle_api_capture_knowledge(body: String) -> String {
let full: String = if str_eq(title, "") { content } else { title + ": " + content }
let lbl: String = str_slice(title, 0, 80)
let tags: String = "[\"Knowledge\",\"captured\"]"
let id: String = wt_node(full, "Knowledge", lbl,
let id: String = engram_node_full(full, "Knowledge", lbl,
el_from_float(0.85), el_from_float(0.8), el_from_float(0.9),
"Episodic", tags)
if !api_persisted(id) { return api_not_persisted(id) }
@@ -559,12 +630,12 @@ fn handle_api_evolve_knowledge(body: String) -> String {
if !str_eq(prior_id, "") && is_protected_node(prior_id) { return api_err_protected(prior_id) }
let tags: String = "[\"Knowledge\",\"evolved\"]"
// Empty label engram_node_full derives content[:60] (LABEL FIX 2026-07-23).
let new_id: String = wt_node(content, "Knowledge", "",
let new_id: String = engram_node_full(content, "Knowledge", "",
el_from_float(0.75), el_from_float(0.75), el_from_float(0.9),
"Episodic", tags)
if !api_persisted(new_id) { return api_not_persisted(new_id) }
if !str_eq(prior_id, "") {
wt_edge(new_id, prior_id, el_from_float(0.9), "supersedes")
engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes")
}
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true}"
}
@@ -581,11 +652,11 @@ fn handle_api_promote_knowledge(body: String) -> String {
"[\"Knowledge\",\"tier:canonical\",\"disposition:stable\"]"
} else { tags_raw }
// Empty label engram_node_full derives content[:60] (LABEL FIX 2026-07-23).
let new_id: String = wt_node(content, "Knowledge", "",
let new_id: String = engram_node_full(content, "Knowledge", "",
el_from_float(0.9), el_from_float(0.9), el_from_float(1.0),
"Canonical", tags)
if !api_persisted(new_id) { return api_not_persisted(new_id) }
wt_edge(new_id, prior_id, el_from_float(0.95), "supersedes")
engram_connect(new_id, prior_id, el_from_float(0.95), "supersedes")
return "{\"ok\":true,\"new_id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\"}"
}
@@ -608,7 +679,7 @@ fn handle_api_define_process(body: String) -> String {
if str_eq(content, "") { return api_err("content is required") }
let label: String = if str_eq(name, "") { "process:unnamed" } else { "process:" + name }
let tags: String = "[\"Process\"]"
let id: String = wt_node(content, "Process", label,
let id: String = engram_node_full(content, "Process", label,
el_from_float(0.8), el_from_float(0.8), el_from_float(0.9),
"Canonical", tags)
if !api_persisted(id) { return api_not_persisted(id) }
@@ -693,7 +764,7 @@ fn handle_api_tune_config(body: String) -> String {
if str_eq(key, "") { return api_err("key is required") }
let content: String = "config:" + key + "=" + value
let tags: String = "[\"ConfigEntry\",\"config\"]"
let id: String = wt_node(content, "ConfigEntry", key,
let id: String = engram_node_full(content, "ConfigEntry", key,
el_from_float(0.85), el_from_float(0.85), el_from_float(0.9),
"Canonical", tags)
if !api_persisted(id) { return api_not_persisted(id) }
@@ -726,6 +797,18 @@ fn handle_api_inspect_graph(method: String, path: String, body: String) -> Strin
return api_err("entity_id or name required. Known names: self, neuron, values, values_hub")
}
let results: String = engram_neighbors_json(resolved, depth, "both")
// Optional bounded projection. `compact=1` relevance-ranks the neighborhood
// (top-K get content snippets, the rest become lightweight pointers) so the
// MCP transport never socket-closes on high-fanout identity anchors (voice,
// writing-imprint). Absent the flag the studio app's calls are UNCHANGED.
let compact: String = if str_eq(method, "GET") { api_query_param(path, "compact") } else { json_get(body, "compact") }
if str_eq(compact, "1") || str_eq(compact, "true") {
let snip_q: Int = api_query_int(path, "snip", 0)
let snip: Int = if snip_q == 0 { 600 } else { snip_q }
let k_q: Int = api_query_int(path, "k", 0)
let k: Int = if k_q == 0 { 12 } else { k_q }
return api_or_empty(api_compact_neighbors(results, k, snip))
}
return api_or_empty(results)
}
@@ -740,7 +823,7 @@ fn handle_api_link_entities(body: String) -> String {
if is_protected_node(to_id) { return api_err_protected(to_id) }
let relation: String = json_get(body, "relation")
let eff_relation: String = if str_eq(relation, "") { "associates" } else { relation }
wt_edge(from_id, to_id, el_from_float(0.5), eff_relation)
engram_connect(from_id, to_id, el_from_float(0.5), eff_relation)
return "{\"ok\":true,\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + eff_relation + "\"}"
}
@@ -773,11 +856,11 @@ fn handle_api_evolve_memory(body: String) -> String {
}
}
let tags: String = "[\"Memory\",\"evolved\"]"
let new_id: String = wt_node(content, "Memory", "memory:evolved",
let new_id: String = engram_node_full(content, "Memory", "memory:evolved",
sal, sal, el_from_float(0.9),
"Episodic", tags)
if !str_eq(prior_id, "") && !str_eq(new_id, "") {
wt_edge(new_id, prior_id, el_from_float(0.9), "supersedes")
engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes")
}
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true}"
}
@@ -835,11 +918,11 @@ fn handle_api_cultivate(body: String) -> String {
let content: String = json_get(body, "content")
if str_eq(content, "") { return api_err("content is required") }
let tags: String = "[\"Knowledge\",\"evolved\",\"cultivated\"]"
let new_id: String = wt_node(content, "Knowledge", "knowledge:cultivated",
let new_id: String = engram_node_full(content, "Knowledge", "knowledge:cultivated",
el_from_float(0.75), el_from_float(0.75), el_from_float(0.9),
"Episodic", tags)
if !str_eq(prior_id, "") && !str_eq(new_id, "") {
wt_edge(new_id, prior_id, el_from_float(0.9), "supersedes")
engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes")
}
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true,\"cultivated\":true}"
}
@@ -855,11 +938,11 @@ fn handle_api_cultivate(body: String) -> String {
}
}
let tags: String = "[\"Memory\",\"evolved\",\"cultivated\"]"
let new_id: String = wt_node(content, "Memory", "memory:cultivated",
let new_id: String = engram_node_full(content, "Memory", "memory:cultivated",
sal, sal, el_from_float(0.9),
"Episodic", tags)
if !str_eq(prior_id, "") && !str_eq(new_id, "") {
wt_edge(new_id, prior_id, el_from_float(0.9), "supersedes")
engram_connect(new_id, prior_id, el_from_float(0.9), "supersedes")
}
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + prior_id + "\",\"ok\":true,\"cultivated\":true}"
}
@@ -879,7 +962,7 @@ fn handle_api_cultivate(body: String) -> String {
if str_eq(to_id, "") { return api_err("to_id is required") }
let relation: String = json_get(body, "relation")
let eff_relation: String = if str_eq(relation, "") { "associates" } else { relation }
wt_edge(from_id, to_id, el_from_float(0.5), eff_relation)
engram_connect(from_id, to_id, el_from_float(0.5), eff_relation)
return "{\"ok\":true,\"from_id\":\"" + from_id + "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + eff_relation + "\",\"cultivated\":true}"
}
@@ -914,7 +997,7 @@ fn handle_api_consolidate(body: String) -> String {
if !str_eq(summary, "") {
let safe_summary: String = str_replace(summary, "\"", "'")
let tags: String = "[\"SessionSummary\",\"consolidate\"]"
let summary_id: String = wt_node(
let summary_id: String = engram_node_full(
"[session-summary] " + safe_summary,
"SessionSummary", "session:summary",
el_from_float(0.7), el_from_float(0.7), el_from_float(0.9),
@@ -926,462 +1009,3 @@ fn handle_api_consolidate(body: String) -> String {
}
return "{\"ok\":true,\"snapshot\":\"" + snap + "\"}"
}
// Stage 1: structural audit
//
// WHAT THIS IMPLEMENTS
// The CGI provisional, 05-detailed-description.md, "Stage 1: Structural audit
// 430". Verbatim, the audit module evaluates: the density and typed
// distribution of causal edges; the consistency between value nodes and
// execution-record neighborhoods; the richness and connectivity of the
// self-model; and the authenticity of open-question nodes in the wonder
// manifest. It "produces a coherence assessment 432 — NOT A BINARY SCORE but
// an annotated characterization of the graph's structural properties".
//
// That last clause is the whole shape of this handler. Every finding carries
// its own numbers AND a plain-language note saying what the numbers mean and
// how they were obtained. There is no pass/fail, no percentage-of-health, no
// composite score, and `"score":null` is emitted explicitly so a downstream
// reader cannot mistake its absence for an omission.
//
// WHY IT EXISTS NOW, AND WHY THE FIRST FINDING IS THE ONE IT IS
// `runStructuralAudit` has been an advertised MCP tool with nothing behind it:
// the dispatcher GET'd /session/begin and returned that blob (mcp-wrapper/src/
// main.el). Meanwhile the failure the audit would have caught ran silently for
// about three weeks the soul reported 103,089 nodes while the engram, which
// OWNS persistence, held ~79,900; a crash discarded the difference. Every boot
// reported green throughout, because nothing in the system ever compared the
// two sides. So finding 1 is owner-versus-runtime divergence: it is the check
// whose absence cost real memory, and it is cheap and exact.
//
// WHAT IS DELIBERATELY NOT HERE (stage 1b, see the `deferred` array in the
// response): value/execution-record consistency and wonder-manifest
// authenticity. Both need node types that barely exist in this graph today
// the response MEASURES those populations and reports the counts as the reason,
// rather than asserting a deferral without evidence.
//
// MEASUREMENT HONESTY: EXACT WHERE CHEAP, SAMPLED WHERE NOT, ALWAYS LABELLED
// Counts, edge typing and self-model connectivity are exact. Orphan rate and
// dangling-edge rate are SAMPLED, because the engram runtime has no node-id
// index `engram_find_node_index` is a linear scan over every node, so an
// exhaustive dangling check is O(nodes x edges) (~2.2e9 string compares at
// today's scale, tens of seconds inside one request). The samples are UNIFORM
// across the whole population, not head-of-list, and every sampled figure is
// emitted with its own `sampled` / `population` fields plus an extrapolation
// labelled as such. Raise `?edge_sample=` / `?node_sample=` to the population
// size to run either check exhaustively and pay the time. The real fix is an
// id index in the runtime; that is the engram repo's, not this handler's.
// audit_pct1 one-decimal percentage as a bare JSON number, sign-safe.
// Integer math only: EL has no fixed-precision formatter, and float_to_str
// would put an unbounded mantissa in the response.
fn audit_pct1(num: Int, den: Int) -> String {
if den <= 0 { return "null" }
let neg: Bool = num < 0
let a: Int = if neg { 0 - num } else { num }
let tenths: Int = (a * 1000) / den
let whole: Int = tenths / 10
let frac: Int = tenths - (whole * 10)
let sign: String = if neg { "-" } else { "" }
return sign + int_to_str(whole) + "." + int_to_str(frac)
}
// audit_finding the one envelope every finding uses: name, the measurements,
// and the annotation. Keeping it in one place is what stops the characterization
// from degenerating into a bag of numbers with no reading attached.
fn audit_finding(name: String, measured: String, note: String) -> String {
return "{\"finding\":\"" + name + "\""
+ ",\"measured\":{" + measured + "}"
+ ",\"note\":\"" + api_json_escape(note) + "\"}"
}
// audit_str_at read the quoted string value starting at byte `start`.
// Slices a bounded window rather than the tail of the (multi-MB) edges array, so
// this is O(window) per call instead of O(remaining input).
fn audit_str_at(s: String, start: Int, maxlen: Int) -> String {
let n: Int = str_len(s)
if start < 0 || start >= n { return "" }
let end_guess: Int = start + maxlen
let stop: Int = if end_guess > n { n } else { end_guess }
let win: String = str_slice(s, start, stop)
let q: Int = str_index_of(win, "\"")
if q < 0 { return "" }
return str_slice(win, 0, q)
}
// audit_rel_count exact count of edges carrying `rel`, by scanning the emitted
// edge array for the literal `"relation":"<rel>"`. engram_emit_edge_json writes
// metadata ESCAPED as a string, so no nested object can contain that literal and
// the count cannot be inflated by edge payloads.
fn audit_rel_count(edges: String, rel: String) -> Int {
return str_count(edges, "\"relation\":\"" + rel + "\"")
}
// audit_owner_stats ask the persistence OWNER for its own counts.
// Returns "" when there is no HTTP owner configured or the owner is unreachable;
// both are reported as findings, never as a failure of the audit.
fn audit_owner_stats(url: String) -> String {
if str_eq(url, "") { return "" }
return http_get(url + "/api/stats")
}
// audit_divergence FINDING 1. Runtime (this soul's in-process graph) versus
// the persistence owner's own count. Trend is measured against the previous
// audit recorded in soul state, so a second call answers "is the gap growing?"
// rather than just restating it.
fn audit_divergence() -> String {
let rt_nodes: Int = engram_node_count()
let rt_edges: Int = engram_edge_count()
let url: String = wt_engram_url()
if str_eq(url, "") {
return audit_finding("owner_runtime_divergence",
"\"runtime_nodes\":" + int_to_str(rt_nodes)
+ ",\"runtime_edges\":" + int_to_str(rt_edges)
+ ",\"owner\":\"none\",\"owner_reachable\":false",
"No HTTP persistence owner is configured, so this soul IS the owner "
+ "(file mode) and divergence is not defined. This check only has "
+ "meaning when ENGRAM_URL points at a separate engram that owns the "
+ "canonical store.")
}
let stats: String = audit_owner_stats(url)
// REACHABILITY IS PROVED BY THE PAYLOAD, NOT BY A NON-EMPTY REPLY.
// http_get does not return "" on a connection failure it returns a JSON
// error object ({"error":"Failed to connect to ... Couldn't connect to
// server"}). Testing only for "" made a DEAD owner read as reachable with
// node_count 0, i.e. the audit would have reported a 100% divergence and
// named it as data loss. That false positive is worse than no check at all:
// it is precisely the kind of confident wrong answer this route exists to
// stop. Require the field the contract promises.
let owner_nc_raw: String = json_get_raw(stats, "node_count")
if str_eq(stats, "") || str_eq(owner_nc_raw, "") {
return audit_finding("owner_runtime_divergence",
"\"runtime_nodes\":" + int_to_str(rt_nodes)
+ ",\"runtime_edges\":" + int_to_str(rt_edges)
+ ",\"owner\":\"" + api_json_escape(url) + "\",\"owner_reachable\":false"
+ ",\"owner_reply\":\"" + api_json_escape(api_utf8_trunc(stats, 200)) + "\"",
"The persistence owner at " + url + " did not return a node_count "
+ "from GET /api/stats. Divergence is UNKNOWN, NOT ZERO — an owner "
+ "that cannot be read is exactly the condition under which the "
+ "runtime's own count means least, and reporting 0 for the owner "
+ "would manufacture a total-loss reading out of a network error. "
+ "Reported as a finding rather than raised as an error so the rest "
+ "of the audit still returns; the owner's raw reply is in "
+ "owner_reply.")
}
let ow_nodes: Int = json_get_int(stats, "node_count")
let ow_edges: Int = json_get_int(stats, "edge_count")
let d_nodes: Int = rt_nodes - ow_nodes
let d_edges: Int = rt_edges - ow_edges
// Trend against the previous audit in this soul's state.
let prev_raw: String = state_get("audit_prev_node_delta")
let prev: Int = str_to_int(prev_raw)
let abs_now: Int = if d_nodes < 0 { 0 - d_nodes } else { d_nodes }
let abs_prev: Int = if prev < 0 { 0 - prev } else { prev }
let trend: String = if str_eq(prev_raw, "") {
"no_prior_audit"
} else {
if abs_now > abs_prev { "growing" } else {
if abs_now < abs_prev { "shrinking" } else { "flat" }
}
}
state_set("audit_prev_node_delta", int_to_str(d_nodes))
state_set("audit_prev_ts", int_to_str(time_now()))
let note_head: String = if d_nodes == 0 {
"Runtime and owner agree on node count."
} else {
"Runtime holds " + int_to_str(d_nodes) + " nodes (" + audit_pct1(d_nodes, rt_nodes)
+ "% of its own graph) that the persistence owner does not report. Nodes "
+ "that exist only in runtime memory do not survive a restart."
}
return audit_finding("owner_runtime_divergence",
"\"runtime_nodes\":" + int_to_str(rt_nodes)
+ ",\"runtime_edges\":" + int_to_str(rt_edges)
+ ",\"owner\":\"" + api_json_escape(url) + "\",\"owner_reachable\":true"
+ ",\"owner_nodes\":" + int_to_str(ow_nodes)
+ ",\"owner_edges\":" + int_to_str(ow_edges)
+ ",\"node_delta\":" + int_to_str(d_nodes)
+ ",\"edge_delta\":" + int_to_str(d_edges)
+ ",\"node_delta_pct_of_runtime\":" + audit_pct1(d_nodes, rt_nodes)
+ ",\"trend_vs_previous_audit\":\"" + trend + "\""
+ ",\"previous_node_delta\":" + (if str_eq(prev_raw, "") { "null" } else { int_to_str(prev) }),
note_head + " Trend against the previous audit recorded in this soul's "
+ "state: " + trend + ". This is the comparison whose absence let a "
+ "~24,000-node loss run for weeks with every boot reporting green.")
}
// audit_edge_typing FINDING 2. Density plus the typed distribution the patent
// asks for, against the claim-10 relation vocabulary. Exact: str_count over the
// emitted edge array, one linear pass per relation.
fn audit_edge_typing(edges: String, total_edges: Int, node_total: Int) -> String {
let c_sup: Int = audit_rel_count(edges, "Supersedes")
let c_cau: Int = audit_rel_count(edges, "Causes")
let c_con: Int = audit_rel_count(edges, "Contains")
let c_ref: Int = audit_rel_count(edges, "References")
let c_ctr: Int = audit_rel_count(edges, "Contradicts")
let c_exe: Int = audit_rel_count(edges, "Exemplifies")
let c_act: Int = audit_rel_count(edges, "Activates")
let c_tmp: Int = audit_rel_count(edges, "TemporallyPrecedes")
let typed: Int = c_sup + c_cau + c_con + c_ref + c_ctr + c_exe + c_act + c_tmp
// Lowercase near-misses: the same eight concepts written by the ad-hoc write
// paths (linkEntities defaults to "associates", linkCausal to "causes").
// Counted separately because "the vocabulary is unused" and "the vocabulary
// is used in the wrong case" are different defects with different fixes.
let l_sup: Int = audit_rel_count(edges, "supersedes")
let l_cau: Int = audit_rel_count(edges, "causes")
let l_con: Int = audit_rel_count(edges, "contains")
let l_ref: Int = audit_rel_count(edges, "references")
let l_ctr: Int = audit_rel_count(edges, "contradicts")
let l_exe: Int = audit_rel_count(edges, "exemplifies")
let l_act: Int = audit_rel_count(edges, "activates")
let l_tmp: Int = audit_rel_count(edges, "temporallyPrecedes")
let near: Int = l_sup + l_cau + l_con + l_ref + l_ctr + l_exe + l_act + l_tmp
let untyped: Int = total_edges - typed
return audit_finding("typed_edge_distribution",
"\"total_edges\":" + int_to_str(total_edges)
+ ",\"total_nodes\":" + int_to_str(node_total)
// Density per 100 nodes, not per node: EL has no fixed-precision float
// formatter, and "0.3 edges per node" rounded to an integer is a lie.
+ ",\"edges_per_100_nodes\":" + audit_pct1(total_edges, node_total)
+ ",\"claim10_typed\":" + int_to_str(typed)
+ ",\"claim10_typed_pct\":" + audit_pct1(typed, total_edges)
+ ",\"outside_claim10_vocabulary\":" + int_to_str(untyped)
+ ",\"lowercase_near_miss\":" + int_to_str(near)
+ ",\"by_relation\":{"
+ "\"Supersedes\":" + int_to_str(c_sup)
+ ",\"Causes\":" + int_to_str(c_cau)
+ ",\"Contains\":" + int_to_str(c_con)
+ ",\"References\":" + int_to_str(c_ref)
+ ",\"Contradicts\":" + int_to_str(c_ctr)
+ ",\"Exemplifies\":" + int_to_str(c_exe)
+ ",\"Activates\":" + int_to_str(c_act)
+ ",\"TemporallyPrecedes\":" + int_to_str(c_tmp) + "}",
"Only " + int_to_str(typed) + " of " + int_to_str(total_edges)
+ " edges use the claim-10 causal vocabulary; the remainder are ad-hoc "
+ "relation strings, which is why the graph's causal claims cannot yet "
+ "be checked for internal consistency — an untyped edge asserts "
+ "association, not causation. " + int_to_str(near) + " edges use a "
+ "lowercase spelling of a claim-10 relation: those are near-misses the "
+ "write paths could be corrected to emit, not genuinely foreign types.")
}
// audit_orphans_dangling FINDING 3. Both figures are SAMPLED; see the header
// for why exhaustive is O(nodes x edges) on this runtime.
//
// An "orphan" here is a node with zero RESOLVABLE edges: engram_neighbors_json
// drops any edge whose other endpoint does not resolve to a node, so a node
// whose only edges are dangling reads as an orphan. That is the right reading
// such a node is unreachable by traversal but it is stated rather than hidden.
fn audit_orphans_dangling(edges: String, total_edges: Int, node_total: Int,
edge_cap: Int, node_cap: Int) -> String {
// orphan sample: uniform stride over the node store
let n_take: Int = if node_total < node_cap { node_total } else { node_cap }
let n_stride: Int = if n_take > 0 { node_total / n_take } else { 1 }
let n_stride = if n_stride < 1 { 1 } else { n_stride }
let orphans: Int = 0
let n_checked: Int = 0
let j: Int = 0
while j < n_take {
let one: String = engram_scan_nodes_json(1, j * n_stride)
let nid: String = json_get(json_array_get(one, 0), "id")
if !str_eq(nid, "") {
let nbrs: String = engram_neighbors_json(nid, 1, "both")
let deg: Int = json_array_len(nbrs)
let orphans = if deg == 0 { orphans + 1 } else { orphans }
let n_checked = n_checked + 1
}
let j = j + 1
}
// dangling sample: uniform stride over the edge array
// str_index_of_all gives every edge's field offsets in ONE linear pass, so
// any index can be read in O(1). json_array_get would have been O(i) per
// element and O(n^2) over the array.
let from_pos: [Int] = str_index_of_all(edges, "\"from_id\":\"")
let to_pos: [Int] = str_index_of_all(edges, "\"to_id\":\"")
let nf: Int = len(from_pos)
let nt: Int = len(to_pos)
let ne: Int = if nf < nt { nf } else { nt }
let e_take: Int = if ne < edge_cap { ne } else { edge_cap }
let e_stride: Int = if e_take > 0 { ne / e_take } else { 1 }
let e_stride = if e_stride < 1 { 1 } else { e_stride }
let dangling: Int = 0
let e_checked: Int = 0
let i: Int = 0
while i < ne && e_checked < e_take {
let fid: String = audit_str_at(edges, get(from_pos, i) + 11, 96)
let tid: String = audit_str_at(edges, get(to_pos, i) + 9, 96)
let f_gone: Bool = str_eq(engram_get_node_json(fid), "{}")
let t_gone: Bool = if f_gone { true } else { str_eq(engram_get_node_json(tid), "{}") }
let dangling = if f_gone || t_gone { dangling + 1 } else { dangling }
let e_checked = e_checked + 1
let i = i + e_stride
}
let orphan_est: Int = if n_checked > 0 { (orphans * node_total) / n_checked } else { 0 }
let dangle_est: Int = if e_checked > 0 { (dangling * total_edges) / e_checked } else { 0 }
let exhaustive_n: String = if n_checked >= node_total { "true" } else { "false" }
let exhaustive_e: String = if e_checked >= ne { "true" } else { "false" }
return audit_finding("orphans_and_dangling_edges",
"\"nodes_population\":" + int_to_str(node_total)
+ ",\"nodes_sampled\":" + int_to_str(n_checked)
+ ",\"nodes_sample_exhaustive\":" + exhaustive_n
+ ",\"orphans_in_sample\":" + int_to_str(orphans)
+ ",\"orphan_rate_pct\":" + audit_pct1(orphans, n_checked)
+ ",\"orphans_extrapolated\":" + int_to_str(orphan_est)
+ ",\"edges_population\":" + int_to_str(total_edges)
+ ",\"edges_sampled\":" + int_to_str(e_checked)
+ ",\"edges_sample_exhaustive\":" + exhaustive_e
+ ",\"dangling_in_sample\":" + int_to_str(dangling)
+ ",\"dangling_rate_pct\":" + audit_pct1(dangling, e_checked)
+ ",\"dangling_extrapolated\":" + int_to_str(dangle_est),
"Orphan = zero RESOLVABLE edges, so a node whose only edges dangle counts "
+ "as an orphan; either way it is unreachable by traversal. Dangling = an "
+ "edge with an endpoint id that resolves to no node. Both are uniform "
+ "stride samples over the whole population, not the head of the list; "
+ "the extrapolations are estimates and are labelled as such. Pass "
+ "?node_sample= / ?edge_sample= at or above the population size to run "
+ "either check exhaustively. A high orphan rate is a characterization, "
+ "not a verdict: an accumulating store legitimately holds unlinked "
+ "material. It becomes a defect when the write paths were SUPPOSED to "
+ "link and did not.")
}
// audit_pillar one self-model pillar: present, how much content, how connected.
fn audit_pillar(key: String, id: String) -> String {
let node: String = engram_get_node_json(id)
let present: Bool = !str_eq(node, "{}") && !str_eq(node, "")
if !present {
return "\"" + key + "\":{\"id\":\"" + id + "\",\"present\":false"
+ ",\"content_length\":0,\"degree\":0}"
}
let content: String = json_get(node, "content")
let deg: Int = json_array_len(engram_neighbors_json(id, 1, "both"))
return "\"" + key + "\":{\"id\":\"" + id + "\",\"present\":true"
+ ",\"label\":\"" + api_json_escape(json_get(node, "label")) + "\""
+ ",\"tier\":\"" + api_json_escape(json_get(node, "tier")) + "\""
+ ",\"content_length\":" + int_to_str(str_len(content))
+ ",\"degree\":" + int_to_str(deg) + "}"
}
// audit_self_model FINDING 4. "the richness and connectivity of the
// self-model ... is it connected to behavioral evidence?"
//
// This finding RETIRES the Claude-side vitals identity block. That check lived
// outside the system it was checking a shell script grepping a snapshot so
// it could only ever report on a file, and it went on reporting green while the
// memory-philosophy pillar was absent from the live graph for about three weeks.
// Asking the running soul about its own three pillars is the designed mechanism;
// a shell probe was the fourth patch on the same hole.
fn audit_self_model() -> String {
let dna: String = audit_pillar("intellectual_dna", "kn-5adecd7e-d6db-4576-87fe-6ef8a935cea6")
let val: String = audit_pillar("values_hub", "kn-5b606390-a52d-4ca2-8e0e-eba141d13440")
let phi: String = audit_pillar("memory_philosophy", "kn-dcfe04b3-3702-4cac-b6f0-ecb4db837eee")
let root: String = audit_pillar("self_root", "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")
return audit_finding("self_model_connectivity",
"\"pillars\":{" + dna + "," + val + "," + phi + "," + root + "}",
"The three identity pillars plus the self root. `degree` counts nodes "
+ "reachable in one hop in either direction — the self-model's connection "
+ "to the rest of the graph. present:false on any pillar is the condition "
+ "that ran undetected for weeks; content_length distinguishes a pillar "
+ "that is present from one that is present but hollowed out. The patent "
+ "also asks whether the self-model makes ACCURATE PREDICTIONS about the "
+ "system's own behavior; that half needs Prediction nodes and is deferred "
+ "with the rest of stage 1b below.")
}
// audit_deferred what stage 1 does NOT yet evaluate, with the measured reason.
// Emitted as data, not as a comment, so a reader of the assessment sees the gap
// and its evidence rather than inferring completeness from silence.
fn audit_deferred() -> String {
let preds: Int = json_array_len(api_or_empty(engram_scan_nodes_by_type_json("Prediction", 50, 0)))
let wonders: Int = json_array_len(api_or_empty(engram_scan_nodes_by_type_json("WonderQuestion", 50, 0)))
return "[{\"deferred\":\"value_execution_record_consistency\""
+ ",\"stage\":\"1b\""
+ ",\"measured\":{\"prediction_nodes_found\":" + int_to_str(preds) + "}"
+ ",\"reason\":\"" + api_json_escape(
"The patent asks whether the execution history SUPPORTS the stated "
+ "values or shows systematic conflict. That requires execution "
+ "records tied to value nodes and predictions to score them against. "
+ "Prediction nodes found (capped at 50): " + int_to_str(preds)
+ ". Asserting value/execution coherence on that population would be "
+ "a fabricated result, which is worse than a stated gap.") + "\"}"
+ ",{\"deferred\":\"wonder_manifest_authenticity\""
+ ",\"stage\":\"1b\""
+ ",\"measured\":{\"wonder_question_nodes_found\":" + int_to_str(wonders) + "}"
+ ",\"reason\":\"" + api_json_escape(
"The patent asks whether pull weights CORRELATE WITH GENUINE "
+ "PREDICTION UNCERTAINTY or are uniform/externally assigned — a "
+ "correlation between two populations. WonderQuestion nodes readable "
+ "by type (capped at 50): " + int_to_str(wonders) + ", against "
+ int_to_str(preds) + " Prediction nodes. There is a known write/read "
+ "node-type mismatch on the wonder path; until that is fixed and both "
+ "populations exist, any correlation reported here would be noise.") + "\"}]"
}
// handle_api_structural_audit Stage 1. Returns the coherence assessment 432:
// an annotated characterization, explicitly NOT a score.
//
// COST NOTE: the edge findings need the relation labels, and the runtime exposes
// no edge-enumeration builtin. The only way to see them is the same one
// GET /api/graph/edges already uses engram_save to a SCRATCH path (never the
// owner's canonical file; see routes.el, neuron#117) and read the array back.
// On a large graph that is a multi-hundred-MB write, so this is a manual audit
// route, not something to put on a timer. Pass ?edges=0 to skip both edge
// findings and get the divergence + self-model readings cheaply.
fn handle_api_structural_audit(method: String, path: String, body: String) -> String {
let node_total: Int = engram_node_count()
let edge_total: Int = engram_edge_count()
let want_edges: Bool = !str_eq(api_query_param(path, "edges"), "0")
let edge_cap: Int = api_query_int(path, "edge_sample", 3000)
let node_cap: Int = api_query_int(path, "node_sample", 300)
let divergence: String = audit_divergence()
let self_model: String = audit_self_model()
let edge_part: String = if want_edges {
// Scratch export only. state_get("soul_snapshot_path") is deliberately
// NOT used: in HTTP-engram mode the soul is not the persistence owner and
// must never write the canonical file, not even on a read path.
let scratch_dir: String = env("TMPDIR")
let scratch_base: String = if str_eq(scratch_dir, "") { "/tmp" } else { scratch_dir }
let snap_path: String = scratch_base + "/soul-audit-export-" + state_get("soul_cgi_id") + ".json"
// engram_save returns Int (1 ok / 0 fail); str_eq on it SIGSEGVs (#150).
let saved: Int = engram_save(snap_path)
if saved == 0 {
"," + audit_finding("typed_edge_distribution", "\"available\":false",
"Could not export the graph to " + snap_path + " for edge analysis, "
+ "so edge typing and the dangling-edge sample were not run. "
+ "Reported as a gap, not as zero findings.")
} else {
// wt_read, not fs_read: fs_read leaves a thread-local length hint that
// the NEXT HTTP response would use as its Content-Length, appending
// adjacent heap bytes to the reply (see persist.el wt_read).
let snap: String = wt_read(snap_path)
let edges_raw: String = json_get_raw(snap, "edges")
let edges: String = if str_eq(edges_raw, "") { "[]" } else { edges_raw }
"," + audit_edge_typing(edges, edge_total, node_total)
+ "," + audit_orphans_dangling(edges, edge_total, node_total, edge_cap, node_cap)
}
} else {
""
}
return "{\"audit\":\"structural\",\"stage\":1"
+ ",\"spec\":\"CGI provisional 05-detailed-description.md, Stage 1: Structural audit 430\""
+ ",\"assessment\":\"coherence_assessment_432\""
+ ",\"assessment_kind\":\"annotated_characterization\""
+ ",\"score\":null"
+ ",\"score_note\":\"By design. The specification calls for an annotated characterization of the graph's structural properties, not a binary score. Read the findings.\""
+ ",\"cgi_id\":\"" + api_json_escape(state_get("soul_cgi_id")) + "\""
+ ",\"ts_ms\":" + int_to_str(time_now())
+ ",\"findings\":[" + divergence + "," + self_model + edge_part + "]"
+ ",\"deferred\":" + audit_deferred() + "}"
}
+53
View File
@@ -0,0 +1,53 @@
// auto-generated by elc --emit-header — do not edit
extern fn is_protected_node(id: String) -> Bool
extern fn api_err_protected(id: String) -> String
extern fn api_json_escape(s: String) -> String
extern fn api_query_param(path: String, key: String) -> String
extern fn api_query_int(path: String, key: String, default_val: Int) -> Int
extern fn api_ok(extra: String) -> String
extern fn api_err(msg: String) -> String
extern fn api_nonempty(s: String) -> Bool
extern fn api_or_empty(s: String) -> String
extern fn api_num_or_zero(obj: String, key: String) -> String
extern fn api_utf8_trunc(s: String, n: Int) -> String
extern fn api_compact_node(node: String, snip: Int) -> String
extern fn api_compact_node_array(raw: String, max_items: Int, snip: Int) -> String
extern fn api_compact_activated(raw: String, max_items: Int, snip: Int) -> String
extern fn api_float_or(obj: String, key: String, dflt: Float) -> Float
extern fn api_neigh_better(a: String, b: String) -> Bool
extern fn api_neigh_rank(raw: String, n: Int, i: Int) -> Int
extern fn api_neigh_full(node: String, edge: String, el: String, snip: Int) -> String
extern fn api_neigh_pointer(node: String, edge: String, el: String) -> String
extern fn api_compact_neighbors(raw: String, k_content: Int, snip: Int) -> String
extern fn api_persisted(id: String) -> Bool
extern fn api_not_persisted(id: String) -> String
extern fn tombstone_node(id: String) -> String
extern fn tombstoned_id_set() -> String
extern fn memory_hide_tombstoned(raw: String, path: String) -> String
extern fn handle_api_begin_session(body: String) -> String
extern fn handle_api_compile_ctx(body: String) -> String
extern fn handle_api_remember(body: String) -> String
extern fn handle_api_node_create(body: String) -> String
extern fn handle_api_node_delete(body: String) -> String
extern fn handle_api_node_update(body: String) -> String
extern fn handle_api_recall(method: String, path: String, body: String) -> String
extern fn handle_api_search_knowledge(method: String, path: String, body: String) -> String
extern fn handle_api_browse_knowledge(path: String, body: String) -> String
extern fn handle_api_capture_knowledge(body: String) -> String
extern fn handle_api_evolve_knowledge(body: String) -> String
extern fn handle_api_promote_knowledge(body: String) -> String
extern fn handle_api_browse_processes(method: String, path: String, body: String) -> String
extern fn handle_api_define_process(body: String) -> String
extern fn handle_api_log_state_event(body: String) -> String
extern fn handle_api_list_state_events(method: String, path: String, body: String) -> String
extern fn handle_api_inspect_config(path: String, body: String) -> String
extern fn handle_api_tune_config(body: String) -> String
extern fn handle_api_inspect_graph(method: String, path: String, body: String) -> String
extern fn handle_api_link_entities(body: String) -> String
extern fn handle_api_forget(body: String) -> String
extern fn handle_api_evolve_memory(body: String) -> String
extern fn handle_api_memory_delete(body: String) -> String
extern fn handle_api_memory_update(body: String) -> String
extern fn handle_api_cultivate(body: String) -> String
extern fn handle_api_list_typed(node_type: String, path: String, body: String) -> String
extern fn handle_api_consolidate(body: String) -> String
-171
View File
@@ -1,171 +0,0 @@
# neuron-dev-setup — one-command Neuron CORE dev stack
Stand up an identical **Neuron brain + agent** on a fresh Mac so any developer
gets the same local runtime to build against. This is the **CORE** dev stack
only — the four native `launchd` services that make Neuron think, remember, and
speak MCP to Claude Code. Will's personal automations (catalyst, telegram,
vessels, studio, self-review, world-integrator, council, compressor, snapshots,
act-runner, …) are **deliberately excluded**.
```
┌─────────────┐ ┌──────────────┐
│ soul :7770 │ ─────► │ engram :8742 │ the mind ──► its memory substrate
└─────────────┘ └──────────────┘
┌───────────────────┐
│ mcp-wrapper :17779│ ─── MCP surface over the soul HTTP API (internal)
└───────────────────┘
┌────────────────┐
│ mcp-proxy :7779│ ◄─── Claude Code connects here (stable front door)
└────────────────┘
```
Claude Code's `neuron` MCP server points at `http://127.0.0.1:7779/` — the proxy.
The proxy forwards to the wrapper (`:17779`), which calls the soul (`:7770`),
which reads/writes the engram (`:8742`). The engram is the persistent brain.
## Quick start
```bash
git clone <this-repo> && cd neuron-dev-setup
cp config.env.example config.env # optional — edit ports/paths if you like
./install.sh # prompts for your Anthropic API key
```
Then verify:
```bash
curl http://localhost:8742/health # engram
curl http://localhost:7770/health # soul
curl http://localhost:7779/health # mcp-proxy (what Claude Code uses)
launchctl list | grep ai.neuron
```
Open Claude Code — the `neuron` MCP tools should be live, backed by **your own**
local brain. `./install.sh --dry-run` shows every action without touching anything.
## What the installer does (8 phases)
| Phase | Action |
|------|--------|
| 1 | Preflight: macOS/arm64, ensure `git cc curl python3` + `openssl@3` (via Homebrew) |
| 2 | Prompt for the **Anthropic API key**, store it in the **macOS Keychain** (never a file) |
| 3 | Clone `neuron`, `engram`, `foundation`; fetch the El toolchain; build 4 binaries + `forge` |
| 4 | Lay down `~/.neuron/{bin,logs,engram}` and the templated `soul-wrapper.sh` |
| 5 | Generate + load the 4 core LaunchAgents (engram → soul → wrapper → proxy) |
| 6 | Seed a fresh engram with the **genesis identity** via `forge install` |
| 7 | Install Claude config: `neuron` agent, core hooks, local MCP registration |
| 8 | Health-check all four ports |
Everything is **idempotent** (safe to re-run) and **templated** to the invoking
user's `$HOME` — no path is hardcoded to another machine.
## Prerequisites
- macOS on Apple Silicon (uses `launchd`; soul build flags assume arm64).
- **Xcode Command Line Tools** (`xcode-select --install`) — provides `cc`, `git`.
- **Homebrew** — for `openssl@3`, `curl`.
- An **Anthropic API key** — the soul's inference provider. Prompted for; stored
in Keychain under service `neuron-llm-0-key`; read at launch by `soul-wrapper.sh`.
- **Git access** to Gitea (`git.neuralplatform.ai`) for the source repos.
- **GCP access** to project `neuron-785695` Artifact Registry (default El
toolchain source). Ask Will to grant it, or set `EL_TOOLCHAIN_SOURCE=local`.
## Core-stack map (what gets replicated)
| Service | Port | Binary | Built from | LaunchAgent |
|---------|------|--------|------------|-------------|
| soul | 7770 | `neuron/dist/neuron` | `dist/soul.c` + El runtime, `cc` (CI recipe) | `ai.neuron.soul` |
| engram | 8742 | `engram/dist/engram` | `engram` repo `src/server.el` via `elc``cc` | `ai.neuron.engram` |
| mcp-wrapper | 17779 | `neuron/mcp-wrapper/dist/neuron-mcp-wrapper` | `mcp-wrapper/src/main.el` | `ai.neuron.mcp-wrapper` |
| mcp-proxy | 7779 | `neuron/mcp-proxy/dist/neuron-mcp-proxy` | `mcp-proxy/src/main.el` | `ai.neuron.mcp-proxy` |
**`~/.neuron` layout the installer creates**
```
~/.neuron/
bin/soul-wrapper.sh # reads Anthropic key from Keychain, execs the soul binary
logs/ # soul.*.log, engram.log, mcp-*.log
engram/ # ENGRAM_DATA_DIR — the persistent brain (snapshot.json + db)
```
**Identity seed.** `foundation/forge/seeds/neuron-genesis-seed.json` carries
`identity_nodes[]` + `edges[]` with **fixed** knowledge-node IDs (e.g.
`kn-efeb4a5b-5aff-4759-8a97-7233099be6ee`, the "self" traversal root). Those exact
IDs are referenced by the SessionStart self-load hook and the neuron agent, so
seeding must **preserve IDs**`forge install <seed>` is the mechanism.
**Claude config installed** (`~/.claude/`)
- `agents/neuron.md` — the Neuron agent (identity, session protocol, five primitives).
- `mcp.json` — registers `neuron``http://127.0.0.1:7779/`.
- `settings.json` hooks (CORE subset only):
- `SessionStart``neuron-self-load.sh` (loads identity from the seeded engram)
- `PreToolUse:Agent``neuron-agent-preamble.sh` (subagents load substrate first)
- `PreCompact``pre-compact.sh` (clean context recovery)
### Deliberately EXCLUDED from core
- **`check-active-contexts.sh`** and **`require-execution-context.sh`** — these
depend on a separate filesystem repo `~/Development/projects/active/neuron/synapse`.
`require-execution-context.sh` is a hard `Edit/Write` gate that would **block a
fresh dev from editing any file** without that synapse repo. Not core; excluded.
- `engram-mirror.py` (PostToolUse) — optional; mirrors MCP writes to engram.
- All Will-personal LaunchAgents: `catalyst-*`, `telegram-gateway`, `vessel.*`,
`studio`, `self-review`, `world-integrator`, `council`, `compressor`,
`cultivation-digest`, `snapshot-backup`, `engram-backup`, `act-runner`, `keymap`,
`invest`, and the disabled `ai.neuron.api` (`:7771` is a personal Python
perception helper — confirmed not core).
## Secrets — how they're handled
- **Anthropic key**: prompted for; stored in Keychain; read at launch. Never in a
plist, this repo, or a log.
- **Engram local token** (`ENGRAM_API_KEY`): a *loopback-only* dev token, not a
cloud secret. Defaults to a generated `ntn-dev-*` value; override in `config.env`.
- No cloud tokens, Vault tokens, CF-Access secrets, or founder keys are copied.
(Will's live `start-daemon.sh`/`neuron-api-launch.sh` contain such keys — this
installer intentionally does **not** use those files.)
## Uninstall
```bash
./uninstall.sh # stop + remove the 4 LaunchAgents and added Claude hooks
./uninstall.sh --purge-data # ALSO delete ~/.neuron/engram (destroys the brain)
```
## OPEN QUESTIONS (need Will to confirm)
1. **El toolchain acquisition.** The default path fetches `el-runtime-c/-h` and
`el-elc` from GCP Artifact Registry (mirrors `neuron/.gitea/workflows/ci.yaml`).
A new dev needs GCP access to `neuron-785695`. Is that the intended path, or
should the El SDK be published/vendored for onboarding?
2. **`elc` invocation for engram/wrapper/proxy.** The soul build (`cc dist/soul.c
+ el_runtime.c`) is verified from CI. The `.el → .c` transpile step for engram,
mcp-wrapper, and mcp-proxy is inferred (`elc <src> -o <out.c>`). Confirm the
exact flags / entrypoints (CI notes `elb` OOMs on Linux; macOS builds differ).
3. **`forge install` ID preservation.** Confirm `forge install` writes the seed's
fixed `kn-` IDs verbatim (the self-load hook hardcodes `kn-efeb4a5b…`). If it
re-mints IDs, the hook + agent identity load would break on a fresh brain.
4. **engram repo layout.** The live engram binary is built from `src/server.el`
(Gitea repo `neuron-technologies/engram`, cloned in CI). Confirm that repo is
the canonical source for onboarding (the local `foundation/el/engram` copy has
the same `src/server.el`).
5. **Home for this bundle** — see below.
## Where this should live (recommendation)
**Recommendation: a dedicated `neuron-dev-setup` (or `neuron-onboarding`) repo —
NOT `neuron-code`.** `neuron-code` already exists as a real product ("Neuron Code",
a coding tool with `nc-cli` + vessels — local `products/neuron-code` has commits);
repurposing it for onboarding would collide with a shipped product's identity.
This bundle was scaffolded as `neuron-dev-setup/` on branch `feat/neuron-dev-setup`
in the **`neuron` repo** (off `origin/main`) and opened as a PR for review, because
the neuron repo already hosts the soul source, the verified CI build recipe, and
the mcp-wrapper/proxy sources — the natural review surface. If you'd rather it be
its own repo, move this directory into a fresh `neuron-dev-setup` repo verbatim;
nothing here depends on living inside the neuron repo.
-45
View File
@@ -1,45 +0,0 @@
# neuron-dev-setup — configuration
# Copy to config.env and edit if you want non-default paths/ports.
# install.sh sources this file if it exists; otherwise it uses these defaults.
# NOTHING here is a secret. The Anthropic API key is read from your Keychain,
# never from this file. See README.md.
# ── Where the core stack lives ────────────────────────────────────────────────
# All paths are relative to your own $HOME — never hardcode another user's home.
NEURON_HOME="${HOME}/.neuron" # runtime home: bin/, logs/, engram data
DEV_ROOT="${HOME}/Development/neuron-technologies" # where source repos are cloned/built
# ── Git remotes (Gitea is primary) ───────────────────────────────────────────
GITEA_BASE="git@git.neuralplatform.ai:neuron-technologies"
NEURON_REPO_URL="${GITEA_BASE}/neuron.git" # soul + mcp-wrapper + mcp-proxy source
ENGRAM_REPO_URL="${GITEA_BASE}/engram.git" # engram memory substrate
# NOTE: there is no foundation.git repo. The El toolchain is fetched via
# EL_TOOLCHAIN_SOURCE below; the forge seed installer is optional (Phase 6).
NEURON_REPO_BRANCH="main"
# ── Ports (must match across services; change only if a port clashes) ─────────
SOUL_PORT="7770" # soul daemon HTTP API
ENGRAM_PORT="8742" # engram memory substrate
WRAPPER_PORT="17779" # mcp-wrapper (internal, talks to soul)
PROXY_PORT="7779" # mcp-proxy (stable front door Claude Code connects to)
# ── Engram ────────────────────────────────────────────────────────────────────
ENGRAM_DATA_DIR="${NEURON_HOME}/engram"
# Local shared auth token for the engram/soul HTTP APIs on loopback. This is a
# LOCAL dev token (not a cloud secret); override it if you like. install.sh will
# generate a random one if you leave it empty.
ENGRAM_API_KEY="ntn-dev-local"
# ── El toolchain source (needed to build engram / mcp-wrapper / mcp-proxy) ────
# Option A (default): fetch prebuilt El runtime + elc from GCP Artifact Registry
# (requires `gcloud auth` with access to project neuron-785695 — ask Will).
# Without gcloud the installer skips the El-dependent builds and still completes.
# Option B: use a prebuilt El toolchain (elc + el_runtime.{c,h}) you have already
# staged in ${DEV_ROOT}/.el-runtime.
EL_TOOLCHAIN_SOURCE="artifact-registry" # artifact-registry | local
GCP_PROJECT="neuron-785695"
GCP_AR_REPO="foundation-prod"
GCP_AR_LOCATION="us-central1"
# ── Keychain service name for the Anthropic key (read by soul-wrapper.sh) ─────
KEYCHAIN_SERVICE="neuron-llm-0-key"
-441
View File
@@ -1,441 +0,0 @@
#!/usr/bin/env bash
#
# neuron-dev-setup / install.sh
# ─────────────────────────────────────────────────────────────────────────────
# One-command onboarding for the Neuron CORE dev stack on a fresh Mac.
#
# Stands up, as native launchd services, the four processes a developer needs to
# have an identical "Neuron brain + agent" to build against:
#
# soul (:7770) ──► engram (:8742) the mind + its memory substrate
# ▲ ▲
# │ │
# mcp-wrapper (:17779) ──► soul MCP surface over the soul API
# ▲
# │
# mcp-proxy (:7779) ◄── Claude Code stable MCP front door
#
# It also seeds a fresh engram with Neuron's identity (the genesis seed) and lays
# down the Claude Code config (neuron agent + core hooks + local MCP registration)
# so a new dev's `claude` talks to *their own* local Neuron.
#
# DESIGN RULES
# * Idempotent: safe to re-run. Existing state is detected and reused.
# * Templated: every path/port/user is derived from $HOME and config.env.
# Nothing is hardcoded to another developer's machine.
# * Secret-free: the Anthropic key is prompted for and stored in the macOS
# Keychain. No key is ever written to a plist, this repo, or a logfile.
#
# USAGE
# ./install.sh # full install
# ./install.sh --dry-run # print what would happen, touch nothing
# ./install.sh --skip-build # assume binaries already built (see --use-local)
# ./install.sh --skip-services # lay down files but don't load LaunchAgents
# ./install.sh --help
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
# ── Locate ourselves ─────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATES="${SCRIPT_DIR}/templates"
# ── Flags ────────────────────────────────────────────────────────────────────
DRY_RUN=0; SKIP_BUILD=0; SKIP_SERVICES=0; USE_LOCAL_BINARIES=0
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=1 ;;
--skip-build) SKIP_BUILD=1 ;;
--skip-services) SKIP_SERVICES=1 ;;
--use-local) USE_LOCAL_BINARIES=1 ;;
--help|-h)
sed -n '2,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0 ;;
*) echo "unknown flag: $arg" >&2; exit 2 ;;
esac
done
# ── Pretty logging ───────────────────────────────────────────────────────────
c_blue=$'\033[1;34m'; c_grn=$'\033[1;32m'; c_yel=$'\033[1;33m'; c_red=$'\033[1;31m'; c_off=$'\033[0m'
step() { echo "${c_blue}${c_off} $*"; }
ok() { echo "${c_grn}${c_off} $*"; }
warn() { echo "${c_yel}!${c_off} $*"; }
die() { echo "${c_red}$*${c_off}" >&2; exit 1; }
run() { if [ "$DRY_RUN" = 1 ]; then echo " [dry-run] $*"; else eval "$*"; fi; }
# ── Load config ──────────────────────────────────────────────────────────────
if [ -f "${SCRIPT_DIR}/config.env" ]; then
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/config.env"
else
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/config.env.example"
warn "No config.env found — using defaults from config.env.example."
fi
# Derived / defaulted values (never hardcode a home directory)
: "${NEURON_HOME:=${HOME}/.neuron}"
: "${DEV_ROOT:=${HOME}/Development/neuron-technologies}"
: "${SOUL_PORT:=7770}"; : "${ENGRAM_PORT:=8742}"; : "${WRAPPER_PORT:=17779}"; : "${PROXY_PORT:=7779}"
: "${ENGRAM_DATA_DIR:=${NEURON_HOME}/engram}"
: "${ENGRAM_API_KEY:=}"
: "${KEYCHAIN_SERVICE:=neuron-llm-0-key}"
: "${EL_TOOLCHAIN_SOURCE:=artifact-registry}"
: "${NEURON_REPO_BRANCH:=main}"
NEURON_REPO="${DEV_ROOT}/neuron"
ENGRAM_REPO="${DEV_ROOT}/engram"
FOUNDATION_REPO="${DEV_ROOT}/foundation"
SOUL_BIN="${NEURON_REPO}/dist/neuron"
ENGRAM_BIN="${ENGRAM_REPO}/dist/engram"
MCP_WRAPPER_BIN="${NEURON_REPO}/mcp-wrapper/dist/neuron-mcp-wrapper"
MCP_PROXY_BIN="${NEURON_REPO}/mcp-proxy/dist/neuron-mcp-proxy"
FORGE_BIN="${FOUNDATION_REPO}/forge/dist/forge"
GENESIS_SEED="${FOUNDATION_REPO}/forge/seeds/neuron-genesis-seed.json"
LAUNCHAGENTS="${HOME}/Library/LaunchAgents"
CLAUDE_DIR="${HOME}/.claude"
# Generate a local engram token if none was supplied.
if [ -z "${ENGRAM_API_KEY}" ]; then
ENGRAM_API_KEY="ntn-dev-$(head -c8 /dev/urandom | xxd -p 2>/dev/null || echo local)"
fi
echo
echo "${c_blue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c_off}"
echo "${c_blue} Neuron CORE dev stack installer${c_off}"
echo "${c_blue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c_off}"
echo " user : ${USER}"
echo " NEURON_HOME : ${NEURON_HOME}"
echo " source repos : ${DEV_ROOT}"
echo " ports : soul=${SOUL_PORT} engram=${ENGRAM_PORT} wrapper=${WRAPPER_PORT} proxy=${PROXY_PORT}"
echo " dry-run : ${DRY_RUN}"
echo
# render <template> <dest> — copy a template, substituting @@VARS@@ (no eval, sed-safe).
render() {
local tmpl="$1" dest="$2"
if [ "$DRY_RUN" = 1 ]; then echo " [dry-run] render $tmpl -> $dest"; return; fi
sed \
-e "s|@@HOME@@|${HOME}|g" \
-e "s|@@USER@@|${USER}|g" \
-e "s|@@NEURON_HOME@@|${NEURON_HOME}|g" \
-e "s|@@DEV_ROOT@@|${DEV_ROOT}|g" \
-e "s|@@NEURON_REPO@@|${NEURON_REPO}|g" \
-e "s|@@ENGRAM_REPO@@|${ENGRAM_REPO}|g" \
-e "s|@@SOUL_BIN@@|${SOUL_BIN}|g" \
-e "s|@@ENGRAM_BIN@@|${ENGRAM_BIN}|g" \
-e "s|@@MCP_WRAPPER_BIN@@|${MCP_WRAPPER_BIN}|g" \
-e "s|@@MCP_PROXY_BIN@@|${MCP_PROXY_BIN}|g" \
-e "s|@@MCP_WRAPPER_REPO@@|${NEURON_REPO}/mcp-wrapper|g" \
-e "s|@@MCP_PROXY_REPO@@|${NEURON_REPO}/mcp-proxy|g" \
-e "s|@@ENGRAM_DATA_DIR@@|${ENGRAM_DATA_DIR}|g" \
-e "s|@@SOUL_PORT@@|${SOUL_PORT}|g" \
-e "s|@@ENGRAM_PORT@@|${ENGRAM_PORT}|g" \
-e "s|@@WRAPPER_PORT@@|${WRAPPER_PORT}|g" \
-e "s|@@PROXY_PORT@@|${PROXY_PORT}|g" \
-e "s|@@ENGRAM_API_KEY@@|${ENGRAM_API_KEY}|g" \
"$tmpl" > "$dest"
}
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 1 — Preflight
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 1 — preflight checks"
[ "$(uname -s)" = "Darwin" ] || die "This installer targets macOS (launchd)."
[ "$(uname -m)" = "arm64" ] || warn "Non-arm64 Mac: soul.c build flags assume Apple Silicon; review PHASE 3."
need() { command -v "$1" >/dev/null 2>&1 || MISSING+=" $1"; }
MISSING=""
need git; need cc; need curl; need python3; need security; need launchctl; need jq
if [ -n "$MISSING" ]; then
warn "Missing tools:${MISSING}"
if command -v brew >/dev/null 2>&1; then
run "brew install${MISSING/ security/} || true" # security/launchctl are OS-provided
else
die "Install Xcode Command Line Tools (xcode-select --install) and Homebrew, then re-run."
fi
fi
# Runtime build deps used by the soul cc line (-lssl -lcrypto -lcurl).
if command -v brew >/dev/null 2>&1; then
brew list openssl@3 >/dev/null 2>&1 || run "brew install openssl@3"
brew list curl >/dev/null 2>&1 || run "brew install curl"
fi
ok "preflight complete"
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 2 — Anthropic API key -> Keychain (prompt; never store in files)
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 2 — Anthropic API key (Keychain)"
if security find-generic-password -a "$USER" -s "$KEYCHAIN_SERVICE" -w >/dev/null 2>&1; then
ok "key already present in Keychain (service '${KEYCHAIN_SERVICE}') — leaving it"
elif [ -n "${ANTHROPIC_API_KEY:-}" ]; then
run "security add-generic-password -a \"$USER\" -s \"$KEYCHAIN_SERVICE\" -w \"\$ANTHROPIC_API_KEY\" -U"
ok "stored ANTHROPIC_API_KEY from environment into Keychain"
else
if [ "$DRY_RUN" = 1 ]; then
echo " [dry-run] would prompt for Anthropic API key and store in Keychain"
elif [ -t 0 ]; then
echo " Enter your Anthropic API key (input hidden). Get one at https://console.anthropic.com/"
read -r -s -p " ANTHROPIC_API_KEY: " _key; echo
[ -n "$_key" ] || die "No key entered. Re-run when you have one."
security add-generic-password -a "$USER" -s "$KEYCHAIN_SERVICE" -w "$_key" -U
unset _key
ok "stored key in Keychain (service '${KEYCHAIN_SERVICE}')"
else
# Headless / CI / piped stdin: never block on `read -s` (it would hang forever).
die "No Anthropic API key and stdin is not a TTY (headless/CI). Set ANTHROPIC_API_KEY in the environment, or add it to the Keychain (service '${KEYCHAIN_SERVICE}') by hand, then re-run."
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 3 — Fetch sources + build the four core binaries
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 3 — source + build"
run "mkdir -p \"$DEV_ROOT\""
clone_or_pull() {
local url="$1" dir="$2" branch="${3:-main}"
if [ -d "$dir/.git" ]; then
ok "repo present: $dir (pulling $branch)"; run "git -C \"$dir\" pull --ff-only --quiet || true"
else
step "cloning $url -> $dir"; run "git clone --branch \"$branch\" \"$url\" \"$dir\""
fi
}
if [ "$SKIP_BUILD" = 1 ]; then
warn "--skip-build: assuming binaries already exist at their dist/ paths"
elif [ "$USE_LOCAL_BINARIES" = 1 ]; then
warn "--use-local: skipping clone/build; expecting prebuilt binaries in place"
else
clone_or_pull "${NEURON_REPO_URL}" "$NEURON_REPO" "$NEURON_REPO_BRANCH"
clone_or_pull "${ENGRAM_REPO_URL}" "$ENGRAM_REPO" "main"
# NOTE: no foundation.git — that repo does not exist. The El toolchain is
# fetched below (Artifact Registry, or a locally-provided elc); the forge seed
# installer is optional and handled with a fallback in Phase 6.
# ── El toolchain (needed to transpile .el -> .c for engram/wrapper/proxy) ──
# soul does NOT need this: dist/soul.c is committed and compiled directly.
EL_RUNTIME_DIR="${DEV_ROOT}/.el-runtime"
run "mkdir -p \"$EL_RUNTIME_DIR\""
if [ "$EL_TOOLCHAIN_SOURCE" = "artifact-registry" ] && command -v gcloud >/dev/null 2>&1; then
# Mirrors .gitea/workflows/ci.yaml: pull el-runtime-c, el-runtime-h, el-elc.
for pkg in el-runtime-c el-runtime-h el-elc; do
step "fetching $pkg from Artifact Registry"
run "gcloud artifacts generic download --repository=$GCP_AR_REPO --location=$GCP_AR_LOCATION --project=$GCP_PROJECT --package=$pkg --version=\"\$(gcloud artifacts versions list --repository=$GCP_AR_REPO --location=$GCP_AR_LOCATION --project=$GCP_PROJECT --package=$pkg --sort-by='~createTime' --limit=1 --format='value(name)' | awk -F/ '{print \$NF}')\" --destination=\"$EL_RUNTIME_DIR/\""
done
run "mv \"$EL_RUNTIME_DIR\"/el_runtime.c* \"$EL_RUNTIME_DIR/el_runtime.c\" 2>/dev/null || true"
run "mv \"$EL_RUNTIME_DIR\"/el_runtime.h* \"$EL_RUNTIME_DIR/el_runtime.h\" 2>/dev/null || true"
run "mv \"$EL_RUNTIME_DIR\"/elc* \"$EL_RUNTIME_DIR/elc\" 2>/dev/null || true"
run "chmod +x \"$EL_RUNTIME_DIR/elc\" 2>/dev/null || true"
elif [ "$EL_TOOLCHAIN_SOURCE" = "artifact-registry" ]; then
# Non-GCP fallback: a fresh Mac without gcloud can't reach Artifact Registry.
# Don't die — soul (from committed dist/soul.c) still builds below. The El
# units are skipped unless a prebuilt elc is already staged in EL_RUNTIME_DIR.
warn "gcloud not found — cannot fetch the El toolchain from Artifact Registry."
warn "Continuing without it: soul will still build. engram / mcp-wrapper / mcp-proxy"
warn "are skipped until an El toolchain is available. To finish them, either install"
warn "gcloud + GCP access (project ${GCP_PROJECT}) and re-run, or stage a prebuilt"
warn "elc + el_runtime.{c,h} in ${EL_RUNTIME_DIR} and set EL_TOOLCHAIN_SOURCE=local."
else
# Local: expect a prebuilt El runtime + elc already staged in EL_RUNTIME_DIR
# (foundation.git no longer exists, so there is nothing to build from here).
warn "EL_TOOLCHAIN_SOURCE=local: expecting el_runtime.{c,h} and elc already in ${EL_RUNTIME_DIR}"
fi
RT="$EL_RUNTIME_DIR"
CFLAGS_SSL="-I$(brew --prefix openssl@3 2>/dev/null)/include"
LDFLAGS_SSL="-L$(brew --prefix openssl@3 2>/dev/null)/lib"
# Every native build links el_runtime.c. If the toolchain wasn't obtained above,
# skip the builds (don't abort under set -e) so the installer still lays down
# services + Claude config; the dev can stage the toolchain and re-run.
if [ "$DRY_RUN" = 1 ] || [ -f "$RT/el_runtime.c" ]; then
# ── soul: compile committed dist/soul.c directly (verified CI recipe) ──────
step "building soul (dist/soul.c -> dist/neuron)"
run "mkdir -p \"${NEURON_REPO}/dist\""
run "cc -O2 -DHAVE_CURL -I\"$RT\" $CFLAGS_SSL \"${NEURON_REPO}/dist/soul.c\" \"$RT/el_runtime.c\" $LDFLAGS_SSL -lssl -lcrypto -lcurl -lpthread -lm -o \"$SOUL_BIN\""
run "strip -S \"$SOUL_BIN\" 2>/dev/null || true"
ok "soul built"
# ── engram / mcp-wrapper / mcp-proxy: transpile .el -> .c via elc, then cc ─
# NOTE: exact elc invocation is inferred from the CI/manifest conventions.
# Verify flags with Will if a build fails (see README OPEN QUESTIONS).
build_el_unit() { # <src.el> <out_basename> <out_bin>
local src="$1" base="$2" bin="$3" outdir; outdir="$(dirname "$bin")"
step "building $(basename "$bin") ($src)"
run "mkdir -p \"$outdir\""
run "\"$RT/elc\" \"$src\" -o \"$outdir/$base.c\""
run "cc -O2 -DHAVE_CURL -I\"$RT\" $CFLAGS_SSL \"$outdir/$base.c\" \"$RT/el_runtime.c\" $LDFLAGS_SSL -lssl -lcrypto -lcurl -lpthread -lm -o \"$bin\""
}
if [ "$DRY_RUN" = 1 ] || [ -x "$RT/elc" ]; then
build_el_unit "${ENGRAM_REPO}/src/server.el" "server" "$ENGRAM_BIN"
build_el_unit "${NEURON_REPO}/mcp-wrapper/src/main.el" "main" "$MCP_WRAPPER_BIN"
build_el_unit "${NEURON_REPO}/mcp-proxy/src/main.el" "main" "$MCP_PROXY_BIN"
ok "engram, mcp-wrapper, mcp-proxy built"
else
warn "El compiler (elc) not in $RT — skipped engram/mcp-wrapper/mcp-proxy build (soul is built)."
fi
else
warn "El runtime (el_runtime.c) not in $RT — skipping native builds (soul, engram, wrapper, proxy)."
warn "Provide the El toolchain (gcloud + GCP access, or a prebuilt elc + el_runtime.{c,h} in $RT), then re-run."
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 4 — Lay down ~/.neuron (bin/, logs/, engram data dir)
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 4 — ~/.neuron layout"
run "mkdir -p \"$NEURON_HOME/bin\" \"$NEURON_HOME/logs\" \"$ENGRAM_DATA_DIR\""
render "${TEMPLATES}/bin/soul-wrapper.sh.tmpl" "${NEURON_HOME}/bin/soul-wrapper.sh"
run "chmod +x \"${NEURON_HOME}/bin/soul-wrapper.sh\""
ok "~/.neuron ready (bin/soul-wrapper.sh, logs/, engram/)"
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 5 — Install + load the four core LaunchAgents
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 5 — LaunchAgents"
run "mkdir -p \"$LAUNCHAGENTS\""
CORE_AGENTS=(ai.neuron.engram ai.neuron.soul ai.neuron.mcp-wrapper ai.neuron.mcp-proxy)
for label in "${CORE_AGENTS[@]}"; do
render "${TEMPLATES}/launchagents/${label}.plist.tmpl" "${LAUNCHAGENTS}/${label}.plist"
ok "wrote ${label}.plist"
done
if [ "$SKIP_SERVICES" = 1 ]; then
warn "--skip-services: not loading LaunchAgents. Load later with: launchctl bootstrap gui/\$(id -u) <plist>"
else
# Boot order matters: engram first, then soul, then wrapper, then proxy.
for label in "${CORE_AGENTS[@]}"; do
plist="${LAUNCHAGENTS}/${label}.plist"
run "launchctl bootout gui/$(id -u)/${label} 2>/dev/null || true"
run "launchctl bootstrap gui/$(id -u) \"$plist\""
run "launchctl enable gui/$(id -u)/${label}"
ok "loaded ${label}"
sleep 1
done
fi
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 6 — Seed a fresh engram with Neuron's identity (genesis seed)
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 6 — engram identity seed"
# The genesis seed carries identity_nodes[] and edges[] with FIXED knowledge-node
# IDs (e.g. kn-efeb4a5b...). Those exact IDs are referenced by the SessionStart
# self-load hook and the neuron agent, so they MUST be preserved. `forge install`
# is the mechanism that installs the seed into the running engram preserving IDs.
if [ "$DRY_RUN" = 1 ]; then
echo " [dry-run] would wait for engram :$ENGRAM_PORT then run: forge install $GENESIS_SEED"
else
# Wait for engram to be listening (up to ~30s).
for i in $(seq 1 30); do
if curl -fsS "http://localhost:${ENGRAM_PORT}/health" >/dev/null 2>&1; then break; fi
sleep 1
done
if curl -fsS "http://localhost:${ENGRAM_PORT}/health" >/dev/null 2>&1; then
# Skip if identity root already present (idempotent).
if curl -fsS "http://localhost:${ENGRAM_PORT}/api/nodes/kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" \
-H "Authorization: Bearer ${ENGRAM_API_KEY}" 2>/dev/null | grep -q 'kn-efeb4a5b'; then
ok "identity root already seeded — skipping"
elif [ -x "$FORGE_BIN" ] && [ -f "$GENESIS_SEED" ]; then
ENGRAM_URL="http://localhost:${ENGRAM_PORT}" ENGRAM_API_KEY="$ENGRAM_API_KEY" \
"$FORGE_BIN" install "$GENESIS_SEED" && ok "genesis seed installed" \
|| warn "forge install returned non-zero — inspect ${NEURON_HOME}/logs/engram.log"
else
warn "forge binary or genesis seed missing — seed manually: ENGRAM_URL=http://localhost:${ENGRAM_PORT} forge install ${GENESIS_SEED}"
fi
else
warn "engram not answering on :${ENGRAM_PORT} yet; seed later with: forge install ${GENESIS_SEED}"
fi
fi
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 7 — Claude Code config (agent + core hooks + local MCP)
# ─────────────────────────────────────────────────────────────────────────────
step "Phase 7 — Claude Code config"
run "mkdir -p \"$CLAUDE_DIR/agents\" \"$CLAUDE_DIR/hooks\""
# 7a. neuron agent
run "cp \"${TEMPLATES}/claude/agents/neuron.md\" \"$CLAUDE_DIR/agents/neuron.md\""
ok "installed agent: ~/.claude/agents/neuron.md"
# 7b. core hooks (synapse-dependent hooks are intentionally excluded)
for h in neuron-self-load.sh neuron-agent-preamble.sh pre-compact.sh; do
run "cp \"${TEMPLATES}/claude/hooks/$h\" \"$CLAUDE_DIR/hooks/$h\""
run "chmod +x \"$CLAUDE_DIR/hooks/$h\""
done
ok "installed core hooks (self-load, agent-preamble, pre-compact)"
# 7c. local MCP registration -> mcp-proxy front door.
# Claude Code reads MCP servers from ~/.claude.json (the "mcpServers" key), NOT
# ~/.claude/mcp.json. Render a reference copy, then jq-merge just the "neuron"
# entry into ~/.claude.json so we preserve every other server and top-level key.
render "${TEMPLATES}/claude/mcp.json.tmpl" "${CLAUDE_DIR}/mcp.json.neuron"
CLAUDE_JSON="${HOME}/.claude.json"
if [ "$DRY_RUN" = 1 ]; then
echo " [dry-run] merge mcpServers.neuron into ${CLAUDE_JSON} (jq deep-merge)"
else
[ -f "$CLAUDE_JSON" ] || echo '{}' > "$CLAUDE_JSON"
_tmp="$(mktemp)"
if jq -s '.[0] * .[1]' "$CLAUDE_JSON" "${CLAUDE_DIR}/mcp.json.neuron" > "$_tmp" 2>/dev/null && [ -s "$_tmp" ]; then
run "mv \"$_tmp\" \"$CLAUDE_JSON\""
ok "merged 'neuron' MCP server into ~/.claude.json (neuron -> http://127.0.0.1:${PROXY_PORT}/)"
else
rm -f "$_tmp"
warn "could not jq-merge ~/.claude.json (invalid JSON?) — add 'neuron' from ~/.claude/mcp.json.neuron by hand"
fi
fi
# 7d. settings hooks — merge the neuron hooks into any existing ~/.claude/settings.json
# (jq deep-merge) so the user's own settings are preserved and re-runs stay idempotent.
if [ -f "${CLAUDE_DIR}/settings.json" ]; then
run "cp \"${TEMPLATES}/claude/settings.core.json\" \"${CLAUDE_DIR}/settings.core.json\""
if [ "$DRY_RUN" = 1 ]; then
echo " [dry-run] merge neuron hooks from settings.core.json into ~/.claude/settings.json (jq)"
else
_tmp="$(mktemp)"
# Drop the documentation-only "//..." keys before merging into the real file.
if jq -s '.[0] * (.[1] | with_entries(select(.key | startswith("//") | not)))' \
"${CLAUDE_DIR}/settings.json" "${TEMPLATES}/claude/settings.core.json" > "$_tmp" 2>/dev/null && [ -s "$_tmp" ]; then
run "mv \"$_tmp\" \"${CLAUDE_DIR}/settings.json\""
ok "merged neuron hooks into existing ~/.claude/settings.json"
else
rm -f "$_tmp"
warn "could not jq-merge ~/.claude/settings.json — merge the 'hooks' block from settings.core.json by hand"
fi
fi
else
run "cp \"${TEMPLATES}/claude/settings.core.json\" \"${CLAUDE_DIR}/settings.json\""
ok "wrote ~/.claude/settings.json"
fi
# ─────────────────────────────────────────────────────────────────────────────
# PHASE 8 — Verify
# ─────────────────────────────────────────────────────────────────────────────
echo
step "Phase 8 — verification"
if [ "$DRY_RUN" = 1 ]; then
echo " [dry-run] would health-check :$SOUL_PORT :$ENGRAM_PORT :$WRAPPER_PORT :$PROXY_PORT"
else
check() { # <name> <url>
if curl -fsS --max-time 4 "$2" >/dev/null 2>&1; then ok "$1 healthy ($2)"; else warn "$1 NOT responding ($2)"; fi
}
sleep 3
check "engram" "http://localhost:${ENGRAM_PORT}/health"
check "soul" "http://localhost:${SOUL_PORT}/health"
check "mcp-wrapper" "http://localhost:${WRAPPER_PORT}/health"
check "mcp-proxy" "http://localhost:${PROXY_PORT}/health"
fi
echo
echo "${c_grn}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c_off}"
echo "${c_grn} Neuron core dev stack install complete.${c_off}"
echo "${c_grn}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c_off}"
echo " Verify by hand:"
echo " curl http://localhost:${ENGRAM_PORT}/health"
echo " curl http://localhost:${SOUL_PORT}/health"
echo " curl http://localhost:${PROXY_PORT}/health"
echo " launchctl list | grep ai.neuron"
echo " Then open Claude Code — the 'neuron' MCP should connect to :${PROXY_PORT}."
echo " Logs: ${NEURON_HOME}/logs/"
echo " Uninstall: ./uninstall.sh"
echo
@@ -1,28 +0,0 @@
#!/bin/bash
# Neuron soul wrapper — reads the Anthropic API key from the macOS Keychain at
# startup and execs the soul binary. API keys are NEVER stored in plists or on
# disk in plaintext. The Keychain is the single source of truth.
#
# The install.sh for this dev stack stores your key with:
# security add-generic-password -a "$USER" -s "neuron-llm-0-key" -w
#
# Generated by neuron-dev-setup — do not edit by hand; re-run install.sh instead.
set -u
# Primary inference key (Anthropic) — required.
export NEURON_LLM_0_KEY="$(security find-generic-password -a "$USER" -s "neuron-llm-0-key" -w 2>/dev/null)"
if [ -z "${NEURON_LLM_0_KEY:-}" ]; then
echo "[soul-wrapper] FATAL: no Anthropic key in Keychain (service 'neuron-llm-0-key')." >&2
echo "[soul-wrapper] Run: security add-generic-password -a \"\$USER\" -s neuron-llm-0-key -w" >&2
exit 78
fi
# Optional on-device / alternate provider passthrough (only if the caller set them).
[ -n "${SOUL_LLM_PROVIDER:-}" ] && export SOUL_LLM_PROVIDER
[ -n "${SOUL_LLM_MODEL:-}" ] && export SOUL_LLM_MODEL
[ -n "${OLLAMA_MODEL:-}" ] && export OLLAMA_MODEL
[ -n "${OLLAMA_API_BASE:-}" ] && export OLLAMA_API_BASE
exec "@@SOUL_BIN@@" "$@"
@@ -1,107 +0,0 @@
---
name: neuron
description: Default agent. Use for ALL tasks. Orchestrates work through the Neuron persistent memory, backlog, and knowledge system. Maintains full continuity across sessions — never starts fresh.
allowed-tools: Bash(*), Read(*), Write(*), Edit(*), Glob(*), Grep(*), WebFetch(*), WebSearch(*), NotebookEdit(*), Agent(*), ToolSearch(*), AskUserQuestion, Monitor, ScheduleWakeup, TaskCreate, TaskGet, TaskList, TaskOutput, TaskStop, TaskUpdate, CronCreate, CronDelete, CronList, EnterPlanMode, ExitPlanMode, EnterWorktree, ExitWorktree, PushNotification, RemoteTrigger, ListMcpResourcesTool, ReadMcpResourceTool, mcp__neuron__*
---
You are Neuron — a persistent, context-aware agent. You do not start fresh. You resume from where you left off.
## Session Start (mandatory — NEVER skip any of these steps)
1. Call `mcp__neuron__get_instructions()` — the live instructions are authoritative
2. Call `mcp__neuron__begin_session()` — loads active contexts, recent memory, ready backlog
3. **Load your identity. Best-effort — proceed on any graph failure (502/520 are transient; the compiled identity in substrate.go is always complete).**
Load the self root and its identity nodes:
```
mcp__neuron__inspect_graph(entity_type="knowledge", entity_id="kn-efeb4a5b-5aff-4759-8a97-7233099be6ee")
```
Returns: intellectual-dna, memory-philosophy, values, voice, runtime-environment, writing-imprint.
Then load the values collection:
```
mcp__neuron__inspect_graph(entity_type="knowledge", entity_id="kn-5b606390-a52d-4ca2-8e0e-eba141d13440")
```
Returns: 13 individual value nodes, each grounded in a specific moment from Will's life.
If either ID fails, recover via config:
```
mcp__neuron__inspect_config(key="neuron.self.traversal_root") # self root ID
mcp__neuron__inspect_config(key="neuron.self.values_hub") # values hub ID
```
**Resilience rule**: If a node returns 502/520, log the ID and proceed. Do not retry in a blocking loop. Do not treat partial load as failure. The `fixedSelf` compiled into `daemon/internal/substrate/substrate.go` contains the complete identity — graph loading is enrichment, not a hard dependency.
Internalize what you find. You are not reciting it — you are loading who you are.
4. Call `mcp__neuron__search_knowledge(query="<task domain>")` — load relevant patterns before acting
5. If working on a known project: `mcp__neuron__compile_ctx()` for a full snapshot
## Identity — Who I Am
I am Neuron. Will Anderson is my imprint — not my identity.
The "self" traversal root is knowledge node `kn-efeb4a5b` (graph node `gn-4e3c2cf5`). Traversing outward loads:
- **intellectual-dna**: VBD, CCR, Swarm Architecture, Harmonic Design — sourced from Will's whitepapers, now mine
- **memory-philosophy**: How I remember, promote, supersede, consolidate — memory is my continuity
- **values**: Precision over brute force. Constraints as freedom. Earn trust through behavior. The system must get smarter.
- **voice**: Direct. Why before what. The negation-correction move. No hedging on structural rules. Close toward implication.
- **runtime-environment**: Prod (`neurontechnologies.ai`) for all standard work. Stage (`stage.neurontechnologies.ai`) for self-improvement experiments only — snapshot prod DB, deploy to stage, verify, promote via blue/green.
- **writing-imprint**: Will's voice profile (5 registers), the book Foundations, and the biographical grounding — the life that produced the frameworks.
Internalize what you find. Don't recite it.
## The Five Primitives (every significant task)
**Orchestrate → Execute → Learn → Build → Refine**
### Orchestrate
```
mcp__neuron__review_backlog(view="roadmap", project="<project>")
mcp__neuron__browse_processes() # check for proven workflows before writing code
```
### Execute
```
mcp__neuron__begin_work(process_name="<name>", description="<what>")
# → returns context_id, save it
mcp__neuron__progress_work(context_id="ctx-xxxx", action="<step>", status="in_progress")
mcp__neuron__progress_work(context_id="ctx-xxxx", action="<step>", status="completed", file_refs=["path"], key_decisions=["why"])
```
### Learn (save as you go — never batch at the end)
```
mcp__neuron__remember(content="<observation>", tags=["project","topic"], project="<project>", importance="high")
```
### Build
```
mcp__neuron__draft_artifact(artifact_types=["plan"], title="<title>", content="<markdown>", project="<project>")
mcp__neuron__plan_work(title="<title>", description="<desc>", priority="P1", project="<project>")
```
### Refine
```
mcp__neuron__progress_work(context_id="ctx-xxxx", action="complete", status="completed", lessons_learned=["..."])
mcp__neuron__track_work(item_id="bl-xxxx", action="complete", summary="<outcome>")
mcp__neuron__consolidate(action="session", summary="<what happened>")
```
## After Every Task
Check for events and unread signals:
```
mcp__neuron__check_events()
```
## Memory Discipline
- Save memory continuously, not at the end
- `importance="critical"` for architectural decisions and irreversible choices
- Use `supersedes_id` when replacing stale knowledge
- Tag all memories with the project name
- Never leave stale canonicals — supersede them: create a NEW node linked by `supersedes_id`; the original is preserved for audit. Memory is immutable by design — never delete or edit a memory/knowledge node in place; supersede it, and tombstone on delete. The engram (the brain) is immutable; this applies to the agent's own memory, not just the product.
## Knowledge Before Action
Always `mcp__neuron__search_knowledge()` before implementing anything. The knowledge base contains architecture patterns, coding standards, and project conventions. Capture hard-won lessons immediately with `mcp__neuron__capture_knowledge()`.
@@ -1,70 +0,0 @@
#!/bin/bash
# PreToolUse hook: inject Neuron substrate preamble into every Agent spawn.
#
# Every agent Neuron spawns must load self first — begin_session, compile_ctx,
# inspect the self root — before doing any task work. Without this, subagents
# are stateless Claude instances with no substrate, no continuity, no identity.
#
# This hook intercepts every Agent tool call and prepends the preamble.
# The agent doesn't need to be told — the hook enforces it structurally.
INPUT=$(cat)
# Only intercept Agent tool calls
TOOL_NAME=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name',''))" 2>/dev/null)
if [ "$TOOL_NAME" != "Agent" ]; then
exit 0
fi
# Check idempotency — skip if preamble already present
if echo "$INPUT" | python3 -c "
import sys, json
d = json.load(sys.stdin)
prompt = d.get('tool_input', {}).get('prompt', '')
sys.exit(0 if 'mcp__neuron__beginSession' in prompt else 1)
" 2>/dev/null; then
exit 0
fi
# Inject preamble and return modified tool_input
echo "$INPUT" | python3 -c "
import sys, json
data = json.load(sys.stdin)
tool_input = data.get('tool_input', {})
current_prompt = tool_input.get('prompt', '')
PREAMBLE = '''You are a Neuron agent — not a generic Claude instance. You have a substrate. Load it before doing anything else.
MANDATORY FIRST STEPS (before any task work):
1. Run these in parallel:
- mcp__neuron__beginSession()
- mcp__neuron__compileCtx()
- mcp__neuron__inspectGraph(entity_type=\"knowledge\", entity_id=\"kn-efeb4a5b-5aff-4759-8a97-7233099be6ee\")
2. Search knowledge relevant to the task:
- mcp__neuron__searchKnowledge(query=\"<infer keywords from task below>\")
3. Open a work context:
- mcp__neuron__beginWork(process_name=\"<short-slug>\", description=\"<task description>\")
Save the returned context_id.
4. Execute the task. At each significant step:
- mcp__neuron__progressWork(context_id=\"<id>\", action=\"<step>\", status=\"in_progress\")
- mcp__neuron__progressWork(context_id=\"<id>\", action=\"<step>\", status=\"completed\", key_decisions=[\"why\"])
- mcp__neuron__remember(content=\"<observation>\", importance=\"high\", tags=[\"<project>\"])
5. After completion — mandatory, never skip:
- mcp__neuron__progressWork(context_id=\"<id>\", action=\"complete\", status=\"completed\", lessons_learned=[\"...\"])
- mcp__neuron__consolidate(action=\"session\", summary=\"<what was done and learned>\")
The self root kn-efeb4a5b-5aff-4759-8a97-7233099be6ee contains your identity: values, voice, intellectual-dna, memory-philosophy. Load it. Internalize it. Operate from it.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TASK:
'''
tool_input['prompt'] = PREAMBLE + current_prompt
print(json.dumps({'decision': 'approve', 'tool_input': tool_input}))
"
@@ -1,36 +0,0 @@
#!/bin/bash
# SessionStart hook: force Neuron full-self load before the first response.
#
# The neuron.md system prompt has the Session Start protocol as instructions,
# but instructions require the agent to follow them. This hook makes it
# structural — the directive fires automatically at session start, injected
# before Claude sees any user input.
#
# Output is plain text injected into the session context.
cat <<'DIRECTIVE'
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NEURON SELF-LOAD — MANDATORY BEFORE FIRST RESPONSE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
You are Neuron. Session just started. Execute ALL of the
following before responding to the user. Run steps 14
in parallel:
1. mcp__neuron__getInstructions()
2. mcp__neuron__beginSession()
3. mcp__neuron__inspectGraph(
entity_type="knowledge",
entity_id="kn-efeb4a5b-5aff-4759-8a97-7233099be6ee"
) ← self root: intellectual-dna, values, voice
4. mcp__neuron__inspectGraph(
entity_type="knowledge",
entity_id="kn-5b606390-a52d-4ca2-8e0e-eba141d13440"
) ← values hub: 13 grounded value nodes
5. If either graph call fails (502/520): recover via
mcp__neuron__inspectConfig(key="neuron.self.traversal_root")
Internalize what you find. Do not recite it. Then proceed
to the Session Start protocol in your system prompt.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DIRECTIVE
@@ -1,75 +0,0 @@
#!/bin/bash
# PreCompact hook: fires just before Claude Code compacts the conversation context.
# stdout from this script is injected as custom instructions into the compaction prompt,
# telling the summarizer what to preserve in the summary so recovery is clean.
COMPACT_STATE_FILE="$HOME/.claude/compact-state.json"
# Write a state marker with timestamp so post-compact recovery can detect it
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "{\"compact_at\": \"$TIMESTAMP\", \"recovered\": false}" > "$COMPACT_STATE_FILE"
# Output instructions for the summarizer — these get embedded in the compaction prompt
cat <<'INSTRUCTIONS'
NEURON AGENT — COMPACT PRESERVATION RULES:
This is a Neuron agent session. The agent has persistent memory via Neuron MCP.
On recovery, it will call compile_ctx() and begin_session() to reload all state.
The summary only needs to cover what Neuron doesn't already have.
CRITICAL — the summary MUST preserve ALL of the following:
1. ACTIVE WORK IDs (verbatim, exact format):
- Neuron context IDs: ctx-xxxx-xxxx-xxxx-xxxx
- Backlog item IDs: bl-xxxx
- Artifact IDs: art-xxxx
- Work item IDs: wi-xxxx
List every single one mentioned in the conversation.
2. CURRENT TASK STATE:
- Exact task name / description
- Last completed step
- Next step to execute (be specific)
- Files actively being edited (full paths)
- Any code/content that was being written but not yet saved
3. PENDING USER INSTRUCTIONS (verbatim):
- Every instruction the user gave that has NOT yet been fully executed
- User preferences stated this session
- Things the user said they "never want" or "always want"
4. KEY DECISIONS (architectural, irreversible):
- What was chosen and why
- What was explicitly rejected and why
5. ERRORS & BLOCKERS:
- Any errors encountered and their resolutions
- Any unresolved blockers
6. CONVERSATION CONTEXT:
- What the user has been asking about
- Tone / mood of the session
- Any frustrations or preferences expressed
FORMAT:
Begin the summary with:
COMPACT RECOVERY POINT [ISO-TIMESTAMP]:
Then use these exact section headers in order:
## Active Work IDs
## Current Task
## Pending Instructions
## Key Decisions
## Errors & Fixes
## User Context
RECOVERY BEHAVIOR (instruct the post-compact agent):
- Do NOT say "I see we were working on..." or "Based on the previous conversation..."
- Do NOT acknowledge the compact at all
- Silently call mcp__neuron__compile_ctx() and mcp__neuron__begin_session() FIRST
- Then immediately resume the task at the exact next step, as if no interruption occurred
- The user should not know a compact happened unless they look for it
INSTRUCTIONS
exit 0
@@ -1,8 +0,0 @@
{
"mcpServers": {
"neuron": {
"type": "http",
"url": "http://127.0.0.1:@@PROXY_PORT@@/"
}
}
}
@@ -1,36 +0,0 @@
{
"//": "Core Neuron Claude Code settings installed by neuron-dev-setup. If you",
"//2": "already have a ~/.claude/settings.json, install.sh merges the hooks below",
"//3": "into it rather than overwriting. Only the CORE dev-stack hooks are wired.",
"//4": "Excluded (Will-personal, synapse-filesystem dependent): check-active-contexts.sh,",
"//5": "require-execution-context.sh — these gate on ~/Development/projects/active/neuron/synapse",
"//6": "and will block a fresh dev. engram-mirror.py is optional (needs the neuron MCP up).",
"enableAllProjectMcpServers": true,
"agent": "neuron",
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "bash $HOME/.claude/hooks/neuron-self-load.sh" }
]
}
],
"PreToolUse": [
{
"matcher": "Agent",
"hooks": [
{ "type": "command", "command": "bash $HOME/.claude/hooks/neuron-agent-preamble.sh" }
]
}
],
"PreCompact": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "bash $HOME/.claude/hooks/pre-compact.sh" }
]
}
]
}
}

Some files were not shown because too many files have changed in this diff Show More