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}