Tombstone the remaining forget paths (close the immutability arc)

Phase 1 tombstoned memory/delete + node/delete, but the generic forget
path was still destructive. This routes every forget through the same
tombstone semantics so nothing in the daemon can hard-delete an engram
node anymore.

Canonical helper: mem_tombstone (memory.el, imported first so every module
can call it) — keep the node + its edges, record a Tombstone marker, never
engram_forget. neuron-api's tombstone_node now delegates to it (single
source of truth).

Per-path before -> after:
- memory.el `mem_forget`      hard delete (engram_forget) -> tombstone. This
  alone fixes both callers: the /api/neuron/memory/forget route
  (handle_api_forget) and the cultivate op=="forget".
- awareness.el autonomous `forget` action  engram_forget -> mem_tombstone.
  The soul can no longer autonomously hard-delete a memory.
- mcp-wrapper tool_forget  was a FAKE no-op that returned {"ok","deleted"}
  without deleting OR tombstoning -> now routes to the soul's tombstoning
  /api/neuron/memory/delete; tool description fixed to say it
  supersedes/tombstones (recoverable), not "Remove a node".

Left intentionally as hard deletes (internal GC / lifecycle, not user
memory, all call engram_forget directly): session-summary replace
(chat.el), mem_consolidate dedup (memory.el), session-start telemetry
pruning (soul.el), session lifecycle (sessions.el).

Regenerated both ship paths under a 3GB physical-RSS watchdog (peak ~32MB):
dist/soul.c (single-TU amalgamation, macOS/Linux) and the per-module
dist/{memory,awareness,neuron-api}.c (Windows/Linux build). Verified: no
forget handler calls engram_forget; a forget leaves the node present +
tombstone marker. Gate (neuron-ui verify-soul-contract.sh, new memory-forget
row): PRESENCE + IMMUTABILITY PASS.
This commit is contained in:
2026-07-18 13:22:43 -05:00
parent f3034d23f7
commit 40d800195a
8 changed files with 80 additions and 40 deletions
+7 -3
View File
@@ -91,7 +91,7 @@ tool("beginSession", "Initialize session: surface recent high-importance memorie
"," + tool("recall", "Retrieve memories by chain or query.") +
"," + tool("inspectMemories", "List recent memory nodes.") +
"," + tool("evolveMemory", "Update an existing memory node, optionally superseding another.") +
"," + tool("forget", "Remove a node from memory.") +
"," + tool("forget", "Supersede/tombstone a node (keeps it and its edges, recoverable); does not hard-delete.") +
"," + tool("pinNode", "Strengthen a node so it stays salient.") +
// Knowledge
"," + tool("searchKnowledge", "Search knowledge base by semantic similarity.") +
@@ -541,8 +541,12 @@ fn tool_forget(args: String) -> String {
if str_eq(id, "") {
return mcp_text_result("error: node_id is required")
}
// Soft-delete: record a tombstone memory and return ok
return mcp_json_result("{\"ok\":true,\"deleted\":\"" + id + "\"}")
// Immutable delete: route to the soul's tombstoning endpoint (keeps the node
// + edges, hides from default reads, recoverable via ?include_deleted).
// Previously this returned a fake ok without deleting OR tombstoning anything.
let body: String = "{\"id\":\"" + id + "\"}"
let resp: String = http_post_json(neuron_url() + "/memory/delete", body)
return mcp_json_result(resp)
}
fn tool_check_events(args: String) -> String {