self-review 2026-07-22: persist canonical snapshot on write routes; newest-first tie-break in node listings
El SDK Release / build-and-release (pull_request) Failing after 14m24s

Durability: the 2026-07-21 fix stopped read routes writing the canonical
snapshot but left no save on ANY write path — every mutation lived in RAM
until a manual POST /api/save. Observed live: two restarts reverted the
store to a 17h-old snapshot, destroying same-day writes. persist_canonical()
now runs after node/edge create, knowledge capture, forget, strengthen, and
load-merge. ISE telemetry excluded deliberately (48h-pruned, loss-tolerant,
~2/min; snapshotting 28MB per heartbeat is waste).

Listing order: scan routes sort by salience with store-order ties, so
equal-salience telemetry (all ISEs are 0.3) returned OLDEST first — a
limited /api/nodes query silently returned a stale window, and a 41h-old
heartbeat series read as a live outage during this review. Ties now break
newest-first by created_at.
This commit is contained in:
2026-07-22 08:51:33 -05:00
parent 409ec99397
commit 8f8ccc945e
4 changed files with 78 additions and 27 deletions
+12 -2
View File
@@ -6958,14 +6958,24 @@ el_val_t engram_search(el_val_t query, el_val_t limit) {
return lst;
}
/* Sort node indices by salience desc (small N, insertion sort is fine). */
/* Sort node indices by salience desc, tie-break created_at desc (newest
* first). The tie-break matters for telemetry: InternalStateEvent nodes all
* share salience 0.3, so before this the listing routes returned them in
* store order OLDEST first and any limited query (/api/nodes?...&limit=N)
* silently returned a stale window. A 41-hour-old heartbeat series read as a
* live outage during the 2026-07-22 self-review. Newest-first ties make
* limited scans return the recent window consumers actually want.
* (Small N, insertion sort is fine.) */
static void engram_sort_indices_by_salience(int64_t* arr, int64_t n,
const EngramNode* nodes) {
for (int64_t i = 1; i < n; i++) {
int64_t key = arr[i];
double ks = nodes[key].salience;
int64_t kc = nodes[key].created_at;
int64_t j = i - 1;
while (j >= 0 && nodes[arr[j]].salience < ks) {
while (j >= 0 && (nodes[arr[j]].salience < ks ||
(nodes[arr[j]].salience == ks &&
nodes[arr[j]].created_at < kc))) {
arr[j + 1] = arr[j];
j--;
}