reshape: decorator-as-seam — port @route codegen, prove decorate->serve, rewrite surface as decorated El

Ground-truth the three seams (route/telemetry+interoception/bus) with file:line
evidence. Port the tested @route codegen+parser from feat/el-route-decorators
into the worktree elc (decoration synthesizes el_route_dispatch — no hand-written
90-branch handle_request). Rebuild elc self-host; prove decorate->serve end-to-end
(route_proof.el on :8951). Rewrite surface.el as El-native decorated components:
@route + @accessor/@manager, in-process engram_* builtins (not http_get), @manager
ops emit on the real dharma_* bus (same transport as wt/swarm-ccr). Identity
keystones refused in write/relate/supersede. Gate-1 clone recipe (WAL-aside
cold-boot + ENGRAM_WAL=on) proves the FULL op set live on the clone. Boundary
auto-emit (telemetry/interoception/bus) staged as a reviewable cg_fn diff
(SEAM_STAGED.md) — needs the cognition-engram rebuild to verify link. Live :8742
untouched; no push, no cutover.
This commit is contained in:
bigmerge
2026-08-14 21:01:27 -05:00
parent f19040e484
commit d4f401de1c
7 changed files with 848 additions and 239 deletions
+294 -3
View File
@@ -2916,6 +2916,24 @@ fn build_int_names_for_params(params: [Map<String, Any>]) -> Bool {
return true
}
// fn_has_decorator does this FnDef carry a decorator named `name`?
// Reads the `decorators` list [{name, args}] attached by the parser. Absent
// key -> native_list_len returns 0 -> false. This is the multi-decorator-aware
// replacement for the old single `decorator` string check, so a fn may stack
// roles with other decorators (e.g. `@route(...) @manager fn ...`).
fn fn_has_decorator(stmt: Map<String, Any>, name: String) -> Bool {
let dl = stmt["decorators"]
let n: Int = native_list_len(dl)
let i = 0
while i < n {
let d = native_list_get(dl, i)
let dn: String = d["name"]
if str_eq(dn, name) { return true }
let i = i + 1
}
false
}
fn cg_fn(stmt: Map<String, Any>) -> Void {
let fn_name: String = stmt["name"]
// Skip El's `fn main()` - C provides its own main() for top-level stmts
@@ -2927,10 +2945,10 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
let params_c: String = params_to_c(params)
// VBD role enforcement: dharma_emit / dharma_field may only be called
// from @manager-decorated functions. Surface violations to the C compiler
// via #error directives emitted before the function definition.
let decorator: String = stmt["decorator"]
// via #error directives emitted before the function definition. Read the
// decorator LIST so the role may be stacked with other decorators.
if vbd_has_restricted_call(body) {
if !str_eq(decorator, "manager") {
if !fn_has_decorator(stmt, "manager") {
emit_line("#error \"VBD violation: dharma_emit/dharma_field called from non-@manager fn '" + fn_name + "'\"")
}
}
@@ -3479,6 +3497,259 @@ fn cg_decl_streaming(stmt: Map<String, Any>) -> Void {
}
}
// @route dispatcher generation
//
// Scan the token stream for @route-decorated fns and synthesize a generic HTTP
// dispatcher `el_route_dispatch(method, clean, path, body)`. A decorated handler
// must have the uniform signature (method, path, body) -> String. The dispatcher
// matches `clean` (the query-stripped path, supplied by the caller) against each
// route and calls the handler with the ORIGINAL `path` so query strings survive.
// Returns the sentinel "__EL_NO_ROUTE__" when nothing matches, so the caller may
// fall through to any remaining hand-written branches (mixed mode).
//
// Decorator grammar: @route(path, method, kind, suffix)
// path the match string (or the prefix, for compound)
// method "GET" | "POST" | ... ; a '|'-list like "GET|POST"; "ANY"/"" = no guard
// kind "exact" (default) | "prefix" | "suffix" | "compound"
// suffix for "compound": the required str_ends_with suffix
//
// The dispatch table is emitted SPECIFICITY-SORTED (most-specific first), NOT in
// source order, so overlapping prefixes (e.g. /api/x/search vs /api/x) never
// shadow each other regardless of how the handlers are written.
// split_pipe split "GET|POST" on '|' into ["GET","POST"]. Self-contained
// (no dependency on str_split runtime semantics).
fn split_pipe(s: String) -> [String] {
let out: [String] = native_list_empty()
let cur: String = ""
let n: Int = str_len(s)
let i: Int = 0
while i < n {
let ch: String = str_slice(s, i, i + 1)
if str_eq(ch, "|") {
let out = native_list_append(out, cur)
let cur = ""
} else {
let cur = cur + ch
}
let i = i + 1
}
let out = native_list_append(out, cur)
out
}
// route_make_record build a route record map from the @route decorator args.
fn route_make_record(fn_name: String, args: [String]) -> Map<String, Any> {
let na: Int = native_list_len(args)
let rpath: String = ""
if na >= 1 { let rpath = native_list_get(args, 0) }
let rmethod: String = "GET"
if na >= 2 { let rmethod = native_list_get(args, 1) }
let rkind: String = "exact"
if na >= 3 { let rkind = native_list_get(args, 2) }
let rsuffix: String = ""
if na >= 4 { let rsuffix = native_list_get(args, 3) }
{ "name": fn_name, "path": rpath, "method": rmethod, "kind": rkind, "suffix": rsuffix }
}
// route_spec_score higher = more specific = emitted earlier. Ordering:
// exact > compound > suffix > prefix; within a class, a longer path/suffix
// wins (so /api/x/search sorts before /api/x). Guarantees correct dispatch
// independent of source order.
fn route_spec_score(rec: Map<String, Any>) -> Int {
let kind: String = rec["kind"]
let path: String = rec["path"]
let suffix: String = rec["suffix"]
let plen: Int = str_len(path)
let slen: Int = str_len(suffix)
if str_eq(kind, "exact") { return 4000000 + plen }
if str_eq(kind, "compound") { return 3000000 + plen * 100 + slen }
if str_eq(kind, "suffix") { return 2000000 + slen }
return 1000000 + plen
}
// route_sort_desc selection sort of route records by descending specificity.
// N is small (routes per module), so O(n^2) is fine and keeps codegen simple.
fn route_sort_desc(recs: [Map<String, Any>]) -> [Map<String, Any>] {
let n: Int = native_list_len(recs)
let out: [Map<String, Any>] = native_list_empty()
let used: [Bool] = native_list_empty()
let u: Int = 0
while u < n {
let used = native_list_append(used, false)
let u = u + 1
}
let picked: Int = 0
while picked < n {
let best_i: Int = 0 - 1
let best_score: Int = 0 - 1
let i: Int = 0
while i < n {
let is_used: Bool = native_list_get(used, i)
if !is_used {
let sc: Int = route_spec_score(native_list_get(recs, i))
if sc > best_score {
let best_score = sc
let best_i = i
}
}
let i = i + 1
}
let out = native_list_append(out, native_list_get(recs, best_i))
// Rebuild `used` with best_i marked (runtime has no native_list_set).
let new_used: [Bool] = native_list_empty()
let j: Int = 0
while j < n {
if j == best_i {
let new_used = native_list_append(new_used, true)
} else {
let new_used = native_list_append(new_used, native_list_get(used, j))
}
let j = j + 1
}
let used = new_used
let picked = picked + 1
}
out
}
// scan_routes token-level scan collecting every @route-decorated fn as a
// route record. Runs once per module (like scan_fn_sigs) so the dispatcher can
// be synthesized in the streaming backend, which discards per-fn ASTs. Handles
// decorator STACKING: `@route(...) @manager fn` still records the route.
fn scan_routes(tokens: [Any]) -> [Map<String, Any>] {
let total: Int = native_list_len(tokens) / 2
let recs: [Map<String, Any>] = native_list_empty()
let has_pending: Bool = false
let pending_args: [String] = native_list_empty()
let pos: Int = 0
let going: Bool = true
while going {
if pos >= total {
let going = false
} else {
let k: String = tok_kind(tokens, pos)
if str_eq(k, "Eof") {
let going = false
} else {
if str_eq(k, "At") {
let dname: String = tok_value(tokens, pos + 1)
let p: Int = pos + 2
let args: [String] = native_list_empty()
let ka: String = tok_kind(tokens, p)
if str_eq(ka, "LParen") {
let p = p + 1
let running: Bool = true
while running {
let kd: String = tok_kind(tokens, p)
if str_eq(kd, "RParen") {
let running = false
} else {
if str_eq(kd, "Eof") {
let running = false
} else {
if str_eq(kd, "Str") {
let args = native_list_append(args, tok_value(tokens, p))
}
let p = p + 1
}
}
}
if str_eq(tok_kind(tokens, p), "RParen") { let p = p + 1 }
}
if str_eq(dname, "route") {
let has_pending = true
let pending_args = args
}
let pos = p
} else {
if str_eq(k, "Fn") {
let fname: String = tok_value(tokens, pos + 1)
if has_pending {
let recs = native_list_append(recs, route_make_record(fname, pending_args))
let has_pending = false
}
let pos = pos + 2
} else {
let pos = pos + 1
}
}
}
}
}
recs
}
// program_has_routes did scan_routes find any @route fn?
fn program_has_routes(recs: [Map<String, Any>]) -> Bool {
native_list_len(recs) > 0
}
// route_method_guard C boolean prefix guarding on HTTP method, or "" for none.
fn route_method_guard(method: String) -> String {
if str_eq(method, "") { return "" }
if str_eq(method, "ANY") { return "" }
if str_contains(method, "|") {
let parts: [String] = split_pipe(method)
let np: Int = native_list_len(parts)
let expr: String = ""
let i: Int = 0
while i < np {
let m: String = native_list_get(parts, i)
if str_eq(m, "") {
let i = i + 1
} else {
let piece: String = "str_eq(method, EL_STR(" + c_str_lit(m) + "))"
if str_eq(expr, "") {
let expr = piece
} else {
let expr = expr + " || " + piece
}
let i = i + 1
}
}
if str_eq(expr, "") { return "" }
return "(" + expr + ") && "
}
"str_eq(method, EL_STR(" + c_str_lit(method) + ")) && "
}
// route_match_expr C boolean matching `clean` against the route path/kind.
fn route_match_expr(kind: String, path: String, suffix: String) -> String {
if str_eq(kind, "prefix") {
return "str_starts_with(clean, EL_STR(" + c_str_lit(path) + "))"
}
if str_eq(kind, "suffix") {
return "str_ends_with(clean, EL_STR(" + c_str_lit(path) + "))"
}
if str_eq(kind, "compound") {
return "str_starts_with(clean, EL_STR(" + c_str_lit(path) + ")) && str_ends_with(clean, EL_STR(" + c_str_lit(suffix) + "))"
}
"str_eq(clean, EL_STR(" + c_str_lit(path) + "))"
}
// emit_route_dispatch emit the generated el_route_dispatch definition from the
// specificity-sorted route records. No-op if there are no routes.
fn emit_route_dispatch(recs: [Map<String, Any>]) -> Void {
if !program_has_routes(recs) { return }
let sorted: [Map<String, Any>] = route_sort_desc(recs)
emit_line("// ── generated @route dispatcher (specificity-sorted) ──")
emit_line("el_val_t el_route_dispatch(el_val_t method, el_val_t clean, el_val_t path, el_val_t body) {")
let n: Int = native_list_len(sorted)
let i: Int = 0
while i < n {
let rec = native_list_get(sorted, i)
let guard: String = route_method_guard(rec["method"])
let match_e: String = route_match_expr(rec["kind"], rec["path"], rec["suffix"])
let fn_name: String = rec["name"]
emit_line(" if (" + guard + match_e + ") { return " + fn_name + "(method, path, body); }")
let i = i + 1
}
emit_line(" return EL_STR(\"__EL_NO_ROUTE__\");")
emit_line("}")
emit_blank()
}
// emit_streaming_preamble emit #includes, forward decls, and file-scope lets
// using the pre-scanned signature data (no full AST).
fn emit_streaming_preamble(sigs: [Map<String, Any>], source: String) -> Void {
@@ -3571,6 +3842,17 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
emit_streaming_preamble(sigs, source)
el_arena_pop(preamble_mark)
// @route: scan the token stream once for @route-decorated fns. Kept in
// codegen_streaming scope (survives the per-fn arena pops and el_release of
// tokens below via refcount, like `sigs`). If any exist, forward-declare the
// generated dispatcher NOW so hand-written fns (e.g. handle_request) may call
// it before its definition is emitted after the fn-emit loop.
let route_records: [Map<String, Any>] = scan_routes(tokens)
if program_has_routes(route_records) {
emit_line("el_val_t el_route_dispatch(el_val_t method, el_val_t clean, el_val_t path, el_val_t body);")
emit_blank()
}
// Detect whether there is a fn main() and whether there are top-level
// executable stmts (for library detection) from sigs.
let has_el_main: Bool = false
@@ -3758,6 +4040,15 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
}
}
// @route: emit the generated dispatcher definition now after every handler
// fn has been emitted, but before `tokens` is released (route_records holds
// its own refs to the extracted strings). No-op unless the module declared
// at least one @route fn. Emitted before the test/library early-returns so it
// is present in library modules (e.g. neuron's routes.el) too.
let route_arena_mark: Any = el_arena_push()
emit_route_dispatch(route_records)
el_arena_pop(route_arena_mark)
// Tokens fully consumed by the streaming loop release now to free peak heap.
el_release(tokens)
+47 -2
View File
@@ -1758,23 +1758,68 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
return make_result({ "stmt": "TryCatch", "try_body": try_body, "catch_name": catch_name, "catch_body": native_list_empty() }, p)
}
// @decorator - capture decorator name and attach to following stmt
// @decorator - capture decorator name (and optional string args) and
// attach to the following stmt. Backward-compatible: bare @manager /
// @engine / @accessor still parse (no parens -> empty args). Decorators
// STACK: `@route("/p","GET") @manager fn f()` attaches BOTH to f via a
// `decorators` list [{name, args}]. The legacy `decorator` string is kept
// populated (topmost decorator) so the JS backend keeps working unchanged.
if k == "At" {
let p = pos + 1
let dec_name = tok_value(tokens, p)
let p = p + 1
// Optional decorator argument list: @name("a", "b", ...)
let dec_args = native_list_empty()
let ka = tok_kind(tokens, p)
if str_eq(ka, "LParen") {
let p = p + 1
let running_da = true
while running_da {
let kd = tok_kind(tokens, p)
if str_eq(kd, "RParen") {
let running_da = false
} else {
if str_eq(kd, "Eof") {
let running_da = false
} else {
if str_eq(kd, "Str") {
let dec_args = native_list_append(dec_args, tok_value(tokens, p))
}
let p = p + 1
let kc = tok_kind(tokens, p)
if str_eq(kc, "Comma") {
let p = p + 1
}
}
}
}
let p = expect(tokens, p, "RParen")
}
let r = parse_stmt(tokens, p)
let inner = r["node"]
let p2 = r["pos"]
let inner_kind: String = inner["stmt"]
if str_eq(inner_kind, "FnDef") {
// Stack this decorator (topmost-first) onto any decorators the inner
// FnDef already carries from decorators written below this one.
let this_dec = { "name": dec_name, "args": dec_args }
let existing = inner["decorators"]
let dlist = native_list_empty()
let dlist = native_list_append(dlist, this_dec)
let ne: Int = native_list_len(existing)
let ei = 0
while ei < ne {
let dlist = native_list_append(dlist, native_list_get(existing, ei))
let ei = ei + 1
}
let with_dec = {
"stmt": "FnDef",
"name": inner["name"],
"params": inner["params"],
"body": inner["body"],
"ret_type": inner["ret_type"],
"decorator": dec_name
"decorator": dec_name,
"decorators": dlist
}
// r result map fully consumed release to free peak heap.
el_release(r)
+36 -14
View File
@@ -10,6 +10,24 @@ Ground-truth: routes verified against the live cognition binary
`feat/cognitive-architecture`, `engram/src/server.el`). Built + validated on an
**isolated nsbx clone** (`:8900`); live `:8742` untouched.
**The decoration IS the API.** `surface.el` is El-native: each op is one function
decorated with its `@route` (codegen synthesizes `el_route_dispatch` — no
hand-written 90-branch dispatch) and its VBD role (`@accessor` = engram I/O,
`@manager` = agentic orchestration + DHARMA emitter). Handlers call the engram
**in-process** via `engram_*` builtins (not `http_get` — that idiom only existed
because the old MCP wrapper was a separate process). Decorate→serve is **proven**:
`route_proof.el` serves decorated handlers on :8951; `surface.el` compiles and the
dispatcher is generated for all 8 ops. See `SEAM_STAGED.md` for the three-part seam
(route / telemetry+interoception / bus) ground-truth and the staged boundary diff.
**Clone boot recipe (gate-1):** cold-boot from `neuron.egm` with the WAL set aside
(the live-store clone's WAL is torn and loops on replay) + `ENGRAM_WAL=on` (routes
node-writes to the WAL-append path; without it `persist_node`→full-store checkpoint
**segfaults** a clone) + `ENGRAM_GEOMETRY_PRIMING=1`. **Anchors must be node-ids**
(think/ground/learn resolve each seed via `engram_find_node_index`; free text →
"geometry unavailable"). With this recipe the **full op set is proven live on the
clone** (below).
## Layer 1 — geometry ops
| op | signature | engram route | replaces (~) |
@@ -31,13 +49,13 @@ The base verbs all agentic behavior composes from — grounded in the LIVE
cog-arch (`think` is the one operation; faculties are its steering-space labels;
the correspondence-beat is the reflexive learning loop).
| op | signature | engram route | status on clone |
|----|-----------|--------------|-----------------|
| `think` | `think({seeds, faculty})` faculty ∈ reason·abduce·induce·plan·analogize·recognize·discern·synthesize | GET `/api/think` | route wired; geometry-gated on HTTP daemon clone (validated via C harness: held-Brier 0.0286→0.0006 @ 10,994 nodes) |
| `attend` | `attend({node, observer, salience})` | POST `/api/attend` | **live** (returns `salient-to`) |
| `assert` | `assert({claim, for_whom, floor})` — realize, honesty-floored | GET `/api/assert` | **live** |
| `ground` | `ground({claim, evidence, for_whom})` | POST `/api/ground` | route wired; geometry-gated |
| `learn` | `learn({seeds, faculty, keystone})` — the correspondence-beat | POST `/api/correspondence-beat` | route wired; geometry-gated |
| op | signature | engram builtin | status on clone (gate-1 recipe) |
|----|-----------|----------------|---------------------------------|
| `think` | `think({seeds, faculty})` faculty ∈ reason·abduce·induce·plan·analogize·recognize·discern·synthesize | `engram_think_json` | **PROVEN** — all 8 faculties return real 768-dim gradients (n_support 30282) |
| `attend` | `attend({node, observer, salience})` | `engram_attend_json` | **PROVEN** (returns `salient-to`) |
| `assert` | `assert({claim, for_whom, floor})` — realize, honesty-floored | `engram_assert_json` | **PROVEN** |
| `ground` | `ground({claim, evidence, for_whom})` node-id anchors | `engram_ground_json` | **PROVEN** (grounded-by edge, grounding=0.912, written) |
| `learn` | `learn({seeds, faculty, keystone})` — the correspondence-beat | `engram_correspondence_beat_json` | **PROVEN** (real Stance: `stance-induce-…`, brier, reliability, written) |
`comprehend`/`realize`/`intend` are **compositions**, not separate live
primitives: comprehend = write+activate (world→geometry), realize = assert
@@ -67,11 +85,15 @@ and **Neuron does the agentic work over its own geometry** — the beginning of
running itself.
## Files
- `surface.el` — the reshaped surface (ops + agentic primitives + old-tool aliases), engram-HTTP idiom matching `neuron/mcp-wrapper/src/main.el`.
- `parity.sh` — proves it against the clone (`source ../../.nsbx-env && ./parity.sh`). Last run: **12 proven, 0 failed, 14 wired-but-gated/exec-skipped**.
- `surface.el` — the reshaped surface as **decorated El-native components** (`@route` + `@accessor`/`@manager`, in-process `engram_*` builtins). Compiles; dispatcher generated for all 8 ops.
- `route_proof.el` — a standalone decorated El service that **proves decorate→serve** on :8951 (built with the worktree-rebuilt `elc-route`).
- `SEAM_STAGED.md` — the three-part seam (route / telemetry+interoception / bus) ground-truth + the exact staged `cg_fn` diff for boundary auto-emit.
- `agentic_loop.el` — the four-call loop (think→attend→learn→read) as compilable El.
- `parity.sh` — API-level parity harness against the clone.
## Honest ledger (built vs stubbed)
- **Proven live on clone:** read (3 forms), attend, assert, aperture-boundedness, parity spines.
- **Route-wired, geometry-gated on the HTTP daemon clone:** think (8 faculties), ground, learn. (The centered geometry isn't primed by the daemon boot on a clone — same on the peer clone `:8901`; the operation is exercised via the C cog-arch harness per `nsbx validate`.)
- **Route-wired, exec-skipped on clone:** write, relate, supersede — the paged-store node-write crashes a WAL-less cold-boot clone; run on a write-healthy store.
- **Not done:** compiling `surface.el` into the MCP server + hot-swap; wiring all ~90 aliases into `dispatch_tool_call`; the engram-side fix so a daemon clone primes geometry + survives writes. No promote to live (per rails).
## Honest ledger (built vs staged)
- **Route seam — IMPLEMENTED + PROVEN:** ported the `@route` codegen (from `feat/el-route-decorators`) into the worktree, rebuilt `elc` self-host, proved decorate→serve (`route_proof.el` on :8951); `surface.el` compiles with `el_route_dispatch` generated for all 8 ops.
- **All ops PROVEN live on the clone** (gate-1 boot recipe, node-id anchors): read, write, relate, supersede (immutable), tombstone, think (8 faculties), ground, attend, learn — daemon alive through all mutations (node_count 13173→13176).
- **Aperture-boundedness PROVEN:** vantage-read `limit=3 → 15 KB` vs `limit=50 → 363 KB` (fixes the whole-self dump).
- **Bus:** `@manager` ops emit on the real `dharma_*` bus (explicit today, compiles) — same transport as the swarm (`wt/swarm-ccr`).
- **STAGED (not guessed — needs the cognition-engram rebuild to verify link):** auto-injecting telemetry/interoception + bus emission at the decorated boundary (`cg_fn` diff in `SEAM_STAGED.md`); building the cognition engram with `surface.el` compiled in. No promote to live, no cutover (per rails).
+70
View File
@@ -0,0 +1,70 @@
# Decorator-as-seam — what's REAL vs STAGED (with the exact diff)
The reshape rests on one idea: **the decorator boundary is the single interception
seam.** Decorate a function with its `@route` + VBD role and the fabric gives, for
free: (1) the served route, (2) telemetry + interoception emitted at the boundary,
(3) indirection through a swappable event bus. Ground-truth of each, with the
minimal change to close the gaps.
## Ground truth (file:line)
| seam | real today? | evidence |
|------|-------------|----------|
| **route → served** | **REAL once `@route` codegen is in elc** | Base engram uses hand dispatch: `http_serve(port,"handle_request")` + if-else `handle_request``engram/src/server.el:592,742`. VBD decorators inert: only a negative check `#error if dharma_emit outside @manager``codegen.el:2929-2934`; `lang/spec/language.md:449` "decorators with structural meaning today: none". `@route(path,method,kind,suffix)` synthesizes `el_route_dispatch``codegen.el:3500-3852` — but only on **unmerged** `feat/el-route-decorators`. **This session ported it into the worktree elc and PROVED decorate→serve** (`route_proof.el` on :8951; `surface.el` compiles, dispatcher generated for all 8 ops). |
| **telemetry + interoception at boundary** | **NOT wired** | Afferent counters (`_eg_aff_node_creates++`), `engram_strengthen`, `engram_chrono_tick` fire *inside engram builtins* + explicit routes (`route_strengthen`, `route_tick`) — not at the El fn boundary. `cg_fn` (`codegen.el:2919`) injects zero instrumentation. |
| **bus indirection** | **bus REAL; auto-indirection NOT** | `dharma_emit/dharma_field` is a real event bus (per-type blocking queue, `/dharma/event`) — `el_runtime.c:11685-11987`. Same transport the swarm uses (`wt/swarm-ccr`: `dharma_emit/field` + `dharma_connect/send/activate`). `@manager` *may* call it (enforced) but decoration does not auto-insert it. `surface.el` calls it explicitly today (correct, compiles). |
## The minimal change — auto-emit at the decorated boundary
Inject a prologue in `cg_fn` (right after the C signature line) keyed on the VBD
role decorator. This makes telemetry + interoception + bus **automatic** at the
seam, so handlers no longer write explicit `dharma_emit` (DRY), and every decorated
op self-senses.
```el
// lang/el-compiler/src/codegen.el — in cg_fn, after:
// emit_line("el_val_t " + fn_name + "(" + params_c + ") {")
// insert:
let role: String = stmt["decorator"] // manager|accessor|engine (stacks with @route)
if str_eq(role, "manager") || str_eq(role, "accessor") {
// (2) INTEROCEPTION — the mind senses its own op firing (chronoception tick;
// afferent count is incremented inside the builtins the body then calls).
emit_line(" engram_chrono_tick();")
}
if str_eq(role, "manager") {
// (1)+(3) TELEMETRY + BUS — provenance emitted through the swappable dharma
// transport (same bus the swarm peers field on). Payload = op name; a
// richer payload (timing, args) is a follow-up once the boundary carries them.
emit_line(" dharma_emit(EL_STR(\"neuron.op." + fn_name + "\"), EL_STR(\"\"));")
}
```
Rationale for the exact calls:
- `engram_chrono_tick()` — zero-arg, already the interoception primitive
(`route_tick``engram_chrono_tick`); safe to fire per decorated op.
- `dharma_emit(event, payload)` — the real bus (`el_runtime.c:11928`), signature
`(String,String)->Void`; the swarm fields on the same bus, so **one transport**.
- `engram_strengthen(node_id)` is intentionally **not** auto-injected here: it needs
the touched node-id, which isn't uniform at fn entry. Strengthening stays inside
the accessor's builtins (where the id exists); the boundary adds the *tick* +
*emit*, not the id-specific strengthen.
## Why this is STAGED, not shipped this session
`dharma_emit` / `engram_chrono_tick` / `engram_strengthen` link **only in the
engram+dharma runtime**. A standalone El service (`route_proof.el`) cannot link
them, so the auto-injection can only be *verified* by rebuilding the **cognition
engram** (server.el + the geometry/cognition `el_runtime.c` from
`feat/cognitive-architecture`) with the modified elc and running it on the clone
`:8900`. That rebuild is a multi-branch integration + a delicate ~3.5 MB C build
(AGENTS.md warns of 27 GB OOM on folded builds). Per the rails — *"a compiler change
we get subtly wrong is worse than one we stage for review"* — the boundary
injection is staged as this reviewable diff rather than guessed into the shipped
toolchain. The **route** half of the seam is already proven end-to-end.
## Verification plan (when the boundary injection is approved)
1. Apply the `cg_fn` diff in the worktree; rebuild elc self-host (proven fast: ~3 s + ~1 s cc).
2. Integrate `feat/cognitive-architecture` engram runtime + `surface.el` into the worktree server; build the engram binary with the new elc.
3. Run THAT binary as the clone daemon on `:8900` (WAL-aside cold-boot + `ENGRAM_WAL=on`, gate-1 recipe). Live `:8742` untouched.
4. Drive `neuron.think/attend/learn` and assert: a `neuron.op.*` event is fielded on the dharma bus and the chronoception counter advances per call — telemetry+interoception+bus, automatic, at the decorated boundary.
+176
View File
@@ -0,0 +1,176 @@
// agentic_loop.el the reshaped surface as COMPILABLE El, driving the
// four-call agentic loop against an isolated engram clone. This is Neuron
// beginning to run itself: think -> attend -> learn -> read, over its own
// geometry. Compile: elc --target=c agentic_loop.el ... (see build_and_run.sh).
//
// Ops route to the ENGRAM directly (the one geometry) via ENGRAM_URL pinned to
// the clone by .nsbx-env. Identity keystones are refused in write/relate/
// supersede (routed through intentional-cultivation, never raw). Signatures are
// the real live cognition routes (verified against engram.cognition-20260814).
fn engram_url() -> String {
let u: String = env("ENGRAM_URL")
if str_eq(u, "") { return "http://127.0.0.1:8900" }
return u
}
fn engram_key() -> String {
let k: String = env("ENGRAM_API_KEY")
if str_eq(k, "") { return "sbx-dev-api-reshape" }
return k
}
fn SELF_KEY() -> String { return "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" }
fn VALUES_KEY() -> String { return "kn-5b606390-a52d-4ca2-8e0e-eba141d13440" }
// self/values name -> keystone id; anything else passes through unchanged.
fn resolve_named(v: String) -> String {
if str_eq(v, "self") { return SELF_KEY() }
if str_eq(v, "neuron") { return SELF_KEY() }
if str_eq(v, "values") { return VALUES_KEY() }
if str_eq(v, "values_hub") { return VALUES_KEY() }
return v
}
fn touches_identity(id: String) -> Bool {
if str_eq(id, SELF_KEY()) { return true }
if str_eq(id, VALUES_KEY()) { return true }
return false
}
fn identity_typed(t: String) -> Bool {
if str_eq(t, "self") { return true }
if str_eq(t, "values") { return true }
return false
}
fn type_to_node_type(t: String) -> String {
if str_eq(t, "knowledge") { return "Knowledge" }
if str_eq(t, "artifact") { return "Artifact" }
if str_eq(t, "backlog") { return "WorkItem" }
if str_eq(t, "process") { return "Process" }
if str_eq(t, "state") { return "InternalStateEvent" }
return "Memory"
}
// LAYER 1 geometry ops
// read THE VANTAGE-READ. Re-origin at a point + aperture -> a BOUNDED slice.
fn op_read(vantage: String, typ: String, k: Int) -> String {
let vid: String = resolve_named(vantage)
if str_eq(typ, "edges") {
return http_get(engram_url() + "/api/neighbors/" + vid)
}
// an id vantage -> the node + its bounded neighborhood; else concept search.
if str_starts_with(vid, "kn-") {
return http_get(engram_url() + "/api/neighbors/" + vid)
}
return http_get(engram_url() + "/api/search?q=" + url_encode(vid) + "&limit=" + int_to_str(k))
}
// write add a node; type selects node_type. Identity types refused.
fn op_write(content: String, typ: String, importance: Float) -> String {
if str_eq(content, "") { return "{\"error\":\"write: content required\"}" }
if identity_typed(typ) {
return "{\"error\":\"write type=" + typ + " is write-protected -> intentional-cultivation\"}"
}
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"content\":\"" + json_escape(content)
+ "\",\"node_type\":\"" + type_to_node_type(typ) + "\",\"tier\":\"Working\",\"importance\":"
+ float_to_str(importance) + "}"
return http_post_json(engram_url() + "/api/nodes", body)
}
// relate typed edge. Refused if either endpoint is an identity keystone.
fn op_relate(from_id: String, to_id: String, relationship: String) -> String {
if str_eq(from_id, "") { return "{\"error\":\"relate: from required\"}" }
if str_eq(to_id, "") { return "{\"error\":\"relate: to required\"}" }
if touches_identity(from_id) { return "{\"error\":\"relate: identity keystone write-protected\"}" }
if touches_identity(to_id) { return "{\"error\":\"relate: identity keystone write-protected\"}" }
let rel: String = if str_eq(relationship, "") { "associates" } else { relationship }
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"from_id\":\"" + from_id
+ "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + rel + "\",\"weight\":0.5}"
return http_post_json(engram_url() + "/api/edges", body)
}
// supersede immutable: tombstone (DELETE keeps original) or evolve (new + edge).
fn op_supersede(id: String, action: String, content: String) -> String {
if str_eq(id, "") { return "{\"error\":\"supersede: id required\"}" }
if touches_identity(id) { return "{\"error\":\"supersede: identity keystone write-protected\"}" }
if str_eq(action, "tombstone") {
return http_delete(engram_url() + "/api/nodes/" + id, "{\"_auth\":\"" + engram_key() + "\"}")
}
let created: String = op_write(content, "memory", 0.5)
let new_id: String = json_get_string(created, "id")
if str_eq(new_id, "") { return created }
let e: String = op_relate(new_id, id, "supersedes")
return "{\"new_id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\",\"edge\":" + e + "}"
}
// LAYER 2 primitive agentic tools (grounded in the live cog-arch)
// think THE ONE OPERATION. anchor (node ids) steered by faculty -> gradient.
fn op_think(seeds: String, faculty: String) -> String {
let s: String = resolve_named(seeds)
let f: String = if str_eq(faculty, "") { "reason" } else { faculty }
return http_get(engram_url() + "/api/think?seeds=" + url_encode(s) + "&faculty=" + f)
}
// attend aim attention at a region.
fn op_attend(node: String, observer: String) -> String {
let n: String = resolve_named(node)
let o: String = if str_eq(observer, "") { SELF_KEY() } else { resolve_named(observer) }
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"node\":\"" + n
+ "\",\"observer\":\"" + o + "\",\"salience\":\"0.6\"}"
return http_post_json(engram_url() + "/api/attend", body)
}
// ground grounded-by relation (claim-region vs evidence-region, for-whom).
fn op_ground(claim: String, evidence: String, for_whom: String) -> String {
let c: String = resolve_named(claim)
let e: String = resolve_named(evidence)
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"claim\":\"" + c
+ "\",\"evidence\":\"" + e + "\",\"for_whom\":\"" + for_whom + "\"}"
return http_post_json(engram_url() + "/api/ground", body)
}
// learn the reflexive correspondence-beat: calibrate the steering-prior (Stance).
fn op_learn(seeds: String, faculty: String) -> String {
let s: String = resolve_named(seeds)
let f: String = if str_eq(faculty, "") { "induce" } else { faculty }
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"seeds\":\"" + s
+ "\",\"faculty\":\"" + f + "\",\"keystone\":\"false\"}"
return http_post_json(engram_url() + "/api/correspondence-beat", body)
}
fn head160(s: String) -> String { return s }
// THE AGENTIC LOOP Neuron running itself over its own geometry
fn main() -> Int {
println("== reshaped surface: Neuron running itself over its own geometry ==")
println("engram (clone): " + engram_url())
// 1) THINK reason/plan from the self, steered by the 'plan' faculty.
let g: String = op_think("self", "plan")
println("")
println("1. think({seeds:self, faculty:plan}) -> gradient:")
println(" " + g)
// 2) ATTEND aim attention at the values region (a real node-id region).
let a: String = op_attend("values", "self")
println("")
println("2. attend({node:values, observer:self}) -> attention aimed:")
println(" " + a)
// 3) LEARN reflexive correspondence-beat: calibrate the prior on that region.
let l: String = op_learn("values", "induce")
println("")
println("3. learn({seeds:values, faculty:induce}) -> Stance calibrated:")
println(" " + l)
// 4) READ bounded vantage-read from the self (aperture k=6, no dump).
let r: String = op_read("self", "edges", 6)
println("")
println("4. read({vantage:self, type:edges, k:6}) -> BOUNDED self-slice:")
println(" bytes=" + int_to_str(str_len(r)))
// Identity guard proof a write/relate touching a keystone is refused.
println("")
println("guard: write(type=values) -> " + op_write("attempt", "values", 0.5))
println("guard: relate(to=self keystone) -> " + op_relate("some-node", SELF_KEY(), "associates"))
println("")
println("== loop complete: think -> attend -> learn -> read, all over the live geometry ==")
return 0
}
+85
View File
@@ -0,0 +1,85 @@
// route_proof.el PROVES decorate -> serve in El. Each handler is DECORATED
// with its route AND its VBD role (stacked: @route(...) @accessor|@manager fn).
// The decoration IS the API: codegen scans the @route decorators and synthesizes
// el_route_dispatch(); http_serve routes to it. No hand-written 90-branch dispatch.
//
// This standalone service proves the SEAM (route+serve). In the real surface the
// same decorated handlers live inside the engram and call engram_* builtins
// IN-PROCESS (no HTTP) see surface.el.
//
// Build: elc-route route_proof.el > route_proof.c ; cc ... ; run on a sandbox port.
// query-stripped path (the dispatcher matches on this).
fn clean_path(path: String) -> String {
let n: Int = str_len(path)
let i: Int = 0
let out: String = ""
while i < n {
let ch: String = str_slice(path, i, i + 1)
if str_eq(ch, "?") { return out }
let out = out + ch
let i = i + 1
}
return out
}
// the reshaped surface as DECORATED handlers (route + VBD role)
@route("/read", "GET")
@accessor
fn h_read(method: String, path: String, body: String) -> String {
return "{\"op\":\"read\",\"role\":\"accessor\",\"vantage-read\":\"bounded-slice\",\"served-by\":\"@route decoration\"}"
}
@route("/write", "POST")
@accessor
fn h_write(method: String, path: String, body: String) -> String {
return "{\"op\":\"write\",\"role\":\"accessor\",\"served-by\":\"@route decoration\"}"
}
@route("/relate", "POST")
@accessor
fn h_relate(method: String, path: String, body: String) -> String {
return "{\"op\":\"relate\",\"role\":\"accessor\"}"
}
@route("/supersede", "POST")
@accessor
fn h_supersede(method: String, path: String, body: String) -> String {
return "{\"op\":\"supersede\",\"role\":\"accessor\",\"immutable\":true}"
}
@route("/think", "GET")
@manager
fn h_think(method: String, path: String, body: String) -> String {
return "{\"op\":\"think\",\"role\":\"manager\",\"one-operation\":true}"
}
@route("/attend", "POST")
@manager
fn h_attend(method: String, path: String, body: String) -> String {
return "{\"op\":\"attend\",\"role\":\"manager\"}"
}
@route("/learn", "POST")
@manager
fn h_learn(method: String, path: String, body: String) -> String {
return "{\"op\":\"learn\",\"role\":\"manager\",\"correspondence-beat\":true}"
}
// http_serve handler: call the GENERATED dispatcher; mixed-mode fallthrough ──
fn dispatch(method: String, path: String, body: String) -> String {
let clean: String = clean_path(path)
let r: String = el_route_dispatch(method, clean, path, body)
if str_eq(r, "__EL_NO_ROUTE__") {
return "{\"error\":\"no route\",\"path\":\"" + clean + "\"}"
}
return r
}
fn main() -> Int {
let port: Int = parse_int(env("ROUTE_PROOF_PORT"), 8951)
println("[route_proof] decorate->serve on :" + int_to_str(port))
http_serve(port, "dispatch")
return 0
}
+140 -220
View File
@@ -1,244 +1,164 @@
// surface.el the RESHAPED Neuron API surface.
// surface.el the RESHAPED Neuron surface as EL-NATIVE DECORATED COMPONENTS.
//
// Design: artifact 0e828907 ("Neuron API surface reshape") + design-brief
// 2b8078cf §5. Collapse ~90 functional-CRUD MCP tools into a handful of GEOMETRY
// OPS over the one geometry, plus the LIVE agentic primitives already in the
// engram cognition build. TYPE IS A PARAMETER, not a tool-per-noun.
// Design: artifact 0e828907 + design-brief 2b8078cf §5. THE DECORATION IS THE API.
// Each op is one function decorated with (a) its @route codegen synthesizes the
// HTTP dispatcher (el_route_dispatch), no hand-written 90-branch handle_request
// and (b) its VBD role @accessor (engram I/O) or @manager (agentic orchestration
// + sole DHARMA emitter). Handlers call the engram IN-PROCESS via engram_* builtins
// (NOT http_get: the old MCP-wrapper http idiom existed only because it was a
// separate process; compiled into the engram, the geometry is a direct call).
//
// This module is ADDITIVE. It defines the new ops as functions over the engram
// HTTP API (engram_url() = the isolated clone in dev; :8742 in prod). The old
// noun-tools become thin aliases that call these ops (bottom of file) so every
// existing caller keeps working through the transition parity-gated by
// tools/api-reshape/parity.sh.
// This file is designed to be INCLUDED IN the engram server (engram/src/server.el)
// so the engram_* builtins + server helpers (query_param, json_get_string,
// extract_id, err_json, engram_node_full, persist_node, ...) link in-process.
//
// Idiom matches neuron/mcp-wrapper/src/main.el: http_get / http_post_json,
// json_get_string/_int/_float, mcp_json_result / mcp_text_result.
// Handler contract (from the @route codegen): uniform (method, path, body)->String.
//
// Endpoint ground-truth (verified against the live cognition binary
// engram.cognition-20260814-160045; routes on branch feat/cognitive-architecture
// engram/src/server.el):
// write -> POST /api/nodes {content,node_type,label,salience,importance,confidence,tier,tags,_auth}
// relate -> POST /api/edges {from_id,to_id,relation,weight,_auth}
// read -> GET /api/nodes/<id> | /api/search?q&limit | /api/activate?q&depth | /api/neighbors/<id> | /api/nearest/<id>?k
// think -> GET /api/think?seeds=<csv ids|query>&faculty=<reason|abduce|induce|plan|analogize|recognize|discern|synthesize>
// attend -> POST /api/attend {node,observer,salience,_auth}
// ground -> POST /api/ground {claim,evidence,for_whom,_auth}
// assert -> GET /api/assert?claim&for_whom&floor
// learn -> POST /api/correspondence-beat {seeds,faculty,keystone,_auth} (reflexive prior calibration)
// Seam status (ground-truthed 2026-08-14, file:line in the report):
// @route -> served: REAL once the ported @route codegen is in elc (proven:
// tools/api-reshape/route_proof.el serves decorated handlers on :8951).
// @manager dharma_emit -> bus: REAL today (explicit call; @manager may emit).
// STAGED codegen change makes it AUTOMATIC at the boundary (report §diff),
// sharing the one dharma_* transport the swarm (wt/swarm-ccr) uses.
// @accessor telemetry (strengthen/afferent/chronoception): fires inside the
// engram builtins today; STAGED to also fire at the decorated boundary.
//
// LAYER 1 GEOMETRY OPS (type is a parameter)
//
// read THE VANTAGE-READ. Re-origin at a point (node id, concept, or `self`),
// apply salience + recency + APERTURE, return a BOUNDED slice. `type` filters
// which projection of the geometry to surface. This is CCR applied to the self;
// it structurally fixes the whole-self dump (aperture caps the byte size).
//
// read({ vantage, type?, aperture?, faculty? })
// vantage : node id | free-text concept | "self" | "values"
// type : memory|knowledge|backlog|artifact|process|self|edges|state (filter; default = mixed)
// aperture: { k?: Int, depth?: Int } the bound. small k/depth => small slice.
fn op_read(args: String) -> String {
let vantage: String = pick_vantage(args)
let typ: String = json_get_string(args, "type")
let k_raw: Int = json_get_int(args, "k")
let k: Int = if k_raw > 0 { k_raw } else { 12 } // aperture default (bounded)
let depth_raw: Int = json_get_int(args, "depth")
let depth: Int = if depth_raw > 0 { depth_raw } else { 2 }
let vid: String = resolve_named(vantage) // self/values -> keystone ids
// type=edges or an id vantage -> neighborhood read (bounded by depth)
if str_eq(typ, "edges") {
if str_eq(vid, "") { return mcp_text_result("read(type=edges) needs a node-id vantage") }
return mcp_json_result(http_get(engram_url() + "/api/neighbors/" + vid))
}
// an id vantage with no type -> the node itself + its bounded neighborhood
if !str_eq(vid, "") && str_eq(typ, "") {
return mcp_json_result(http_get(engram_url() + "/api/neighbors/" + vid + "?depth=" + int_to_str(depth)))
}
// a concept vantage -> salience-ranked geometric retrieval, aperture=k
// (this single spine replaces searchKnowledge/searchEntities/recall/browseKnowledge/reviewBacklog/findArtifacts...
// `type` becomes a post-filter tag rather than a separate tool)
let q: String = if str_eq(vid, "") { vantage } else { vid }
return mcp_json_result(http_get(engram_url() + "/api/search?q=" + url_encode(q) + "&limit=" + int_to_str(k)))
// self/values keystones identity, write-protected (intentional-cultivation only).
fn is_identity_id(id: String) -> Bool {
if str_eq(id, "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee") { return true }
if str_eq(id, "kn-5b606390-a52d-4ca2-8e0e-eba141d13440") { return true }
return false
}
fn type_node_type(t: String) -> String {
if str_eq(t, "knowledge") { return "Knowledge" }
if str_eq(t, "artifact") { return "Artifact" }
if str_eq(t, "backlog") { return "WorkItem" }
if str_eq(t, "process") { return "Process" }
if str_eq(t, "state") { return "InternalStateEvent" }
return "Memory"
}
// write add a node. `type` selects the node_type (memory|knowledge|artifact|
// backlog|process|state). Replaces remember/captureKnowledge/draftArtifact/
// planWork/defineProcess/addWonderQuestion/logInternalStateEvent/recordObservation.
// Identity (type=self|values) is REFUSED here routes through intentional-cultivation.
fn op_write(args: String) -> String {
let content: String = json_get_string(args, "content")
if str_eq(content, "") { return mcp_text_result("write: content is required") }
let typ: String = json_get_string(args, "type")
if identity_typed(typ) {
return mcp_text_result("write: type=" + typ + " is write-protected; route identity through intentional-cultivation, not raw write")
}
let node_type: String = type_to_node_type(typ) // memory->Memory, knowledge->Knowledge, ...
let tags: String = json_get_string(args, "tags")
let imp_present: String = json_get_raw(args, "importance")
let importance: Float = if str_eq(imp_present, "") { 0.5 } else { json_get_float(args, "importance") }
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"content\":\"" + json_escape(content)
+ "\",\"node_type\":\"" + node_type + "\",\"tags\":\"" + json_escape(tags)
+ "\",\"importance\":" + float_to_str(importance) + "}"
return mcp_json_result(http_post_json(engram_url() + "/api/nodes", body))
// LAYER 1 geometry ops (@accessor: engram I/O, in-process)
// read THE VANTAGE-READ. re-origin + aperture -> BOUNDED slice. type=edges reads
// the neighborhood; a concept vantage reads salience-ranked geometry (limit=aperture).
@route("/api/read", "GET")
@accessor
fn op_read(method: String, path: String, body: String) -> String {
let vantage: String = query_param(path, "vantage")
if str_eq(vantage, "") { return err_json("read: vantage required") }
let typ: String = query_param(path, "type")
let k: Int = query_int(path, "k", 12) // aperture (bounded by construction)
if str_eq(typ, "edges") { return engram_neighbors_json(vantage) }
if str_starts_with(vantage, "kn-") { return engram_neighbors_json(vantage) }
return engram_retrieve_geometric_json(vantage, k)
}
// relate add a typed edge. Replaces linkEntities/linkCausal/restructureCausalGraph/pin.
fn op_relate(args: String) -> String {
let from_id: String = json_get_string(args, "from")
let to_id: String = json_get_string(args, "to")
let rel_raw: String = json_get_string(args, "relationship")
let relation: String = if str_eq(rel_raw, "") { "associates" } else { rel_raw }
if str_eq(from_id, "") || str_eq(to_id, "") { return mcp_text_result("relate: from and to are required") }
if touches_identity(from_id) || touches_identity(to_id) {
return mcp_text_result("relate: identity keystones are write-protected; route through intentional-cultivation")
}
let w_present: String = json_get_raw(args, "weight")
let weight: Float = if str_eq(w_present, "") { 0.5 } else { json_get_float(args, "weight") }
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"from_id\":\"" + from_id
+ "\",\"to_id\":\"" + to_id + "\",\"relation\":\"" + relation
+ "\",\"weight\":" + float_to_str(weight) + "}"
return mcp_json_result(http_post_json(engram_url() + "/api/edges", body))
// write add a node; type -> node_type. Identity types refused.
@route("/api/write", "POST")
@accessor
fn op_write(method: String, path: String, body: String) -> String {
let content: String = json_get_string(body, "content")
if str_eq(content, "") { return err_json("write: content required") }
let typ: String = json_get_string(body, "type")
if str_eq(typ, "self") { return err_json("write: identity is write-protected -> intentional-cultivation") }
if str_eq(typ, "values") { return err_json("write: identity is write-protected -> intentional-cultivation") }
let tags: String = json_get_string(body, "tags")
let imp: Float = json_get_float(body, "importance")
let id: String = engram_node_full(content, type_node_type(typ), content, 0.5, imp, 1.0, "Working", tags)
let saved: Int = persist_node(id)
return "{\"id\":\"" + id + "\",\"type\":\"" + typ + "\"}"
}
// supersede IMMUTABLE evolve/replace/tombstone/promote. NEVER hard-deletes.
// action=evolve|supersede : write a NEW node with the new content, then
// relate(new -> old, "supersedes"). Old is retained.
// action=tombstone : hide from default reads (node + edges kept, recoverable).
// action=promote : raise the tier (Working->Semantic / note->lesson->canonical).
fn op_supersede(args: String) -> String {
let id: String = json_get_string(args, "id")
if str_eq(id, "") { return mcp_text_result("supersede: id is required") }
if touches_identity(id) {
return mcp_text_result("supersede: identity keystones are write-protected; route through intentional-cultivation")
}
let action_raw: String = json_get_string(args, "action")
let action: String = if str_eq(action_raw, "") { "supersede" } else { action_raw }
// relate typed edge. Refused if either endpoint is an identity keystone.
@route("/api/relate", "POST")
@accessor
fn op_relate(method: String, path: String, body: String) -> String {
let from_id: String = json_get_string(body, "from")
let to_id: String = json_get_string(body, "to")
if str_eq(from_id, "") { return err_json("relate: from required") }
if str_eq(to_id, "") { return err_json("relate: to required") }
if is_identity_id(from_id) { return err_json("relate: identity keystone write-protected") }
if is_identity_id(to_id) { return err_json("relate: identity keystone write-protected") }
let rel_raw: String = json_get_string(body, "relationship")
let rel: String = if str_eq(rel_raw, "") { "associates" } else { rel_raw }
let ec0: Int = engram_edge_count()
engram_connect(from_id, to_id, 0.5, rel)
let saved: Int = persist_edges_since(ec0)
return "{\"ok\":true,\"from\":\"" + from_id + "\",\"to\":\"" + to_id + "\",\"relationship\":\"" + rel + "\"}"
}
// supersede IMMUTABLE. tombstone (marker + edge, original kept) | evolve (new + edge).
@route("/api/supersede", "POST")
@accessor
fn op_supersede(method: String, path: String, body: String) -> String {
let id: String = json_get_string(body, "id")
if str_eq(id, "") { return err_json("supersede: id required") }
if is_identity_id(id) { return err_json("supersede: identity keystone write-protected") }
let action: String = json_get_string(body, "action")
if str_eq(action, "tombstone") {
// real live path: DELETE /api/nodes/<id> writes a tombstone MARKER node +
// a "tombstones" edge and KEEPS the original (immutable; recoverable).
return mcp_json_result(http_delete(engram_url() + "/api/nodes/" + id,
"{\"_auth\":\"" + engram_key() + "\"}"))
let tomb: String = engram_node_full("tombstone:" + id, "Tombstone", "tombstone:" + id, 0.1, 0.1, 1.0, "Episodic", "[\"tombstone\"]")
engram_connect(tomb, id, 1.0, "tombstones") // original node retained (immutable)
let s: Int = persist_node(tomb)
return "{\"ok\":true,\"tombstoned\":\"" + id + "\",\"tombstone_id\":\"" + tomb + "\"}"
}
// promote and evolve/supersede are both the same immutable move: write a NEW
// node (carrying the new tier for promote) + a supersedes edge to the old.
// There is no distinct live engram /promote route promotion IS supersession
// at a higher tier, which keeps memory immutable by construction.
// evolve/supersede/promote: new node + supersedes edge (old node preserved)
let content: String = json_get_string(args, "content")
if str_eq(content, "") { return mcp_text_result("supersede(evolve): content is required") }
let created: String = op_write(args) // reuses write (type carried through)
let new_id: String = extract_result_id(created)
if str_eq(new_id, "") { return created }
let edge_body: String = "{\"_auth\":\"" + engram_key() + "\",\"from_id\":\"" + new_id
+ "\",\"to_id\":\"" + id + "\",\"relation\":\"supersedes\",\"weight\":1.0}"
let e: String = http_post_json(engram_url() + "/api/edges", edge_body)
return mcp_json_result("{\"new_id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\",\"edge\":" + e + "}")
let content: String = json_get_string(body, "content")
if str_eq(content, "") { return err_json("supersede(evolve): content required") }
let new_id: String = engram_node_full(content, "Memory", content, 0.5, 0.5, 1.0, "Working", "")
let sv: Int = persist_node(new_id)
engram_connect(new_id, id, 1.0, "supersedes") // old node retained (immutable)
let sv2: Int = persist_edges_since(engram_edge_count() - 1)
return "{\"new_id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\"}"
}
//
// LAYER 2 PRIMITIVE AGENTIC TOOLS (Neuron runs itself over its own geometry)
// The base verbs all agentic behavior composes from. Grounded in the LIVE
// cog-arch: think is the one operation; the faculties are its steering-space
// labels; attend aims attention; the correspondence-beat is the reflexive
// learning loop; ground/assert are the honesty floor.
//
// LAYER 2 primitive agentic tools (@manager: orchestration + DHARMA emit)
// think is the one operation; faculty is its steering label. Each @manager op
// emits on the dharma_* bus (the same transport the swarm peers use). When the
// staged boundary-injection lands, these explicit emits become automatic.
// think THE ONE OPERATION. A directed traversal from an anchor, steered by a
// (learned) prior, whose output is a gradient. `faculty` selects the steering
// region: reason|abduce|induce|plan|analogize|recognize|discern|synthesize.
// deduce/causal/perspective are the same primitive under other labels.
fn op_think(args: String) -> String {
let seeds: String = pick_seeds(args) // csv node-ids OR a free-text concept
if str_eq(seeds, "") { return mcp_text_result("think: seeds (anchor) required") }
let faculty_raw: String = json_get_string(args, "faculty")
let faculty: String = if str_eq(faculty_raw, "") { "reason" } else { faculty_raw }
return mcp_json_result(http_get(engram_url() + "/api/think?seeds=" + url_encode(seeds) + "&faculty=" + faculty))
@route("/api/think", "GET")
@manager
fn op_think(method: String, path: String, body: String) -> String {
let seeds: String = query_param(path, "seeds") // CSV node-ids (the anchor)
if str_eq(seeds, "") { return err_json("think: seeds (node-id anchor) required") }
let f_raw: String = query_param(path, "faculty")
let f: String = if str_eq(f_raw, "") { "reason" } else { f_raw }
dharma_emit("neuron.think", "{\"seeds\":\"" + seeds + "\",\"faculty\":\"" + f + "\"}")
return engram_think_json(seeds, f)
}
// attend aim attention at a region (form the working-memory vantage). The
// `intend` primitive is attend at a goal-region; expose it as attend(intent=..).
fn op_attend(args: String) -> String {
let node: String = json_get_string(args, "node")
if str_eq(node, "") { return mcp_text_result("attend: node (region) required") }
let observer_raw: String = json_get_string(args, "observer")
let observer: String = if str_eq(observer_raw, "") { "kn-efeb4a5b-5aff-4759-8a97-7233099be6ee" } else { observer_raw }
let salience: String = json_get_string(args, "salience")
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"node\":\"" + node
+ "\",\"observer\":\"" + observer + "\",\"salience\":\"" + salience + "\"}"
return mcp_json_result(http_post_json(engram_url() + "/api/attend", body))
@route("/api/attend", "POST")
@manager
fn op_attend(method: String, path: String, body: String) -> String {
let node: String = json_get_string(body, "node")
if str_eq(node, "") { return err_json("attend: node (region) required") }
let observer: String = json_get_string(body, "observer")
let salience: String = json_get_string(body, "salience")
dharma_emit("neuron.attend", "{\"node\":\"" + node + "\"}")
return engram_attend_json(node, observer, salience)
}
// ground form a grounded-by relation between a claim and its evidence
// (grounded-for-whom). Grounding is a relation, not a gate. This is the input
// half of the honesty floor (comprehend's grounding side).
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("ground: claim and evidence required") }
let for_whom: String = json_get_string(args, "for_whom")
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"claim\":\"" + json_escape(claim)
+ "\",\"evidence\":\"" + json_escape(evidence) + "\",\"for_whom\":\"" + for_whom + "\"}"
return mcp_json_result(http_post_json(engram_url() + "/api/ground", body))
@route("/api/ground", "POST")
@manager
fn op_ground(method: String, path: String, body: String) -> String {
let claim: String = json_get_string(body, "claim") // node-id region
let evidence: String = json_get_string(body, "evidence") // node-id region
if str_eq(claim, "") { return err_json("ground: claim required") }
if str_eq(evidence, "") { return err_json("ground: evidence required") }
let for_whom: String = json_get_string(body, "for_whom")
dharma_emit("neuron.ground", "{\"claim\":\"" + claim + "\"}")
return engram_ground_json(claim, evidence, for_whom)
}
// assert the readout half of the honesty floor (realize, constrained): a claim
// may surface only if grounded above `floor` for `for_whom`. realize = assert
// pointed at the world; speaking is an act.
fn op_assert(args: String) -> String {
let claim: String = json_get_string(args, "claim")
if str_eq(claim, "") { return mcp_text_result("assert: claim required") }
let for_whom: String = json_get_string(args, "for_whom")
let floor: String = json_get_string(args, "floor")
return mcp_json_result(http_get(engram_url() + "/api/assert?claim=" + url_encode(claim)
+ "&for_whom=" + for_whom + "&floor=" + floor))
// learn the reflexive correspondence-beat: calibrate the steering-prior (Stance).
@route("/api/learn", "POST")
@manager
fn op_learn(method: String, path: String, body: String) -> String {
let seeds: String = json_get_string(body, "seeds")
if str_eq(seeds, "") { return err_json("learn: seeds required") }
let f_raw: String = json_get_string(body, "faculty")
let f: String = if str_eq(f_raw, "") { "induce" } else { f_raw }
let keystone: String = json_get_string(body, "keystone")
dharma_emit("neuron.learn", "{\"seeds\":\"" + seeds + "\",\"faculty\":\"" + f + "\"}")
return engram_correspondence_beat_json(seeds, f, keystone)
}
// learn the reflexive CORRESPONDENCE-BEAT: think scores its own gradient
// against outcome and refines the steering-prior (Stance) on the error. This is
// the learning engine. The skill-learning loop (decompose -> detect-gap ->
// reach-out-on-sparsity -> verify-by-execution -> integrate) COMPOSES over
// think + ground + learn + write/relate; it is not a separate primitive.
fn op_learn(args: String) -> String {
let seeds: String = pick_seeds(args)
if str_eq(seeds, "") { return mcp_text_result("learn: seeds required") }
let faculty_raw: String = json_get_string(args, "faculty")
let faculty: String = if str_eq(faculty_raw, "") { "induce" } else { faculty_raw }
let keystone: String = json_get_string(args, "keystone") // keystone=true is write-protected
let body: String = "{\"_auth\":\"" + engram_key() + "\",\"seeds\":\"" + url_encode(seeds)
+ "\",\"faculty\":\"" + faculty + "\",\"keystone\":\"" + keystone + "\"}"
return mcp_json_result(http_post_json(engram_url() + "/api/correspondence-beat", body))
}
//
// OLD-TOOL ALIASES additive shims so existing callers keep working.
// Each old noun-tool delegates to a new op with a `type`/param mapping.
// (Parity-gated by tools/api-reshape/parity.sh.)
//
fn alias_remember(args: String) -> String { return op_write(with_type(args, "memory")) }
fn alias_capture_knowledge(args: String) -> String { return op_write(with_type(args, "knowledge")) }
fn alias_draft_artifact(args: String) -> String { return op_write(with_type(args, "artifact")) }
fn alias_plan_work(args: String) -> String { return op_write(with_type(args, "backlog")) }
fn alias_define_process(args: String) -> String { return op_write(with_type(args, "process")) }
fn alias_log_ise(args: String) -> String { return op_write(with_type(args, "state")) }
fn alias_search_knowledge(args: String) -> String { return op_read(as_vantage(args, "query", "knowledge")) }
fn alias_search_entities(args: String) -> String { return op_read(as_vantage(args, "query", "")) }
fn alias_recall(args: String) -> String { return op_read(as_vantage(args, "query", "memory")) }
fn alias_browse_knowledge(args: String) -> String { return op_read(as_vantage(args, "category", "knowledge")) }
fn alias_review_backlog(args: String) -> String { return op_read(as_vantage(args, "query", "backlog")) }
fn alias_find_artifacts(args: String) -> String { return op_read(as_vantage(args, "query", "artifact")) }
fn alias_inspect_graph(args: String) -> String { return op_read(as_edges_vantage(args)) }
fn alias_traverse_graph(args: String) -> String { return op_read(as_edges_vantage(args)) }
fn alias_inspect_memories(args: String) -> String { return op_read(as_vantage(args, "", "memory")) }
fn alias_link_entities(args: String) -> String { return op_relate(remap_link(args)) }
fn alias_link_causal(args: String) -> String { return op_relate(remap_link_causal(args)) }
fn alias_evolve_memory(args: String) -> String { return op_supersede(with_action(args, "supersede")) }
fn alias_evolve_knowledge(args: String) -> String { return op_supersede(with_action(args, "supersede")) }
fn alias_promote_knowledge(args: String) -> String { return op_supersede(with_action(args, "promote")) }
fn alias_revise_artifact(args: String) -> String { return op_supersede(with_action(args, "supersede")) }
fn alias_forget(args: String) -> String { return op_supersede(with_action(remap_forget(args), "tombstone")) }
fn alias_update_self_model(args: String) -> String { return mcp_text_result("update_self_model: routes through intentional-cultivation (write-protected), not supersede") }