code wiki / _hdl_build / nx_system_triage.nx

nx_system_triage.nx source

↩ module page · 536 lines · 26944 B

1// nx_system_triage.nx -- post-event situational awareness: WHAT IS RUNNING, WHAT IS CRASHED, 2// WHAT IS RECOVERABLE, reported live + durable. Built for the reboot/crash case: /tmp organs die, 3// daemons die, and until now nothing in the team could SAY so -- the operator had to ask Claude. 4// (1) RUNNING -- /proc sweep: every live process whose cmdline is team-ish (nx_/.sov.elf), pid+cmdline 5// (2) DAEMONS -- conf-listed expected daemons (D rows) matched against the same sweep -> RUNNING/DOWN 6// (3) ORGANS -- conf-listed expected elfs (E rows): /tmp/<n>.sov.elf|.elf present? 7// missing + source present = RECOVERABLE (rebuild lane named: S=sov_build_run W=wheeler) 8// missing + source missing = UNRECOVERABLE (loud) 9// (4) CRASH-SIGNALS-- RUNTIME-CRASH count in knowledge/status/issues_durable.log (filed history) 10// Verdict: GREEN (all present, daemons up) / RECOVER (only rebuildable losses) / ATTENTION (unrecoverable 11// or expected daemon down). Durable TRIAGE line -> knowledge/status/system_triage.log; exit 0/2/1. 12// Expected set is DATA-DRIVEN: knowledge/status/triage_expected.conf (add a row = widen coverage). 13// Sovereign (syscalls only, no .sh). license_tier: ORIGINAL 14import "nx_syscalls.nx" 15import "nx_framed_append.nx" // torn-write fix: the TRIAGE record = ONE locked fa_appendz (was ~34 sequential _fp/_fn) 16const ST_MAGIC_1073741824: i64 = 1073741824 17const ST_MAGIC_65536: i64 = 65536 18const ST_MAGIC_65520: i64 = 65520 19const ST_MAGIC_16384: i64 = 16384 20const ST_MAGIC_1048592: i64 = 1048592 21const ST_MAGIC_1048576: i64 = 1048576 22const ST_CAP: i64 = 1024 // bounded TRIAGE record (>= the full field set + verdict) 23func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 24func _pn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 } 25func _fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 26func _fn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(fd,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 27func st_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 28func st_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 29// does file at path open for read? (presence probe) 30func st_exists(path: *u8) -> i64 { 31 let fd: i64 = sys_openat_rd(path) 32 if fd < 0 { return 0 } 33 sys_close(fd) 34 return 1 35} 36// read up to cap bytes of path into buf; return byte count (0 if absent) 37func st_read_all(path: *u8, buf: *u8, cap: i64) -> i64 { 38 let fd: i64 = sys_openat_rd(path) 39 if fd < 0 { return 0 } 40 var n: i64 = 0 41 var go: i64 = 1 42 while go == 1 { let base: i64 = buf as i64; let r: i64 = sys_read(fd, (base + n) as *u8, cap - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } } 43 sys_close(fd) 44 return n 45} 46// substring scan over a raw byte buffer (hay may contain NULs; pat is NUL-terminated, NUL-free) 47func st_contains(hay: *u8, n: i64, pat: *u8) -> i64 { 48 let pl: i64 = st_len(pat) 49 if pl == 0 { return 0 } 50 var i: i64 = 0 51 while i + pl <= n { 52 var k: i64 = 0 53 var hit: i64 = 1 54 while k < pl { if hay[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 55 if hit == 1 { return 1 } 56 i = i + 1 57 } 58 return 0 59} 60// count occurrences of pat in buffer 61func st_count_pat(hay: *u8, n: i64, pat: *u8) -> i64 { 62 let pl: i64 = st_len(pat) 63 if pl == 0 { return 0 } 64 var cnt: i64 = 0 65 var i: i64 = 0 66 while i + pl <= n { 67 var k: i64 = 0 68 var hit: i64 = 1 69 while k < pl { if hay[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 70 if hit == 1 { cnt = cnt + 1 } 71 i = i + 1 72 } 73 return cnt 74} 75// is dirent name all digits (= a pid dir under /proc)? 76func st_is_pid(name: *u8) -> i64 { 77 var i: i64 = 0 78 if name[0] == (0 as u8) { return 0 } 79 while name[i] != (0 as u8) { 80 if name[i] < (48 as u8) { return 0 } 81 if name[i] > (57 as u8) { return 0 } 82 i = i + 1 83 } 84 return 1 85} 86// organ presence: /tmp/<name>.sov.elf OR /tmp/<name>.elf OR _offc/<name>.elf 87// (_offc = the DURABLE home; 2026-06-10 the vault/audit helpers moved there so a 88// /tmp wipe can never kill them again -- durable presence counts as present) 89func tr_organ_present(name: *u8) -> i64 { 90 let p: *u8 = sys_mmap(256) 91 var o: i64 = 0 92 o = st_cat(p, o, "/tmp/" as *u8); o = st_cat(p, o, name); o = st_cat(p, o, ".sov.elf" as *u8); p[o] = 0 as u8 93 if st_exists(p) == 1 { return 1 } 94 o = 0 95 o = st_cat(p, o, "/tmp/" as *u8); o = st_cat(p, o, name); o = st_cat(p, o, ".elf" as *u8); p[o] = 0 as u8 96 if st_exists(p) == 1 { return 1 } 97 o = 0 98 o = st_cat(p, o, "_offc/" as *u8); o = st_cat(p, o, name); o = st_cat(p, o, ".elf" as *u8); p[o] = 0 as u8 99 return st_exists(p) 100} 101// organ source present: runtime/_hdl_build/<n>.nx OR runtime/<n>.nx OR nxasm/<n>.nx (mirrors runner fallback) 102func tr_source_present(name: *u8) -> i64 { 103 let p: *u8 = sys_mmap(256) 104 var o: i64 = 0 105 o = st_cat(p, o, "runtime/_hdl_build/" as *u8); o = st_cat(p, o, name); o = st_cat(p, o, ".nx" as *u8); p[o] = 0 as u8 106 if st_exists(p) == 1 { return 1 } 107 o = 0 108 o = st_cat(p, o, "runtime/" as *u8); o = st_cat(p, o, name); o = st_cat(p, o, ".nx" as *u8); p[o] = 0 as u8 109 if st_exists(p) == 1 { return 1 } 110 o = 0 111 o = st_cat(p, o, "nxasm/" as *u8); o = st_cat(p, o, name); o = st_cat(p, o, ".nx" as *u8); p[o] = 0 as u8 112 return st_exists(p) 113} 114// free GB on the filesystem holding path. x86 statfs=137 called DIRECT (probe-proven 2026-06-10: 115// rv64 43 returns -9 through the translation table; 137 raw matches df exactly). -1 = statfs failed. 116func tr_free_gb(path: *u8) -> i64 { 117 let buf: *i64 = sys_mmap(144) as *i64 118 let rc: i64 = __syscall(137, path, buf, 0, 0, 0, 0) 119 if rc != 0 { return 0 - 1 } 120 return (buf[1] * buf[4]) / ST_MAGIC_1073741824 121} 122// fork+exec elf with up to 2 args, output muted; exit code (128+sig on crash) 123func tr_run2(elf: *u8, a1: *u8, a2: *u8) -> i64 { 124 let pid: i64 = sys_fork() 125 if pid == 0 { 126 let argv: *i64 = sys_mmap(64) as *i64 127 argv[0] = elf as i64 128 var i: i64 = 1 129 if (a1 as i64) != 0 { argv[i] = a1 as i64; i = i + 1 } 130 if (a2 as i64) != 0 { argv[i] = a2 as i64; i = i + 1 } 131 argv[i] = 0 132 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0 133 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 134 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) } 135 sys_execve(elf, argv, envp) 136 sys_exit(127) 137 } 138 let st: *i64 = sys_mmap(16) as *i64 139 sys_wait4(pid, st, 0) 140 let sig: i64 = st[0] & 0x7f 141 if sig != 0 { return 128 + sig } 142 return (st[0] >> 8) & 0xff 143} 144// one /proc/<pid> entry: read cmdline; if team-ish print pid+cmdline; mark any matched D rows. 145// returns 1 if team-ish (counted as RUNNING). 6 args (register path, no stack args). 146func tr_check_pid(nm: *u8, cmdl: *u8, ppath: *u8, dnames: *i64, dfound: *i64, nd: i64) -> i64 { 147 var po: i64 = 0 148 po = st_cat(ppath, po, "/proc/" as *u8); po = st_cat(ppath, po, nm); po = st_cat(ppath, po, "/cmdline" as *u8); ppath[po] = 0 as u8 149 let cl: i64 = st_read_all(ppath, cmdl, 500) 150 if cl <= 0 { return 0 } 151 var dk: i64 = 0 152 while dk < nd { if st_contains(cmdl, cl, dnames[dk] as *u8) == 1 { dfound[dk] = 1 } dk = dk + 1 } 153 var teamish: i64 = st_contains(cmdl, cl, "nx_" as *u8) 154 if teamish == 0 { teamish = st_contains(cmdl, cl, ".sov.elf" as *u8) } 155 if teamish == 0 { return 0 } 156 var b: i64 = 0 157 while b < cl { if cmdl[b] == (0 as u8) { cmdl[b] = 32 as u8 } b = b + 1 } 158 _p(" pid " as *u8); _p(nm); _p(" " as *u8); sys_write(1, cmdl, cl); _p("\n" as *u8) 159 return 1 160} 161// LAST number following pat in buffer (digits parsed immediately after the final match; -1 = no match) 162func st_last_num_after(hay: *u8, n: i64, pat: *u8) -> i64 { 163 let pl: i64 = st_len(pat) 164 if pl == 0 { return 0 - 1 } 165 var best: i64 = 0 - 1 166 var i: i64 = 0 167 while i + pl <= n { 168 var k: i64 = 0 169 var hit: i64 = 1 170 while k < pl { if hay[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 171 if hit == 1 { best = i + pl } 172 i = i + 1 173 } 174 if best < 0 { return 0 - 1 } 175 var v: i64 = 0 176 var q: i64 = best 177 var any: i64 = 0 178 while q < n { 179 if hay[q] >= (48 as u8) { if hay[q] <= (57 as u8) { v = v * 10 + ((hay[q] as i64) - 48); any = 1; q = q + 1 } else { q = n } } else { q = n } 180 } 181 if any == 0 { return 0 - 1 } 182 return v 183} 184// argv[1] = conf path override, argv[2] = durable log override (defaults below) -- lets the 185// triage GATE run synthetic fixtures without polluting the real trend log (site-gate pattern) 186// argv[3] = beat-log override, argv[4] = boot-journal override (CR5 grace fixtures). 187// CR5 boot-grace (crash-recovery ladder, 2026-06-10): a stale beat/evidence verdict caused by 188// a JOURNALED recent boot (boot_journal.log BOOT row within the conf G window) grades RECOVER, 189// not ATTENTION -- the crash is already measured + revived; a wedged-while-up daemon has no 190// recent BOOT row and stays red. G row absent/0 = grace off. 191func main(argc: i64, argv: *i64) -> i64 { 192 _p("=== SYSTEM TRIAGE: running / crashed / recoverable (post-event situational awareness) ===\n" as *u8) 193 var confpath: *u8 = "knowledge/status/triage_expected.conf" as *u8 194 if argc >= 2 { confpath = argv[1] as *u8 } 195 var logpath: *u8 = "knowledge/status/system_triage.log" as *u8 196 if argc >= 3 { logpath = argv[2] as *u8 } 197 var beatpath: *u8 = "knowledge/status/pulse_daemon.log" as *u8 198 if argc >= 4 { beatpath = argv[3] as *u8 } 199 var journalpath: *u8 = "knowledge/status/boot_journal.log" as *u8 200 if argc >= 5 { journalpath = argv[4] as *u8 } 201 // ---- parse the expected-set conf (data-driven) ---- 202 // CAP REMOVED 2026-08-23 (nx_capcensus lane): st_read_all stops at `n >= cap` and RETURNS with 203 // NO truncation signal -- so a conf above 65,520 B parsed as exactly its first 65,520 bytes and 204 // the expected-set silently lost its tail. That is the nx_cron_reconcile shape (a 24,356-byte 205 // registry read through a 16,383-byte buffer left 11 production jobs inert while printing 206 // declared=55 as if that were the population), and it is HEAD-anchored truncation -- NOT the 207 // declared tail horizon that append-only ledger readers correctly use. This organ already fails 208 // LOUD on a missing conf; it must not fail SILENT on a long one. sys_read_file sizes its buffer 209 // from the file itself (lseek END) and cannot short-read, so the conf is read WHOLE at any size. 210 let cnp: *i64 = sys_mmap(8) as *i64 211 *cnp = 0 212 let conf: *u8 = sys_read_file(confpath, cnp) 213 let cn: i64 = *cnp 214 if cn <= 0 { _p(" TRIAGE: conf MISSING (" as *u8); _p(confpath); _p(") -- fail loud\n" as *u8); sys_exit(1); return 1 } 215 let pool: *u8 = sys_mmap(ST_MAGIC_16384) 216 var poff: i64 = 0 217 let enames: *i64 = sys_mmap(64*8) as *i64 218 let elanes: *u8 = sys_mmap(64) 219 var ne: i64 = 0 220 let dnames: *i64 = sys_mmap(16*8) as *i64 221 var nd: i64 = 0 222 var mingb: i64 = 0 223 var hmax: i64 = 0 224 var grace: i64 = 0 225 let fbud: *i64 = sys_mmap(16*8) as *i64 226 let fpath: *i64 = sys_mmap(16*8) as *i64 227 var nf: i64 = 0 228 let rhost: *i64 = sys_mmap(8*8) as *i64 229 let rport: *i64 = sys_mmap(8*8) as *i64 230 var nr: i64 = 0 231 var ci: i64 = 0 232 while ci < cn { 233 // find end of this line (flag loop, no sentinels) 234 var le: i64 = ci 235 var scan: i64 = 1 236 while scan == 1 { 237 if le >= cn { scan = 0 } 238 else { if conf[le] == (10 as u8) { scan = 0 } else { le = le + 1 } } 239 } 240 // 'E <name> <lane>' 241 if conf[ci] == (69 as u8) { 242 var te: i64 = ci + 2 243 var s2: i64 = 1 244 while s2 == 1 { 245 if te >= le { s2 = 0 } 246 else { if conf[te] == (32 as u8) { s2 = 0 } else { te = te + 1 } } 247 } 248 let nptr: i64 = (pool as i64) + poff 249 var q: i64 = ci + 2 250 while q < te { pool[poff] = conf[q]; poff = poff + 1; q = q + 1 } 251 pool[poff] = 0 as u8; poff = poff + 1 252 if ne < 64 { 253 enames[ne] = nptr 254 elanes[ne] = 83 as u8 255 if te + 1 < le { elanes[ne] = conf[te+1] } 256 ne = ne + 1 257 } 258 } 259 // 'T <min-free-GB>' (disk budget; 0/absent = report-only, no gating) 260 if conf[ci] == (84 as u8) { 261 var tv: i64 = 0 262 var q3: i64 = ci + 2 263 while q3 < le { 264 if conf[q3] >= (48 as u8) { if conf[q3] <= (57 as u8) { tv = tv * 10 + ((conf[q3] as i64) - 48) } } 265 q3 = q3 + 1 266 } 267 mingb = tv 268 } 269 // 'H <max-beat-age-sec>' (heartbeat budget: last DAEMON-BEAT must be younger; 0/absent = skip) 270 if conf[ci] == (72 as u8) { 271 var hv: i64 = 0 272 var q4: i64 = ci + 2 273 while q4 < le { 274 if conf[q4] >= (48 as u8) { if conf[q4] <= (57 as u8) { hv = hv * 10 + ((conf[q4] as i64) - 48) } } 275 q4 = q4 + 1 276 } 277 hmax = hv 278 } 279 // 'G <grace-sec>' (CR5 boot-grace window after a journaled boot; 0/absent = off) 280 if conf[ci] == (71 as u8) { 281 var gv: i64 = 0 282 var q9: i64 = ci + 2 283 while q9 < le { 284 if conf[q9] >= (48 as u8) { if conf[q9] <= (57 as u8) { gv = gv * 10 + ((conf[q9] as i64) - 48) } } 285 q9 = q9 + 1 286 } 287 grace = gv 288 } 289 // 'F <max-age-sec> <logpath>' (evidence freshness: last epoch= in that log must be younger) 290 if conf[ci] == (70 as u8) { 291 var fv: i64 = 0 292 var q5: i64 = ci + 2 293 var s5: i64 = 1 294 while s5 == 1 { 295 if q5 >= le { s5 = 0 } 296 else { 297 if conf[q5] == (32 as u8) { s5 = 0 } 298 else { 299 if conf[q5] >= (48 as u8) { if conf[q5] <= (57 as u8) { fv = fv * 10 + ((conf[q5] as i64) - 48) } } 300 q5 = q5 + 1 301 } 302 } 303 } 304 let fptr: i64 = (pool as i64) + poff 305 var q6: i64 = q5 + 1 306 while q6 < le { pool[poff] = conf[q6]; poff = poff + 1; q6 = q6 + 1 } 307 pool[poff] = 0 as u8; poff = poff + 1 308 if nf < 16 { fbud[nf] = fv; fpath[nf] = fptr; nf = nf + 1 } 309 } 310 // 'R <host-or-ip> <port>' (remote reachability via nx_reach_probe; both tokens kept as strings) 311 if conf[ci] == (82 as u8) { 312 var t7: i64 = ci + 2 313 var s7: i64 = 1 314 while s7 == 1 { 315 if t7 >= le { s7 = 0 } 316 else { if conf[t7] == (32 as u8) { s7 = 0 } else { t7 = t7 + 1 } } 317 } 318 let hptr: i64 = (pool as i64) + poff 319 var q7: i64 = ci + 2 320 while q7 < t7 { pool[poff] = conf[q7]; poff = poff + 1; q7 = q7 + 1 } 321 pool[poff] = 0 as u8; poff = poff + 1 322 let pptr: i64 = (pool as i64) + poff 323 var q8: i64 = t7 + 1 324 while q8 < le { pool[poff] = conf[q8]; poff = poff + 1; q8 = q8 + 1 } 325 pool[poff] = 0 as u8; poff = poff + 1 326 if nr < 8 { rhost[nr] = hptr; rport[nr] = pptr; nr = nr + 1 } 327 } 328 // 'D <substr>' 329 if conf[ci] == (68 as u8) { 330 let dptr: i64 = (pool as i64) + poff 331 var q2: i64 = ci + 2 332 while q2 < le { pool[poff] = conf[q2]; poff = poff + 1; q2 = q2 + 1 } 333 pool[poff] = 0 as u8; poff = poff + 1 334 if nd < 16 { dnames[nd] = dptr; nd = nd + 1 } 335 } 336 ci = le + 1 337 } 338 // ---- /proc sweep: RUNNING team-ish processes + daemon matching ---- 339 let dfound: *i64 = sys_mmap(16*8) as *i64 340 var di: i64 = 0 341 while di < nd { dfound[di] = 0; di = di + 1 } 342 var nproc: i64 = 0 343 _p(" -- RUNNING (team-ish processes in /proc) --\n" as *u8) 344 let pfd: i64 = sys_openat_rd("/proc" as *u8) 345 if pfd >= 0 { 346 let dirbuf: *u8 = sys_mmap(ST_MAGIC_65536) 347 let cmdl: *u8 = sys_mmap(512) 348 let ppath: *u8 = sys_mmap(256) 349 var done: i64 = 0 350 while done == 0 { 351 let nb: i64 = sys_getdents64(pfd, dirbuf, ST_MAGIC_65536) 352 if nb <= 0 { done = 1 } 353 else { 354 var off: i64 = 0 355 while off < nb { 356 let dbase: i64 = dirbuf as i64 357 let rec: *u8 = (dbase + off) as *u8 358 let rl: i64 = dirent_reclen(rec) 359 if rl <= 0 { off = nb } 360 else { 361 let nm: *u8 = dirent_name(rec) 362 if st_is_pid(nm) == 1 { nproc = nproc + tr_check_pid(nm, cmdl, ppath, dnames, dfound, nd) } 363 off = off + rl 364 } 365 } 366 } 367 } 368 sys_close(pfd) 369 } 370 if nproc == 0 { _p(" (none)\n" as *u8) } 371 // ---- expected daemons verdicts ---- 372 var ddown: i64 = 0 373 if nd > 0 { 374 _p(" -- EXPECTED DAEMONS --\n" as *u8) 375 var dv: i64 = 0 376 while dv < nd { 377 _p(" " as *u8); _p(dnames[dv] as *u8); _p(": " as *u8) 378 if dfound[dv] == 1 { _p("RUNNING\n" as *u8) } else { _p("DOWN\n" as *u8); ddown = ddown + 1 } 379 dv = dv + 1 380 } 381 } 382 // ---- expected organs: present / recoverable / unrecoverable ---- 383 _p(" -- EXPECTED ORGANS (/tmp elfs) --\n" as *u8) 384 var present: i64 = 0 385 var recov: i64 = 0 386 var unrec: i64 = 0 387 var ei: i64 = 0 388 while ei < ne { 389 let en: *u8 = enames[ei] as *u8 390 _p(" " as *u8); _p(en); _p(": " as *u8) 391 if tr_organ_present(en) == 1 { _p("PRESENT\n" as *u8); present = present + 1 } 392 else { 393 if tr_source_present(en) == 1 { 394 recov = recov + 1 395 _p("RECOVERABLE via " as *u8) 396 if elanes[ei] == (87 as u8) { _p("wheeler-bootstrap lane\n" as *u8) } else { _p("nx_sov_build_run\n" as *u8) } 397 } else { _p("UNRECOVERABLE (no source!)\n" as *u8); unrec = unrec + 1 } 398 } 399 ei = ei + 1 400 } 401 // ---- remote reachability (the NAS/live-site eye; probe is watchdog-bounded at 10s/row) ---- 402 var rdown: i64 = 0 403 if nr > 0 { 404 _p(" -- REACHABILITY (remote, via own DNS+TCP probe) --\n" as *u8) 405 let probe2: i64 = sys_openat_rd("/tmp/nx_reach_probe.sov.elf" as *u8) 406 if probe2 >= 0 { sys_close(probe2) } 407 else { tr_run2("_offc/nx_sov_build_run.elf" as *u8, "nx_reach_probe" as *u8, 0 as *u8) } 408 var ri: i64 = 0 409 while ri < nr { 410 _p(" " as *u8); _p(rhost[ri] as *u8); _p(":" as *u8); _p(rport[ri] as *u8); _p(" " as *u8) 411 let rrc: i64 = tr_run2("/tmp/nx_reach_probe.sov.elf" as *u8, rhost[ri] as *u8, rport[ri] as *u8) 412 if rrc == 0 { _p("REACHABLE\n" as *u8) } else { _p("UNREACHABLE rc=" as *u8); _pn(rrc); _p("\n" as *u8); rdown = rdown + 1 } 413 ri = ri + 1 414 } 415 } 416 // ---- crash signals filed (history; does not gate the verdict) ---- 417 // CAP REMOVED 2026-08-23 (nx_capcensus lane): issues_durable.log is APPEND-ONLY and this read 418 // was HEAD-anchored at 1 MiB, so once the log crossed that bound the RUNTIME-CRASH count was 419 // computed over the OLDEST megabyte and every RECENT crash was invisible -- a crash gauge that 420 // degrades exactly as new crashes arrive, which is the worst possible failure direction for it. 421 // Note the distinction this lane drew: a TAIL-anchored ledger read (nx_trajscan, nx_catalog_lib, 422 // nx_toolatency) is a declared horizon and correct; a HEAD-anchored one on an append-only file 423 // is silent blindness to the newest rows. Reading whole removes the horizon entirely. 424 let inp: *i64 = sys_mmap(8) as *i64 425 *inp = 0 426 let ibuf: *u8 = sys_read_file("knowledge/status/issues_durable.log" as *u8, inp) 427 let inn: i64 = *inp 428 let ncrash: i64 = st_count_pat(ibuf, inn, "RUNTIME-CRASH" as *u8) 429 _p(" -- CRASH SIGNALS filed (issues_durable.log): " as *u8); _pn(ncrash); _p(" RUNTIME-CRASH lines --\n" as *u8) 430 // ---- disk budget (the 2026-06-10 lesson: 147GB of crash dumps emptied C: and NOTHING said so; 431 // T row in the conf = the threshold; 0/absent = report-only) ---- 432 let repo_gb: i64 = tr_free_gb("." as *u8) 433 let tmp_gb: i64 = tr_free_gb("/tmp" as *u8) 434 var dlow: i64 = 0 435 _p(" -- DISK: repo-disk free " as *u8); _pn(repo_gb); _p("GB, /tmp free " as *u8); _pn(tmp_gb) 436 _p("GB (budget min " as *u8); _pn(mingb); _p("GB) --\n" as *u8) 437 if mingb > 0 { 438 if repo_gb >= 0 { if repo_gb < mingb { dlow = dlow + 1; _p(" DISK-LOW: repo disk below budget!\n" as *u8) } } 439 if tmp_gb >= 0 { if tmp_gb < mingb { dlow = dlow + 1; _p(" DISK-LOW: /tmp below budget!\n" as *u8) } } 440 } 441 // ---- heartbeat staleness (a daemon can be ALIVE in /proc yet wedged -- the beat log is the 442 // proof of life; H row = max age budget) ---- 443 var stale: i64 = 0 444 var beat_age: i64 = 0 - 1 445 if hmax > 0 { 446 let hbuf: *u8 = sys_mmap(ST_MAGIC_1048592) 447 let hn: i64 = st_read_all(beatpath, hbuf, ST_MAGIC_1048576) 448 let bepoch: i64 = st_last_num_after(hbuf, hn, "DAEMON-BEAT epoch=" as *u8) 449 if bepoch > 0 { beat_age = sys_now_realtime_sec() - bepoch } 450 _p(" -- HEARTBEAT: last DAEMON-BEAT age " as *u8); _pn(beat_age); _p("s (budget " as *u8); _pn(hmax); _p("s) --\n" as *u8) 451 if beat_age < 0 { stale = 1; _p(" BEAT-STALE: no DAEMON-BEAT line found!\n" as *u8) } 452 if beat_age > hmax { stale = 1; _p(" BEAT-STALE: daemon exists but is not beating!\n" as *u8) } 453 } 454 // ---- evidence freshness (claims EXPIRE: a green verdict from days ago is not a grade; 455 // each F row = one durable log whose last epoch= must be within budget) ---- 456 var fstale: i64 = 0 457 if nf > 0 { 458 _p(" -- EVIDENCE FRESHNESS (claims expire; per-log budgets) --\n" as *u8) 459 let fb: *u8 = sys_mmap(ST_MAGIC_1048592) 460 var fi: i64 = 0 461 while fi < nf { 462 let fp2: *u8 = fpath[fi] as *u8 463 let fn2: i64 = st_read_all(fp2, fb, ST_MAGIC_1048576) 464 let fe: i64 = st_last_num_after(fb, fn2, "epoch=" as *u8) 465 var age2: i64 = 0 - 1 466 if fe > 0 { age2 = sys_now_realtime_sec() - fe } 467 _p(" " as *u8); _p(fp2); _p(": age " as *u8); _pn(age2); _p("s (budget " as *u8); _pn(fbud[fi]); _p("s) " as *u8) 468 var rowok: i64 = 1 469 if age2 < 0 { rowok = 0 } 470 if age2 > fbud[fi] { rowok = 0 } 471 if rowok == 1 { _p("FRESH\n" as *u8) } else { _p("STALE\n" as *u8); fstale = fstale + 1 } 472 fi = fi + 1 473 } 474 } 475 // ---- CR5 boot-grace: is current staleness explained by a JOURNALED recent boot? ---- 476 var graced: i64 = 0 477 var boot_age: i64 = 0 - 1 478 if grace > 0 { 479 if stale + fstale > 0 { 480 let jb: *u8 = sys_mmap(ST_MAGIC_1048592) 481 let jn2: i64 = st_read_all(journalpath, jb, ST_MAGIC_1048576) 482 let be2: i64 = st_last_num_after(jb, jn2, "BOOT boot_epoch=" as *u8) 483 if be2 > 0 { 484 boot_age = sys_now_realtime_sec() - be2 485 if boot_age >= 0 { if boot_age <= grace { graced = stale + fstale } } 486 } 487 } 488 } 489 if graced > 0 { 490 _p(" -- BOOT-GRACE: staleness is explained by a journaled boot " as *u8); _pn(boot_age) 491 _p("s ago (window " as *u8); _pn(grace); _p("s) -> RECOVERING, not wedged --\n" as *u8) 492 } 493 // ---- verdict + durable line ---- 494 var verdict: i64 = 0 // 0 GREEN / 2 RECOVER / 1 ATTENTION 495 if fstale > 0 { verdict = 1 } 496 if stale > 0 { verdict = 1 } 497 if graced > 0 { if verdict == 1 { verdict = 2 } } // staleness covered by the journaled boot 498 if recov > 0 { verdict = 2 } 499 if unrec > 0 { verdict = 1 } 500 if ddown > 0 { verdict = 1 } 501 if dlow > 0 { verdict = 1 } 502 if rdown > 0 { verdict = 1 } 503 let lfd: i64 = sys_openat_append(logpath, 0x1a4) 504 if lfd >= 0 { 505 sys_close(lfd) // writability check; the TRIAGE record below is ONE atomic fa_appendz write 506 let tb: *u8 = sys_mmap(ST_CAP + 16) 507 var o: i64 = 0 508 o = fa_cat(tb, o, "TRIAGE epoch=" as *u8); o = fa_catn(tb, o, sys_now_realtime_sec()) 509 o = fa_cat(tb, o, " procs_running=" as *u8); o = fa_catn(tb, o, nproc) 510 o = fa_cat(tb, o, " daemons_down=" as *u8); o = fa_catn(tb, o, ddown) 511 o = fa_cat(tb, o, " organs_present=" as *u8); o = fa_catn(tb, o, present) 512 o = fa_cat(tb, o, " organs_recoverable=" as *u8); o = fa_catn(tb, o, recov) 513 o = fa_cat(tb, o, " organs_unrecoverable=" as *u8); o = fa_catn(tb, o, unrec) 514 o = fa_cat(tb, o, " crash_signals=" as *u8); o = fa_catn(tb, o, ncrash) 515 o = fa_cat(tb, o, " disk_repo_gb=" as *u8); o = fa_catn(tb, o, repo_gb) 516 o = fa_cat(tb, o, " disk_tmp_gb=" as *u8); o = fa_catn(tb, o, tmp_gb) 517 o = fa_cat(tb, o, " disk_low=" as *u8); o = fa_catn(tb, o, dlow) 518 o = fa_cat(tb, o, " beat_age=" as *u8); o = fa_catn(tb, o, beat_age) 519 o = fa_cat(tb, o, " beat_stale=" as *u8); o = fa_catn(tb, o, stale) 520 o = fa_cat(tb, o, " evidence_stale=" as *u8); o = fa_catn(tb, o, fstale) 521 o = fa_cat(tb, o, " boot_age=" as *u8); o = fa_catn(tb, o, boot_age) 522 o = fa_cat(tb, o, " graced=" as *u8); o = fa_catn(tb, o, graced) 523 o = fa_cat(tb, o, " reach_down=" as *u8); o = fa_catn(tb, o, rdown) 524 if verdict == 0 { o = fa_cat(tb, o, " verdict=GREEN" as *u8) } 525 if verdict == 2 { o = fa_cat(tb, o, " verdict=RECOVER" as *u8) } 526 if verdict == 1 { o = fa_cat(tb, o, " verdict=ATTENTION" as *u8) } 527 tb[o] = 0 as u8 528 fa_appendz(logpath, tb, ST_CAP) 529 } 530 _p(" durable: " as *u8); _p(logpath); _p("\n" as *u8) 531 if verdict == 0 { _p(" SYSTEM TRIAGE: GREEN (everything expected is alive or present)\n" as *u8); sys_exit(0); return 0 } 532 if verdict == 2 { _p(" SYSTEM TRIAGE: RECOVER (losses are rebuildable -- run the named lanes)\n" as *u8); sys_exit(2); return 2 } 533 _p(" SYSTEM TRIAGE: ATTENTION (unrecoverable organ, daemon down, disk below budget, or beat stale)\n" as *u8) 534 sys_exit(1) 535 return 1 536}