code wiki / _hdl_build / nx_featuremine.nx

nx_featuremine.nx source

↩ module page · 395 lines · 18205 B

1// nx_featuremine.nx -- FEATURES-CLASS MINER over the frontier- plane (miner-sota F1016, 2026-07-23). 2// The census (nx_minecov MC05) measured features COVERED-but-UNMINED: nx_frontier_board derives CPM 3// (ES/EF/float/critical-path/READY/BLOCKED) over the DAG, but nobody mines the BACKLOG ITSELF. The 4// signal CPM does not give is DEPENDENCY IN-DEGREE: which feature BLOCKS THE MOST OTHERS. A row with 5// float 0 is on the critical path; a row with in-degree 9 is a BOTTLENECK -- different questions, and 6// the second is what tells a PM where to put people. Also: per-owner load with a real done-rate, 7// status distribution, unowned rows, and BLOCKED (a dep that is not yet D) kept distinct from READY. 8// ★HONEST LIMIT DECLARED IN THE OUTPUT: frontier ids are F-numbers, NOT epochs, so AGE IS NOT 9// DERIVABLE from this plane -- the organ reports aging_available:0 rather than inventing a number. 10// Composes nx_sovjson_lib (sj_*). Read-only. No hw writes (Rule 26). 11// mine <source> [loader|-] [minsup] -> JSON: status/owner/dep-bottleneck/blocked + declared limits 12// selftest <scratch-prefix> -> gate T1..T8 13// row cols: id TAB title TAB w TAB _ TAB owner TAB status(T|D|X) TAB deps TAB lane TAB milestone 14// license_tier: ORIGINAL 15import "nx_sovjson_lib.nx" 16import "nx_syscalls.nx" 17import "nx_gate_verdict.nx" 18import "nx_tool_run.nx" 19const FM_MAGIC_4088: i64 = 4088 20 21const FM_CAP: i64 = 1048576 22const FM_OUT: i64 = 65536 23const FM_PATH: i64 = 512 24const FM_ROWS: i64 = 1024 25const FM_OWNERS: i64 = 256 26const FM_TOPN: i64 = 15 27const FM_ALLOW: i64 = 131072 28const FM_PERMIL: i64 = 1000 29const FM_NAME_MAX: i64 = 120 30const FM_MODE_644: i64 = 420 31 32func fm_read(path: *u8, buf: *u8, cap: i64) -> i64 { 33 let fd: i64 = sys_openat_rd(path) 34 if fd < 0 { return -1 } 35 var n: i64 = 0 36 var go: i64 = 1 37 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 } } 38 sys_close(fd) 39 return n 40} 41func fm_find(q: *u8, n: i64, lit: *u8) -> i64 { 42 let ll: i64 = sj_vlen(lit) 43 if ll == 0 { return 0 } 44 var i: i64 = 0 45 while i + ll <= n { 46 var k: i64 = 0 47 var ok: i64 = 1 48 while k < ll { if q[i+k] != lit[k] { ok = 0; k = ll } else { k = k + 1 } } 49 if ok == 1 { return 1 } 50 i = i + 1 51 } 52 return 0 53} 54// DELEGATES to the shared base (rule-15 extraction 2026-07-23) 55func fm_allow_path(name: *u8, outp: *u8) -> i64 { return sj_allow_path(name, outp) } 56func fm_key(d: *u8, o: i64, name: *u8) -> i64 { 57 d[o] = 34 as u8 58 var p: i64 = o + 1 59 p = sj_cat(d, p, name) 60 d[p] = 34 as u8 61 p = p + 1 62 d[p] = 58 as u8 63 p = p + 1 64 return p 65} 66func fm_absent(q: *u8, s: i64, e: i64) -> i64 { 67 if e <= s { return 1 } 68 if e - s == 1 { if q[s] == (45 as u8) { return 1 } } 69 return 0 70} 71func fm_span_eq(q: *u8, a: i64, ae: i64, b: i64, be: i64) -> i64 { 72 if ae - a != be - b { return 0 } 73 var i: i64 = 0 74 while a + i < ae { if q[a+i] != q[b+i] { return 0 } i = i + 1 } 75 return 1 76} 77func fm_load(src: *u8, loader: *u8, buf: *u8, lcx: *i64) -> i64 { 78 lcx[0] = 0 79 if sj_lit_eq(loader, 0, sj_vlen(loader), "-" as *u8) == 1 { 80 let n: i64 = fm_read(src, buf, FM_CAP - 8) 81 if n <= 0 { return -1 } 82 return n 83 } 84 let lp: *u8 = sys_mmap(FM_PATH) 85 if fm_allow_path(loader, lp) == 0 { return -2 } 86 lcx[0] = 1 87 let av: *i64 = sys_mmap(64) as *i64 88 av[0] = lp as i64 89 av[1] = src as i64 90 av[2] = "load" as *u8 as i64 91 av[3] = 0 92 let cl: *i64 = sys_mmap(16) as *i64 93 let ex: i64 = tr_run_capture(lp, av, buf, FM_CAP - 8, cl) 94 if ex != 0 { return -3 } 95 if cl[0] <= 0 { return -1 } 96 return cl[0] 97} 98func fm_mine_json(src: *u8, loader: *u8, minsup: i64, d: *u8) -> i64 { 99 let q: *u8 = sys_mmap(FM_CAP) 100 let lcx: *i64 = sys_mmap(16) as *i64 101 let n: i64 = fm_load(src, loader, q, lcx) 102 if n < 0 { return n } 103 // pass 1: index every row's id / status / owner / deps 104 let ids: *i64 = sys_mmap(8 * FM_ROWS) as *i64 105 let ide: *i64 = sys_mmap(8 * FM_ROWS) as *i64 106 let stat: *i64 = sys_mmap(8 * FM_ROWS) as *i64 107 let deps: *i64 = sys_mmap(8 * FM_ROWS) as *i64 108 let depe: *i64 = sys_mmap(8 * FM_ROWS) as *i64 109 let indeg: *i64 = sys_mmap(8 * FM_ROWS) as *i64 110 let ows: *i64 = sys_mmap(8 * FM_OWNERS) as *i64 111 let owe: *i64 = sys_mmap(8 * FM_OWNERS) as *i64 112 let owc: *i64 = sys_mmap(8 * FM_OWNERS) as *i64 113 let owd: *i64 = sys_mmap(8 * FM_OWNERS) as *i64 114 let done: *i64 = sys_mmap(8 * FM_ROWS) as *i64 115 let sp: *i64 = sys_mmap(16) as *i64 116 var nr: i64 = 0 117 var nown: i64 = 0 118 var rcapped: i64 = 0 119 var malformed: i64 = 0 120 var todo: i64 = 0 121 var ndone: i64 = 0 122 var ext: i64 = 0 123 var other: i64 = 0 124 var unowned: i64 = 0 125 var i: i64 = 0 126 while i < n { 127 let le: i64 = sj_le(q, i, n) 128 var ok: i64 = 0 129 var cmt: i64 = 0 130 if le > i { if q[i] == (35 as u8) { cmt = 1 } } 131 if le > i { if cmt == 0 { if sj_col(q, i, le, 6, sp) == 1 { ok = 1 } } } 132 if ok == 0 { if le > i { if cmt == 0 { malformed = malformed + 1 } } } else { 133 if nr >= FM_ROWS { rcapped = 1 } else { 134 deps[nr] = sp[0] 135 depe[nr] = sp[1] 136 sj_col(q, i, le, 0, sp) 137 ids[nr] = sp[0] 138 ide[nr] = sp[1] 139 sj_col(q, i, le, 5, sp) 140 var st: i64 = 3 141 if sj_lit_eq(q, sp[0], sp[1], "T" as *u8) == 1 { st = 0; todo = todo + 1 } 142 if sj_lit_eq(q, sp[0], sp[1], "D" as *u8) == 1 { st = 1; ndone = ndone + 1 } 143 if sj_lit_eq(q, sp[0], sp[1], "X" as *u8) == 1 { st = 2; ext = ext + 1 } 144 if st == 3 { other = other + 1 } 145 stat[nr] = st 146 sj_col(q, i, le, 4, sp) 147 if fm_absent(q, sp[0], sp[1]) == 1 { unowned = unowned + 1 } else { 148 var oi: i64 = 0 - 1 149 var k: i64 = 0 150 while k < nown { 151 if fm_span_eq(q, sp[0], sp[1], ows[k], owe[k]) == 1 { oi = k; k = nown } 152 k = k + 1 153 } 154 if oi < 0 { if nown < FM_OWNERS { ows[nown] = sp[0]; owe[nown] = sp[1]; oi = nown; nown = nown + 1 } } 155 if oi >= 0 { owc[oi] = owc[oi] + 1; if st == 1 { owd[oi] = owd[oi] + 1 } } 156 } 157 nr = nr + 1 158 } 159 } 160 i = le + 1 161 } 162 // pass 2: dependency in-degree + blocked (a dep that is not yet D) 163 var blocked: i64 = 0 164 var ready: i64 = 0 165 var dangling: i64 = 0 166 var r: i64 = 0 167 while r < nr { 168 if fm_absent(q, deps[r], depe[r]) == 1 { if stat[r] == 0 { ready = ready + 1 } } else { 169 var di: i64 = 0 - 1 170 var k: i64 = 0 171 while k < nr { 172 if fm_span_eq(q, deps[r], depe[r], ids[k], ide[k]) == 1 { di = k; k = nr } 173 k = k + 1 174 } 175 if di < 0 { dangling = dangling + 1 } else { 176 indeg[di] = indeg[di] + 1 177 if stat[r] == 0 { if stat[di] != 1 { blocked = blocked + 1 } else { ready = ready + 1 } } 178 } 179 } 180 r = r + 1 181 } 182 var p: i64 = 0 183 d[p] = 123 as u8; p = p + 1 184 p = fm_key(d, p, "v" as *u8); p = sj_catn(d, p, 1); d[p] = 44 as u8; p = p + 1 185 p = fm_key(d, p, "tool" as *u8) 186 d[p] = 34 as u8; p = p + 1 187 p = sj_cat(d, p, "nx_featuremine" as *u8) 188 d[p] = 34 as u8; p = p + 1 189 d[p] = 44 as u8; p = p + 1 190 p = fm_key(d, p, "averb" as *u8) 191 d[p] = 34 as u8; p = p + 1 192 p = sj_cat(d, p, "mine" as *u8) 193 d[p] = 34 as u8; p = p + 1 194 d[p] = 44 as u8; p = p + 1 195 p = fm_key(d, p, "source" as *u8) 196 d[p] = 34 as u8; p = p + 1 197 p = sj_cat_esc(d, p, src, 0, sj_vlen(src), FM_NAME_MAX) 198 d[p] = 34 as u8; p = p + 1 199 d[p] = 44 as u8; p = p + 1 200 p = fm_key(d, p, "loader_resolved" as *u8); p = sj_catn(d, p, lcx[0]); d[p] = 44 as u8; p = p + 1 201 p = fm_key(d, p, "rows" as *u8); p = sj_catn(d, p, nr); d[p] = 44 as u8; p = p + 1 202 p = fm_key(d, p, "malformed" as *u8); p = sj_catn(d, p, malformed); d[p] = 44 as u8; p = p + 1 203 p = fm_key(d, p, "rows_capped" as *u8); p = sj_catn(d, p, rcapped); d[p] = 44 as u8; p = p + 1 204 p = fm_key(d, p, "todo" as *u8); p = sj_catn(d, p, todo); d[p] = 44 as u8; p = p + 1 205 p = fm_key(d, p, "done" as *u8); p = sj_catn(d, p, ndone); d[p] = 44 as u8; p = p + 1 206 p = fm_key(d, p, "external" as *u8); p = sj_catn(d, p, ext); d[p] = 44 as u8; p = p + 1 207 p = fm_key(d, p, "other_status" as *u8); p = sj_catn(d, p, other); d[p] = 44 as u8; p = p + 1 208 var dp: i64 = 0 - 1 209 if nr > 0 { dp = ndone * FM_PERMIL / nr } 210 p = fm_key(d, p, "done_permil" as *u8); p = sj_catn(d, p, dp); d[p] = 44 as u8; p = p + 1 211 p = fm_key(d, p, "unowned" as *u8); p = sj_catn(d, p, unowned); d[p] = 44 as u8; p = p + 1 212 p = fm_key(d, p, "blocked_todo" as *u8); p = sj_catn(d, p, blocked); d[p] = 44 as u8; p = p + 1 213 p = fm_key(d, p, "ready_todo" as *u8); p = sj_catn(d, p, ready); d[p] = 44 as u8; p = p + 1 214 p = fm_key(d, p, "dangling_deps" as *u8); p = sj_catn(d, p, dangling); d[p] = 44 as u8; p = p + 1 215 // ★declared limit: frontier ids are F-numbers, not epochs -- age is NOT derivable here 216 p = fm_key(d, p, "aging_available" as *u8); p = sj_catn(d, p, 0); d[p] = 44 as u8; p = p + 1 217 // dep bottlenecks: highest in-degree first (who blocks the most) 218 p = fm_key(d, p, "dep_bottleneck" as *u8) 219 d[p] = 91 as u8; p = p + 1 220 var shown: i64 = 0 221 var efirst: i64 = 1 222 var pass: i64 = 0 223 while pass < nr { 224 if shown < FM_TOPN { 225 var mi: i64 = 0 - 1 226 var mx: i64 = 0 227 var k: i64 = 0 228 while k < nr { 229 if done[k] == 0 { if indeg[k] > mx { mx = indeg[k]; mi = k } } 230 k = k + 1 231 } 232 if mi >= 0 { 233 done[mi] = 1 234 if mx >= minsup { 235 if efirst == 0 { d[p] = 44 as u8; p = p + 1 } 236 efirst = 0 237 d[p] = 123 as u8; p = p + 1 238 p = fm_key(d, p, "id" as *u8) 239 d[p] = 34 as u8; p = p + 1 240 p = sj_cat_esc(d, p, q, ids[mi], ide[mi], FM_NAME_MAX) 241 d[p] = 34 as u8; p = p + 1 242 d[p] = 44 as u8; p = p + 1 243 p = fm_key(d, p, "blocks" as *u8); p = sj_catn(d, p, mx); d[p] = 44 as u8; p = p + 1 244 p = fm_key(d, p, "status" as *u8) 245 d[p] = 34 as u8; p = p + 1 246 if stat[mi] == 0 { p = sj_cat(d, p, "T" as *u8) } else { 247 if stat[mi] == 1 { p = sj_cat(d, p, "D" as *u8) } else { 248 if stat[mi] == 2 { p = sj_cat(d, p, "X" as *u8) } else { p = sj_cat(d, p, "?" as *u8) } 249 } 250 } 251 d[p] = 34 as u8; p = p + 1 252 d[p] = 125 as u8; p = p + 1 253 shown = shown + 1 254 } 255 } 256 } 257 pass = pass + 1 258 } 259 d[p] = 93 as u8; p = p + 1 260 d[p] = 44 as u8; p = p + 1 261 p = fm_key(d, p, "bottlenecks_shown" as *u8); p = sj_catn(d, p, shown); d[p] = 44 as u8; p = p + 1 262 // per-owner load with a REAL done-rate 263 p = fm_key(d, p, "by_owner" as *u8) 264 d[p] = 91 as u8; p = p + 1 265 let odone: *i64 = sys_mmap(8 * FM_OWNERS) as *i64 266 var oshown: i64 = 0 267 var ofirst: i64 = 1 268 var op: i64 = 0 269 while op < nown { 270 var mi2: i64 = 0 - 1 271 var mx2: i64 = 0 - 1 272 var k2: i64 = 0 273 while k2 < nown { 274 if odone[k2] == 0 { if owc[k2] > mx2 { mx2 = owc[k2]; mi2 = k2 } } 275 k2 = k2 + 1 276 } 277 if mi2 >= 0 { 278 odone[mi2] = 1 279 if oshown < FM_TOPN { 280 if ofirst == 0 { d[p] = 44 as u8; p = p + 1 } 281 ofirst = 0 282 d[p] = 123 as u8; p = p + 1 283 p = fm_key(d, p, "owner" as *u8) 284 d[p] = 34 as u8; p = p + 1 285 p = sj_cat_esc(d, p, q, ows[mi2], owe[mi2], FM_NAME_MAX) 286 d[p] = 34 as u8; p = p + 1 287 d[p] = 44 as u8; p = p + 1 288 p = fm_key(d, p, "rows" as *u8); p = sj_catn(d, p, owc[mi2]); d[p] = 44 as u8; p = p + 1 289 p = fm_key(d, p, "done" as *u8); p = sj_catn(d, p, owd[mi2]); d[p] = 44 as u8; p = p + 1 290 var odp: i64 = 0 291 if owc[mi2] > 0 { odp = owd[mi2] * FM_PERMIL / owc[mi2] } 292 p = fm_key(d, p, "done_permil" as *u8); p = sj_catn(d, p, odp) 293 d[p] = 125 as u8; p = p + 1 294 oshown = oshown + 1 295 } 296 } 297 op = op + 1 298 } 299 d[p] = 93 as u8; p = p + 1 300 d[p] = 44 as u8; p = p + 1 301 p = fm_key(d, p, "owners" as *u8); p = sj_catn(d, p, nown) 302 d[p] = 125 as u8; p = p + 1 303 d[p] = 10 as u8; p = p + 1 304 return p 305} 306func fm_selftest(prefix: *u8) -> i64 { 307 let ctr: *i64 = gv_ctr() 308 gv_head("nx_featuremine selftest -- dep in-degree bottleneck teeth (blocks-the-most is not float)" as *u8) 309 let fx: *u8 = sys_mmap(FM_PATH) 310 var o: i64 = sj_cat(fx, 0, prefix) 311 o = sj_catn(fx, o, sys_now_realtime_sec()) 312 o = sj_cat(fx, o, ".fm" as *u8) 313 fx[o] = 0 as u8 314 let miss: *u8 = sys_mmap(FM_PATH) 315 o = sj_cat(miss, 0, fx) 316 o = sj_cat(miss, o, ".absent" as *u8) 317 miss[o] = 0 as u8 318 // K1 done, blocks 3 (K2,K3,K4 depend on it). K5 depends on K2 (still T) => blocked. 319 // K6 depends on GHOST => dangling. K7 no deps, T => ready. K8 unowned. 320 let fd: i64 = sys_openat_wr(fx, FM_MODE_644) 321 let lb: *u8 = sys_mmap(FM_MAGIC_4088) 322 var lo: i64 = sj_cat(lb, 0, "# fixture\n" as *u8) 323 lo = sj_cat(lb, lo, "K1\tbase\t8\t0\tpm\tD\t-\tlane\tms\n" as *u8) 324 lo = sj_cat(lb, lo, "K2\tone\t7\t0\tpm\tT\tK1\tlane\tms\n" as *u8) 325 lo = sj_cat(lb, lo, "K3\ttwo\t7\t0\thr\tT\tK1\tlane\tms\n" as *u8) 326 lo = sj_cat(lb, lo, "K4\tthree\t6\t0\tpm\tD\tK1\tlane\tms\n" as *u8) 327 lo = sj_cat(lb, lo, "K5\tfour\t6\t0\thr\tT\tK2\tlane\tms\n" as *u8) 328 lo = sj_cat(lb, lo, "K6\tfive\t5\t0\tpm\tT\tGHOST\tlane\tms\n" as *u8) 329 lo = sj_cat(lb, lo, "K7\tsix\t5\t0\tpm\tT\t-\tlane\tms\n" as *u8) 330 lo = sj_cat(lb, lo, "K8\tseven\t4\t0\t-\tX\t-\tlane\tms\n" as *u8) 331 lo = sj_cat(lb, lo, "junk\n" as *u8) 332 sys_write(fd, lb, lo) 333 sys_close(fd) 334 let d: *u8 = sys_mmap(FM_OUT) 335 let r1: i64 = fm_mine_json(miss, "-" as *u8, 1, d) 336 var ok1: i64 = 0 337 if r1 < 0 { ok1 = 1 } 338 gv_check("T1 missing source refused fail closed" as *u8, ok1, ctr) 339 let dl: i64 = fm_mine_json(fx, "-" as *u8, 1, d) 340 var ok2: i64 = 0 341 if dl > 0 { if fm_find(d, dl, "\"rows\":8,\"malformed\":1" as *u8) == 1 { ok2 = 1 } } 342 gv_check("T2 exact rows 8, junk row counted malformed (comment skipped)" as *u8, ok2, ctr) 343 var ok3: i64 = 0 344 if fm_find(d, dl, "\"todo\":5,\"done\":2,\"external\":1,\"other_status\":0" as *u8) == 1 { if fm_find(d, dl, "\"done_permil\":250" as *u8) == 1 { ok3 = 1 } } 345 gv_check("T3 status split exact 5T/2D/1X and done_permil 250" as *u8, ok3, ctr) 346 var ok4: i64 = 0 347 if fm_find(d, dl, "\"id\":\"K1\",\"blocks\":3,\"status\":\"D\"" as *u8) == 1 { ok4 = 1 } 348 gv_check("T4 dep IN-DEGREE exact: K1 blocks 3 (the bottleneck CPM float cannot name)" as *u8, ok4, ctr) 349 var ok5: i64 = 0 350 if fm_find(d, dl, "\"id\":\"K2\",\"blocks\":1,\"status\":\"T\"" as *u8) == 1 { ok5 = 1 } 351 gv_check("T5 a still-open bottleneck is ranked with its OWN status (K2 blocks 1, T)" as *u8, ok5, ctr) 352 var ok6: i64 = 0 353 // K5 alone has a non-done dep (K2 is T) => blocked 1. K2/K3 depend on the DONE K1, K7 has no deps 354 // => ready 3. K6's dangling dep counts DANGLING only, deliberately not blocked (declared choice). 355 if fm_find(d, dl, "\"blocked_todo\":1,\"ready_todo\":3" as *u8) == 1 { ok6 = 1 } 356 gv_check("T6 BLOCKED (dep not yet D) kept distinct from READY: exactly 1 and 3" as *u8, ok6, ctr) 357 var ok7: i64 = 0 358 if fm_find(d, dl, "\"dangling_deps\":1" as *u8) == 1 { if fm_find(d, dl, "\"id\":\"GHOST\"" as *u8) == 0 { ok7 = 1 } } 359 gv_check("T7 a dep pointing at a nonexistent id is DANGLING, never invented as a row" as *u8, ok7, ctr) 360 var ok8: i64 = 0 361 if fm_find(d, dl, "\"unowned\":1" as *u8) == 1 { if fm_find(d, dl, "\"owner\":\"pm\",\"rows\":5,\"done\":2,\"done_permil\":400" as *u8) == 1 { if fm_find(d, dl, "\"aging_available\":0" as *u8) == 1 { ok8 = 1 } } } 362 gv_check("T8 per-owner done-rate exact (pm 2/5=400) + unowned counted + aging LIMIT declared" as *u8, ok8, ctr) 363 let rc: i64 = gv_verdict("FEATUREMINE-GATE" as *u8, ctr, "feature mining: dep in-degree bottlenecks, blocked-vs-ready, per-owner done-rate, dangling deps, declared aging limit" as *u8) 364 return rc 365} 366 367func main(argc: i64, argv: *i64) -> i64 { 368 if argc < 3 { sj_puts("usage: nx_featuremine {mine <source> [loader|-] [minsup] | selftest <scratch-prefix>}\n" as *u8); sys_exit(2); return 2 } 369 let verb: *u8 = argv[1] as *u8 370 let vl: i64 = sj_vlen(verb) 371 if sj_lit_eq(verb, 0, vl, "selftest" as *u8) == 1 { 372 let rc: i64 = fm_selftest(argv[2] as *u8) 373 sys_exit(rc) 374 return rc 375 } 376 if sj_lit_eq(verb, 0, vl, "mine" as *u8) == 1 { 377 let loader: *u8 = sys_mmap(128) 378 var lo: i64 = 0 379 if argc >= 4 { lo = sj_cat(loader, 0, argv[3] as *u8) } else { lo = sj_cat(loader, 0, "nx_store_put" as *u8) } 380 loader[lo] = 0 as u8 381 var minsup: i64 = 1 382 if argc >= 5 { let mz: i64 = sj_atoi_z(argv[4] as *u8); if mz > 0 { minsup = mz } } 383 let d: *u8 = sys_mmap(FM_OUT) 384 let dl: i64 = fm_mine_json(argv[2] as *u8, loader, minsup, d) 385 if dl == (0 - 2) { sj_puts("REFUSED loader not in the allowlist\n" as *u8); sys_exit(3); return 3 } 386 if dl == (0 - 3) { sj_puts("REFUSED loader exited nonzero\n" as *u8); sys_exit(3); return 3 } 387 if dl < 0 { sj_puts("REFUSED source missing or empty\n" as *u8); sys_exit(3); return 3 } 388 sys_write(1, d, dl) 389 sys_exit(0) 390 return 0 391 } 392 sj_puts("unknown verb\n" as *u8) 393 sys_exit(2) 394 return 2 395}