M-INTEROCEPTION P5: dream-recall builtin engram_dreams_json (honesty rail)

Adds a read-only builtin that returns the curiosity_scan InternalStateEvents
created after a `since` cutoff that are STILL RESIDENT — "what I was chewing on
while you were gone." curiosity_scan is identified by the marker the soul writes
into each ISE's JSON content; heartbeat and other ISEs are excluded.

HARD honesty rail: it reports only ISEs still in the buffer. Anything rotated
out by the 48h engram_prune_telemetry is simply ABSENT — rotated-out = "I don't
remember", never a synthesized/plausible dream. Purely additive, no flag.

The HTTP route (GET /api/dreams?since=) is DEFERRED to cutover per the elc-drift
blocker (regenerating engram/dist diverges with no source change); the builtin
is exercised directly by the pure-C gate.

MEASURED on a copy: seeded 3 curiosity_scan (ancient/1h/now) + 1 heartbeat;
before prune dreams returned exactly the 3 curiosity_scan (heartbeat excluded);
after the 48h prune the ancient one was ABSENT (not confabulated), leaving 2;
the since-filter returned only the post-cutoff event; no returned id was ever
fabricated. ASan+UBSan clean.
This commit is contained in:
2026-08-12 23:49:51 -05:00
parent 65ca0a3253
commit 77a4bc9326
5 changed files with 158 additions and 0 deletions
+32
View File
@@ -12184,6 +12184,38 @@ el_val_t engram_scan_nodes_emb_json(el_val_t limit, el_val_t offset) {
return el_wrap_str(b.buf);
}
/* engram_dreams_json — M-INTEROCEPTION P5: dream-recall-on-wake (read-only).
* Returns the curiosity_scan InternalStateEvents created after `since_ms` that
* are STILL RESIDENT "what I was chewing on while you were gone." HARD honesty
* rail: it reports only ISEs still in the buffer. Anything rotated out by the
* 48h engram_prune_telemetry is simply absent rotated-out = "I don't
* remember", NEVER a synthesized/plausible dream. curiosity_scan is identified
* by the marker in the ISE's JSON content (the soul tags each ISE with its
* kind); heartbeat and other ISEs are excluded. Purely additive; no flag. */
el_val_t engram_dreams_json(el_val_t since_ms) {
EngramStore* g = engram_get();
int64_t since = (int64_t)since_ms; if (since < 0) since = 0;
JsonBuf b; jb_init(&b);
jb_putc(&b, '[');
int first = 1;
for (int64_t i = 0; i < g->node_count; i++) {
EngramNode* n = &g->nodes[i];
if (!n->node_type || strcmp(n->node_type, "InternalStateEvent") != 0) continue;
if (n->created_at <= since) continue;
if (!n->content || !strstr(n->content, "curiosity_scan")) continue;
if (!first) jb_putc(&b, ',');
first = 0;
jb_putc(&b, '{');
jb_puts(&b, "\"id\":"); jb_emit_escaped(&b, n->id ? n->id : "");
char t[32]; snprintf(t, sizeof t, ",\"created_at\":%lld", (long long)n->created_at);
jb_puts(&b, t);
jb_puts(&b, ",\"content\":"); jb_emit_escaped(&b, n->content ? n->content : "");
jb_putc(&b, '}');
}
jb_putc(&b, ']');
return el_wrap_str(b.buf);
}
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction) {
/* Re-implement here directly so we serialize without going through
* the ElList path. Walks BFS to max_depth, emits {node, edge, hops}