code wiki / (root) / nx_shipcheck.nx

nx_shipcheck.nx source

↩ module page · 543 lines · 20914 B

1// nx_shipcheck.nx -- SHIP-COMPLETE integration-orphan detector (POST-submit lane; operator law 2// feedback-ship-complete-lifecycle-standard 2026-07-21, ws=auto-shipcomplete-guards). 3// Counts integration orphans per class over the REAL registry surfaces: 4// noschema tool_allowlist.conf rows with NO schema row in knowledge/tool_schemas.conf 5// staleschema tool_schemas.conf rows whose tool is NOT in tool_allowlist.conf (ghost schema) 6// stagedref registry rows whose binary path contains .new (registered-but-never-promoted) 7// missingbin registry rows whose binary is ABSENT on disk (tools/call would exec-fail) 8// straynew stranded .new artifacts in the deploy root (one-level walk, DECLARED) 9// verb `listnew [root]` PRINTS those names (exit code = the count) -- see sc_scan_new's header for why. 10// noatlas registry tools absent from knowledge/registry/atlas_catalog.tsv col1 11// Composed context (owned elsewhere, echoed for the trend line, NEVER re-scanned or re-filed): 12// dupsrc last count from knowledge/status/dup_source.log (seq207 lane) 13// unreg_l003 last L003 measured from knowledge/status/law_warden.jrnl (warden lane) 14// frowjoin=-1 DECLARED UNMEASURED: frontier rows carry no ws column (F209 evidence gap). 15// verbs: scan [root] | beat [root] | file [root] [filer] 16// scan: sweep + print + O_APPEND one trend line to <root>/knowledge/status/shipcheck.log 17// beat: scan + auto-file debts via the ALLOWLIST-RESOLVED filer (default nx_debt) 18// file: scan + file via an INJECTED filer tool name (the gate passes argecho -- selftest 19// never writes the production debt plane; F872 harden pattern) 20// Descs are CANONICAL and COUNT-FREE so nx_debt content-idempotent add (F868) makes any 21// beat cadence spam-proof BY CONSTRUCTION. Envelope: per-file read caps DECLARED via capped=. 22// license_tier: ORIGINAL Read-only sweep + O_APPEND log + debt filing. No hw writes (Rule 26). 23import "nx_syscalls.nx" 24 25const SC_TAB: i64 = 9 26const SC_NL: i64 = 10 27const SC_HASH: i64 = 35 28const SC_DOT: i64 = 46 29const SC_SLASH: i64 = 47 30const SC_COLON: i64 = 58 31const SC_ZERO: i64 = 48 32const SC_NINE: i64 = 57 33const SC_MINUS: i64 = 45 34const SC_B10: i64 = 10 35const SC_B256: i64 = 256 36const SC_LC_N: i64 = 110 37const SC_LC_E: i64 = 101 38const SC_LC_W: i64 = 119 39const SC_BUFCAP: i64 = 262144 40const SC_LOGCAP: i64 = 1048576 41const SC_PATHCAP: i64 = 1024 42const SC_OUTCAP: i64 = 8192 43const SC_DESCCAP: i64 = 1024 44const SC_DIRCAP: i64 = 262144 45const SC_MODE_F: i64 = 420 46const SC_EXITRED: i64 = 3 47const SC_EXEFAIL: i64 = 127 48const SC_STATBUF: i64 = 160 49const SC_NEWLEN: i64 = 4 50const SC_SEV_MISSINGBIN: i64 = 7 51const SC_SEV_STAGEDREF: i64 = 7 52const SC_SEV_NOSCHEMA: i64 = 6 53const SC_SEV_STALESCHEMA: i64 = 5 54const SC_SEV_STRAYNEW: i64 = 5 55const SC_SEV_NOATLAS: i64 = 4 56 57static g_root: *u8 58static g_allow: *u8 59static g_an: i64 60static g_schema: *u8 61static g_sn: i64 62static g_atlas: *u8 63static g_atn: i64 64static g_scr: *u8 65static g_scr2: *u8 66static g_sp: *i64 67static g_rn: i64 68static g_hitcap: i64 69static g_sev: i64 70static g_c_allow: i64 71static g_c_schema: i64 72static g_c_noschema: i64 73static g_c_staleschema: i64 74static g_c_stagedref: i64 75static g_c_missingbin: i64 76static g_c_straynew: i64 77static g_c_dupsrc: i64 78static g_c_unreg: i64 79static g_c_noatlas: i64 80 81func sc_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 82 83func sc_cat(d: *u8, o: i64, s: *u8) -> i64 { 84 var i: i64 = 0 85 var p: i64 = o 86 while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } 87 d[p] = 0 as u8 88 return p 89} 90 91func sc_catn(d: *u8, o: i64, v: i64) -> i64 { 92 let t: *u8 = sys_mmap(32) 93 var m: i64 = v 94 var p: i64 = o 95 if m < 0 { d[p] = SC_MINUS as u8; p = p + 1; m = 0 - m } 96 var k: i64 = 0 97 if m == 0 { t[0] = SC_ZERO as u8; k = 1 } 98 while m > 0 { t[k] = (SC_ZERO + (m % SC_B10)) as u8; m = m / SC_B10; k = k + 1 } 99 var i: i64 = 0 100 while i < k { d[p] = t[k-1-i]; p = p + 1; i = i + 1 } 101 d[p] = 0 as u8 102 return p 103} 104 105func sc_path(d: *u8, rel: *u8) -> *u8 { 106 var o: i64 = sc_cat(d, 0, g_root) 107 d[o] = SC_SLASH as u8 108 o = o + 1 109 o = sc_cat(d, o, rel) 110 return d 111} 112 113func sc_readf(rel: *u8) -> *u8 { 114 let pb: *u8 = sys_mmap(SC_PATHCAP) 115 let pp: *u8 = sc_path(pb, rel) 116 // CAP REMOVED 2026-08-23 (nx_capcensus subject resolver). MEASURED: this read was HEAD-anchored 117 // at SC_BUFCAP=262,144 while knowledge/tool_schemas.conf is 312,604 B -- 1,192 permil, ALREADY 118 // 19% OVER -- so this SHIP GATE has been scanning a TRUNCATED schema registry and any schema past 119 // the 256 KiB mark was invisible to it. To its credit it did NOT fail silently: it set g_hitcap=1 120 // and printed it, which is what makes a bounded read legitimate rather than a lie. But an 121 // announcement nobody acts on still leaves the gate judging a prefix, and the fix costs nothing: 122 // sys_read_file sizes its buffer from the file (lseek END) and cannot short-read, so g_hitcap can 123 // now only ever be 0 -- truncation is impossible by construction rather than merely reported. 124 // `cap` is retained in the signature so every call site is unchanged; it is deliberately unused. 125 g_rn = 0 126 let lp: *i64 = sys_mmap(8) as *i64 127 *lp = 0 128 var buf: *u8 = sys_read_file(pp, lp) 129 // sys_read_file returns NULL when the file is absent; the original returned an EMPTY MAPPED 130 // buffer and callers index into it, so preserve that non-null contract exactly. 131 if (buf as i64) == 0 { buf = sys_mmap(16); *lp = 0 } 132 var t: i64 = *lp 133 if t < 0 { t = 0 } 134 g_rn = t 135 return buf 136} 137 138func sc_le(b: *u8, i: i64, n: i64) -> i64 { 139 var j: i64 = i 140 var d: i64 = 0 141 while d == 0 { 142 if j >= n { d = 1 } else { if b[j] == (SC_NL as u8) { d = 1 } else { j = j + 1 } } 143 } 144 return j 145} 146 147func sc_col(b: *u8, ls: i64, le: i64, want: i64, sp: *i64) -> i64 { 148 var c: i64 = 0 149 var s: i64 = ls 150 var j: i64 = ls 151 var fnd: i64 = 0 152 var d: i64 = 0 153 while d == 0 { 154 var hit: i64 = 0 155 if j >= le { hit = 1 } else { if b[j] == (SC_TAB as u8) { hit = 1 } } 156 if hit == 1 { 157 if c == want { sp[0] = s; sp[1] = j; fnd = 1; d = 1 } else { c = c + 1; s = j + 1; if j >= le { d = 1 } else { j = j + 1 } } 158 } else { j = j + 1 } 159 } 160 return fnd 161} 162 163func sc_span_lit(b: *u8, s: i64, e: i64, lit: *u8) -> i64 { 164 let n: i64 = sc_len(lit) 165 if e - s != n { return 0 } 166 var i: i64 = 0 167 while i < n { let bi: i64 = s + i; if b[bi] != lit[i] { return 0 } i = i + 1 } 168 return 1 169} 170 171func sc_cpspan(b: *u8, sp: *i64) -> *u8 { 172 var i: i64 = sp[0] 173 var k: i64 = 0 174 while i < sp[1] { g_scr[k] = b[i]; k = k + 1; i = i + 1 } 175 g_scr[k] = 0 as u8 176 return g_scr 177} 178 179func sc_cpspan2(b: *u8, sp: *i64) -> *u8 { 180 var i: i64 = sp[0] 181 var k: i64 = 0 182 while i < sp[1] { g_scr2[k] = b[i]; k = k + 1; i = i + 1 } 183 g_scr2[k] = 0 as u8 184 return g_scr2 185} 186 187func sc_name_in(b: *u8, n: i64, name: *u8, col: i64) -> i64 { 188 var i: i64 = 0 189 var fnd: i64 = 0 190 while i < n { 191 let le: i64 = sc_le(b, i, n) 192 var skip: i64 = 0 193 if le <= i { skip = 1 } else { if b[i] == (SC_HASH as u8) { skip = 1 } } 194 if skip == 0 { 195 if fnd == 0 { 196 if sc_col(b, i, le, col, g_sp) == 1 { if sc_span_lit(b, g_sp[0], g_sp[1], name) == 1 { fnd = 1 } } 197 } 198 } 199 i = le + 1 200 } 201 return fnd 202} 203 204func sc_has_new(b: *u8, sp: *i64) -> i64 { 205 var i: i64 = sp[0] 206 while i + SC_NEWLEN <= sp[1] { 207 if b[i] == (SC_DOT as u8) { if b[i+1] == (SC_LC_N as u8) { if b[i+2] == (SC_LC_E as u8) { if b[i+3] == (SC_LC_W as u8) { return 1 } } } } 208 i = i + 1 209 } 210 return 0 211} 212 213func sc_exists(p: *u8) -> i64 { 214 let sb: *u8 = sys_mmap(SC_STATBUF) 215 let r: i64 = sys_fstatat(p, sb) 216 if r == 0 { return 1 } 217 return 0 218} 219 220// emit=1 prints each offending NAME to stdout instead of only counting it. 221// WHY (2026-08-03, debt 1785773047): this class grew 40 -> 512 in 13 days and the only interface to it was a 222// COUNT. `nx_shelltool glob '*.elf.new' .` BUDGET-EXCEEDS before finishing the root walk, and the log stores 223// counts only -- so the estate could see the number and never the members. A DETECTOR THAT COUNTS A CLASS IT 224// CANNOT ENUMERATE HANDS YOU A NUMBER YOU CANNOT ACT ON. Same one-level walk, same predicate, so the list can 225// never disagree with the count it reports. 226func sc_scan_new(emit: i64) -> i64 { 227 let fd: i64 = sys_openat_rd(g_root) 228 if fd < 0 { return 0 } 229 let db: *u8 = sys_mmap(SC_DIRCAP) 230 var cnt: i64 = 0 231 var done: i64 = 0 232 while done == 0 { 233 let nr: i64 = sys_getdents64(fd, db, SC_DIRCAP) 234 if nr <= 0 { done = 1 } else { 235 var p: i64 = 0 236 while p < nr { 237 let rlo: i64 = db[p+16] as i64 238 let rhi: i64 = db[p+17] as i64 239 let rl: i64 = rlo + rhi * SC_B256 240 let ns: i64 = p + 19 241 var ne: i64 = ns 242 while db[ne] != (0 as u8) { ne = ne + 1 } 243 if ne - ns >= SC_NEWLEN { if db[ne-4] == (SC_DOT as u8) { if db[ne-3] == (SC_LC_N as u8) { if db[ne-2] == (SC_LC_E as u8) { if db[ne-1] == (SC_LC_W as u8) { cnt = cnt + 1; if emit == 1 { sys_write(1, (((db as i64) + ns) as *u8), ne - ns); sys_write(1, "\n" as *u8, 1) } } } } } } 244 if rl <= 0 { p = nr } else { p = p + rl } 245 } 246 } 247 } 248 sys_close(fd) 249 return cnt 250} 251 252func sc_atoi_span(b: *u8, sp: *i64) -> i64 { 253 var v: i64 = 0 254 var i: i64 = sp[0] 255 while i < sp[1] { let ch: i64 = b[i] as i64; if ch >= SC_ZERO { if ch <= SC_NINE { v = v * SC_B10 + (ch - SC_ZERO) } } i = i + 1 } 256 return v 257} 258 259func sc_last_num_after(b: *u8, pat: *u8) -> i64 { 260 let n: i64 = g_rn 261 let pl: i64 = sc_len(pat) 262 var best: i64 = 0 - 1 263 var i: i64 = 0 264 while i + pl <= n { 265 var k: i64 = 0 266 var ok: i64 = 1 267 while k < pl { let bi: i64 = i + k; if b[bi] != pat[k] { ok = 0; k = pl } else { k = k + 1 } } 268 if ok == 1 { best = i + pl } 269 i = i + 1 270 } 271 if best < 0 { return 0 - 1 } 272 var v: i64 = 0 273 var j: i64 = best 274 var any: i64 = 0 275 var d: i64 = 0 276 while d == 0 { 277 let ch: i64 = b[j] as i64 278 var isd: i64 = 0 279 if ch >= SC_ZERO { if ch <= SC_NINE { isd = 1 } } 280 if isd == 1 { v = v * SC_B10 + (ch - SC_ZERO); any = 1; j = j + 1 } else { d = 1 } 281 } 282 if any == 0 { return 0 - 1 } 283 return v 284} 285 286func sc_l003(b: *u8) -> i64 { 287 let n: i64 = g_rn 288 var best: i64 = 0 - 1 289 var i: i64 = 0 290 while i < n { 291 let le: i64 = sc_le(b, i, n) 292 if sc_col(b, i, le, 2, g_sp) == 1 { 293 if sc_span_lit(b, g_sp[0], g_sp[1], "L003") == 1 { 294 if sc_col(b, i, le, 3, g_sp) == 1 { 295 if sc_span_lit(b, g_sp[0], g_sp[1], "unregistered") == 1 { 296 if sc_col(b, i, le, 4, g_sp) == 1 { best = sc_atoi_span(b, g_sp) } 297 } 298 } 299 } 300 } 301 i = le + 1 302 } 303 return best 304} 305 306func sc_scan() -> i64 { 307 g_hitcap = 0 308 g_allow = sc_readf("tool_allowlist.conf") 309 g_an = g_rn 310 g_schema = sc_readf("knowledge/tool_schemas.conf") 311 g_sn = g_rn 312 g_atlas = sc_readf("knowledge/registry/atlas_catalog.tsv") 313 g_atn = g_rn 314 g_c_allow = 0 315 g_c_schema = 0 316 g_c_noschema = 0 317 g_c_staleschema = 0 318 g_c_stagedref = 0 319 g_c_missingbin = 0 320 g_c_straynew = 0 321 g_c_dupsrc = 0 322 g_c_unreg = 0 323 g_c_noatlas = 0 324 var i: i64 = 0 325 while i < g_an { 326 let le: i64 = sc_le(g_allow, i, g_an) 327 var skip: i64 = 0 328 if le <= i { skip = 1 } else { if g_allow[i] == (SC_HASH as u8) { skip = 1 } } 329 if skip == 0 { 330 if sc_col(g_allow, i, le, 0, g_sp) == 1 { 331 if g_sp[1] > g_sp[0] { 332 g_c_allow = g_c_allow + 1 333 let nm: *u8 = sc_cpspan(g_allow, g_sp) 334 if sc_name_in(g_schema, g_sn, nm, 0) == 0 { g_c_noschema = g_c_noschema + 1 } 335 if sc_name_in(g_atlas, g_atn, nm, 1) == 0 { g_c_noatlas = g_c_noatlas + 1 } 336 if sc_col(g_allow, i, le, 1, g_sp) == 1 { 337 if sc_has_new(g_allow, g_sp) == 1 { g_c_stagedref = g_c_stagedref + 1 } else { 338 let pp: *u8 = sc_cpspan2(g_allow, g_sp) 339 if sc_exists(pp) == 0 { g_c_missingbin = g_c_missingbin + 1 } 340 } 341 } 342 } 343 } 344 } 345 i = le + 1 346 } 347 var j: i64 = 0 348 while j < g_sn { 349 let le2: i64 = sc_le(g_schema, j, g_sn) 350 var skip2: i64 = 0 351 if le2 <= j { skip2 = 1 } else { if g_schema[j] == (SC_HASH as u8) { skip2 = 1 } } 352 if skip2 == 0 { 353 if sc_col(g_schema, j, le2, 0, g_sp) == 1 { 354 if g_sp[1] > g_sp[0] { 355 g_c_schema = g_c_schema + 1 356 let nm2: *u8 = sc_cpspan(g_schema, g_sp) 357 if sc_name_in(g_allow, g_an, nm2, 0) == 0 { g_c_staleschema = g_c_staleschema + 1 } 358 } 359 } 360 } 361 j = le2 + 1 362 } 363 g_c_straynew = sc_scan_new(0) 364 let db: *u8 = sc_readf("knowledge/status/dup_source.log") 365 g_c_dupsrc = sc_last_num_after(db, "basenames found: ") 366 let wb: *u8 = sc_readf("knowledge/status/law_warden.jrnl") 367 g_c_unreg = sc_l003(wb) 368 return 0 369} 370 371func sc_append(b: *u8, n: i64) -> i64 { 372 let pb: *u8 = sys_mmap(SC_PATHCAP) 373 let lp: *u8 = sc_path(pb, "knowledge/status/shipcheck.log") 374 let fd: i64 = sys_openat_append(lp, SC_MODE_F) 375 if fd < 0 { return 0 } 376 sys_write(fd, b, n) 377 sys_close(fd) 378 return 1 379} 380 381func sc_emit(verb: *u8) -> i64 { 382 let ts: *i64 = sys_mmap(16) as *i64 383 sys_clock_gettime_real(ts) 384 let ep: i64 = ts[0] 385 let ob: *u8 = sys_mmap(SC_OUTCAP) 386 var o: i64 = 0 387 o = sc_cat(ob, o, "SHIPCHECK epoch=" as *u8) 388 o = sc_catn(ob, o, ep) 389 o = sc_cat(ob, o, " verb=" as *u8) 390 o = sc_cat(ob, o, verb) 391 o = sc_cat(ob, o, " allow=" as *u8) 392 o = sc_catn(ob, o, g_c_allow) 393 o = sc_cat(ob, o, " schema=" as *u8) 394 o = sc_catn(ob, o, g_c_schema) 395 o = sc_cat(ob, o, " noschema=" as *u8) 396 o = sc_catn(ob, o, g_c_noschema) 397 o = sc_cat(ob, o, " staleschema=" as *u8) 398 o = sc_catn(ob, o, g_c_staleschema) 399 o = sc_cat(ob, o, " stagedref=" as *u8) 400 o = sc_catn(ob, o, g_c_stagedref) 401 o = sc_cat(ob, o, " missingbin=" as *u8) 402 o = sc_catn(ob, o, g_c_missingbin) 403 o = sc_cat(ob, o, " straynew=" as *u8) 404 o = sc_catn(ob, o, g_c_straynew) 405 o = sc_cat(ob, o, " dupsrc=" as *u8) 406 o = sc_catn(ob, o, g_c_dupsrc) 407 o = sc_cat(ob, o, " unreg_l003=" as *u8) 408 o = sc_catn(ob, o, g_c_unreg) 409 o = sc_cat(ob, o, " noatlas=" as *u8) 410 o = sc_catn(ob, o, g_c_noatlas) 411 o = sc_cat(ob, o, " frowjoin=-1 capped=" as *u8) 412 o = sc_catn(ob, o, g_hitcap) 413 var bad: i64 = 0 414 bad = g_c_noschema + g_c_staleschema + g_c_stagedref + g_c_missingbin + g_c_straynew + g_c_noatlas 415 if bad > 0 { o = sc_cat(ob, o, " verdict=RED" as *u8) } else { o = sc_cat(ob, o, " verdict=GREEN" as *u8) } 416 ob[o] = SC_NL as u8 417 o = o + 1 418 sys_write(1, ob, o) 419 sc_append(ob, o) 420 if bad > 0 { return SC_EXITRED } 421 return 0 422} 423 424func sc_allow_path(name: *u8, outp: *u8) -> i64 { 425 var i: i64 = 0 426 var fnd: i64 = 0 427 while i < g_an { 428 let le: i64 = sc_le(g_allow, i, g_an) 429 var skip: i64 = 0 430 if le <= i { skip = 1 } else { if g_allow[i] == (SC_HASH as u8) { skip = 1 } } 431 if skip == 0 { 432 if fnd == 0 { 433 if sc_col(g_allow, i, le, 0, g_sp) == 1 { 434 if sc_span_lit(g_allow, g_sp[0], g_sp[1], name) == 1 { 435 if sc_col(g_allow, i, le, 1, g_sp) == 1 { 436 var k: i64 = 0 437 var z: i64 = g_sp[0] 438 while z < g_sp[1] { outp[k] = g_allow[z]; k = k + 1; z = z + 1 } 439 outp[k] = 0 as u8 440 fnd = 1 441 } 442 } 443 } 444 } 445 } 446 i = le + 1 447 } 448 return fnd 449} 450 451func sc_spawn_filer(fpath: *u8, desc: *u8) -> i64 { 452 let sevs: *u8 = sys_mmap(8) 453 let so: i64 = sc_catn(sevs, 0, g_sev) 454 let av: *i64 = sys_mmap(64) as *i64 455 av[0] = fpath as i64 456 av[1] = "add" as *u8 as i64 457 av[2] = sevs as i64 458 av[3] = "shipcomplete" as *u8 as i64 459 av[4] = desc as i64 460 av[5] = 0 461 let env: *i64 = sys_mmap(8) as *i64 462 env[0] = 0 463 let pid: i64 = sys_fork() 464 if pid == 0 { sys_execve(fpath, av, env); sys_exit_group(SC_EXEFAIL) } 465 let st: *i64 = sys_mmap(16) as *i64 466 st[0] = 0 467 sys_wait4(pid, st, 0) 468 let raw: i64 = st[0] 469 let code: i64 = (raw / SC_B256) % SC_B256 470 return code 471} 472 473func sc_file_cls(fpath: *u8, tail: *u8) -> i64 { 474 let desc: *u8 = sys_mmap(SC_DESCCAP) 475 var o: i64 = 0 476 o = sc_cat(desc, 0, "SHIPCOMPLETE-ORPHAN " as *u8) 477 o = sc_cat(desc, o, tail) 478 let rc: i64 = sc_spawn_filer(fpath, desc) 479 let rb: *u8 = sys_mmap(SC_PATHCAP) 480 var ro: i64 = 0 481 ro = sc_cat(rb, 0, "FILED class=" as *u8) 482 var i: i64 = 0 483 var d: i64 = 0 484 while d == 0 { 485 let ch: i64 = tail[i] as i64 486 if ch == 0 { d = 1 } else { if ch == SC_COLON { d = 1 } else { rb[ro] = tail[i]; ro = ro + 1; i = i + 1 } } 487 } 488 ro = sc_cat(rb, ro, " exit=" as *u8) 489 ro = sc_catn(rb, ro, rc) 490 rb[ro] = SC_NL as u8 491 ro = ro + 1 492 sys_write(1, rb, ro) 493 sc_append(rb, ro) 494 return rc 495} 496 497func sc_file(filer: *u8) -> i64 { 498 let fpath: *u8 = sys_mmap(SC_PATHCAP) 499 let fok: i64 = sc_allow_path(filer, fpath) 500 if fok == 0 { 501 let eb: *u8 = sys_mmap(SC_PATHCAP) 502 var eo: i64 = 0 503 eo = sc_cat(eb, 0, "SHIPCHECK-FILE filer-unresolved" as *u8) 504 eb[eo] = SC_NL as u8 505 eo = eo + 1 506 sys_write(1, eb, eo) 507 sc_append(eb, eo) 508 return 1 509 } 510 if g_c_stagedref > 0 { g_sev = SC_SEV_STAGEDREF; sc_file_cls(fpath, "stagedref: registered MCP tools whose tool_allowlist.conf binary path points at a staged .new artifact (registered-but-never-promoted) -- promote or withdraw each; live counts in knowledge/status/shipcheck.log (nx_shipcheck)" as *u8) } 511 if g_c_missingbin > 0 { g_sev = SC_SEV_MISSINGBIN; sc_file_cls(fpath, "missingbin: registered MCP tools whose allowlisted binary is ABSENT on disk (tools-call would exec-fail) -- restore or deregister; live counts in knowledge/status/shipcheck.log (nx_shipcheck)" as *u8) } 512 if g_c_noschema > 0 { g_sev = SC_SEV_NOSCHEMA; sc_file_cls(fpath, "noschema: tool registry rows with NO schema row in knowledge/tool_schemas.conf (agents fly blind on safety and verbs) -- add schema-bridge rows; live counts in knowledge/status/shipcheck.log (nx_shipcheck)" as *u8) } 513 if g_c_staleschema > 0 { g_sev = SC_SEV_STALESCHEMA; sc_file_cls(fpath, "staleschema: tool_schemas.conf rows whose tool is NOT in tool_allowlist.conf (ghost schema, nx_seat-class staleness) -- reconcile or retire each row; live counts in knowledge/status/shipcheck.log (nx_shipcheck)" as *u8) } 514 if g_c_straynew > 0 { g_sev = SC_SEV_STRAYNEW; sc_file_cls(fpath, "straynew: stranded staged .new artifacts in the deploy root (never promoted, never cleaned) -- promote or evict via nx_evict; live counts in knowledge/status/shipcheck.log (nx_shipcheck)" as *u8) } 515 if g_c_noatlas > 0 { g_sev = SC_SEV_NOATLAS; sc_file_cls(fpath, "noatlas: registered MCP tools absent from the atlas recombination catalog knowledge/registry/atlas_catalog.tsv (invisible to discovery and recombination) -- register via nx_atlas_catseed; live counts in knowledge/status/shipcheck.log (nx_shipcheck)" as *u8) } 516 return 0 517} 518 519func main(argc: i64, argv: *i64) -> i64 { 520 g_scr = sys_mmap(SC_PATHCAP) 521 g_scr2 = sys_mmap(SC_PATHCAP) 522 g_sp = sys_mmap(16) as *i64 523 g_root = "." as *u8 524 var verb: *u8 = "scan" as *u8 525 if argc >= 2 { verb = argv[1] as *u8 } 526 if argc >= 3 { g_root = argv[2] as *u8 } 527 var filer: *u8 = "nx_debt" as *u8 528 if argc >= 4 { filer = argv[3] as *u8 } 529 // listnew: PRINT the straynew members (the class this organ could previously only count). Deliberately 530 // BEFORE sc_scan() so the listing costs one directory walk and nothing else, and returns the COUNT as its 531 // exit code -- so a caller can pipe the names AND branch on how many there were. 532 if sc_span_lit(verb, 0, sc_len(verb), "listnew" as *u8) == 1 { 533 let ln: i64 = sc_scan_new(1) 534 return ln 535 } 536 sc_scan() 537 let rc: i64 = sc_emit(verb) 538 var dofile: i64 = 0 539 if sc_span_lit(verb, 0, sc_len(verb), "beat" as *u8) == 1 { dofile = 1 } 540 if sc_span_lit(verb, 0, sc_len(verb), "file" as *u8) == 1 { dofile = 1 } 541 if dofile == 1 { sc_file(filer) } 542 return rc 543}