code wiki / _hdl_build / nx_bootmine.nx
nx_bootmine.nx source
↩ module page · 455 lines · 21669 B
1// nx_bootmine.nx -- BOOT/PROCESS-STABILITY MINER over the sovereign supervisor log (miner-sota, 2026-07-24,
2// operator 'go SOTA from the FIRST BYTE up'). The miner suite reaches the LOWEST layer: the system's own
3// process lifecycle. A SOTA boot is STABLE from the first byte -- services come up and STAY up. A CRASH
4// LOOP (a subsystem spawn->die->respawn without end) burns fork bandwidth continuously and hides the real
5// fault. This mines the supervisor.log for exactly that: per-[tag] respawn counts, and any tag whose
6// respawns exceed minloop is flagged CRASH-LOOPING. Auto-detects the reader-keep loop (debt seq699).
7// Composes nx_sovjson_lib (sj_*). Read-only. No hw writes (Rule 26).
8// mine <logfile> [minloop] -> JSON: totals, by_tag ranked, crash_looping, stability
9// selftest <scratch-prefix> -> gate T1..T7
10// license_tier: ORIGINAL
11import "nx_sovjson_lib.nx"
12import "nx_syscalls.nx"
13import "nx_gate_verdict.nx"
14const BM_MAGIC_8192: i64 = 8192
15
16const BM_WIN: i64 = 8388608
17const BM_OUT: i64 = 65536
18const BM_PATH: i64 = 512
19const BM_TAGS: i64 = 128
20const BM_TOPN: i64 = 20
21const BM_MINLOOP: i64 = 10
22const BM_NAME_MAX: i64 = 120
23const BM_MODE_644: i64 = 420
24const BM_LBRK: i64 = 91
25const BM_RBRK: i64 = 93
26
27func bm_read(path: *u8, buf: *u8, cap: i64) -> i64 {
28 let fd: i64 = sys_openat_rd(path)
29 if fd < 0 { return -1 }
30 var n: i64 = 0
31 var go: i64 = 1
32 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64)+n) as *u8, cap-n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } }
33 sys_close(fd)
34 return n
35}
36// substring search in q[s..e) -> 1 if found
37func bm_has(q: *u8, s: i64, e: i64, lit: *u8) -> i64 {
38 let ll: i64 = sj_vlen(lit)
39 if ll == 0 { return 0 }
40 var i: i64 = s
41 while i + ll <= e {
42 var k: i64 = 0
43 var ok: i64 = 1
44 while k < ll { if q[i+k] != lit[k] { ok = 0; k = ll } else { k = k + 1 } }
45 if ok == 1 { return 1 }
46 i = i + 1
47 }
48 return 0
49}
50func bm_find(q: *u8, n: i64, lit: *u8) -> i64 { return bm_has(q, 0, n, lit) }
51// first [tag] in line q[s..e): fills tp[0]/tp[1] with the span BETWEEN the brackets; returns 1 if found
52func bm_tag(q: *u8, s: i64, e: i64, tp: *i64) -> i64 {
53 var i: i64 = s
54 var lb: i64 = 0 - 1
55 while i < e { if q[i] == (BM_LBRK as u8) { lb = i; i = e } else { i = i + 1 } }
56 if lb < 0 { return 0 }
57 var j: i64 = lb + 1
58 while j < e { if q[j] == (BM_RBRK as u8) { tp[0] = lb + 1; tp[1] = j; return 1 } j = j + 1 }
59 return 0
60}
61func bm_intern(q: *u8, cs: i64, ce: i64, ts: *i64, te: *i64, np: *i64) -> i64 {
62 var i: i64 = 0
63 while i < np[0] {
64 if ce - cs == te[i] - ts[i] {
65 var m: i64 = 0
66 var eq: i64 = 1
67 while m < ce - cs { if q[cs+m] != q[ts[i]+m] { eq = 0; m = ce - cs } else { m = m + 1 } }
68 if eq == 1 { return i }
69 }
70 i = i + 1
71 }
72 if np[0] >= BM_TAGS { return -1 }
73 ts[np[0]] = cs
74 te[np[0]] = ce
75 np[0] = np[0] + 1
76 return np[0] - 1
77}
78func bm_key(d: *u8, o: i64, name: *u8) -> i64 {
79 d[o] = 34 as u8
80 var p: i64 = o + 1
81 p = sj_cat(d, p, name)
82 d[p] = 34 as u8
83 p = p + 1
84 d[p] = 58 as u8
85 p = p + 1
86 return p
87}
88func bm_mine_json(logf: *u8, minloop: i64, d: *u8) -> i64 {
89 let q: *u8 = sys_mmap(BM_WIN)
90 let cap: i64 = BM_WIN - 8
91 let n: i64 = bm_read(logf, q, cap)
92 if n < 0 { return -1 }
93 var trunc: i64 = 0
94 if n >= cap { trunc = 1 }
95 let ts: *i64 = sys_mmap(8 * BM_TAGS) as *i64
96 let te: *i64 = sys_mmap(8 * BM_TAGS) as *i64
97 let tresp: *i64 = sys_mmap(8 * BM_TAGS) as *i64
98 let tspawn: *i64 = sys_mmap(8 * BM_TAGS) as *i64
99 let trest: *i64 = sys_mmap(8 * BM_TAGS) as *i64
100 let done: *i64 = sys_mmap(8 * BM_TAGS) as *i64
101 let np: *i64 = sys_mmap(16) as *i64
102 let tp: *i64 = sys_mmap(16) as *i64
103 var lines: i64 = 0
104 var respawns: i64 = 0
105 var spawns: i64 = 0
106 var restarts: i64 = 0
107 var capped: i64 = 0
108 var i: i64 = 0
109 while i < n {
110 let le: i64 = sj_le(q, i, n)
111 if le > i {
112 lines = lines + 1
113 let isresp: i64 = bm_has(q, i, le, "-> respawn" as *u8)
114 let isrest: i64 = bm_has(q, i, le, "(was dead)" as *u8)
115 let isspawn: i64 = bm_has(q, i, le, "spawned" as *u8)
116 if isresp == 1 { respawns = respawns + 1 }
117 if isrest == 1 { restarts = restarts + 1 }
118 if isspawn == 1 { spawns = spawns + 1 }
119 if isresp + isrest + isspawn > 0 {
120 if bm_tag(q, i, le, tp) == 1 {
121 let ti: i64 = bm_intern(q, tp[0], tp[1], ts, te, np)
122 if ti < 0 { capped = 1 } else {
123 if isresp == 1 { tresp[ti] = tresp[ti] + 1 }
124 if isspawn == 1 { tspawn[ti] = tspawn[ti] + 1 }
125 if isrest == 1 { trest[ti] = trest[ti] + 1 }
126 }
127 }
128 }
129 }
130 i = le + 1
131 }
132 var crash: i64 = 0
133 var k: i64 = 0
134 while k < np[0] { if tresp[k] >= minloop { crash = crash + 1 } k = k + 1 }
135 var p: i64 = 0
136 d[p] = 123 as u8; p = p + 1
137 p = bm_key(d, p, "v" as *u8); p = sj_catn(d, p, 1); d[p] = 44 as u8; p = p + 1
138 p = bm_key(d, p, "tool" as *u8)
139 d[p] = 34 as u8; p = p + 1
140 p = sj_cat(d, p, "nx_bootmine" as *u8)
141 d[p] = 34 as u8; p = p + 1
142 d[p] = 44 as u8; p = p + 1
143 p = bm_key(d, p, "averb" as *u8)
144 d[p] = 34 as u8; p = p + 1
145 p = sj_cat(d, p, "mine" as *u8)
146 d[p] = 34 as u8; p = p + 1
147 d[p] = 44 as u8; p = p + 1
148 p = bm_key(d, p, "log" as *u8)
149 d[p] = 34 as u8; p = p + 1
150 p = sj_cat_esc(d, p, logf, 0, sj_vlen(logf), BM_NAME_MAX)
151 d[p] = 34 as u8; p = p + 1
152 d[p] = 44 as u8; p = p + 1
153 p = bm_key(d, p, "log_bytes" as *u8); p = sj_catn(d, p, n); d[p] = 44 as u8; p = p + 1
154 p = bm_key(d, p, "truncated" as *u8); p = sj_catn(d, p, trunc); d[p] = 44 as u8; p = p + 1
155 p = bm_key(d, p, "minloop" as *u8); p = sj_catn(d, p, minloop); d[p] = 44 as u8; p = p + 1
156 p = bm_key(d, p, "lines" as *u8); p = sj_catn(d, p, lines); d[p] = 44 as u8; p = p + 1
157 p = bm_key(d, p, "spawns" as *u8); p = sj_catn(d, p, spawns); d[p] = 44 as u8; p = p + 1
158 p = bm_key(d, p, "respawns" as *u8); p = sj_catn(d, p, respawns); d[p] = 44 as u8; p = p + 1
159 p = bm_key(d, p, "guard_restarts" as *u8); p = sj_catn(d, p, restarts); d[p] = 44 as u8; p = p + 1
160 p = bm_key(d, p, "tags" as *u8); p = sj_catn(d, p, np[0]); d[p] = 44 as u8; p = p + 1
161 p = bm_key(d, p, "crash_loops" as *u8); p = sj_catn(d, p, crash); d[p] = 44 as u8; p = p + 1
162 p = bm_key(d, p, "stability" as *u8)
163 d[p] = 34 as u8; p = p + 1
164 if crash == 0 { p = sj_cat(d, p, "STABLE" as *u8) } else { p = sj_cat(d, p, "UNSTABLE" as *u8) }
165 d[p] = 34 as u8; p = p + 1
166 d[p] = 44 as u8; p = p + 1
167 // by_tag ranked by respawns desc
168 p = bm_key(d, p, "by_tag" as *u8)
169 d[p] = 91 as u8; p = p + 1
170 var shown: i64 = 0
171 var efirst: i64 = 1
172 var pass: i64 = 0
173 while pass < np[0] {
174 var mi: i64 = 0 - 1
175 var mx: i64 = 0 - 1
176 var k2: i64 = 0
177 while k2 < np[0] {
178 if done[k2] == 0 { if tresp[k2] > mx { mx = tresp[k2]; mi = k2 } }
179 k2 = k2 + 1
180 }
181 if mi >= 0 {
182 done[mi] = 1
183 if shown < BM_TOPN {
184 if efirst == 0 { d[p] = 44 as u8; p = p + 1 }
185 efirst = 0
186 d[p] = 123 as u8; p = p + 1
187 p = bm_key(d, p, "tag" as *u8)
188 d[p] = 34 as u8; p = p + 1
189 p = sj_cat_esc(d, p, q, ts[mi], te[mi], BM_NAME_MAX)
190 d[p] = 34 as u8; p = p + 1
191 d[p] = 44 as u8; p = p + 1
192 p = bm_key(d, p, "respawns" as *u8); p = sj_catn(d, p, tresp[mi]); d[p] = 44 as u8; p = p + 1
193 p = bm_key(d, p, "spawns" as *u8); p = sj_catn(d, p, tspawn[mi]); d[p] = 44 as u8; p = p + 1
194 p = bm_key(d, p, "guard_restarts" as *u8); p = sj_catn(d, p, trest[mi]); d[p] = 44 as u8; p = p + 1
195 var cl: i64 = 0
196 if tresp[mi] >= minloop { cl = 1 }
197 p = bm_key(d, p, "crash_looping" as *u8); p = sj_catn(d, p, cl)
198 d[p] = 125 as u8; p = p + 1
199 shown = shown + 1
200 }
201 }
202 pass = pass + 1
203 }
204 d[p] = 93 as u8; p = p + 1
205 d[p] = 44 as u8; p = p + 1
206 p = bm_key(d, p, "tags_shown" as *u8); p = sj_catn(d, p, shown); d[p] = 44 as u8; p = p + 1
207 p = bm_key(d, p, "tags_capped" as *u8); p = sj_catn(d, p, capped)
208 d[p] = 125 as u8; p = p + 1
209 d[p] = 10 as u8; p = p + 1
210 return p
211}
212// bm_word_after: the non-space word immediately after <lit> in q[s..e) -> dp[0]/dp[1]; 1 if found.
213func bm_word_after(q: *u8, s: i64, e: i64, lit: *u8, dp: *i64) -> i64 {
214 let ll: i64 = sj_vlen(lit)
215 if ll == 0 { return 0 }
216 var i: i64 = s
217 while i + ll <= e {
218 var k: i64 = 0
219 var ok: i64 = 1
220 while k < ll { if q[i+k] != lit[k] { ok = 0; k = ll } else { k = k + 1 } }
221 if ok == 1 {
222 var ws: i64 = i + ll
223 var g1: i64 = 1
224 while g1 == 1 { if ws >= e { g1 = 0 } else { if q[ws] == (32 as u8) { ws = ws + 1 } else { g1 = 0 } } }
225 var we: i64 = ws
226 var g2: i64 = 1
227 while g2 == 1 { if we >= e { g2 = 0 } else { if q[we] == (32 as u8) { g2 = 0 } else { we = we + 1 } } }
228 if we > ws { dp[0] = ws; dp[1] = we; return 1 }
229 return 0
230 }
231 i = i + 1
232 }
233 return 0
234}
235// fleet: the FLEET-ORGAN supervisor log (daemon_supervisor.log) -- richer than the reader-keep log:
236// per-daemon PROBE failures (HUNG=up-but-not-serving, REFUSED=down) + WEDGE-KILL events. A daemon
237// chronically HUNG/REFUSED >= minfail is SICK. This catches the SECOND boot-layer instability the
238// reader-keep miner cannot see (e.g. the nx_mgmt_api / docportal / vroom flap that disrupts ops).
239func bm_fleet_json(logf: *u8, minfail: i64, d: *u8) -> i64 {
240 let q: *u8 = sys_mmap(BM_WIN)
241 let cap: i64 = BM_WIN - 8
242 let n: i64 = bm_read(logf, q, cap)
243 if n < 0 { return -1 }
244 var trunc: i64 = 0
245 if n >= cap { trunc = 1 }
246 let ds: *i64 = sys_mmap(8 * BM_TAGS) as *i64
247 let de: *i64 = sys_mmap(8 * BM_TAGS) as *i64
248 let dhung: *i64 = sys_mmap(8 * BM_TAGS) as *i64
249 let dref: *i64 = sys_mmap(8 * BM_TAGS) as *i64
250 let dwedge: *i64 = sys_mmap(8 * BM_TAGS) as *i64
251 let done: *i64 = sys_mmap(8 * BM_TAGS) as *i64
252 let np: *i64 = sys_mmap(16) as *i64
253 let dp: *i64 = sys_mmap(16) as *i64
254 var lines: i64 = 0
255 var thung: i64 = 0
256 var tref: i64 = 0
257 var twedge: i64 = 0
258 var capped: i64 = 0
259 var i: i64 = 0
260 while i < n {
261 let le: i64 = sj_le(q, i, n)
262 if le > i {
263 lines = lines + 1
264 let isw: i64 = bm_has(q, i, le, "WEDGE-KILL" as *u8)
265 let ish: i64 = bm_has(q, i, le, "probe=HUNG" as *u8)
266 let isr: i64 = bm_has(q, i, le, "probe=REFUSED" as *u8)
267 var got: i64 = 0
268 if isw == 1 { got = bm_word_after(q, i, le, "WEDGE-KILL" as *u8, dp) } else {
269 if ish + isr > 0 { got = bm_word_after(q, i, le, "supervisor:" as *u8, dp) }
270 }
271 if got == 1 {
272 let di: i64 = bm_intern(q, dp[0], dp[1], ds, de, np)
273 if di < 0 { capped = 1 } else {
274 if isw == 1 { dwedge[di] = dwedge[di] + 1; twedge = twedge + 1 }
275 if isw == 0 { if ish == 1 { dhung[di] = dhung[di] + 1; thung = thung + 1 } }
276 if isw == 0 { if isr == 1 { dref[di] = dref[di] + 1; tref = tref + 1 } }
277 }
278 }
279 }
280 i = le + 1
281 }
282 var sick: i64 = 0
283 var k: i64 = 0
284 while k < np[0] { if dhung[k] + dref[k] >= minfail { sick = sick + 1 } k = k + 1 }
285 var p: i64 = 0
286 d[p] = 123 as u8; p = p + 1
287 p = bm_key(d, p, "v" as *u8); p = sj_catn(d, p, 1); d[p] = 44 as u8; p = p + 1
288 p = bm_key(d, p, "tool" as *u8); d[p]=34 as u8;p=p+1; p = sj_cat(d,p,"nx_bootmine" as *u8); d[p]=34 as u8;p=p+1; d[p]=44 as u8;p=p+1
289 p = bm_key(d, p, "averb" as *u8); d[p]=34 as u8;p=p+1; p = sj_cat(d,p,"fleet" as *u8); d[p]=34 as u8;p=p+1; d[p]=44 as u8;p=p+1
290 p = bm_key(d, p, "log" as *u8); d[p]=34 as u8;p=p+1; p = sj_cat_esc(d,p,logf,0,sj_vlen(logf),BM_NAME_MAX); d[p]=34 as u8;p=p+1; d[p]=44 as u8;p=p+1
291 p = bm_key(d, p, "log_bytes" as *u8); p = sj_catn(d,p,n); d[p]=44 as u8;p=p+1
292 p = bm_key(d, p, "truncated" as *u8); p = sj_catn(d,p,trunc); d[p]=44 as u8;p=p+1
293 p = bm_key(d, p, "minfail" as *u8); p = sj_catn(d,p,minfail); d[p]=44 as u8;p=p+1
294 p = bm_key(d, p, "lines" as *u8); p = sj_catn(d,p,lines); d[p]=44 as u8;p=p+1
295 p = bm_key(d, p, "hung" as *u8); p = sj_catn(d,p,thung); d[p]=44 as u8;p=p+1
296 p = bm_key(d, p, "refused" as *u8); p = sj_catn(d,p,tref); d[p]=44 as u8;p=p+1
297 p = bm_key(d, p, "wedge_kills" as *u8); p = sj_catn(d,p,twedge); d[p]=44 as u8;p=p+1
298 p = bm_key(d, p, "daemons" as *u8); p = sj_catn(d,p,np[0]); d[p]=44 as u8;p=p+1
299 p = bm_key(d, p, "sick_daemons" as *u8); p = sj_catn(d,p,sick); d[p]=44 as u8;p=p+1
300 p = bm_key(d, p, "stability" as *u8); d[p]=34 as u8;p=p+1
301 if sick == 0 { if twedge == 0 { p = sj_cat(d,p,"STABLE" as *u8) } else { p = sj_cat(d,p,"DEGRADED" as *u8) } } else { p = sj_cat(d,p,"UNSTABLE" as *u8) }
302 d[p]=34 as u8;p=p+1; d[p]=44 as u8;p=p+1
303 p = bm_key(d, p, "by_daemon" as *u8)
304 d[p] = 91 as u8; p = p + 1
305 var shown: i64 = 0
306 var efirst: i64 = 1
307 var pass: i64 = 0
308 while pass < np[0] {
309 var mi: i64 = 0 - 1
310 var mx: i64 = 0 - 1
311 var k2: i64 = 0
312 while k2 < np[0] {
313 if done[k2] == 0 { let sc: i64 = dhung[k2] + dref[k2] + dwedge[k2]; if sc > mx { mx = sc; mi = k2 } }
314 k2 = k2 + 1
315 }
316 if mi >= 0 {
317 done[mi] = 1
318 if shown < BM_TOPN {
319 if efirst == 0 { d[p] = 44 as u8; p = p + 1 }
320 efirst = 0
321 d[p] = 123 as u8; p = p + 1
322 p = bm_key(d, p, "daemon" as *u8); d[p]=34 as u8;p=p+1; p = sj_cat_esc(d,p,q,ds[mi],de[mi],BM_NAME_MAX); d[p]=34 as u8;p=p+1; d[p]=44 as u8;p=p+1
323 p = bm_key(d, p, "hung" as *u8); p = sj_catn(d,p,dhung[mi]); d[p]=44 as u8;p=p+1
324 p = bm_key(d, p, "refused" as *u8); p = sj_catn(d,p,dref[mi]); d[p]=44 as u8;p=p+1
325 p = bm_key(d, p, "wedge_kills" as *u8); p = sj_catn(d,p,dwedge[mi]); d[p]=44 as u8;p=p+1
326 var sk: i64 = 0
327 if dhung[mi] + dref[mi] >= minfail { sk = 1 }
328 p = bm_key(d, p, "sick" as *u8); p = sj_catn(d,p,sk)
329 d[p] = 125 as u8; p = p + 1
330 shown = shown + 1
331 }
332 }
333 pass = pass + 1
334 }
335 d[p] = 93 as u8; p = p + 1
336 d[p] = 44 as u8; p = p + 1
337 p = bm_key(d, p, "daemons_shown" as *u8); p = sj_catn(d,p,shown); d[p]=44 as u8;p=p+1
338 p = bm_key(d, p, "daemons_capped" as *u8); p = sj_catn(d,p,capped)
339 d[p] = 125 as u8; p = p + 1
340 d[p] = 10 as u8; p = p + 1
341 return p
342}
343func bm_selftest(prefix: *u8) -> i64 {
344 let ctr: *i64 = gv_ctr()
345 gv_head("nx_bootmine selftest -- crash-loop detection over the supervisor log (a stable boot is SOTA)" as *u8)
346 let fx: *u8 = sys_mmap(BM_PATH)
347 var o: i64 = sj_cat(fx, 0, prefix)
348 o = sj_catn(fx, o, sys_now_realtime_sec())
349 o = sj_cat(fx, o, ".bm" as *u8)
350 fx[o] = 0 as u8
351 let miss: *u8 = sys_mmap(BM_PATH)
352 o = sj_cat(miss, 0, fx)
353 o = sj_cat(miss, o, ".absent" as *u8)
354 miss[o] = 0 as u8
355 // [reader-keep] crash-loops (12 respawns), [guard] restarts once (stable), a non-event line
356 let fd: i64 = sys_openat_wr(fx, BM_MODE_644)
357 let lb: *u8 = sys_mmap(BM_MAGIC_8192)
358 var lo: i64 = 0
359 var r: i64 = 0
360 while r < 12 {
361 lo = sj_cat(lb, lo, " [reader-keep] spawned reader pid=1\n" as *u8)
362 lo = sj_cat(lb, lo, " [reader-keep] reader pid=1 died sig=0 code=1 -> respawn\n" as *u8)
363 r = r + 1
364 }
365 lo = sj_cat(lb, lo, " [guard] restarted nx_torrent_daemon :8097 (was dead)\n" as *u8)
366 lo = sj_cat(lb, lo, "[hostctl] sovereign supervisor: PID-liveness\n" as *u8)
367 sys_write(fd, lb, lo)
368 sys_close(fd)
369 let d: *u8 = sys_mmap(BM_OUT)
370 // T1 missing log fails closed
371 let r1: i64 = bm_mine_json(miss, BM_MINLOOP, d)
372 var ok1: i64 = 0
373 if r1 == (0 - 1) { ok1 = 1 }
374 gv_check("T1 missing log refused (-1) fail closed" as *u8, ok1, ctr)
375 let dl: i64 = bm_mine_json(fx, BM_MINLOOP, d)
376 var ok2: i64 = 0
377 if dl > 0 { if bm_find(d, dl, "\"respawns\":12,\"guard_restarts\":1" as *u8) == 1 { if bm_find(d, dl, "\"spawns\":12" as *u8) == 1 { ok2 = 1 } } }
378 gv_check("T2 exact tallies: 12 respawns, 12 spawns, 1 guard restart" as *u8, ok2, ctr)
379 var ok3: i64 = 0
380 if bm_find(d, dl, "\"tag\":\"reader-keep\",\"respawns\":12,\"spawns\":12,\"guard_restarts\":0,\"crash_looping\":1" as *u8) == 1 { ok3 = 1 }
381 gv_check("T3 reader-keep tag is CRASH-LOOPING (12 respawns >= minloop 10)" as *u8, ok3, ctr)
382 var ok4: i64 = 0
383 if bm_find(d, dl, "\"tag\":\"guard\",\"respawns\":0,\"spawns\":0,\"guard_restarts\":1,\"crash_looping\":0" as *u8) == 1 { ok4 = 1 }
384 gv_check("T4 guard is NOT crash-looping (1 restart, 0 respawns) -- a restart is not a loop" as *u8, ok4, ctr)
385 var ok5: i64 = 0
386 if bm_find(d, dl, "\"crash_loops\":1,\"stability\":\"UNSTABLE\"" as *u8) == 1 { ok5 = 1 }
387 gv_check("T5 one crash loop => UNSTABLE verdict" as *u8, ok5, ctr)
388 // T6 raise minloop above 12 => the same log reads STABLE (threshold is data-driven, honest)
389 let dl6: i64 = bm_mine_json(fx, 99, d)
390 var ok6: i64 = 0
391 if dl6 > 0 { if bm_find(d, dl6, "\"crash_loops\":0,\"stability\":\"STABLE\"" as *u8) == 1 { ok6 = 1 } }
392 gv_check("T6 minloop 99 => 0 crash loops STABLE (threshold data-driven, not baked)" as *u8, ok6, ctr)
393 // T7 tags counted (2: reader-keep, guard); hostctl line has a tag but no event => not counted
394 var ok7: i64 = 0
395 if bm_find(d, dl, "\"tags\":2" as *u8) == 1 { if bm_find(d, dl, "\"tag\":\"hostctl\"" as *u8) == 0 { ok7 = 1 } }
396 gv_check("T7 only event-bearing tags counted (hostctl no-event line excluded)" as *u8, ok7, ctr)
397 // T8/T9 FLEET: the daemon_supervisor.log format (probe=HUNG/REFUSED + WEDGE-KILL per daemon)
398 let ff: *u8 = sys_mmap(BM_PATH)
399 var o8: i64 = sj_cat(ff, 0, prefix)
400 o8 = sj_catn(ff, o8, sys_now_realtime_sec())
401 o8 = sj_cat(ff, o8, ".ff" as *u8)
402 ff[o8] = 0 as u8
403 let ffd: i64 = sys_openat_wr(ff, BM_MODE_644)
404 var fo: i64 = sj_cat(lb, 0, "supervisor: docportal_admin_daemon probe=HUNG(up-but-not-serving) fails=1\n" as *u8)
405 fo = sj_cat(lb, fo, "supervisor: docportal_admin_daemon probe=HUNG(up-but-not-serving) fails=2\n" as *u8)
406 fo = sj_cat(lb, fo, "supervisor: docportal_admin_daemon probe=HUNG(up-but-not-serving) fails=3\n" as *u8)
407 fo = sj_cat(lb, fo, "supervisor: vroom_daemon probe=REFUSED(down) fails=1\n" as *u8)
408 fo = sj_cat(lb, fo, "supervisor: WEDGE-KILL reader killed=0 (relaunch: primary owner, else backstop)\n" as *u8)
409 fo = sj_cat(lb, fo, "supervisor: crash-loop guard armed\n" as *u8)
410 sys_write(ffd, lb, fo)
411 sys_close(ffd)
412 let fdl: i64 = bm_fleet_json(ff, 3, d)
413 var ok8: i64 = 0
414 if fdl > 0 { if bm_find(d, fdl, "\"hung\":3,\"refused\":1,\"wedge_kills\":1,\"daemons\":3" as *u8) == 1 { if bm_find(d, fdl, "\"daemon\":\"docportal_admin_daemon\",\"hung\":3,\"refused\":0,\"wedge_kills\":0,\"sick\":1" as *u8) == 1 { ok8 = 1 } } }
415 gv_check("T8 fleet: docportal 3 HUNG = SICK, vroom 1 REFUSED, reader 1 WEDGE-KILL, 3 daemons" as *u8, ok8, ctr)
416 var ok9: i64 = 0
417 if fdl > 0 { if bm_find(d, fdl, "\"sick_daemons\":1,\"stability\":\"UNSTABLE\"" as *u8) == 1 { if bm_find(d, fdl, "\"daemon\":\"reader\",\"hung\":0,\"refused\":0,\"wedge_kills\":1,\"sick\":0" as *u8) == 1 { ok9 = 1 } } }
418 gv_check("T9 fleet: 1 sick => UNSTABLE; a WEDGE-KILL-only daemon (reader) is not itself sick (no probe fails)" as *u8, ok9, ctr)
419 let rc: i64 = gv_verdict("BOOTMINE-GATE" as *u8, ctr, "boot stability: crash-loop + fleet probe-failure/wedge-kill detection, data-driven thresholds" as *u8)
420 return rc
421}
422
423func main(argc: i64, argv: *i64) -> i64 {
424 if argc < 3 { sj_puts("usage: nx_bootmine {mine <logfile> [minloop] | fleet <daemon_supervisor.log> [minfail] | selftest <scratch-prefix>}\n" as *u8); sys_exit(2); return 2 }
425 let verb: *u8 = argv[1] as *u8
426 let vl: i64 = sj_vlen(verb)
427 if sj_lit_eq(verb, 0, vl, "selftest" as *u8) == 1 {
428 let rc: i64 = bm_selftest(argv[2] as *u8)
429 sys_exit(rc)
430 return rc
431 }
432 if sj_lit_eq(verb, 0, vl, "mine" as *u8) == 1 {
433 var minloop: i64 = BM_MINLOOP
434 if argc >= 4 { let mz: i64 = sj_atoi_z(argv[3] as *u8); if mz > 0 { minloop = mz } }
435 let d: *u8 = sys_mmap(BM_OUT)
436 let dl: i64 = bm_mine_json(argv[2] as *u8, minloop, d)
437 if dl < 0 { sj_puts("REFUSED log missing or empty\n" as *u8); sys_exit(3); return 3 }
438 sys_write(1, d, dl)
439 sys_exit(0)
440 return 0
441 }
442 if sj_lit_eq(verb, 0, vl, "fleet" as *u8) == 1 {
443 var minfail: i64 = BM_MINLOOP
444 if argc >= 4 { let mz: i64 = sj_atoi_z(argv[3] as *u8); if mz > 0 { minfail = mz } }
445 let d: *u8 = sys_mmap(BM_OUT)
446 let dl: i64 = bm_fleet_json(argv[2] as *u8, minfail, d)
447 if dl < 0 { sj_puts("REFUSED log missing or empty\n" as *u8); sys_exit(3); return 3 }
448 sys_write(1, d, dl)
449 sys_exit(0)
450 return 0
451 }
452 sj_puts("unknown verb\n" as *u8)
453 sys_exit(2)
454 return 2
455}