code wiki / _hdl_build / nx_debtmine.nx

nx_debtmine.nx source

↩ module page · 393 lines · 18127 B

1// nx_debtmine.nx -- BUGS-CLASS MINER over the debt- plane (miner-sota F1009/MS06a, 2026-07-23). 2// The census (nx_minecov MC06) measured 661 debt rows COVERED but UNMINED: listed, never analysed. 3// This mines the management signals a list cannot give: WHERE debt concentrates (per-scope open 4// counts), WHICH lanes actually eat it (eat_permil = eaten/(open+eaten) per scope -- low eat-rate 5// with high open = rot), severity distribution, and AGE (ids ARE epochs, so open-debt age buckets 6// and oldest_open are derivable). Source is a seg-store plane loaded through the ALLOWLIST-RESOLVED 7// loader, or a FILE when loader is "-" (deterministic gate fixtures; live uses nx_store_put). 8// now is INJECTABLE so aging is bit-deterministic under test. Composes nx_sovjson_lib (sj_*). 9// Read-only. No hw writes (Rule 26). 10// mine <source> [loader] [minsup] [now] -> JSON: totals, by_scope ranked, sev dist, aging 11// selftest <scratch-prefix> -> gate T1..T8 12// plane row cols: id(epoch) TAB sev TAB scope TAB status(open|eaten) TAB desc 13// license_tier: ORIGINAL 14import "nx_sovjson_lib.nx" 15import "nx_syscalls.nx" 16import "nx_gate_verdict.nx" 17import "nx_tool_run.nx" 18const DM_MAGIC_1785000000: i64 = 1785000000 19const DM_MAGIC_4088: i64 = 4088 20 21const DM_CAP: i64 = 4194304 22const DM_OUT: i64 = 65536 23const DM_PATH: i64 = 512 24const DM_SCOPES: i64 = 256 25const DM_TOPN: i64 = 20 26const DM_ALLOW: i64 = 131072 27const DM_DAY: i64 = 86400 28const DM_PERMIL: i64 = 1000 29const DM_SEVN: i64 = 12 30const DM_NAME_MAX: i64 = 120 31const DM_MODE_644: i64 = 420 32 33func dm_read(path: *u8, buf: *u8, cap: i64) -> i64 { 34 let fd: i64 = sys_openat_rd(path) 35 if fd < 0 { return -1 } 36 var n: i64 = 0 37 var go: i64 = 1 38 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 } } 39 sys_close(fd) 40 return n 41} 42func dm_find(q: *u8, n: i64, lit: *u8) -> i64 { 43 let ll: i64 = sj_vlen(lit) 44 if ll == 0 { return 0 } 45 var i: i64 = 0 46 while i + ll <= n { 47 var k: i64 = 0 48 var ok: i64 = 1 49 while k < ll { if q[i+k] != lit[k] { ok = 0; k = ll } else { k = k + 1 } } 50 if ok == 1 { return 1 } 51 i = i + 1 52 } 53 return 0 54} 55// DELEGATES to the shared base (rule-15 extraction 2026-07-23) 56func dm_allow_path(name: *u8, outp: *u8) -> i64 { return sj_allow_path(name, outp) } 57func dm_key(d: *u8, o: i64, name: *u8) -> i64 { 58 d[o] = 34 as u8 59 var p: i64 = o + 1 60 p = sj_cat(d, p, name) 61 d[p] = 34 as u8 62 p = p + 1 63 d[p] = 58 as u8 64 p = p + 1 65 return p 66} 67func dm_digits(q: *u8, s: i64, e: i64) -> i64 { 68 if e <= s { return 0 } 69 var i: i64 = s 70 while i < e { let c: i64 = q[i] as i64; if c < 48 { return 0 } if c > 57 { return 0 } i = i + 1 } 71 return 1 72} 73// load the source: loader "-" => read the file directly; else fork <loader> <source> load. 74// scx[0]=bytes scx[1]=truncated scx[2]=loader_resolved 75func dm_load(src: *u8, loader: *u8, buf: *u8, scx: *i64) -> i64 { 76 scx[0] = 0 77 scx[1] = 0 78 scx[2] = 0 79 if sj_lit_eq(loader, 0, sj_vlen(loader), "-" as *u8) == 1 { 80 let n: i64 = dm_read(src, buf, DM_CAP - 8) 81 if n < 0 { return -1 } 82 scx[0] = n 83 if n >= DM_CAP - 8 { scx[1] = 1 } 84 return 0 85 } 86 let lp: *u8 = sys_mmap(DM_PATH) 87 if dm_allow_path(loader, lp) == 0 { return -2 } 88 scx[2] = 1 89 let av: *i64 = sys_mmap(64) as *i64 90 av[0] = lp as i64 91 av[1] = src as i64 92 av[2] = "load" as *u8 as i64 93 av[3] = 0 94 let cl: *i64 = sys_mmap(16) as *i64 95 let ex: i64 = tr_run_capture(lp, av, buf, DM_CAP - 8, cl) 96 if ex != 0 { return -3 } 97 scx[0] = cl[0] 98 if cl[0] >= DM_CAP - 8 { scx[1] = 1 } 99 return 0 100} 101func dm_mine_json(src: *u8, loader: *u8, minsup: i64, now: i64, d: *u8) -> i64 { 102 let q: *u8 = sys_mmap(DM_CAP) 103 let scx: *i64 = sys_mmap(64) as *i64 104 let lr: i64 = dm_load(src, loader, q, scx) 105 if lr < 0 { return lr } 106 let n: i64 = scx[0] 107 if n <= 0 { return -1 } 108 let ss: *i64 = sys_mmap(8 * DM_SCOPES) as *i64 109 let se: *i64 = sys_mmap(8 * DM_SCOPES) as *i64 110 let sopen: *i64 = sys_mmap(8 * DM_SCOPES) as *i64 111 let seat: *i64 = sys_mmap(8 * DM_SCOPES) as *i64 112 let sevo: *i64 = sys_mmap(8 * DM_SEVN) as *i64 113 let done: *i64 = sys_mmap(8 * DM_SCOPES) as *i64 114 let sp: *i64 = sys_mmap(16) as *i64 115 var nsc: i64 = 0 116 var scapped: i64 = 0 117 var rows: i64 = 0 118 var open: i64 = 0 119 var eaten: i64 = 0 120 var malformed: i64 = 0 121 var b0: i64 = 0 122 var b1: i64 = 0 123 var b2: i64 = 0 124 var b3: i64 = 0 125 var oldest: i64 = 0 126 var i: i64 = 0 127 while i < n { 128 let le: i64 = sj_le(q, i, n) 129 var ok: i64 = 0 130 var cmt: i64 = 0 131 if le > i { if q[i] == (35 as u8) { cmt = 1 } } 132 if le > i { if cmt == 0 { if sj_col(q, i, le, 3, sp) == 1 { ok = 1 } } } 133 if ok == 0 { if le > i { if cmt == 0 { malformed = malformed + 1 } } } else { 134 rows = rows + 1 135 var isopen: i64 = 0 136 if sj_lit_eq(q, sp[0], sp[1], "open" as *u8) == 1 { isopen = 1 } 137 if isopen == 1 { open = open + 1 } else { eaten = eaten + 1 } 138 // scope intern (col 2) 139 sj_col(q, i, le, 2, sp) 140 let cs: i64 = sp[0] 141 let ce: i64 = sp[1] 142 var idx: i64 = 0 - 1 143 var k: i64 = 0 144 while k < nsc { 145 if ce - cs == se[k] - ss[k] { 146 var m: i64 = 0 147 var eq: i64 = 1 148 while m < ce - cs { if q[cs+m] != q[ss[k]+m] { eq = 0; m = ce - cs } else { m = m + 1 } } 149 if eq == 1 { idx = k; k = nsc } 150 } 151 k = k + 1 152 } 153 if idx < 0 { 154 if nsc >= DM_SCOPES { scapped = 1 } else { 155 ss[nsc] = cs 156 se[nsc] = ce 157 idx = nsc 158 nsc = nsc + 1 159 } 160 } 161 if idx >= 0 { 162 if isopen == 1 { sopen[idx] = sopen[idx] + 1 } else { seat[idx] = seat[idx] + 1 } 163 } 164 // severity (col 1), open only 165 if isopen == 1 { 166 sj_col(q, i, le, 1, sp) 167 let sv: i64 = sj_atoi_span(q, sp[0], sp[1]) 168 if sv >= 0 { if sv < DM_SEVN { sevo[sv] = sevo[sv] + 1 } } 169 // aging from the id-epoch (col 0), open only 170 sj_col(q, i, le, 0, sp) 171 if dm_digits(q, sp[0], sp[1]) == 1 { 172 let ep: i64 = sj_atoi_span(q, sp[0], sp[1]) 173 if ep > 0 { 174 if oldest == 0 { oldest = ep } else { if ep < oldest { oldest = ep } } 175 let age: i64 = now - ep 176 if age < DM_DAY { b0 = b0 + 1 } else { 177 if age < 7 * DM_DAY { b1 = b1 + 1 } else { 178 if age < 30 * DM_DAY { b2 = b2 + 1 } else { b3 = b3 + 1 } 179 } 180 } 181 } 182 } 183 } 184 } 185 i = le + 1 186 } 187 var p: i64 = 0 188 d[p] = 123 as u8; p = p + 1 189 p = dm_key(d, p, "v" as *u8); p = sj_catn(d, p, 1); d[p] = 44 as u8; p = p + 1 190 p = dm_key(d, p, "tool" as *u8) 191 d[p] = 34 as u8; p = p + 1 192 p = sj_cat(d, p, "nx_debtmine" as *u8) 193 d[p] = 34 as u8; p = p + 1 194 d[p] = 44 as u8; p = p + 1 195 p = dm_key(d, p, "averb" as *u8) 196 d[p] = 34 as u8; p = p + 1 197 p = sj_cat(d, p, "mine" as *u8) 198 d[p] = 34 as u8; p = p + 1 199 d[p] = 44 as u8; p = p + 1 200 p = dm_key(d, p, "source" as *u8) 201 d[p] = 34 as u8; p = p + 1 202 p = sj_cat_esc(d, p, src, 0, sj_vlen(src), DM_NAME_MAX) 203 d[p] = 34 as u8; p = p + 1 204 d[p] = 44 as u8; p = p + 1 205 p = dm_key(d, p, "loader_resolved" as *u8); p = sj_catn(d, p, scx[2]); d[p] = 44 as u8; p = p + 1 206 p = dm_key(d, p, "bytes" as *u8); p = sj_catn(d, p, n); d[p] = 44 as u8; p = p + 1 207 p = dm_key(d, p, "truncated" as *u8); p = sj_catn(d, p, scx[1]); d[p] = 44 as u8; p = p + 1 208 p = dm_key(d, p, "minsup" as *u8); p = sj_catn(d, p, minsup); d[p] = 44 as u8; p = p + 1 209 p = dm_key(d, p, "rows" as *u8); p = sj_catn(d, p, rows); d[p] = 44 as u8; p = p + 1 210 p = dm_key(d, p, "open" as *u8); p = sj_catn(d, p, open); d[p] = 44 as u8; p = p + 1 211 p = dm_key(d, p, "eaten" as *u8); p = sj_catn(d, p, eaten); d[p] = 44 as u8; p = p + 1 212 p = dm_key(d, p, "malformed" as *u8); p = sj_catn(d, p, malformed); d[p] = 44 as u8; p = p + 1 213 p = dm_key(d, p, "scopes" as *u8); p = sj_catn(d, p, nsc); d[p] = 44 as u8; p = p + 1 214 var eatp: i64 = 0 - 1 215 if rows > 0 { eatp = eaten * DM_PERMIL / rows } 216 p = dm_key(d, p, "eat_permil" as *u8); p = sj_catn(d, p, eatp); d[p] = 44 as u8; p = p + 1 217 // by_scope ranked by OPEN desc (bounded selection, capped + declared) 218 p = dm_key(d, p, "by_scope" as *u8) 219 d[p] = 91 as u8; p = p + 1 220 var shown: i64 = 0 221 var efirst: i64 = 1 222 var below: i64 = 0 223 var pass: i64 = 0 224 while pass < nsc { 225 var mi: i64 = 0 - 1 226 var mx: i64 = 0 - 1 227 var k2: i64 = 0 228 while k2 < nsc { 229 if done[k2] == 0 { if sopen[k2] > mx { mx = sopen[k2]; mi = k2 } } 230 k2 = k2 + 1 231 } 232 if mi >= 0 { 233 done[mi] = 1 234 if mx < minsup { below = below + 1 } else { 235 if shown < DM_TOPN { 236 if efirst == 0 { d[p] = 44 as u8; p = p + 1 } 237 efirst = 0 238 d[p] = 123 as u8; p = p + 1 239 p = dm_key(d, p, "scope" as *u8) 240 d[p] = 34 as u8; p = p + 1 241 p = sj_cat_esc(d, p, q, ss[mi], se[mi], DM_NAME_MAX) 242 d[p] = 34 as u8; p = p + 1 243 d[p] = 44 as u8; p = p + 1 244 p = dm_key(d, p, "open" as *u8); p = sj_catn(d, p, sopen[mi]); d[p] = 44 as u8; p = p + 1 245 p = dm_key(d, p, "eaten" as *u8); p = sj_catn(d, p, seat[mi]); d[p] = 44 as u8; p = p + 1 246 var sep: i64 = 0 - 1 247 let tot: i64 = sopen[mi] + seat[mi] 248 if tot > 0 { sep = seat[mi] * DM_PERMIL / tot } 249 p = dm_key(d, p, "eat_permil" as *u8); p = sj_catn(d, p, sep) 250 d[p] = 125 as u8; p = p + 1 251 shown = shown + 1 252 } 253 } 254 } 255 pass = pass + 1 256 } 257 d[p] = 93 as u8; p = p + 1 258 d[p] = 44 as u8; p = p + 1 259 p = dm_key(d, p, "scopes_shown" as *u8); p = sj_catn(d, p, shown); d[p] = 44 as u8; p = p + 1 260 p = dm_key(d, p, "scopes_below_minsup" as *u8); p = sj_catn(d, p, below); d[p] = 44 as u8; p = p + 1 261 p = dm_key(d, p, "scopes_capped" as *u8); p = sj_catn(d, p, scapped); d[p] = 44 as u8; p = p + 1 262 // severity distribution of OPEN debt 263 p = dm_key(d, p, "sev_open" as *u8) 264 d[p] = 91 as u8; p = p + 1 265 var sv2: i64 = 0 266 var sfirst: i64 = 1 267 while sv2 < DM_SEVN { 268 if sevo[sv2] > 0 { 269 if sfirst == 0 { d[p] = 44 as u8; p = p + 1 } 270 sfirst = 0 271 d[p] = 123 as u8; p = p + 1 272 p = dm_key(d, p, "sev" as *u8); p = sj_catn(d, p, sv2); d[p] = 44 as u8; p = p + 1 273 p = dm_key(d, p, "open" as *u8); p = sj_catn(d, p, sevo[sv2]) 274 d[p] = 125 as u8; p = p + 1 275 } 276 sv2 = sv2 + 1 277 } 278 d[p] = 93 as u8; p = p + 1 279 d[p] = 44 as u8; p = p + 1 280 // aging of OPEN debt (ids are epochs) 281 p = dm_key(d, p, "aging_open" as *u8) 282 d[p] = 123 as u8; p = p + 1 283 p = dm_key(d, p, "lt_1d" as *u8); p = sj_catn(d, p, b0); d[p] = 44 as u8; p = p + 1 284 p = dm_key(d, p, "d1_7" as *u8); p = sj_catn(d, p, b1); d[p] = 44 as u8; p = p + 1 285 p = dm_key(d, p, "d7_30" as *u8); p = sj_catn(d, p, b2); d[p] = 44 as u8; p = p + 1 286 p = dm_key(d, p, "gt_30d" as *u8); p = sj_catn(d, p, b3); d[p] = 44 as u8; p = p + 1 287 // DECLARED: rows whose id is not an epoch contribute no age -- never silently dropped 288 p = dm_key(d, p, "counted" as *u8); p = sj_catn(d, p, b0 + b1 + b2 + b3); d[p] = 44 as u8; p = p + 1 289 p = dm_key(d, p, "no_epoch_id" as *u8); p = sj_catn(d, p, open - (b0 + b1 + b2 + b3)) 290 d[p] = 125 as u8; p = p + 1 291 d[p] = 44 as u8; p = p + 1 292 var oldd: i64 = 0 - 1 293 if oldest > 0 { oldd = (now - oldest) / DM_DAY } 294 p = dm_key(d, p, "oldest_open_days" as *u8); p = sj_catn(d, p, oldd) 295 d[p] = 125 as u8; p = p + 1 296 d[p] = 10 as u8; p = p + 1 297 return p 298} 299func dm_selftest(prefix: *u8) -> i64 { 300 let ctr: *i64 = gv_ctr() 301 gv_head("nx_debtmine selftest -- bugs-class mining teeth (exact counts, honest eat-rate, deterministic aging)" as *u8) 302 let now: i64 = DM_MAGIC_1785000000 303 let fx: *u8 = sys_mmap(DM_PATH) 304 var o: i64 = sj_cat(fx, 0, prefix) 305 o = sj_catn(fx, o, sys_now_realtime_sec()) 306 o = sj_cat(fx, o, ".dm" as *u8) 307 fx[o] = 0 as u8 308 let miss: *u8 = sys_mmap(DM_PATH) 309 o = sj_cat(miss, 0, fx) 310 o = sj_cat(miss, o, ".absent" as *u8) 311 miss[o] = 0 as u8 312 // fixture: alpha 3 open (sev 8,8,5) + 1 eaten ; beta 1 open ; gamma 0 open 2 eaten ; 1 malformed 313 // ages vs now=1785000000: 1784990000 (~2.8h <1d) 1784800000 (~2.3d) 1784200000 (~9.3d) 1782000000 (~34.7d) 314 let fd: i64 = sys_openat_wr(fx, DM_MODE_644) 315 let lb: *u8 = sys_mmap(DM_MAGIC_4088) 316 var lo: i64 = sj_cat(lb, 0, "# fixture\n" as *u8) 317 lo = sj_cat(lb, lo, "1784990000\t8\talpha\topen\tfresh one\n" as *u8) 318 lo = sj_cat(lb, lo, "1784800000\t8\talpha\topen\ttwo days\n" as *u8) 319 lo = sj_cat(lb, lo, "1784200000\t5\talpha\topen\tnine days\n" as *u8) 320 lo = sj_cat(lb, lo, "1784100000\t5\talpha\teaten\tdone one\n" as *u8) 321 lo = sj_cat(lb, lo, "1782000000\t9\tbeta\topen\tthirty five days\n" as *u8) 322 lo = sj_cat(lb, lo, "1784000000\t3\tgamma\teaten\tdone two\n" as *u8) 323 lo = sj_cat(lb, lo, "1784000001\t3\tgamma\teaten\tdone three\n" as *u8) 324 lo = sj_cat(lb, lo, "nocolumns\n" as *u8) 325 sys_write(fd, lb, lo) 326 sys_close(fd) 327 let d: *u8 = sys_mmap(DM_OUT) 328 // T1 missing source fails closed 329 let r1: i64 = dm_mine_json(miss, "-" as *u8, 1, now, d) 330 var ok1: i64 = 0 331 if r1 < 0 { ok1 = 1 } 332 gv_check("T1 missing source refused fail closed" as *u8, ok1, ctr) 333 let dl: i64 = dm_mine_json(fx, "-" as *u8, 1, now, d) 334 var ok2: i64 = 0 335 if dl > 0 { if dm_find(d, dl, "\"rows\":7,\"open\":4,\"eaten\":3" as *u8) == 1 { ok2 = 1 } } 336 gv_check("T2 exact totals rows 7 open 4 eaten 3 (comment skipped)" as *u8, ok2, ctr) 337 var ok3: i64 = 0 338 if dm_find(d, dl, "\"malformed\":1" as *u8) == 1 { if dm_find(d, dl, "\"scopes\":3" as *u8) == 1 { ok3 = 1 } } 339 gv_check("T3 malformed row COUNTED not silently dropped; 3 distinct scopes" as *u8, ok3, ctr) 340 var ok4: i64 = 0 341 if dm_find(d, dl, "\"scope\":\"alpha\",\"open\":3,\"eaten\":1,\"eat_permil\":250" as *u8) == 1 { ok4 = 1 } 342 gv_check("T4 top scope by open with EXACT per-scope eat_permil 250" as *u8, ok4, ctr) 343 // minsup 0 = show every scope; separate buffer so the T6/T7 assertions keep reading the minsup-1 run 344 let d5: *u8 = sys_mmap(DM_OUT) 345 let dl5: i64 = dm_mine_json(fx, "-" as *u8, 0, now, d5) 346 var ok5: i64 = 0 347 if dl5 > 0 { if dm_find(d5, dl5, "\"scope\":\"gamma\",\"open\":0,\"eaten\":2,\"eat_permil\":1000" as *u8) == 1 { if dm_find(d, dl, "\"scope\":\"gamma\"" as *u8) == 0 { ok5 = 1 } } } 348 gv_check("T5 minsup 0 surfaces the fully-eaten scope at EXACT 1000 permil; minsup 1 correctly suppresses it" as *u8, ok5, ctr) 349 var ok6: i64 = 0 350 if dm_find(d, dl, "\"sev\":8,\"open\":2" as *u8) == 1 { if dm_find(d, dl, "\"sev\":3" as *u8) == 0 { ok6 = 1 } } 351 gv_check("T6 severity distribution counts OPEN only (sev8 = 2; eaten sev3 absent)" as *u8, ok6, ctr) 352 var ok7: i64 = 0 353 if dm_find(d, dl, "\"lt_1d\":1,\"d1_7\":1,\"d7_30\":1,\"gt_30d\":1" as *u8) == 1 { if dm_find(d, dl, "\"oldest_open_days\":34" as *u8) == 1 { ok7 = 1 } } 354 gv_check("T7 aging buckets exact 1/1/1/1 + oldest_open_days 34 (injected now)" as *u8, ok7, ctr) 355 let dl8: i64 = dm_mine_json(fx, "-" as *u8, 3, now, d) 356 var ok8: i64 = 0 357 if dl8 > 0 { if dm_find(d, dl8, "\"scope\":\"alpha\"" as *u8) == 1 { if dm_find(d, dl8, "\"scope\":\"beta\"" as *u8) == 0 { if dm_find(d, dl8, "\"scopes_below_minsup\":2" as *u8) == 1 { ok8 = 1 } } } } 358 gv_check("T8 minsup 3 emits only alpha and DECLARES the 2 suppressed scopes" as *u8, ok8, ctr) 359 let rc: i64 = gv_verdict("DEBTMINE-GATE" as *u8, ctr, "bugs-class mining: exact totals, per-scope eat-rate, open-only severity, deterministic aging, declared suppression" as *u8) 360 return rc 361} 362 363func main(argc: i64, argv: *i64) -> i64 { 364 if argc < 3 { sj_puts("usage: nx_debtmine {mine <source> [loader|-] [minsup] [now] | selftest <scratch-prefix>}\n" as *u8); sys_exit(2); return 2 } 365 let verb: *u8 = argv[1] as *u8 366 let vl: i64 = sj_vlen(verb) 367 if sj_lit_eq(verb, 0, vl, "selftest" as *u8) == 1 { 368 let rc: i64 = dm_selftest(argv[2] as *u8) 369 sys_exit(rc) 370 return rc 371 } 372 if sj_lit_eq(verb, 0, vl, "mine" as *u8) == 1 { 373 let loader: *u8 = sys_mmap(128) 374 var lo: i64 = 0 375 if argc >= 4 { lo = sj_cat(loader, 0, argv[3] as *u8) } else { lo = sj_cat(loader, 0, "nx_store_put" as *u8) } 376 loader[lo] = 0 as u8 377 var minsup: i64 = 1 378 if argc >= 5 { let mz: i64 = sj_atoi_z(argv[4] as *u8); if mz > 0 { minsup = mz } } 379 var now: i64 = sys_now_realtime_sec() 380 if argc >= 6 { let nz: i64 = sj_atoi_z(argv[5] as *u8); if nz > 0 { now = nz } } 381 let d: *u8 = sys_mmap(DM_OUT) 382 let dl: i64 = dm_mine_json(argv[2] as *u8, loader, minsup, now, d) 383 if dl == (0 - 2) { sj_puts("REFUSED loader not in the allowlist\n" as *u8); sys_exit(3); return 3 } 384 if dl == (0 - 3) { sj_puts("REFUSED loader exited nonzero\n" as *u8); sys_exit(3); return 3 } 385 if dl < 0 { sj_puts("REFUSED source missing or empty\n" as *u8); sys_exit(3); return 3 } 386 sys_write(1, d, dl) 387 sys_exit(0) 388 return 0 389 } 390 sj_puts("unknown verb\n" as *u8) 391 sys_exit(2) 392 return 2 393}