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}