The five agentic ops (think/attend/assert/ground/learn) reported
"status":"pending-cognition-promotion" on every call, with a note saying the
cognition build had not been promoted yet and would "light up automatically".
That diagnosis was invented. Nothing was pending promotion. Cognition has been
live and answering the whole time — :8742/api/think returns a real 768-dim
geometry today, and the running binary already contains every cog_* symbol.
Three real bugs, all here in the wrapper:
1. Wrong service and path. The ops called the SOUL (neuron_url() -> :7770) on
paths the soul does not serve. Every call 404'd. The routes live on the
ENGRAM: /api/think, /api/attend, /api/assert, /api/ground, and — its real
name — /api/correspondence-beat for learn.
2. agentic_result() invented a cause. It treated ""/"not found"/"geometry
unavailable"/"not registered" as proof of a promotion gap and returned a
confident explanation it never verified. That message sent multiple agents
chasing infrastructure work that did not need doing. It now passes the real
response through and reports an empty response as exactly that.
3. Missing auth on the POST ops. The engram's check_auth_ok() requires
"_auth":"<key>" in the body for mutating requests (it cannot read headers
yet), so attend/ground/learn would have returned unauthorized even once
routed correctly.
Also: assert was POSTing a JSON body to a route that reads query params;
seeds/claim/faculty are now URL-encoded; and the engram port derives from
ENGRAM_BIND (the same var launchd already sets for the engram) instead of a
hardcoded literal, so it cannot drift out of sync with the plist.
Verified end-to-end through the rebuilt wrapper against the live engram — all
five return real cognition: think n_support=207 dim=768; attend written=true;
assert floor=0.5 still_held=true; ground grounding=1 written=true; learn a full
correspondence-beat with stance_id, 8 axes, 180 probes, 25 epochs.
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.
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.
The ship-soul builds from this branch, which has the bounded-persona floor
(#93) but never received the tombstone/supersede immutability fix (that
went to main; hotfix diverged before it). So the launch soul failed
verify-soul-contract IMMUTABILITY on the delete/update/forget routes —
they hard-removed engram nodes via engram_forget/mem_forget.
Apply the same fix, mirroring the knowledge routes' supersede pattern:
- node/update -> create new node + "supersedes" edge to the original, KEEP
the original (no engram_forget).
- node/delete, memory/delete, memory/forget, cultivate forget, and the
autonomous awareness forget -> TOMBSTONE via the canonical mem_tombstone
(memory.el): keep the node + its edges, record a Tombstone marker, hide
from default bounded list reads (?include_deleted recovers). Never
engram_forget. The MCP forget tool now routes to the tombstoning delete
instead of faking a delete.
Internal GC that genuinely removes transient nodes (awareness inbox-trigger
consume, consolidation dedup, session-summary replace, telemetry pruning)
still calls engram_forget directly and is unchanged.
Regenerated dist/soul.c (single-TU) + per-module dist/{memory,awareness,
neuron-api}.c from THIS branch's sources under a 3GB physical-RSS watchdog
(peak ~32MB), built against the release el_runtime (v1.0.0-20260501). The
bounded-persona floor is preserved — verified in the emitted C and the
linked binary (BOUNDED PERSONA / SOUL_PERSONA_NAME strings present).
verify-soul-contract.sh: GATE PASS — PRESENCE all 27 routes, IMMUTABILITY
5/5 KEPT (memory-update, memory-delete, node-update, node-delete,
memory-forget).
planWork fell through create_typed_node to a generic /api/neuron/memory write — a [BacklogItem]-prefixed
memory blob with title/project/priority DROPPED, never a real BacklogItem. reviewBacklog used a lexical
/recall (top-50, untyped). Now: planWork -> /api/neuron/node/create {node_type:BacklogItem,...} via new
create_node_typed; reviewBacklog -> list_typed('BacklogItem') (GET /api/neuron/list/BacklogItem). elc-clean.
Depends on neuron PR #58 (the list/<type> slice fix) to round-trip; needs the wrapper binary rebuilt +
:7779 restarted to take effect.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>