Files
el/engram/test/test_m3_parity.c
T
will.anderson 9a0266cbf9 engram tiered storage M3.5: persist activation field updates (pre-flip gate)
Flag-on checkpoint now full-walks the resident graph: store_put_node (WM weight,
activation_count, last_activated, wm_anchor) + store_put_edge (hebb, last_fired)
for every node/edge, then engram_checkpoint. Uses store_put_edge (idempotent
upsert) not store_hebb_batch, because activation FORMS new hebbian-associate edges
that bypass the create hook and delta-only hebb_batch can't create them. Store-on
boot now applies the same WM-halving + floor + cap transforms as engram_load.

This is the hebb-survives-restart fix. Gate: reboot from neuron.egm with
snapshot.json deleted -> edge hebb + activation_count survive unchanged, WM weight
survives with identical boot transform; negative control proves persist is
load-bearing (hebb->0 without it). M1 33/33 + M2 36/36 + M3 parity PASS, ASan/UBSan
clean, flag-off untouched. Engine unchanged (boundary held).
2026-08-11 23:37:52 -05:00

156 lines
8.8 KiB
C

/* test_m3_parity.c — M3 JSON-parity gate for the ENGRAM_STORE wiring.
*
* This is a REAL el-level harness: it links the actual el_runtime.o (the soul's
* native engram builtins) + engram_store.o and calls the engram_node family plus
* engram_connect, engram_activate_json, engram_save, engram_store_boot directly. No EL interpreter
* and no full soul build are needed — el_runtime.c compiles to a standalone .o
* whose engram builtins operate on the process-global engram store, and the
* string arena is inert unless el_request_start() is called, so the builtins are
* callable straight from C (el_val_t is int64_t; EL_STR/EL_CSTR are pointer casts).
*
* Modes (argv[1]), data dir (argv[2]):
* seed — ENGRAM_STORE unset: build a fixed seed graph, write snapshot.json +
* off_graph.json (pristine, pre-activation), then activate → off_act.json.
* on — ENGRAM_STORE=1: engram_store_boot(dir) imports snapshot.json ONCE into
* neuron.egm and loads it resident; write on_graph.json, then activate →
* on_act.json; checkpoint + close.
* reboot — ENGRAM_STORE=1 with snapshot.json DELETED: boot must reload from
* neuron.egm (WAL replay), never re-reading JSON; write reboot_graph.json.
* offcheck — assert flag-off leaves the store untouched.
*
* The graph comparison (done by run_m3_parity.sh via python, modulo ordering) is
* the deterministic gate; activation ids/promoted are compared as a robust set.
*/
#include "el_runtime.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Builtins the header declares are pulled in via el_runtime.h. The M3 additions
* are not in the header yet, so declare them here. */
extern int engram_store_enabled(void);
extern el_val_t engram_store_boot(el_val_t data_dir);
extern el_val_t engram_store_checkpoint(void);
extern el_val_t engram_store_close(void);
extern el_val_t engram_node_layered(el_val_t content, el_val_t node_type, el_val_t label,
el_val_t salience, el_val_t certainty, el_val_t confidence,
el_val_t status, el_val_t tags, el_val_t layer_id);
static el_val_t S(const char* s){ return EL_STR(s); }
static el_val_t F(double d){ return el_from_float(d); }
/* Build a fixed, deterministic seed graph: 12 nodes across two layers + 9 edges.
* Content is chosen so an activation query has real matches to rank. */
static void build_seed(void){
/* core-identity layer (1) via engram_node_full */
el_val_t n0 = engram_node_full(S("tiered storage engine design"), S("Concept"),
S("storage-engine"), F(0.9), F(0.8), F(1.0), S("Semantic"), S("design,storage"));
el_val_t n1 = engram_node_full(S("write-ahead log durability"), S("Concept"),
S("wal"), F(0.85), F(0.75), F(1.0), S("Semantic"), S("wal,durability"));
el_val_t n2 = engram_node_full(S("paged buffer pool with checkpointing"), S("Concept"),
S("buffer-pool"), F(0.8), F(0.7), F(1.0), S("Semantic"), S("paging"));
el_val_t n3 = engram_node_full(S("spreading activation over the graph"), S("Concept"),
S("activation"), F(0.8), F(0.7), F(1.0), S("Semantic"), S("activation,graph"));
el_val_t n4 = engram_node_full(S("hebbian co-activation potentiation"), S("Concept"),
S("hebbian"), F(0.7), F(0.6), F(1.0), S("Semantic"), S("hebb"));
el_val_t n5 = engram_node_full(S("crash recovery replays the log"), S("Concept"),
S("recovery"), F(0.75), F(0.65), F(1.0), S("Semantic"), S("recovery,wal"));
/* domain-knowledge layer (2) via engram_node_layered */
el_val_t n6 = engram_node_layered(S("b-tree primary index id to location"), S("Fact"),
S("btree"), F(0.7), F(0.6), F(1.0), S(""), S("index"), (el_val_t)2);
el_val_t n7 = engram_node_layered(S("adjacency index for edge lookup"), S("Fact"),
S("adjacency"), F(0.7), F(0.6), F(1.0), S(""), S("index,graph"), (el_val_t)2);
el_val_t n8 = engram_node_layered(S("slotted pages hold tlv records"), S("Fact"),
S("slotted-page"), F(0.65), F(0.55), F(1.0), S(""), S("format"), (el_val_t)2);
el_val_t n9 = engram_node_full(S("memory tiers working semantic episodic"), S("Concept"),
S("tiers"), F(0.7), F(0.6), F(1.0), S("Semantic"), S("tiers,memory"));
el_val_t n10 = engram_node_full(S("embeddings enable nearest neighbour search"), S("Concept"),
S("embeddings"), F(0.65), F(0.55), F(1.0), S("Semantic"), S("embeddings"));
el_val_t n11 = engram_node_full(S("the durable engram is the mind's memory"), S("Belief"),
S("engram"), F(0.95), F(0.9), F(1.0), S("Semantic"), S("engram,memory"));
engram_connect(n0, n1, F(0.8), S("depends-on"));
engram_connect(n0, n2, F(0.8), S("depends-on"));
engram_connect(n0, n3, F(0.7), S("enables"));
engram_connect(n1, n5, F(0.9), S("enables"));
engram_connect(n3, n4, F(0.6), S("triggers"));
engram_connect(n2, n6, F(0.7), S("uses"));
engram_connect(n3, n7, F(0.7), S("uses"));
engram_connect(n0, n8, F(0.6), S("uses"));
engram_connect(n11, n9, F(0.8), S("about"));
engram_connect(n11, n10, F(0.5), S("about"));
}
static void write_file(const char* path, const char* content){
FILE* f = fopen(path, "wb");
if (!f){ fprintf(stderr, "cannot open %s\n", path); exit(2); }
if (content) fwrite(content, 1, strlen(content), f);
fclose(f);
}
static const char* QUERY = "storage engine activation and the durable log";
int main(int argc, char** argv){
if (argc < 3){ fprintf(stderr, "usage: %s <seed|on|reboot|offcheck> <dir>\n", argv[0]); return 2; }
const char* mode = argv[1];
const char* dir = argv[2];
char p[1024];
if (!strcmp(mode, "seed")){
if (engram_store_enabled()){ fprintf(stderr, "seed mode requires ENGRAM_STORE unset\n"); return 2; }
build_seed();
snprintf(p, sizeof p, "%s/snapshot.json", dir);
if (!engram_save(S(p))){ fprintf(stderr, "seed save failed\n"); return 2; }
snprintf(p, sizeof p, "%s/off_graph.json", dir);
engram_save(S(p)); /* pristine off-path graph */
el_val_t act = engram_activate_json(S(QUERY), (el_val_t)3);
snprintf(p, sizeof p, "%s/off_act.json", dir);
write_file(p, EL_CSTR(act));
printf("[seed] nodes=%lld edges=%lld\n",
(long long)(int64_t)engram_node_count(), (long long)(int64_t)engram_edge_count());
return 0;
}
if (!strcmp(mode, "on")){
if (!engram_store_enabled()){ fprintf(stderr, "on mode requires ENGRAM_STORE=1\n"); return 2; }
if (!engram_store_boot(S(dir))){ fprintf(stderr, "store boot failed\n"); return 2; }
snprintf(p, sizeof p, "%s/on_graph.json", dir);
engram_save(S(p)); /* export resident (== store) */
/* Checkpoint the freshly-imported (pristine) graph — this is the state
* the reboot comparison expects to round-trip. Under M3.5 a checkpoint
* persists the resident graph's CURRENT field state, so it must run
* BEFORE activation mutates fields in place; activation itself is
* exercised below only for the activation-result-set parity check. The
* M3.5 gate (test_m35_hebb_persist) separately proves that a checkpoint
* taken AFTER activation durably carries the learned hebb/WM state. */
engram_store_checkpoint();
el_val_t act = engram_activate_json(S(QUERY), (el_val_t)3);
snprintf(p, sizeof p, "%s/on_act.json", dir);
write_file(p, EL_CSTR(act));
printf("[on] nodes=%lld edges=%lld\n",
(long long)(int64_t)engram_node_count(), (long long)(int64_t)engram_edge_count());
engram_store_close();
return 0;
}
if (!strcmp(mode, "reboot")){
if (!engram_store_enabled()){ fprintf(stderr, "reboot mode requires ENGRAM_STORE=1\n"); return 2; }
/* snapshot.json has been deleted by the runner — boot MUST come from
* neuron.egm (+ WAL replay), never re-reading JSON. */
if (!engram_store_boot(S(dir))){ fprintf(stderr, "reboot boot failed\n"); return 2; }
snprintf(p, sizeof p, "%s/reboot_graph.json", dir);
engram_save(S(p));
printf("[reboot] nodes=%lld edges=%lld\n",
(long long)(int64_t)engram_node_count(), (long long)(int64_t)engram_edge_count());
engram_store_close();
return 0;
}
if (!strcmp(mode, "offcheck")){
/* ENGRAM_STORE unset: enabled()==0 and boot is a no-op returning 0. */
int en = engram_store_enabled();
el_val_t b = engram_store_boot(S(dir));
printf("[offcheck] enabled=%d boot_ret=%lld\n", en, (long long)(int64_t)b);
return (en == 0 && (int64_t)b == 0) ? 0 : 1;
}
fprintf(stderr, "unknown mode %s\n", mode);
return 2;
}