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}