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.
This commit is contained in:
2026-08-10 10:28:50 -05:00
parent 64cd5055c5
commit 4bff40fa4a
10 changed files with 2314 additions and 466 deletions
+9 -2
View File
@@ -302,7 +302,11 @@ fn fetch_by_id(args: String) -> String {
if str_eq(id, "") {
return mcp_text_result("error: id is required")
}
let resp: String = http_get(neuron_url() + "/graph?id=" + id + "&depth=0")
// NB: the soul's engram_neighbors_json coerces depth<=0 to depth=1, so this
// "single node fetch" actually pulls the full 1-hop neighborhood. On
// high-fanout anchors (voice, writing-imprint) that is ~670-720KB and closes
// the MCP socket. compact=1 bounds it identically to inspectGraph.
let resp: String = http_get(neuron_url() + "/graph?id=" + id + "&depth=0&compact=1")
return mcp_json_result(resp)
}
@@ -516,7 +520,10 @@ fn tool_inspect_graph(args: String) -> String {
if str_eq(resolved_id, "") {
return mcp_text_result("error: entity_id or name is required. Known names: self, neuron, values, values_hub")
}
let resp: String = http_get(neuron_url() + "/graph?id=" + resolved_id + "&depth=" + int_to_str(depth))
// compact=1: soul returns a bounded, relevance-ranked neighborhood (top-K
// with content, the rest as pointers) so high-fanout nodes (voice,
// writing-imprint) no longer overflow the MCP transport and close the socket.
let resp: String = http_get(neuron_url() + "/graph?id=" + resolved_id + "&depth=" + int_to_str(depth) + "&compact=1")
return mcp_json_result(resp)
}