code wiki / _hdl_build / nx_symdrop.nx
nx_symdrop.nx
buildroot/runtime/_hdl_build/nx_symdrop.nx
about
nx_symdrop.nx -- catch SILENT SYMBOL LOSS in shared sources (seq1407 root fix).
THE DEFECT CLASS, witnessed three times: a session lands a source change, verifies it LIVE, and a
sibling later writes back a stale WHOLE-FILE copy that erases it. The running binary keeps serving
the fix, so every health check stays green -- and the revert only detonates at the NEXT rebuild,
silently restoring the old behaviour with NO failing test. My own seq1318 supervisor fix was erased
this way 25 minutes after I proved it live (diff against my pre-edit original: ZERO lines = a pure
stale-copy write-back, not a competing edit).
WHY THIS SHAPE: a fix that must be REMEMBERED at N write sites is not a fix -- and the writes arrive
through many doors (nx_fs_write, ssh redirects, treepack unpack, cron rewrites). So bind the check to
the one property they all share: after any of them, the FILE'S SYMBOL SET either kept everything it
had, or something was removed. Removal is the signal. Additions are normal and ignored.
FAIL-CLOSED, and deliberately NOT auto-healing: an unreadable/vanished watched file is itself the
defect, so it is reported RED rather than skipped; and nothing here ever rewrites a source. A real
removal is legitimised by an explicit `bless`, which makes an intentional deletion a deliberate,
recorded act instead of an indistinguishable accident.
nx_symdrop check [watchconf] [snapfile] -- compare live symbols vs the snapshot; exit 1 on ANY drop
nx_symdrop bless [watchconf] [snapfile] -- accept current symbols as the new baseline
nx_symdrop list [watchconf] -- show what is watched and each file's symbol count
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 2 imports · 2 importers
imports: nx_syscalls.nxnx_itoa_lib.nx
imported by: nx_symdrop_gate.nxnx_treepack.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 27 | const SD_SRCCAP: i64 = 2097152 // largest guarded source (nx_hostctl.nx measured ~281KB) with headroom |
| 28 | const SD_SYMCAP: i64 = 1048576 // symbol csv per file |
| 29 | const SD_SNAPCAP: i64 = 8388608 // whole snapshot file |
| 30 | const SD_CONFCAP: i64 = 65536 |
| 31 | const SD_PATHCAP: i64 = 1024 |
| 32 | const SD_NAMECAP: i64 = 256 |
| 33 | const SD_NL: i64 = 10 |
| 34 | const SD_TAB: i64 = 9 |
| 35 | const SD_COMMA: i64 = 44 |
| 36 | const SD_HASH: i64 = 35 |
| 37 | const SD_SPACE: i64 = 32 |
| 38 | const SD_STDOUT: i64 = 1 |
| 39 | const SD_STDERR: i64 = 2 |
| 40 | const SD_MODE: i64 = 420 |
| 41 | const SD_EXIT_DROP: i64 = 1 |
| 42 | const SD_EXIT_USAGE: i64 = 2 |
| 43 | const SD_EXIT_REFUSED: i64 = 3 |
| 44 | const SD_EXIT_IO: i64 = 4 |
| 45 | const SD_DEF_CONF: *u8 = "knowledge/symdrop_watch.conf" as *u8 |
| 46 | const SD_DEF_SNAP: *u8 = "knowledge/symdrop_snapshot.tsv" as *u8 |
| 47 | const SD_DEF_HIST: *u8 = "knowledge/symdrop_history.tsv" as *u8 |
| 48 | const SD_FNV_OFF: i64 = 1469598103934665603 |
| 49 | const SD_FNV_PRIME: i64 = 1099511628211 |
| 296 | const SD_BODYCAP: i64 = 1048576 |
| 297 | const SD_EQ: i64 = 61 |
| 298 | const SD_MAXGEN: i64 = 256 // generations compared per declaration; bounded, and a longer history simply compares its most recent 256 |
functions
| 68 | func sd_fnv1a(b: *u8, n: i64) -> i64 |
| 79 | func sd_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 80 | func sd_puts(s: *u8) -> i64 { sys_write(SD_STDOUT, s, sd_slen(s)); return 0 } |
| 81 | func sd_werr(s: *u8) -> i64 { sys_write(SD_STDERR, s, sd_slen(s)); return 0 } |
| 86 | func sd_putn(v: i64) -> i64 { nxi_out(v); return 0 } |
| 87 | func sd_eqs(a: *u8, b: *u8) -> i64 called by 1: main |
| 93 | func sd_read(path: *u8, b: *u8, cap: i64) -> i64 |
| 106 | func sd_write(path: *u8, b: *u8, n: i64) -> i64 |
| 115 | func sd_starts(buf: *u8, at: i64, n: i64, lit: *u8) -> i64 |
| 123 | func sd_identch(c: i64) -> i64 |
| 136 | func sd_symbols(src: *u8, n: i64, out: *u8, cap: i64) -> i64 |
| 183 | func sd_has(csv: *u8, cn: i64, name: *u8, nl: i64) -> i64 |
| 205 | func sd_hist_probe(hist: *u8, hn: i64, path: *u8, h: i64, out3: *i64) -> i64 |
| 247 | func sd_hist_line(dst: *u8, off: i64, path: *u8, h: i64) -> i64 { return sd_hist_line_b(dst, off, path, h, 0 as *u8, 0) } |
| 248 | func sd_hist_line_b(dst: *u8, off: i64, path: *u8, h: i64, bodies: *u8, bn: i64) -> i64 |
| 266 | func sd_hist_write(histp: *u8, buf: *u8, n: i64) -> i64 |
| 275 | func sd_hist_append(histp: *u8, path: *u8, h: i64) -> i64 |
| 301 | func sd_bhash(src: *u8, s: i64, e: i64) -> i64 called by 1: sd_bodies |
| 308 | func sd_declkw(src: *u8, ls: i64, n: i64) -> i64 |
| 318 | func sd_bodies(src: *u8, n: i64, out: *u8, cap: i64) -> i64 |
| 361 | func sd_emit_pair(out: *u8, off: i64, cap: i64, src: *u8, ns: i64, ne: i64, h: i64) -> i64 |
| 381 | func sd_body_of(csv: *u8, cn: i64, name: *u8, nl: i64) -> i64 called by 1: sd_bodies_reverted |
| 415 | func sd_hist_field3(hist: *u8, ls: i64, le: i64, out2: *i64) -> i64 called by 1: sd_bodies_reverted |
| 433 | func sd_bodies_reverted(hist: *u8, hn: i64, path: *u8, cur: *u8, cn: i64, firstnm: *u8) -> i64 |
| 504 | func sd_snap_find(snap: *u8, sn: i64, path: *u8, out2: *i64) -> i64 |
| 532 | func main(argc: i64, argv: *i64) -> i64 |