nx_crashresume_census_core.nx source
↩ module page · 162 lines · 6267 B
1// nx_crashresume_census_core.nx -- importable CORE of the crash-resume census (CR-R0 of the
2// crash-resume->SOTA roadmap 2026-07-15): MEASURES whether the workstream capture plane is LIVE --
3// heartbeat staleness (max HBX epoch= in the log vs wall clock), registry/file mtime staleness
4// (newfstatat), and session-activity vs capture LAG (newest transcript mtime vs newest captured
5// state). Bank: registry+heartbeats sat DEAD 5 days at the 07-15 crash while 36 sessions worked ->
6// resume cost ~5h of hand archaeology; this census makes that rot VISIBLE within a cron tick.
7// PURE verdict funcs here (gate-locked). Cells NEVER fabricate: no evidence -> UNKNOWN; bounded
8// reads bias STALE (a truncated log can only under-report freshness, never invent it). Read-only.
9// license_tier: ORIGINAL
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx"
12
13// Buffer sizes promoted out of the call sites (rule 11) as part of the 2026-07-31 mmap-leak
14// root-fix (debt 1785516350). st_mtime lives at byte 88 of the x86_64 struct stat = i64 slot 11.
15const CCZ_EPBUF: i64 = 16
16const CCZ_STATBUF: i64 = 256
17const CCZ_STAT_MTIME_SLOT: i64 = 11
18const CCZ_DENTBUF: i64 = 65536
19const CCZ_PATHBUF: i64 = 2048
20
21const CR_RED: i64 = 0
22const CR_UNKNOWN: i64 = 1
23const CR_GREEN: i64 = 2
24
25func ccz_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
26
27func ccz_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
28
29// parse leading unsigned decimal from s[off..n): value, or -1 if no digit at off. end offset in endp[0].
30func ccz_num_at(s: *u8, n: i64, off: i64, endp: *i64) -> i64 {
31 var v: i64 = 0
32 var any: i64 = 0
33 var i: i64 = off
34 var go: i64 = 1
35 while go == 1 {
36 go = 0
37 if i < n { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48); any = 1; i = i + 1; go = 1 } } }
38 }
39 endp[0] = i
40 if any == 0 { return 0 - 1 }
41 return v
42}
43
44// bounded whole-file read. -1 absent/unreadable.
45func ccz_read(path: *u8, buf: *u8, cap: i64) -> i64 {
46 let fd: i64 = sys_openat_rd(path)
47 if fd < 0 { return 0 - 1 }
48 var tot: i64 = 0
49 var n: i64 = sys_read(fd, buf, cap)
50 while n > 0 { tot = tot + n; if tot >= cap { n = 0 } else { n = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot) } }
51 sys_close(fd)
52 return tot
53}
54
55// max "epoch=<digits>" value in buf[0..n). 0 if none (upstream -> UNKNOWN, never fabricated).
56func ccz_max_epoch(buf: *u8, n: i64) -> i64 {
57 let pat: *u8 = "epoch=" as *u8
58 let pl: i64 = 6
59 let ep: *i64 = sys_mmap(CCZ_EPBUF) as *i64
60 var best: i64 = 0
61 var i: i64 = 0
62 while i + pl <= n {
63 var k: i64 = 0
64 var ok: i64 = 1
65 while k < pl { if buf[i+k] != pat[k] { ok = 0; k = pl } k = k + 1 }
66 if ok == 1 {
67 let v: i64 = ccz_num_at(buf, n, i + pl, ep)
68 if v > best { best = v }
69 i = ep[0]
70 } else { i = i + 1 }
71 }
72 sys_munmap(ep as *u8, CCZ_EPBUF)
73 return best
74}
75
76// mtime (epoch sec) of path via newfstatat. -1 absent/unreadable. st_mtim.tv_sec @ byte 88 = i64[11].
77func ccz_mtime(path: *u8) -> i64 {
78 let sb: *u8 = sys_mmap(CCZ_STATBUF)
79 let r: i64 = sys_fstatat(path, sb)
80 // the FAILED-stat path leaked too, and a corpus walk hits it most -- free before every return.
81 if r < 0 { sys_munmap(sb, CCZ_STATBUF); return 0 - 1 }
82 let sw: *i64 = sb as *i64
83 let mt: i64 = sw[CCZ_STAT_MTIME_SLOT]
84 sys_munmap(sb, CCZ_STATBUF)
85 return mt
86}
87
88func ccz_ends_with(name: *u8, ext: *u8) -> i64 {
89 let nl: i64 = ccz_slen(name)
90 let el: i64 = ccz_slen(ext)
91 if el > nl { return 0 }
92 var i: i64 = 0
93 while i < el { if name[nl-el+i] != ext[i] { return 0 } i = i + 1 }
94 return 1
95}
96
97// newest mtime among dir entries whose name ends with ext. -1 if none/unreadable dir.
98func ccz_newest_mtime_ext(dir: *u8, ext: *u8) -> i64 {
99 let fd: i64 = sys_openat_rd(dir)
100 if fd < 0 { return 0 - 1 }
101 let dbuf: *u8 = sys_mmap(CCZ_DENTBUF)
102 let path: *u8 = sys_mmap(CCZ_PATHBUF)
103 var best: i64 = 0 - 1
104 var run: i64 = 1
105 while run == 1 {
106 let n: i64 = sys_getdents64(fd, dbuf, CCZ_DENTBUF)
107 if n <= 0 { run = 0 } else {
108 var off: i64 = 0
109 while off < n {
110 let rec: *u8 = ((dbuf as i64 + off) as *u8)
111 let reclen: i64 = dirent_reclen(rec)
112 if reclen <= 0 { off = n } else {
113 let name: *u8 = dirent_name(rec)
114 if ccz_ends_with(name, ext) == 1 {
115 var o: i64 = 0
116 var a: i64 = 0
117 while dir[a] != (0 as u8) { path[o] = dir[a]; o = o + 1; a = a + 1 }
118 path[o] = 47 as u8
119 o = o + 1
120 a = 0
121 while name[a] != (0 as u8) { path[o] = name[a]; o = o + 1; a = a + 1 }
122 path[o] = 0 as u8
123 let mt: i64 = ccz_mtime(path)
124 if mt > best { best = mt }
125 }
126 off = off + reclen
127 }
128 }
129 }
130 }
131 sys_close(fd)
132 sys_munmap(dbuf, CCZ_DENTBUF)
133 sys_munmap(path, CCZ_PATHBUF)
134 return best
135}
136
137// append s at buf[off..], NUL-terminate, return new off.
138func ccz_cat_str(buf: *u8, off: i64, s: *u8) -> i64 {
139 var o: i64 = off
140 var a: i64 = 0
141 while s[a] != (0 as u8) { buf[o] = s[a]; o = o + 1; a = a + 1 }
142 buf[o] = 0 as u8
143 return o
144}
145
146// append decimal v (handles 0 and negatives) at buf[off..], NUL-terminate, return new off.
147
148// PURE: staleness verdict. ts<=0 -> UNKNOWN (no evidence, never fabricate); age<=window -> GREEN; else RED.
149func ccz_stale_verdict(ts: i64, now: i64, window: i64) -> i64 {
150 if ts <= 0 { return CR_UNKNOWN }
151 if now - ts <= window { return CR_GREEN }
152 return CR_RED
153}
154
155// PURE: capture-lag verdict. src<=0 -> UNKNOWN (no activity evidence); cap<=0 with live src -> RED
156// (activity but ZERO capture); src-cap<=window -> GREEN (capture keeps up); else RED.
157func ccz_lag_verdict(src_ts: i64, cap_ts: i64, window: i64) -> i64 {
158 if src_ts <= 0 { return CR_UNKNOWN }
159 if cap_ts <= 0 { return CR_RED }
160 if src_ts - cap_ts <= window { return CR_GREEN }
161 return CR_RED
162}