From cf154387ce48bb23d378fb1687d8f29d2aced6ce Mon Sep 17 00:00:00 2001 From: "will.anderson" Date: Sat, 15 Aug 2026 20:13:46 -0500 Subject: [PATCH] fix(routes): /api/graph/edges must not write the canonical snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This route called engram_save() over ~/.neuron/engram/snapshot.json — the engram server's CANONICAL store — then fs_read it back, to answer a READ query. A read route overwriting the persistence owner's file. This defect was fixed once before (export redirected to a scratch path). It came back tonight in the @route dispatch conversion: the hand-written dispatch block held the FIXED version, the @route-decorated copy held the unfixed one, and the merge kept the decorated copy. Calling the endpoint afterward overwrote the canonical snapshot and immediately preceded an engram crash. Now calls engram_edges_json(limit, offset) — the builtin the route's own TODO asked for — which reads g->edges directly. No file is written or read. Bounded: limit defaults to 1000, offset supported, so the whole-graph read that fell over is not reachable by default. Verified: same request that previously rewrote snapshot.json now leaves it byte-identical (sha256 unchanged before/after), and returns real edge records with every persisted field. --- routes.el | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/routes.el b/routes.el index bf1fac1..a4f555b 100644 --- a/routes.el +++ b/routes.el @@ -420,14 +420,23 @@ fn r_api_graph_nodes(method: String, path: String, body: String) -> String { @route("/api/graph/edges", "GET", "exact") @manager fn r_api_graph_edges(method: String, path: String, body: String) -> String { - // TODO(reliability #8): engram_save races with awareness loop mem_save(). - // Both now use atomic write-to-temp+rename (el_runtime.c). Serialised - // by engram_global_mu. Future: add engram_edges_json() builtin. - let snap_path: String = env("HOME") + "/.neuron/engram/snapshot.json" - engram_save(snap_path) - let snap: String = fs_read(snap_path) - let edges_raw: String = json_get_raw(snap, "edges") - return if str_eq(edges_raw, "") { "[]" } else { edges_raw } + // Reads edges straight from the store. No file is written or read. + // + // This route used to engram_save() the ENTIRE graph over + // ~/.neuron/engram/snapshot.json — the engram server's CANONICAL store — + // and then fs_read it back, just to answer a read query. Two defects in + // one line: a READ route clobbering the persistence owner's canonical + // file (the defect fixed once already, then reintroduced when the + // hand-written dispatch block was replaced by @route dispatch and the + // unfixed copy is the one that survived), and a 128 MB serialize + + // reread + parse per request. Calling it on 2026-08-15 overwrote the + // canonical snapshot and preceded an engram crash loop. + // + // engram_edges_json is the builtin the old TODO here asked for. Bounded + // by default (1000) — the unbounded whole-graph read is what fell over. + let lim: Int = api_query_int(path, "limit", 1000) + let off: Int = api_query_int(path, "offset", 0) + return engram_edges_json(lim, off) } // ── GET /api/chat — legacy probe interface; body may be empty ───────────────