Compare commits

..

1 Commits

Author SHA1 Message Date
Tim Lingo 17e53456b5 chore(engine): regenerate dist/soul.c from merged main — the build input was 4 days behind the source
Neuron Soul CI / build (pull_request) Failing after 14m33s
Neuron Soul CI / deploy (pull_request) Has been skipped
Merging #109, #114, #115, #127 and #130 landed the SOURCE on main. It did not
change what the build actually compiles. CI compiles the committed
dist/soul.c, and that file was last regenerated 2026-08-03 (e610a41). Measured
against origin/main before this commit:

  conv_hist_key          0     (the per-session history key — #129's fix)
  agentic_safety_screen  0     (the crisis-escalation fix itself)
  plain_chat_envelope    0     (#109)
  size                   1,147,662 bytes

So a CI or GKE build off main today would have shipped an engine with none of
the five merged fixes in it, including the P0 safety fix, while main's source
read as correct. That gap is the whole of issue #111.

After regeneration from merged main:

  conv_hist_key          9
  agentic_safety_screen  3
  plain_chat_envelope    5
  size                   1,164,103 bytes, 1226 inlined bodies (gate wants >= 1200)

Compiles clean: 903,096 bytes, 13 warnings, 0 errors. Boots on a throwaway
port with a throwaway HOME and answers /health — RUNS, not just BUILT.
Production on :7770 was never touched; no live store was read or written.

Recipe: gen-soul-amalgam.sh (elc --target=c in a scratch tree with the .elh
headers removed, which is what forces full inlining) then cc-brain.sh.

TWO THINGS THIS DOES NOT FIX, both worth their own work:

1. Nothing in the tree regenerates this file. It is only ever updated by a
   human running the recipe and committing, and history shows it lags in
   batches, never per-change. It will drift again. neuron-ui#209 tracks the
   automation and is the actual fix.

2. The shipped round-9.1 brain still matches no committed source: it was built
   from fix/liveness-engine-91 with a regenerated amalgam that was deliberately
   not committed (8b06bbe). Strings present in that binary — plain_chat_envelope,
   __working__, max_tokens":16384 — were absent from every committed state.
   This commit makes main reproducible going forward; it does not make the
   already-shipped artifact reproducible retroactively.

Refs #111

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 11:16:36 -05:00
2 changed files with 741 additions and 520 deletions
Generated Vendored
+731 -386
View File
File diff suppressed because one or more lines are too long
+10 -134
View File
@@ -430,130 +430,7 @@ fn handle_api_node_update(body: String) -> String {
return "{\"id\":\"" + new_id + "\",\"supersedes\":\"" + id + "\",\"ok\":true}"
}
// Recall through spreading activation
//
// api_activation_depth traversal depth for a retrieval query. Honours ?depth= /
// body "depth" for callers that want a wider or tighter associative horizon;
// defaults to 2, matching every other production activation caller (the
// knowledge-search path here, chat.el's per-turn activation) one hop reaches a
// node's direct associations, two reaches its siblings through a shared hub,
// which is exactly the sibling-recovery case recall was failing.
fn api_activation_depth(path: String, body: String) -> Int {
let d: Int = api_query_int(path, "depth", 0)
let d = if d == 0 { json_get_int(body, "depth") } else { d }
if d <= 0 { return 2 }
return d
}
// api_merge_activated_nodes project an activation result array down to a bare
// node array in activation order, then backfill from the lexical seed list until
// `limit` nodes are collected. Deduped by node id.
//
// SHAPE CONTRACT: the return value is a BARE array of full engram node objects
// byte-for-byte the same node JSON engram_search_json emits, so every existing
// /recall consumer keeps working unchanged (the MCP wrapper's recall/
// searchKnowledge, tools/telegram-gateway.sh which reads `.value.content`,
// cli/neuron_mcp.py). Activation strength is a RANKING input here, not a payload
// change; the scalars stay available on /api/activate and in compileCtx.
fn api_merge_activated_nodes(act_raw: String, lex_raw: String, limit: Int) -> String {
let seen: String = ""
let out: String = ""
let n: Int = 0
// Pass 1 activation-ranked. engram_activate_json already sorts promoted
// (working-memory) nodes first by wm_weight desc, then background-only nodes
// by background_activation desc, so element order IS the activation ranking.
let an: Int = if api_nonempty(act_raw) { json_array_len(act_raw) } else { 0 }
let i: Int = 0
while i < an && n < limit {
let entry: String = json_array_get(act_raw, i)
let anode: String = json_get_raw(entry, "node")
let aid: String = json_get(anode, "id")
let adup: Bool = str_eq(aid, "") || str_contains(seen, "<" + aid + ">")
let asep: String = if n == 0 { "" } else { "," }
let out = if adup { out } else { out + asep + anode }
let seen = if adup { seen } else { seen + "<" + aid + ">" }
let n = if adup { n } else { n + 1 }
let i = i + 1
}
// Pass 2 lexical seed backfill (see the exact-lookup note on
// handle_api_recall). Only runs when activation left room under `limit`.
let ln: Int = if api_nonempty(lex_raw) { json_array_len(lex_raw) } else { 0 }
let j: Int = 0
while j < ln && n < limit {
let lnode: String = json_array_get(lex_raw, j)
let lid: String = json_get(lnode, "id")
let ldup: Bool = str_eq(lid, "") || str_contains(seen, "<" + lid + ">")
let lsep: String = if n == 0 { "" } else { "," }
let out = if ldup { out } else { out + lsep + lnode }
let seen = if ldup { seen } else { seen + "<" + lid + ">" }
let n = if ldup { n } else { n + 1 }
let j = j + 1
}
return "[" + out + "]"
}
// api_retrieve THE retrieval path. Spreading activation over the weighted
// directed graph, lexical seeds backfilling the tail.
//
// WAS (until 2026-08-07): `engram_search_json(q, limit)` alone a case-
// insensitive substring matcher scored by how many distinct query tokens appear
// in a node's content/label/tags, tie-broken by raw salience. It never read a
// single edge. Recall could not see an association: querying an identity value
// returned unrelated documents that happened to contain the word, and NOT the
// twelve sibling value nodes one hop off the same hub.
//
// NOW: recall runs the spreading-activation traversal that has been compiled
// into the runtime the whole time (engram_activate / engram_activate_json,
// el_runtime.c) and ranks by the resulting activation strength. This restores
// the designed retrieval mechanism Engram provisional 64/064,260, claim 1:
// "no data is retrieved from the weighted directed graph except through the
// spreading activation traversal", with activation strength computed as the
// PRODUCT of parent strength, edge weight, target salience, and query/target
// cosine similarity, because "the multiplication of all four factors enforces a
// conjunctive property... addition would allow many weak associations to
// accumulate into false relevance."
//
// SEEDING derived from the runtime, not assumed. engram_activate takes the
// query TEXT (not seed ids) and seeds internally in two passes: (1) lexical
// every node matching at least one query token seeds, with initial activation
// = salience x temporal_decay x dampening x token_coverage, so a node covering
// the whole phrase ignites harder than one covering a single word; (2) semantic
// supplement the top-K unreached nodes by cosine against the query embedding.
// All four other production call sites (neuron-api.el begin_session/compileCtx,
// chat.el:352/1715, awareness.el's curiosity scans) pass query text the same
// way, so this follows the established convention exactly. The consequence for
// recall is direct: the lexical surface recall used to RETURN is now the SEED
// SET of the traversal, and what comes back is what those seeds activate. That
// is why multi-word queries stop returning nothing every token that matches
// anything ignites, and the traversal ranks the resulting field.
//
// EXACT-LOOKUP GUARANTEE (no regression): engram_activate's result collector
// drops any reached node whose background_activation x confidence < 0.1 unless
// it was promoted to working memory, and it never seeds from InternalStateEvent
// nodes. So a rare exact token on a dormant, low-salience node can seed the
// traversal and still go unreported. Retrieval therefore appends the lexical
// seed list after the activated ranking, deduped by id, until `limit` is filled.
// This is a seeded hybrid, not a parallel search bolted alongside activation:
// the backfill is the SAME seed set the traversal itself computed, restored to
// the tail of the result rather than recomputed by a different mechanism.
// Activation always leads the ranking; nothing that used to be findable becomes
// unfindable.
//
// COST/EFFECT NOTE: activation is a stateful read by design claim 29, "update
// the last-activation timestamp and increment the activation count... in
// response to any access to that node record during spreading activation
// traversal". Promoted nodes get reinforced, working-memory weights are
// rewritten, and the query folds into the context centroid. That is the
// intended semantics of retrieval-as-activation and is already what every chat
// turn does; it does mean recall now participates in shaping working memory.
fn api_retrieve(q: String, path: String, body: String, limit: Int) -> String {
let depth: Int = api_activation_depth(path, body)
let act_raw: String = engram_activate_json(q, depth)
let lex_raw: String = engram_search_json(q, limit)
return api_or_empty(api_merge_activated_nodes(act_raw, lex_raw, limit))
}
// handle_api_recall retrieve memory by query, through spreading activation.
// handle_api_recall search or activate memory by query.
fn handle_api_recall(method: String, path: String, body: String) -> String {
// Accept the query from the URL ?query= / ?q= params, or, when those are
// empty (e.g. a POST with a JSON body), from the body fields "query"/"q".
@@ -573,7 +450,8 @@ 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))
}
return api_retrieve(eff_q, path, body, limit)
let results: String = engram_search_json(eff_q, limit)
return api_or_empty(results)
}
// Knowledge
@@ -592,15 +470,13 @@ fn handle_api_search_knowledge(method: String, path: String, body: String) -> St
let limit = if limit == 0 { json_get_int(body, "limit") } else { limit }
let limit = if limit == 0 { 10 } else { limit }
if str_eq(q, "") { return api_err("query is required") }
// Same retrieval path as recall and it is the SAME change, not a copy of
// one. The "activate fallback" this replaced was unreachable dead code: it
// only fired when engram_search_json's return did not start with '[' or '{',
// and engram_search_json always emits a '['-prefixed array (el_runtime.c
// jb_putc('[') before any hit test), so the guard was false on every call
// including the zero-hit "[]" case. Knowledge search therefore had exactly
// the substring-matcher behavior recall had, with a comment claiming
// otherwise. Routing it through api_retrieve makes the claim true.
return api_retrieve(q, path, body, limit)
let results: String = engram_search_json(q, limit)
if str_eq(results, "") { return "[]" }
let first: String = str_slice(results, 0, 1)
if !str_eq(first, "[") && !str_eq(first, "{") {
return api_or_empty(engram_activate_json(q, 2))
}
return results
}
// handle_api_browse_knowledge list Knowledge nodes.