code wiki / _hdl_build / nx_memcliff.nx

nx_memcliff.nx source

↩ module page · 822 lines · 38401 B

1// nx_memcliff.nx -- SOVEREIGN owner of the MEMORY.md byte budget. Replaces memindex_enforce.py 2// (Stop hook) and the eviction half of memory_cliff_check.ps1 (SessionStart/PostToolUse) with ONE 3// nishi organ, so no interpreter sits in the hook path. 4// 5// WHY AN ORGAN AND NOT A SCRIPT 6// ----------------------------- 7// MEMORY.md is injected into every session; past a byte limit the harness DROPS THE TAIL SILENTLY -- 8// no error, no marker. The dropped lines are exactly the ones that would have told you they were 9// dropped. The rail-side enforcer cannot own this file (it runs NAS-side; MEMORY.md is laptop-local, 10// nx_memindex_put -> `no-md-file`), so ownership has to live here, next to the file. 11// 12// WHAT WAS ACTUALLY BROKEN -- MEASURED 2026-08-06, not assumed 13// ------------------------------------------------------------ 14// The Python enforcer was NOT missing: it had run 64 times (64 eviction stamps in the overflow) and 15// had compacted the file at 13:47 the same day. It had JAMMED: 16// "22013 bytes over budget but NO evictable entries found -- refusing to guess" 17// Root cause, measured: its rule NEVER EVICT THE NEWEST DAY pinned 8 of 8 candidates, because every 18// evictable entry carried today's date. Survivors after the pin: ZERO. 19// ★LAW: A PIN THAT CAN COVER 100% OF ITS CANDIDATES IS AN OFF SWITCH. The rule that protects today's 20// record disables enforcement precisely on the heaviest days -- the file grew 4.2KB in the two hours 21// after that successful compaction, with nothing left able to evict it. 22// The fix is not to drop the pin (it earned its place: a pure oldest-first cut once ate the current 23// session's own record) but to make it YIELD: keep the newest `keep_newest_day` entries of the newest 24// day and release the rest, rather than pinning the whole day unconditionally. 25// 26// SECOND MEASURED DEFECT -- the enforcer could not measure its subject in the unit the limit uses. 27// MEMORY.md is CRLF (63 CRLF, 0 bare LF). Python read it in text mode, which collapses CRLF to LF, so 28// it measured 22,013 against a file of 22,076 bytes -- UNDERSTATING by exactly the line count. The 29// harness cliff is a property of the bytes on disk, so every decision was made against a number that 30// was never the file's size. 31// ★LAW: A BUDGET ENFORCER THAT CANNOT MEASURE ITS SUBJECT IN THE UNIT THE LIMIT USES IS GUESSING. 32// ⚠WHAT I FIRST WROTE HERE AND THEN DISPROVED: I claimed the two owners also flipped the file's line 33// endings against each other (py writing LF, the ps1 writing CRLF). THE EXPERIMENT REFUTED IT -- a 34// CRLF fixture through the oracle came out CRLF=26 / bare-LF=0, because Python's text-mode WRITE 35// translates back on Windows, exactly undoing the read. The round trip is lossless; only the 36// measurement was wrong. Recorded rather than quietly deleted, because a plausible mechanism I had 37// not run is precisely the kind of premise that spends like evidence. 38// This organ works in RAW BYTES and reconstructs each line with its original terminator, so it is 39// byte-exact by construction rather than by a translation that happens to cancel out (verified: the 40// same CRLF fixture out at CRLF=26 / bare-LF=0). 41// 42// WHERE THE BUDGET NUMBERS COME FROM (and why they are config, per the no-magic-numbers rule) 43// ------------------------------------------------------------------------------------------- 44// Three numbers were in use and all three were folklore: 17,100 (py budget), 24,400 (py hard), 45// 25,000 (ps1 cliff, itself back-derived from a recorded "26,229 B = 104.9%"). Attempts to recover the 46// true cliff from the 333 session transcripts FAILED and the attempt is recorded here so nobody 47// repeats it: the harness's injected memory context is never persisted to the transcript. What IS 48// measured, first-hand: 49// 22,076 B loaded COMPLETE (2026-08-06: the file's on-disk last line was present in context) 50// 26,229 B truncated (2026-07-31 record) 51// So the cliff lies in (22,076, 26,229]. Defaults sit inside that bracket with real headroom, and a 52// 17,100-style target is REFUSED as a default: it would have evicted five of today's work records on 53// the strength of a number nobody measured. 54// ★LAW: AN UNMEASURED BUDGET IS NOT CONSERVATIVE JUST BECAUSE IT IS SMALL -- OVER-EVICTION DESTROYS 55// THE SAME INFORMATION THE CLIFF WOULD HAVE. 56// 57// SAFETY PROPERTIES (each inherited from a real incident) 58// ------------------------------------------------------ 59// * FAIL-CLOSED ON UNREADABLE. Exists-but-unreadable -> refuse to write. (nx_ws_index_lib's 2026-07-31 60// data-loss law: A GENERATOR THAT CANNOT READ THE FILE IT SPLICES INTO MUST REFUSE TO WRITE IT. 61// ABSENT and UNREADABLE are opposite situations; only stat separates them.) 62// * PRESERVE BEFORE REMOVE. Victims are appended VERBATIM to the overflow file and the append is 63// VERIFIED BY BYTE GROWTH before a single line leaves the index. Nothing is ever deleted. 64// * COMPARE-AND-SWAP. The file is re-measured immediately before install; if a concurrent writer 65// touched it, ABORT without writing. (Hand compaction was measured losing this race inside ONE 66// MINUTE.) 67// * ATOMIC INSTALL. tmp + renameat, so a reader sees old-whole or new-whole, never torn. 68// * NEVER TOUCHES PROTECTED REGIONS. The pinned head, and anything inside rail-owned COINDEX/CURATED 69// marker blocks, is untouchable -- eviction only ever moves ordinary "- " entries carrying a topic 70// link, so a moved line always leaves a followable pointer behind in the overflow. 71// * FIXED POINT. The eviction pointer line is UPDATED IN PLACE, never re-added, so a run near the 72// budget cannot evict an entry to pay for its own bookkeeping. 73// * IDEMPOTENT + SILENT. Under target = zero writes, zero stdout, zero model tokens. 74// 75// usage: nx_memcliff.elf [--report] [--dry-run] 76// exit: 0 = under target, or compacted; 1 = REFUSED (over target, could not act safely) 77// CONVERTED TO nx_memplane_lib 2026-08-07 (the last of the six). It keeps what is specific to the 78// budget -- the byte-exact line model, config parsing, date keys, the eviction ladder and the hook 79// JSON envelope -- and takes the generic byte/file primitives from the lib. 80// Sovereign: imports nx_memplane_lib. license_tier: ORIGINAL 81import "nx_memplane_lib.nx" 82 83const MC_DIR: *u8 = "/mnt/c/Users/elder/.claude/projects/C--Users-elder/memory" 84const MC_N_MEM: *u8 = "MEMORY.md" 85const MC_N_OVF: *u8 = "reference-index-recent-refs-dormant.md" 86const MC_N_CNF: *u8 = "memcliff.conf" 87const MC_N_TMP: *u8 = "MEMORY.md.mctmp" 88 89const MC_MAXL: i64 = 8192 // max lines in the index (64 today; vast headroom) 90const MC_BUF: i64 = 4194304 // 4MB read buffer 91const MC_OUT: i64 = 4194304 // 4MB assembled output 92const MC_MSG: i64 = 8192 // report buffer 93 94// evidence-set defaults; every one overridable from memcliff.conf (no magic numbers in code) 95const MC_D_CLIFF: i64 = 25000 // below the 26,229 B measured truncation 96const MC_D_TARGET: i64 = 23000 // evict down to this; above the 22,076 B measured-good load 97const MC_D_WARN: i64 = 21000 // report headroom from here up 98const MC_D_KEEP: i64 = 3 // newest-day entries always kept when the pin has to yield 99const MC_D_PIN: i64 = 12 // pinned head: header + survival block, never evicted 100// THE SECOND CLIFF, measured 2026-08-06 and previously enforced by nobody. The harness applies a 101// LINE limit as well as a byte limit, whichever binds first. Probes at 100 B/line truncated at 102// EXACTLY 200 lines every time (20,000 B), while probes at 1000 B/line truncated at exactly 25,000 B 103// (24 lines). A file of few long lines is governed by bytes; a file of many short lines is governed 104// by LINES and can lose its tail with the byte budget entirely satisfied. 105const MC_D_MAXLINE: i64 = 200 // measured: line 201+ is not delivered 106const MC_D_TGTLINE: i64 = 180 // evict down to this, so ordinary appends don't immediately rebreach 107 108const MC_PTRTAG: *u8 = "verbatim by nx_memcliff:" 109 110// ---------- primitives ------------------------------------------------------------------------- 111 112// mc_len / mc_streq / mc_cat / mc_catn / mc_write_all / mc_say / mc_read / mc_join deleted at the 113// lib conversion -- they were byte-identical copies of the lib's primitives in all six organs. 114 115// does NUL-terminated `pat` occur at buf[pos]? (bounded by `lim`) 116func mc_at(buf: *u8, pos: i64, lim: i64, pat: *u8) -> i64 { 117 let pl: i64 = mp_len(pat) 118 if pos + pl > lim { return 0 } 119 var j: i64 = 0 120 var ok: i64 = 1 121 while j < pl { 122 if buf[pos + j] != pat[j] { ok = 0; j = pl } else { j = j + 1 } 123 } 124 return ok 125} 126 127// first index of `pat` in buf[from..lim), or -1 128func mc_find(buf: *u8, from: i64, lim: i64, pat: *u8) -> i64 { 129 var i: i64 = from 130 var r: i64 = 0 - 1 131 var go: i64 = 1 132 while go == 1 { 133 if i >= lim { go = 0 } else { 134 if mc_at(buf, i, lim, pat) == 1 { r = i; go = 0 } else { i = i + 1 } 135 } 136 } 137 return r 138} 139 140func mc_isdigit(c: u8) -> i64 { 141 if c < (48 as u8) { return 0 } 142 if c > (57 as u8) { return 0 } 143 return 1 144} 145 146 147// Emit a report. `json` = 0 -> raw stdout (the Stop hook shows it verbatim). `json` = 1 -> the hook 148// envelope, so SessionStart/PostToolUse can carry the text into context WITHOUT a PowerShell wrapper 149// doing the JSON assembly. Keeping this here is the point: no interpreter in the hook path. 150func mc_emit(json: i64, buf: *u8, n: i64) -> i64 { 151 if json == 0 { return mp_write_all(1, buf, n) } 152 let j: *u8 = sys_mmap(MC_MSG * 4) 153 var o: i64 = 0 154 o = mp_cat(j, o, "{\"systemMessage\":\"" as *u8) 155 var pass: i64 = 0 156 while pass < 2 { 157 var i: i64 = 0 158 while i < n { 159 let c: u8 = buf[i] 160 if c == (34 as u8) { j[o] = 92 as u8; o = o + 1; j[o] = 34 as u8; o = o + 1 } else { 161 if c == (92 as u8) { j[o] = 92 as u8; o = o + 1; j[o] = 92 as u8; o = o + 1 } else { 162 if c == (10 as u8) { j[o] = 92 as u8; o = o + 1; j[o] = 110 as u8; o = o + 1 } else { 163 if c == (13 as u8) { o = o + 0 } else { j[o] = c; o = o + 1 } 164 } 165 } 166 } 167 i = i + 1 168 } 169 if pass == 0 { 170 o = mp_cat(j, o, "\",\"hookSpecificOutput\":{\"hookEventName\":\"SessionStart\",\"additionalContext\":\"" as *u8) 171 } 172 pass = pass + 1 173 } 174 o = mp_cat(j, o, "\"}}" as *u8) 175 return mp_write_all(1, j, o) 176} 177 178// dir + "/" + name -> a fresh NUL-terminated buffer. Lets --dir point the organ at a FIXTURE, which 179// is what makes the differential test against the known-good oracle possible at all. 180func mc_join(dir: *u8, name: *u8) -> *u8 { 181 let dst: *u8 = sys_mmap(4096) 182 var o: i64 = 0 183 var i: i64 = 0 184 while dir[i] != (0 as u8) { dst[o] = dir[i]; o = o + 1; i = i + 1 } 185 if o > 0 { if dst[o - 1] != (47 as u8) { dst[o] = 47 as u8; o = o + 1 } } 186 i = 0 187 while name[i] != (0 as u8) { dst[o] = name[i]; o = o + 1; i = i + 1 } 188 dst[o] = 0 as u8 189 return dst 190} 191 192// TRUE byte size via lseek(END) -- deliberately NOT struct-stat offset arithmetic, which would be an 193// assumption about layout. -1 if the file cannot be opened. 194func mc_fsize(path: *u8) -> i64 { 195 let fd: i64 = sys_openat_rd(path) 196 if fd < 0 { return 0 - 1 } 197 let n: i64 = sys_lseek(fd, 0, 2) 198 sys_close(fd) 199 return n 200} 201 202func mc_exists(path: *u8) -> i64 { 203 let stb: *u8 = sys_mmap(160) 204 if sys_fstatat(path, stb) == 0 { return 1 } 205 return 0 206} 207 208// whole-file read into buf; returns length, or -1 if open failed 209func mp_readf(path: *u8, buf: *u8, cap: i64) -> i64 { 210 let fd: i64 = sys_openat_rd(path) 211 if fd < 0 { return 0 - 1 } 212 var total: i64 = 0 213 var go: i64 = 1 214 while go == 1 { 215 let n: i64 = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) 216 if n <= 0 { go = 0 } else { 217 total = total + n 218 if total >= cap { go = 0 } 219 } 220 } 221 sys_close(fd) 222 return total 223} 224 225// ---------- config ----------------------------------------------------------------------------- 226 227// read `key=<int>` from the conf buffer; `dflt` when absent or malformed. 228func mc_conf(cbuf: *u8, clen: i64, key: *u8, dflt: i64) -> i64 { 229 if clen <= 0 { return dflt } 230 var i: i64 = 0 231 var val: i64 = dflt 232 var found: i64 = 0 233 while i < clen { 234 var atline: i64 = 0 235 if i == 0 { atline = 1 } 236 if i > 0 { if cbuf[i - 1] == (10 as u8) { atline = 1 } } 237 if atline == 1 { 238 if found == 0 { 239 if mc_at(cbuf, i, clen, key) == 1 { 240 var p: i64 = i + mp_len(key) 241 if p < clen { 242 if cbuf[p] == (61 as u8) { // '=' 243 p = p + 1 244 var acc: i64 = 0 245 var any: i64 = 0 246 var go: i64 = 1 247 while go == 1 { 248 if p >= clen { go = 0 } else { 249 if mc_isdigit(cbuf[p]) == 1 { 250 acc = acc * 10 + ((cbuf[p] as i64) - 48) 251 any = 1 252 p = p + 1 253 } else { go = 0 } 254 } 255 } 256 if any == 1 { val = acc; found = 1 } 257 } 258 } 259 } 260 } 261 } 262 i = i + 1 263 } 264 return val 265} 266 267// ---------- line model ------------------------------------------------------------------------- 268// ls[i] = start offset, ll[i] = length INCLUDING the line terminator. Reconstructing from these 269// preserves CRLF byte-exactly -- the organ can never flip a file's line endings. 270 271func mc_split(buf: *u8, n: i64, ls: *i64, ll: *i64, cap: i64) -> i64 { 272 var cnt: i64 = 0 273 var start: i64 = 0 274 var i: i64 = 0 275 while i < n { 276 if buf[i] == (10 as u8) { 277 if cnt < cap { ls[cnt] = start; ll[cnt] = i - start + 1; cnt = cnt + 1 } 278 start = i + 1 279 } 280 i = i + 1 281 } 282 if start < n { 283 if cnt < cap { ls[cnt] = start; ll[cnt] = n - start; cnt = cnt + 1 } 284 } 285 return cnt 286} 287 288// date key for a line: YYYY-MM-DD -> YYYYMMDD; else MM-DD -> 2026MMDD; else 99999999 (undated, 289// sorts last = kept longest, matching the known-good behaviour). 290func mc_datekey(buf: *u8, s: i64, e: i64) -> i64 { 291 var i: i64 = s 292 // full YYYY-MM-DD 293 while i + 10 <= e { 294 var ok: i64 = 1 295 if mc_isdigit(buf[i]) == 0 { ok = 0 } 296 if ok == 1 { if mc_isdigit(buf[i + 1]) == 0 { ok = 0 } } 297 if ok == 1 { if mc_isdigit(buf[i + 2]) == 0 { ok = 0 } } 298 if ok == 1 { if mc_isdigit(buf[i + 3]) == 0 { ok = 0 } } 299 if ok == 1 { if buf[i + 4] != (45 as u8) { ok = 0 } } 300 if ok == 1 { if mc_isdigit(buf[i + 5]) == 0 { ok = 0 } } 301 if ok == 1 { if mc_isdigit(buf[i + 6]) == 0 { ok = 0 } } 302 if ok == 1 { if buf[i + 7] != (45 as u8) { ok = 0 } } 303 if ok == 1 { if mc_isdigit(buf[i + 8]) == 0 { ok = 0 } } 304 if ok == 1 { if mc_isdigit(buf[i + 9]) == 0 { ok = 0 } } 305 if ok == 1 { 306 var v: i64 = 0 307 v = v * 10 + ((buf[i] as i64) - 48) 308 v = v * 10 + ((buf[i + 1] as i64) - 48) 309 v = v * 10 + ((buf[i + 2] as i64) - 48) 310 v = v * 10 + ((buf[i + 3] as i64) - 48) 311 v = v * 10 + ((buf[i + 5] as i64) - 48) 312 v = v * 10 + ((buf[i + 6] as i64) - 48) 313 v = v * 10 + ((buf[i + 8] as i64) - 48) 314 v = v * 10 + ((buf[i + 9] as i64) - 48) 315 return v 316 } 317 i = i + 1 318 } 319 // bare MM-DD 320 i = s 321 while i + 5 <= e { 322 var ok2: i64 = 1 323 if mc_isdigit(buf[i]) == 0 { ok2 = 0 } 324 if ok2 == 1 { if mc_isdigit(buf[i + 1]) == 0 { ok2 = 0 } } 325 if ok2 == 1 { if buf[i + 2] != (45 as u8) { ok2 = 0 } } 326 if ok2 == 1 { if mc_isdigit(buf[i + 3]) == 0 { ok2 = 0 } } 327 if ok2 == 1 { if mc_isdigit(buf[i + 4]) == 0 { ok2 = 0 } } 328 // must not be part of a longer digit run (guards against slicing a YYYY-MM-DD tail) 329 if ok2 == 1 { if i > s { if mc_isdigit(buf[i - 1]) == 1 { ok2 = 0 } } } 330 if ok2 == 1 { if i + 5 < e { if mc_isdigit(buf[i + 5]) == 1 { ok2 = 0 } } } 331 if ok2 == 1 { 332 var v2: i64 = 2026 333 v2 = v2 * 10 + ((buf[i] as i64) - 48) 334 v2 = v2 * 10 + ((buf[i + 1] as i64) - 48) 335 v2 = v2 * 10 + ((buf[i + 3] as i64) - 48) 336 v2 = v2 * 10 + ((buf[i + 4] as i64) - 48) 337 return v2 338 } 339 i = i + 1 340 } 341 return 99999999 342} 343 344// a line is EVICTABLE only if it is an ordinary entry carrying a topic link, so a moved line always 345// leaves a followable pointer behind. 346func mc_is_entry(buf: *u8, s: i64, e: i64) -> i64 { 347 if e - s < 4 { return 0 } 348 if buf[s] != (45 as u8) { return 0 } // '-' 349 if buf[s + 1] != (32 as u8) { return 0 } // ' ' 350 if mc_find(buf, s, e, "](" as *u8) < 0 { return 0 } 351 if mc_find(buf, s, e, ".md)" as *u8) < 0 { return 0 } 352 return 1 353} 354 355// ---------- main ------------------------------------------------------------------------------- 356 357func main(argc: i64, argv: *i64) -> i64 { 358 var dry: i64 = 0 359 var report: i64 = 0 360 var jsonf: i64 = 0 361 var announce: i64 = 0 362 var dir: *u8 = MC_DIR 363 var ai: i64 = 1 364 while ai < argc { 365 let a: *u8 = argv[ai] as *u8 366 if mp_streq(a, "--dry-run" as *u8) == 1 { dry = 1 } 367 if mp_streq(a, "--report" as *u8) == 1 { report = 1 } 368 if mp_streq(a, "--json" as *u8) == 1 { jsonf = 1 } 369 if mp_streq(a, "--announce" as *u8) == 1 { announce = 1; report = 1 } 370 if mp_streq(a, "--dir" as *u8) == 1 { 371 if ai + 1 < argc { dir = argv[ai + 1] as *u8; ai = ai + 1 } 372 } 373 ai = ai + 1 374 } 375 376 let MC_MEM: *u8 = mc_join(dir, MC_N_MEM) 377 let MC_OVF: *u8 = mc_join(dir, MC_N_OVF) 378 let MC_CONF: *u8 = mc_join(dir, MC_N_CNF) 379 let MC_TMP: *u8 = mc_join(dir, MC_N_TMP) 380 381 let msg: *u8 = sys_mmap(MC_MSG) 382 383 // ---- config (data-driven thresholds) ---- 384 let cbuf: *u8 = sys_mmap(65536) 385 let clen: i64 = mp_readf(MC_CONF, cbuf, 65536) 386 let cliff: i64 = mc_conf(cbuf, clen, "cliff_bytes" as *u8, MC_D_CLIFF) 387 let target: i64 = mc_conf(cbuf, clen, "target_bytes" as *u8, MC_D_TARGET) 388 let warn: i64 = mc_conf(cbuf, clen, "warn_bytes" as *u8, MC_D_WARN) 389 let keepn: i64 = mc_conf(cbuf, clen, "keep_newest_day" as *u8, MC_D_KEEP) 390 let pinl: i64 = mc_conf(cbuf, clen, "pin_lines" as *u8, MC_D_PIN) 391 let maxl: i64 = mc_conf(cbuf, clen, "max_lines" as *u8, MC_D_MAXLINE) 392 let tlines: i64 = mc_conf(cbuf, clen, "target_lines" as *u8, MC_D_TGTLINE) 393 394 // ---- FAIL-CLOSED read ---- 395 let size0: i64 = mc_fsize(MC_MEM) 396 if size0 < 0 { 397 if mc_exists(MC_MEM) == 1 { 398 var m: i64 = mp_cat(msg, 0, "nx_memcliff: REFUSED -- MEMORY.md EXISTS but could not be read. Refusing to write a file I cannot see.\n" as *u8) 399 mc_emit(jsonf, msg, m) 400 return 1 401 } 402 return 0 // genuinely absent: nothing to enforce 403 } 404 405 // Read BEFORE deciding anything: the line cliff is a property of the content, not the size, so 406 // an enforcer that short-circuits on bytes alone can never see the axis that is about to bite. 407 let buf: *u8 = sys_mmap(MC_BUF) 408 let n: i64 = mp_readf(MC_MEM, buf, MC_BUF) 409 if n <= 0 { 410 var m2: i64 = mp_cat(msg, 0, "nx_memcliff: REFUSED -- MEMORY.md unreadable at eviction time.\n" as *u8) 411 mc_emit(jsonf, msg, m2) 412 return 1 413 } 414 415 let ls: *i64 = sys_mmap(8 * MC_MAXL) as *i64 416 let ll: *i64 = sys_mmap(8 * MC_MAXL) as *i64 417 let lc: i64 = mc_split(buf, n, ls, ll, MC_MAXL) 418 419 // ---- report mode: state the headroom on BOTH axes, never write ---- 420 if report == 1 { 421 var loud: i64 = 0 422 if size0 >= warn { loud = 1 } 423 if lc >= tlines { loud = 1 } 424 // ★ DISCOVERABILITY IS PART OF THE CAPABILITY. Operator, 2026-08-06: a tool that needs "greps 425 // and all this other bullshit" to find at session start is not SOTA. The memory plane is 426 // laptop-local, and the MCP transport is remote http (nishifamily.com/mcp) -- every MCP tool 427 // executes NAS-side and cannot read this directory, which is the same no-md-file wall the rail 428 // hits. So MCP registration cannot carry these organs; the SessionStart surface can, and it is 429 // already proven and already reads this file. --announce rides the existing call: live budget 430 // state AND the organ roster, one invocation, no extra cost. 431 if announce == 1 { loud = 1 } 432 if loud == 1 { 433 var r: i64 = mp_cat(msg, 0, "MEMORY.md " as *u8) 434 r = mp_catn(msg, r, size0) 435 r = mp_cat(msg, r, " B of the " as *u8) 436 r = mp_catn(msg, r, cliff) 437 r = mp_cat(msg, r, " B cliff (" as *u8) 438 r = mp_catn(msg, r, cliff - size0) 439 r = mp_cat(msg, r, " B headroom), " as *u8) 440 r = mp_catn(msg, r, lc) 441 r = mp_cat(msg, r, " of " as *u8) 442 r = mp_catn(msg, r, maxl) 443 r = mp_cat(msg, r, " lines (" as *u8) 444 r = mp_catn(msg, r, maxl - lc) 445 r = mp_cat(msg, r, " spare). Targets " as *u8) 446 r = mp_catn(msg, r, target) 447 r = mp_cat(msg, r, " B / " as *u8) 448 r = mp_catn(msg, r, tlines) 449 r = mp_cat(msg, r, " lines.\n" as *u8) 450 if announce == 1 { 451 r = mp_cat(msg, r, "MEMORY-PLANE ORGANS (laptop-local; MCP is remote http so it cannot reach this dir). Run via wsl -e /mnt/c/Users/elder/nishi-core/nxc2/_offc/<organ>:\n" as *u8) 452 r = mp_cat(msg, r, " nx_memfind --file <f> | <terms> BM25 ranked retrieval -- ASK THIS BEFORE WRITING A NEW MEMORY\n" as *u8) 453 r = mp_cat(msg, r, " nx_memroot [--apply] from-god rooting: node->parent->ORIGIN, censuses FLOATING\n" as *u8) 454 r = mp_cat(msg, r, " nx_memhealth [--dups] dangling / orphans / duplicate candidates (read-only)\n" as *u8) 455 r = mp_cat(msg, r, " nx_memfix [--apply] repairs ONLY provably-safe dead links\n" as *u8) 456 r = mp_cat(msg, r, " nx_memorph --apply regenerates the orphan catalogue\n" as *u8) 457 } 458 mc_emit(jsonf, msg, r) 459 } 460 return 0 461 } 462 463 // ---- IDEMPOTENT: under BOTH targets = zero writes, zero stdout ---- 464 var over: i64 = 0 465 if size0 > target { over = 1 } 466 if lc > tlines { over = 1 } 467 if over == 0 { return 0 } 468 469 // ---- classify ---- 470 let cand: *i64 = sys_mmap(8 * MC_MAXL) as *i64 471 let dkey: *i64 = sys_mmap(8 * MC_MAXL) as *i64 472 var inrail: i64 = 0 473 var i: i64 = 0 474 while i < lc { 475 let s: i64 = ls[i] 476 let e: i64 = ls[i] + ll[i] 477 cand[i] = 0 478 dkey[i] = 99999999 479 if mc_find(buf, s, e, "COINDEX:BEGIN" as *u8) >= 0 { inrail = 1 } 480 if mc_find(buf, s, e, "CURATED:BEGIN" as *u8) >= 0 { inrail = 1 } 481 var protd: i64 = 0 482 if i < pinl { protd = 1 } 483 if inrail == 1 { protd = 1 } 484 if protd == 0 { 485 if mc_is_entry(buf, s, e) == 1 { 486 cand[i] = 1 487 dkey[i] = mc_datekey(buf, s, e) 488 } 489 } 490 if mc_find(buf, s, e, "COINDEX:END" as *u8) >= 0 { inrail = 0 } 491 if mc_find(buf, s, e, "CURATED:END" as *u8) >= 0 { inrail = 0 } 492 i = i + 1 493 } 494 495 // ---- the newest-day pin, AND the yield that stops it becoming an off switch ---- 496 var newest: i64 = 0 - 1 497 i = 0 498 while i < lc { 499 if cand[i] == 1 { 500 if dkey[i] != 99999999 { if dkey[i] > newest { newest = dkey[i] } } 501 } 502 i = i + 1 503 } 504 505 var pool: i64 = 0 // candidates NOT on the newest day 506 i = 0 507 while i < lc { 508 if cand[i] == 1 { if dkey[i] != newest { pool = pool + 1 } } 509 i = i + 1 510 } 511 512 // eligible[] = may be evicted this run 513 let elig: *i64 = sys_mmap(8 * MC_MAXL) as *i64 514 i = 0 515 while i < lc { elig[i] = 0; i = i + 1 } 516 i = 0 517 while i < lc { 518 if cand[i] == 1 { if dkey[i] != newest { elig[i] = 1 } } 519 i = i + 1 520 } 521 522 var jam: i64 = 0 523 524 // The pointer line is part of the OUTPUT, so it must be part of the ARITHMETIC. Detect the prior 525 // pointer and its cost BEFORE choosing victims: a loop that ignores it lands over target by 526 // exactly the pointer's length -- measured 23,043 B against a 23,000 B target, 43 B over, which 527 // is precisely where the margin matters. Worst-case digit count, so the estimate can only ever 528 // over-evict by a few bytes, never under. 529 var prior: i64 = 0 530 var ptrline: i64 = 0 - 1 531 i = 0 532 while i < lc { 533 let pf: i64 = mc_find(buf, ls[i], ls[i] + ll[i], MC_PTRTAG) 534 if pf >= 0 { 535 if ptrline < 0 { 536 ptrline = i 537 var q: i64 = pf + mp_len(MC_PTRTAG) 538 var acc: i64 = 0 539 var any: i64 = 0 540 var g2: i64 = 1 541 while g2 == 1 { 542 if q >= ls[i] + ll[i] { g2 = 0 } else { 543 if mc_isdigit(buf[q]) == 1 { acc = acc * 10 + ((buf[q] as i64) - 48); any = 1; q = q + 1 } else { 544 if any == 1 { g2 = 0 } else { q = q + 1 } 545 } 546 } 547 } 548 if any == 1 { prior = acc } 549 } 550 } 551 i = i + 1 552 } 553 let scratch: *u8 = sys_mmap(1024) 554 var pc: i64 = mp_cat(scratch, 0, "- EVICTED -> [OVERFLOW](reference-index-recent-refs-dormant.md) " as *u8) 555 pc = mp_cat(scratch, pc, MC_PTRTAG) 556 pc = mp_cat(scratch, pc, " 999999 entries." as *u8) 557 let ptr_add: i64 = pc + 2 // + CRLF, the worst case 558 var ptr_del: i64 = 0 559 var line_delta: i64 = 1 560 if ptrline >= 0 { ptr_del = ll[ptrline]; line_delta = 0 } 561 562 // ---- choose victims: oldest key first; within a key, bottom-most first ---- 563 let vic: *i64 = sys_mmap(8 * MC_MAXL) as *i64 564 var nvic: i64 = 0 565 var running: i64 = size0 566 567 // TWO PHASES, because the newest-day pin is an off switch at ANY coverage, not only at 100%. 568 // The first fix here yielded when the non-newest-day pool was EMPTY. Measured on the live file 569 // 2026-08-06: the pool was ONE -- 13 of 14 candidates carried today's date -- so the organ evicted 570 // a single line, saved nothing, and refused while the file sat 1,797 B OVER the measured cliff. 571 // ★★★★★★ A PIN DOES NOT HAVE TO COVER EVERYTHING TO DISABLE ENFORCEMENT; IT ONLY HAS TO COVER 572 // ENOUGH THAT WHAT REMAINS CANNOT CLOSE THE GAP. The trigger is therefore not "is the pool empty" 573 // but "did the pool actually reach the target" -- phase 0 spends the unpinned candidates, and if 574 // that was not enough, phase 1 releases the newest day except the `keepn` nearest the head. 575 var phase: i64 = 0 576 while phase < 2 { 577 var go: i64 = 1 578 while go == 1 { 579 // drive on BOTH cliffs: stop only when the file is under the byte target AND the line target 580 var more: i64 = 0 581 if running - ptr_del + ptr_add > target { more = 1 } 582 if lc - nvic + line_delta > tlines { more = 1 } 583 if more == 0 { go = 0 } else { 584 var best: i64 = 0 - 1 585 var bestk: i64 = 0 586 i = 0 587 while i < lc { 588 if elig[i] == 1 { 589 var take: i64 = 0 590 if best < 0 { take = 1 } else { 591 if dkey[i] < bestk { take = 1 } 592 if dkey[i] == bestk { take = 1 } // later index wins => bottom-most first 593 } 594 if take == 1 { best = i; bestk = dkey[i] } 595 } 596 i = i + 1 597 } 598 if best < 0 { go = 0 } else { 599 elig[best] = 0 600 vic[nvic] = best 601 nvic = nvic + 1 602 running = running - ll[best] 603 } 604 } 605 } 606 // THE YIELD, driven by outcome rather than by pool size: only if spending everything 607 // unpinned still left the file over target does the newest day get released. 608 var still: i64 = 0 609 if running - ptr_del + ptr_add > target { still = 1 } 610 if lc - nvic + line_delta > tlines { still = 1 } 611 if still == 1 { 612 if phase == 0 { 613 jam = 1 614 // ★★★★★★ A FLOOR SET AGAINST A LARGE CANDIDATE POOL BECOMES A TOTAL BLOCK ONCE THE 615 // POOL SHRINKS TO THE FLOOR. Measured live 2026-08-07: the index held FOUR eligible 616 // candidates -- one undated, three newest-day -- so phase 0 spent the single unpinned 617 // one and phase 1 released NOTHING, because keep_newest_day=3 protected exactly the 618 // three that remained. The organ was 298 B from the cliff and structurally unable to 619 // act. Same shape as the pin bug this organ already fixed once: the guard became the 620 // off switch when the pool shrank to its own size. 621 // The floor exists to stop ROUTINE target-driven eviction eating today's record. It 622 // must never stop the organ preventing SILENT TRUNCATION, because eviction preserves 623 // the line VERBATIM in the overflow while the cliff destroys it unseen. 624 // ⇒ over the cliff, the floor drops to 1. Never to 0: something of today always stays. 625 var effkeep: i64 = keepn 626 if size0 > cliff { effkeep = 1 } 627 var kept: i64 = 0 628 i = 0 629 while i < lc { 630 if cand[i] == 1 { 631 if dkey[i] == newest { 632 if kept < effkeep { kept = kept + 1 } else { elig[i] = 1 } 633 } 634 } 635 i = i + 1 636 } 637 } 638 } 639 phase = phase + 1 640 } 641 642 if nvic == 0 { 643 var m3: i64 = mp_cat(msg, 0, "*** nx_memcliff: MEMORY.md is " as *u8) 644 m3 = mp_catn(msg, m3, size0) 645 m3 = mp_cat(msg, m3, " B, over the " as *u8) 646 m3 = mp_catn(msg, m3, target) 647 m3 = mp_cat(msg, m3, " B target, and NOTHING IS ELIGIBLE TO EVICT -- every remaining entry is protected (pinned head or rail-owned block). A human must compact. ***\n" as *u8) 648 mc_emit(jsonf, msg, m3) 649 return 1 650 } 651 652 // ---- PRESERVE FIRST: append victims verbatim, then VERIFY BY BYTE GROWTH ---- 653 let ovf0: i64 = mc_fsize(MC_OVF) 654 let ob: *u8 = sys_mmap(MC_OUT) 655 var oo: i64 = 0 656 oo = mp_cat(ob, oo, "\n\n## EVICTED FROM MEMORY.md by nx_memcliff (verbatim, nothing dropped) -- index was " as *u8) 657 oo = mp_catn(ob, oo, size0) 658 oo = mp_cat(ob, oo, " B against a " as *u8) 659 oo = mp_catn(ob, oo, target) 660 oo = mp_cat(ob, oo, " B target.\n" as *u8) 661 var k: i64 = nvic - 1 662 while k >= 0 { 663 let vi: i64 = vic[k] 664 var c: i64 = 0 665 while c < ll[vi] { ob[oo] = buf[ls[vi] + c]; oo = oo + 1; c = c + 1 } 666 if ob[oo - 1] != (10 as u8) { ob[oo] = 10 as u8; oo = oo + 1 } 667 k = k - 1 668 } 669 if dry == 0 { 670 let ofd: i64 = sys_openat_append(MC_OVF, 420) 671 if ofd < 0 { 672 var m4: i64 = mp_cat(msg, 0, "*** nx_memcliff: REFUSED -- over target but the overflow file could not be opened. NOTHING EVICTED (refusing to drop lines I cannot preserve). ***\n" as *u8) 673 mc_emit(jsonf, msg, m4) 674 return 1 675 } 676 mp_write_all(ofd, ob, oo) 677 sys_close(ofd) 678 let ovf1: i64 = mc_fsize(MC_OVF) 679 if ovf1 - ovf0 < oo { 680 var m5: i64 = mp_cat(msg, 0, "*** nx_memcliff: REFUSED -- the overflow append did not land (grew " as *u8) 681 m5 = mp_catn(msg, m5, ovf1 - ovf0) 682 m5 = mp_cat(msg, m5, " B, expected " as *u8) 683 m5 = mp_catn(msg, m5, oo) 684 m5 = mp_cat(msg, m5, " B). NOTHING REMOVED. ***\n" as *u8) 685 mc_emit(jsonf, msg, m5) 686 return 1 687 } 688 } 689 690 // ---- assemble the survivor file, preserving every original terminator byte-exactly ---- 691 // the eviction pointer is a FIXED POINT: the prior one was located and its count absorbed above 692 // (before victim selection, so its byte cost is inside the arithmetic); here it is simply dropped 693 // and ONE updated pointer re-emitted at the position of the first victim. 694 var firstv: i64 = lc 695 k = 0 696 while k < nvic { if vic[k] < firstv { firstv = vic[k] } k = k + 1 } 697 if firstv < pinl { firstv = pinl } 698 699 // CRLF or LF, taken from the file itself -- never assumed 700 var crlf: i64 = 0 701 if lc > 0 { if ll[0] >= 2 { if buf[ls[0] + ll[0] - 2] == (13 as u8) { crlf = 1 } } } 702 703 let out: *u8 = sys_mmap(MC_OUT) 704 var o: i64 = 0 705 i = 0 706 while i < lc { 707 if i == firstv { 708 o = mp_cat(out, o, "- EVICTED -> [OVERFLOW](reference-index-recent-refs-dormant.md) " as *u8) 709 o = mp_cat(out, o, MC_PTRTAG) 710 o = mp_cat(out, o, " " as *u8) 711 o = mp_catn(out, o, prior + nvic) 712 o = mp_cat(out, o, " entries." as *u8) 713 if crlf == 1 { out[o] = 13 as u8; o = o + 1 } 714 out[o] = 10 as u8; o = o + 1 715 } 716 var drop: i64 = 0 717 if i == ptrline { drop = 1 } 718 k = 0 719 while k < nvic { if vic[k] == i { drop = 1 } k = k + 1 } 720 if drop == 0 { 721 var c2: i64 = 0 722 while c2 < ll[i] { out[o] = buf[ls[i] + c2]; o = o + 1; c2 = c2 + 1 } 723 } 724 i = i + 1 725 } 726 727 // ---- REFUSE A NO-OP: a run that does not shrink the file is pure churn ---- 728 // ⚠A REFUSAL THAT DOES NOT SHOW ITS ARITHMETIC CANNOT BE DEBUGGED. This branch fired while the 729 // live file sat 1,797 B OVER the measured cliff, and the message gave nothing to reason from. 730 // It also must NOT be silent-and-zero when the file is over the CLIFF (not merely over target): 731 // that is a fail-open in exactly the situation the organ exists for. 732 if o >= size0 { 733 var m6: i64 = mp_cat(msg, 0, "nx_memcliff: no net saving (in " as *u8) 734 m6 = mp_catn(msg, m6, size0) 735 m6 = mp_cat(msg, m6, " B, out " as *u8) 736 m6 = mp_catn(msg, m6, o) 737 m6 = mp_cat(msg, m6, " B, lines " as *u8) 738 m6 = mp_catn(msg, m6, lc) 739 m6 = mp_cat(msg, m6, ", victims " as *u8) 740 m6 = mp_catn(msg, m6, nvic) 741 m6 = mp_cat(msg, m6, ", ptr +" as *u8) 742 m6 = mp_catn(msg, m6, ptr_add) 743 m6 = mp_cat(msg, m6, "/-" as *u8) 744 m6 = mp_catn(msg, m6, ptr_del) 745 m6 = mp_cat(msg, m6, ") -- refusing to churn." as *u8) 746 if size0 > cliff { 747 m6 = mp_cat(msg, m6, "\n*** AND THE FILE IS OVER THE MEASURED CLIFF BY " as *u8) 748 m6 = mp_catn(msg, m6, size0 - cliff) 749 m6 = mp_cat(msg, m6, " B -- THE TAIL IS BEING DROPPED FROM EVERY SESSION RIGHT NOW AND I CANNOT FIX IT. A human must compact. ***" as *u8) 750 } 751 m6 = mp_cat(msg, m6, "\n" as *u8) 752 mc_emit(jsonf, msg, m6) 753 if size0 > cliff { return 1 } 754 return 0 755 } 756 757 if dry == 1 { 758 var m7: i64 = mp_cat(msg, 0, "nx_memcliff: DRY-RUN " as *u8) 759 m7 = mp_catn(msg, m7, size0) 760 m7 = mp_cat(msg, m7, " -> " as *u8) 761 m7 = mp_catn(msg, m7, o) 762 m7 = mp_cat(msg, m7, " B, would evict " as *u8) 763 m7 = mp_catn(msg, m7, nvic) 764 m7 = mp_cat(msg, m7, " entries" as *u8) 765 if jam == 1 { m7 = mp_cat(msg, m7, " (NEWEST-DAY PIN YIELDED: every candidate was newest-day; kept the " as *u8); m7 = mp_catn(msg, m7, keepn); m7 = mp_cat(msg, m7, " nearest the head)" as *u8) } 766 m7 = mp_cat(msg, m7, ".\n" as *u8) 767 mc_emit(jsonf, msg, m7) 768 return 0 769 } 770 771 // ---- COMPARE-AND-SWAP: abort if a concurrent writer touched the file since we read it ---- 772 if mc_fsize(MC_MEM) != size0 { 773 var m8: i64 = mp_cat(msg, 0, "nx_memcliff: MEMORY.md changed under us mid-eviction (concurrent writer) -- ABORTED, nothing removed. The entries were already preserved to the overflow; the next run retries.\n" as *u8) 774 mc_emit(jsonf, msg, m8) 775 return 1 776 } 777 778 // ---- ATOMIC INSTALL ---- 779 let wfd: i64 = sys_openat_wr(MC_TMP, 420) 780 if wfd < 0 { 781 var m9: i64 = mp_cat(msg, 0, "*** nx_memcliff: REFUSED -- could not open the temp file; nothing removed. ***\n" as *u8) 782 mc_emit(jsonf, msg, m9) 783 return 1 784 } 785 let wrote: i64 = mp_write_all(wfd, out, o) 786 sys_close(wfd) 787 if wrote != o { 788 var ma: i64 = mp_cat(msg, 0, "*** nx_memcliff: REFUSED -- short write to the temp file; live file untouched. ***\n" as *u8) 789 mc_emit(jsonf, msg, ma) 790 return 1 791 } 792 if sys_renameat(MC_TMP, MC_MEM) != 0 { 793 var mb: i64 = mp_cat(msg, 0, "*** nx_memcliff: REFUSED -- atomic rename failed; live file untouched. ***\n" as *u8) 794 mc_emit(jsonf, msg, mb) 795 return 1 796 } 797 798 var m: i64 = mp_cat(msg, 0, "nx_memcliff: MEMORY.md " as *u8) 799 m = mp_catn(msg, m, size0) 800 m = mp_cat(msg, m, " -> " as *u8) 801 m = mp_catn(msg, m, o) 802 m = mp_cat(msg, m, " B (target " as *u8) 803 m = mp_catn(msg, m, target) 804 m = mp_cat(msg, m, ", cliff " as *u8) 805 m = mp_catn(msg, m, cliff) 806 m = mp_cat(msg, m, "); evicted " as *u8) 807 m = mp_catn(msg, m, nvic) 808 m = mp_cat(msg, m, " entries VERBATIM to the overflow (nothing deleted); lines " as *u8) 809 m = mp_catn(msg, m, lc) 810 m = mp_cat(msg, m, " -> " as *u8) 811 m = mp_catn(msg, m, lc - nvic) 812 m = mp_cat(msg, m, " of max " as *u8) 813 m = mp_catn(msg, m, maxl) 814 if jam == 1 { 815 m = mp_cat(msg, m, ". NOTE: the unpinned candidates could not reach the target, so the newest-day pin YIELDED and kept the " as *u8) 816 m = mp_catn(msg, m, keepn) 817 m = mp_cat(msg, m, " nearest the head -- without this the enforcer would have refused and the budget would be unenforced" as *u8) 818 } 819 m = mp_cat(msg, m, ".\n" as *u8) 820 mc_emit(jsonf, msg, m) 821 return 0 822}