Archived
iteration-1: the compiler stops adjudicating #164
@@ -14379,7 +14379,67 @@ el_val_t engram_ise_log_append(el_val_t content_v){
|
||||
}
|
||||
}
|
||||
fputs("\"}\n", f);
|
||||
/* RETENTION (2026-08-17 self-review). The on-graph ISE branch in
|
||||
* server.el calls engram_prune_telemetry(48h) on every insert, but that
|
||||
* branch is DEAD in production: ENGRAM_ISE_OFFGRAPH=1 is the live
|
||||
* setting, so every state event lands here instead — and this path had
|
||||
* no retention of any kind. Measured: 17.1 MB / 14,305 events over 3.56
|
||||
* days = 4.81 MB/day, growing without bound (~1.76 GB/year). The graph
|
||||
* got its telemetry-growth fix on 2026-07-16; moving telemetry off-graph
|
||||
* moved the leak rather than closing it.
|
||||
*
|
||||
* Byte-bounded rather than time-bounded on purpose: this is a flat
|
||||
* append-only file with no index, so size is the property that actually
|
||||
* has to be bounded, and a byte check is O(1) against the handle we
|
||||
* already hold (ftell) instead of an O(file) timestamp scan per append.
|
||||
* At the measured rate the 64 MB default retains ~13 days — comfortably
|
||||
* more history than the 48h the on-graph path kept.
|
||||
*
|
||||
* Compaction keeps the TAIL, never the head: engram_dreams_json reads
|
||||
* the last ~2 MB of this file for dream-recall, so the recent end is the
|
||||
* end that has a reader. KEEP is held well above that 2 MB window so
|
||||
* recall is never truncated by a rotation. The honesty rail is
|
||||
* preserved exactly as before — rotated-out remains "I don't remember",
|
||||
* never a synthesized dream; this only makes the forgetting bounded and
|
||||
* explicit instead of deferred forever. */
|
||||
long pos = ftell(f);
|
||||
fclose(f);
|
||||
{
|
||||
long maxb = 64L*1024L*1024L;
|
||||
long keepb = 16L*1024L*1024L;
|
||||
const char* mv = getenv("ENGRAM_ISE_LOG_MAX_BYTES");
|
||||
if (mv && *mv) { long v = atol(mv); if (v > 0) maxb = v; }
|
||||
if (keepb > maxb/2) keepb = maxb/2;
|
||||
if (pos > 0 && pos > maxb) {
|
||||
FILE* rf = fopen(path, "rb");
|
||||
if (rf) {
|
||||
if (fseek(rf, pos - keepb, SEEK_SET) == 0) {
|
||||
char* buf = (char*)malloc((size_t)keepb + 1);
|
||||
if (buf) {
|
||||
size_t rd = fread(buf, 1, (size_t)keepb, rf);
|
||||
buf[rd] = 0;
|
||||
/* Resume at the first LINE boundary so the tail never
|
||||
* begins with a half-written JSON record. */
|
||||
char* start = memchr(buf, '\n', rd);
|
||||
start = start ? start + 1 : buf;
|
||||
size_t keep_n = rd - (size_t)(start - buf);
|
||||
char tmp[4096];
|
||||
snprintf(tmp, sizeof tmp, "%s/state-events.jsonl.tmp", dir);
|
||||
FILE* wf = fopen(tmp, "wb");
|
||||
if (wf) {
|
||||
int ok = (fwrite(start, 1, keep_n, wf) == keep_n);
|
||||
fclose(wf);
|
||||
/* Only replace the live log if the tail was written
|
||||
* in full — a short write must not destroy history. */
|
||||
if (ok) rename(tmp, path); else remove(tmp);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
fclose(rf);
|
||||
}
|
||||
}
|
||||
}
|
||||
return EL_INT(1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user