code wiki / (root) / nx_confdup.nx

nx_confdup.nx source

↩ module page · 531 lines · 21091 B

1// nx_confdup.nx -- find CONFIG FILES THAT DECLARE THE SAME CONCERN TWICE. 2// 3// WHY THIS EXISTS (measured 2026-07-30, media-vault lane): /api/promote reads 4// `knowledge/organ_kinds.conf` (PLURAL, values hyphenated `one-shot`) while 5// nx_organkind reads `knowledge/status/organ_kind.conf` (SINGULAR, values 6// unhyphenated `oneshot`). I added a row to the singular file, promote kept 7// working, and I concluded the row was the fix -- it was INERT. Undeclared 8// names fall through to a name-substring heuristic, so the wrong file "worked" 9// by luck until a name that failed the heuristic (nx_mvault_walk) became 10// unshippable by any sanctioned route. 11// 12// ★★★LAW: A SILENTLY-INERT CONFIG ROW IS WORSE THAN A MISSING ONE -- it 13// manufactures false confidence. The failure is invisible precisely because 14// the other source of truth keeps answering. 15// 16// This is a RECURRING class in this ecosystem, not a one-off: 17// * organ_kind.conf vs organ_kinds.conf (this session) 18// * nx_fs root=nishihost vs /api/build buildroot/runtime -- TWO ROOTS ONE NAME 19// * the ruler reading the wrong evidence root (rollup saw 181 of 1164 logs) 20// So it gets a DETECTOR rather than another banked warning. 21// 22// WHAT IT FLAGS -- two independent signals, reported separately because they 23// have different remedies: 24// COLLIDE-STEM two config files whose names normalize to the SAME stem 25// (plural/singular, hyphen/underscore, trailing digits). 26// Remedy: one file, or make one provably derived. 27// COLLIDE-BASE the SAME basename living under two different directories. 28// Remedy: name the roots apart, or collapse them. 29// 30// Normalization is deliberately AGGRESSIVE (drop ext, lowercase, '-'->'_', 31// strip trailing digits/underscores, strip ONE trailing plural 's') because a 32// false positive costs a glance while a false negative costs a session. 33// 34// nx_confdup scan [root] (default root: knowledge) 35// 36// Read-only. license_tier: ORIGINAL No hw writes (Rule 26). 37 38import "nx_syscalls.nx" 39const CD_MAGIC_2048: i64 = 2048 40 41const CD_QCAP: i64 = 1048576 // dir-queue arena 42const CD_DIRBUF: i64 = 262144 // getdents buffer 43const CD_ARENA: i64 = 1048576 // path + stem text arena 44const CD_MAXFILES: i64 = 8192 // bounded, and the cap is REPORTED not silent 45const CD_PATH: i64 = 4096 46const CD_STEM: i64 = 256 47const CD_OUT: i64 = 1048576 48const CD_FBUF: i64 = 1048576 // per-file read buffer for the COLLIDE-ROW pass 49const CD_MAXROWS: i64 = 16384 // bounded, and the cap is REPORTED not silent 50const CD_KEY: i64 = 160 51const CD_UNIQPCT: i64 = 90 // a file is "unique-key shaped" when >=90% of its rows carry a distinct key 52 53func cd_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 54 55func cd_cat(dst: *u8, off: i64, s: *u8) -> i64 { 56 var o: i64 = off 57 var i: i64 = 0 58 while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } 59 return o 60} 61 62func cd_ch(dst: *u8, off: i64, c: i64) -> i64 { dst[off] = c as u8; return off + 1 } 63 64func cd_udec(dst: *u8, off: i64, v: i64) -> i64 { 65 if v == 0 { dst[off] = 48 as u8; return off + 1 } 66 let tmp: *u8 = sys_mmap(32) 67 var m: i64 = v 68 var k: i64 = 0 69 while m > 0 { tmp[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 70 var o: i64 = off 71 var i: i64 = k - 1 72 while i >= 0 { dst[o] = tmp[i]; o = o + 1; i = i - 1 } 73 sys_munmap(tmp, 32) 74 return o 75} 76 77// "<dir>/<name>" into dst; returns length 78func cd_join(dst: *u8, dir: *u8, name: *u8) -> i64 { 79 var o: i64 = cd_cat(dst, 0, dir) 80 o = cd_ch(dst, o, 47) 81 o = cd_cat(dst, o, name) 82 dst[o] = 0 as u8 83 return o 84} 85 86// last path component of a NUL-terminated path 87func cd_basename(p: *u8) -> *u8 { 88 let n: i64 = cd_len(p) 89 var last: i64 = 0 - 1 90 var i: i64 = 0 91 while i < n { 92 if p[i] == (47 as u8) { last = i } 93 i = i + 1 94 } 95 return ((p as i64) + last + 1) as *u8 96} 97 98// does the name end in ".conf"? 99func cd_is_conf(name: *u8) -> i64 { 100 let n: i64 = cd_len(name) 101 if n < 6 { return 0 } 102 let s: *u8 = ".conf" as *u8 103 var i: i64 = 0 104 while i < 5 { 105 if name[n - 5 + i] != s[i] { return 0 } 106 i = i + 1 107 } 108 return 1 109} 110 111// Normalize a basename to a concern STEM. See the header for why this is 112// aggressive. Returns the stem length. 113func cd_norm(dst: *u8, name: *u8) -> i64 { 114 let n: i64 = cd_len(name) 115 // drop the extension (last '.') 116 var end: i64 = n 117 var i: i64 = n - 1 118 var scanning: i64 = 1 119 while scanning == 1 { 120 if i <= 0 { scanning = 0 } else { 121 if name[i] == (46 as u8) { end = i; scanning = 0 } else { i = i - 1 } 122 } 123 } 124 var o: i64 = 0 125 i = 0 126 while i < end { 127 var c: i64 = name[i] 128 if c >= 65 { if c <= 90 { c = c + 32 } } // lowercase 129 if c == 45 { c = 95 } // '-' -> '_' 130 if o < CD_STEM - 2 { dst[o] = c as u8; o = o + 1 } 131 i = i + 1 132 } 133 // strip trailing digits (foo2 == foo) 134 var go: i64 = 1 135 while go == 1 { 136 go = 0 137 if o > 1 { 138 let c2: i64 = dst[o - 1] 139 if c2 >= 48 { if c2 <= 57 { o = o - 1; go = 1 } } 140 } 141 } 142 // strip trailing underscores 143 go = 1 144 while go == 1 { 145 go = 0 146 if o > 1 { if dst[o - 1] == (95 as u8) { o = o - 1; go = 1 } } 147 } 148 // strip ONE trailing plural 's' (kind/kinds) 149 if o > 2 { if dst[o - 1] == (115 as u8) { o = o - 1 } } 150 dst[o] = 0 as u8 151 return o 152} 153 154func cd_streq(a: *u8, b: *u8) -> i64 { 155 var i: i64 = 0 156 while 1 == 1 { 157 if a[i] != b[i] { return 0 } 158 if a[i] == (0 as u8) { return 1 } 159 i = i + 1 160 } 161 return 0 162} 163 164// ---- COLLIDE-ROW: the SAME KEY declared TWICE INSIDE one config file ------------------------- 165// WHY THIS WAS ADDED (measured 2026-08-06). This detector shipped able to see duplicate config 166// FILES and completely blind to duplicate config ROWS -- and the row case is the COMMON one. It 167// bit live: nx_schema_backfill auto-appended a second `nx_prodladder` row to tool_schemas.conf 168// carrying DIFFERENT safety flags (destructive=1 open_world=1) than the hand-written row 169// (read_only=1 destructive=0). Both were true-looking, both were live, and whichever the reader 170// happens to hit decides how the fleet treats that tool. nx_confdup scanned that exact file and 171// returned collisions=3 -- none of them this one. 172// ★★AN INSTRUMENT BLIND TO THE COMMON CASE ONLY EVER SEES CRISES. 173// ★A GENERATED ROW AND A HAND-WRITTEN ROW DISAGREEING IS NOT A TIE -- IT IS TWO ANSWERS TO A 174// QUESTION THAT MUST HAVE ONE. 175// Key = field 0 (up to the first TAB or SPACE), which is how every reader in this corpus keys a 176// .conf row. Blank lines and `#` comments are skipped; a repeated key is reported with EVERY line 177// number, because the remedy needs to know which copy to delete. 178func cd_read(path: *u8, buf: *u8, cap: i64) -> i64 { 179 let fd: i64 = sys_openat_rd(path) 180 if fd < 0 { return 0 } 181 var tot: i64 = 0 182 var go: i64 = 1 183 while go == 1 { 184 let n: i64 = sys_read(fd, ((buf as i64) + tot) as *u8, cap - tot) 185 if n <= 0 { go = 0 } else { 186 tot = tot + n 187 if tot >= cap { go = 0 } 188 } 189 } 190 sys_close(fd) 191 return tot 192} 193 194func cd_rowdups(path: *u8, fbuf: *u8, keyoff: *i64, keyline: *i64, keytaken: *i64, 195 karena: *u8, out: *u8, oin: i64, dupbox: *i64) -> i64 { 196 var o: i64 = oin 197 let n: i64 = cd_read(path, fbuf, CD_FBUF) 198 if n <= 0 { return o } 199 200 var ko: i64 = 0 201 var nk: i64 = 0 202 var line: i64 = 0 203 var i: i64 = 0 204 while i < n { 205 line = line + 1 206 var e: i64 = i 207 var scan: i64 = 1 208 while scan == 1 { 209 if e >= n { scan = 0 } else { 210 if fbuf[e] == (10 as u8) { scan = 0 } else { e = e + 1 } 211 } 212 } 213 // ★FIELD 0 ENDS AT A TAB IN A TAB-DELIMITED FILE, EVEN IF IT CONTAINS SPACES. 214 // Splitting unconditionally on space MANUFACTURED a duplicate: langintel_axiskw.conf is 215 // TAB-delimited and its keys are phrases -- "stack overflow", "stack frame", "stack probe" 216 // -- which a space-split collapses into three copies of "stack". A detector that invents 217 // findings is worse than one that misses them, because it burns the reader's trust on the 218 // real ones. So: if the line has a TAB, the key ends at the TAB; fall back to SPACE only 219 // for genuinely whitespace-delimited files. 220 var hastab: i64 = 0 221 var w: i64 = i 222 while w < e { 223 if fbuf[w] == (9 as u8) { hastab = 1; w = e } else { w = w + 1 } 224 } 225 var kend: i64 = i 226 var s2: i64 = 1 227 while s2 == 1 { 228 if kend >= e { s2 = 0 } else { 229 let c: i64 = fbuf[kend] as i64 230 var stop: i64 = 0 231 if c == 9 { stop = 1 } 232 if hastab == 0 { if c == 32 { stop = 1 } } 233 if stop == 1 { s2 = 0 } else { kend = kend + 1 } 234 } 235 } 236 let klen: i64 = kend - i 237 var ok: i64 = 1 238 if klen <= 0 { ok = 0 } 239 if klen >= CD_KEY { ok = 0 } 240 if ok == 1 { if fbuf[i] == (35 as u8) { ok = 0 } } 241 if ok == 1 { if fbuf[i] == (13 as u8) { ok = 0 } } 242 if ok == 1 { 243 if nk < CD_MAXROWS { 244 if ko + klen + 2 < CD_FBUF { 245 keyoff[nk] = ko 246 var c3: i64 = 0 247 while c3 < klen { karena[ko + c3] = fbuf[i + c3]; c3 = c3 + 1 } 248 karena[ko + klen] = 0 as u8 249 ko = ko + klen + 1 250 keyline[nk] = line 251 keytaken[nk] = 0 252 nk = nk + 1 253 } 254 } 255 } 256 i = e + 1 257 } 258 259 // ★★A DUPLICATE IS ONLY SUSPICIOUS IN A FILE WHOSE KEYS ARE OTHERWISE UNIQUE. 260 // Many .conf files here are deliberately MULTI-ROW-PER-KEY tables -- a category in field 0 261 // followed by N parameter rows (body_state_profiles.conf `preg` x20, uefi_fence_deny.conf `a` x12). 262 // Those are correct BY DESIGN. Reporting them buries the one real finding under noise, which is 263 // exactly how a detector earns being ignored -- the same way this one was ignorable before. 264 // So measure the FILE'S OWN SHAPE first and only report duplicates where uniqueness is the rule. 265 // Files skipped for shape are COUNTED and reported, never silently dropped. 266 var distinct: i64 = 0 267 var z: i64 = 0 268 while z < nk { keytaken[z] = 0; z = z + 1 } 269 z = 0 270 while z < nk { 271 if keytaken[z] == 0 { 272 distinct = distinct + 1 273 let kz: *u8 = ((karena as i64) + keyoff[z]) as *u8 274 keytaken[z] = 1 275 var y: i64 = z + 1 276 while y < nk { 277 if keytaken[y] == 0 { 278 let ky: *u8 = ((karena as i64) + keyoff[y]) as *u8 279 if cd_streq(kz, ky) == 1 { keytaken[y] = 1 } 280 } 281 y = y + 1 282 } 283 } 284 z = z + 1 285 } 286 if nk > 0 { 287 if (distinct * 100) / nk < CD_UNIQPCT { dupbox[1] = dupbox[1] + 1; return o } 288 } 289 z = 0 290 while z < nk { keytaken[z] = 0; z = z + 1 } 291 292 var a: i64 = 0 293 while a < nk { 294 if keytaken[a] == 0 { 295 let ka: *u8 = ((karena as i64) + keyoff[a]) as *u8 296 var cnt: i64 = 1 297 var b: i64 = a + 1 298 while b < nk { 299 if keytaken[b] == 0 { 300 let kb: *u8 = ((karena as i64) + keyoff[b]) as *u8 301 if cd_streq(ka, kb) == 1 { cnt = cnt + 1 } 302 } 303 b = b + 1 304 } 305 if cnt > 1 { 306 dupbox[0] = dupbox[0] + 1 307 if o < CD_OUT - CD_MAGIC_2048 { 308 o = cd_cat(out, o, "COLLIDE-ROW file=" as *u8) 309 o = cd_cat(out, o, path) 310 o = cd_cat(out, o, " key=" as *u8) 311 o = cd_cat(out, o, ka) 312 o = cd_cat(out, o, " count=" as *u8) 313 o = cd_udec(out, o, cnt) 314 o = cd_cat(out, o, " lines=" as *u8) 315 o = cd_udec(out, o, keyline[a]) 316 } 317 keytaken[a] = 1 318 b = a + 1 319 while b < nk { 320 if keytaken[b] == 0 { 321 let kb2: *u8 = ((karena as i64) + keyoff[b]) as *u8 322 if cd_streq(ka, kb2) == 1 { 323 if o < CD_OUT - CD_MAGIC_2048 { 324 o = cd_ch(out, o, 44) 325 o = cd_udec(out, o, keyline[b]) 326 } 327 keytaken[b] = 1 328 } 329 } 330 b = b + 1 331 } 332 if o < CD_OUT - CD_MAGIC_2048 { o = cd_ch(out, o, 10) } 333 } 334 } 335 a = a + 1 336 } 337 return o 338} 339 340func main(argc: i64, argv: *i64) -> i64 { 341 var root: *u8 = "knowledge" as *u8 342 if argc > 2 { root = (argv[2]) as *u8 } 343 344 let q: *u8 = sys_mmap(CD_QCAP) 345 let dbuf: *u8 = sys_mmap(CD_DIRBUF) 346 let arena: *u8 = sys_mmap(CD_ARENA) 347 let pathoff: *i64 = sys_mmap(8 * CD_MAXFILES) as *i64 348 let stemoff: *i64 = sys_mmap(8 * CD_MAXFILES) as *i64 349 let taken: *i64 = sys_mmap(8 * CD_MAXFILES) as *i64 350 let path: *u8 = sys_mmap(CD_PATH) 351 let stem: *u8 = sys_mmap(CD_STEM) 352 let out: *u8 = sys_mmap(CD_OUT) 353 354 var ao: i64 = 0 // arena offset 355 var nf: i64 = 0 // file count 356 var truncated: i64 = 0 357 var dirs: i64 = 0 358 var qh: i64 = 0 359 var qt: i64 = 0 360 361 var rl: i64 = 0 362 while root[rl] != (0 as u8) { q[qt] = root[rl]; qt = qt + 1; rl = rl + 1 } 363 q[qt] = 0 as u8 364 qt = qt + 1 365 366 while qh < qt { 367 let dir: *u8 = ((q as i64) + qh) as *u8 368 var dl: i64 = 0 369 while dir[dl] != (0 as u8) { dl = dl + 1 } 370 qh = qh + dl + 1 371 dirs = dirs + 1 372 let dfd: i64 = sys_openat_rd(dir) 373 if dfd >= 0 { 374 var done: i64 = 0 375 while done == 0 { 376 let nread: i64 = sys_getdents64(dfd, dbuf, CD_DIRBUF) 377 if nread <= 0 { done = 1 } else { 378 var off: i64 = 0 379 while off < nread { 380 let drec: *u8 = ((dbuf as i64) + off) as *u8 381 let reclen: i64 = dirent_reclen(drec) 382 let dtype: i64 = dirent_type(drec) 383 let name: *u8 = dirent_name(drec) 384 var skip: i64 = 0 385 if name[0] == (46 as u8) { skip = 1 } // . .. dotdirs 386 if skip == 0 { 387 if dtype == DT_DIR { 388 let need: i64 = cd_join(path, dir, name) 389 if qt + need + 2 < CD_QCAP { 390 var c: i64 = 0 391 while c <= need { q[qt + c] = path[c]; c = c + 1 } 392 qt = qt + need + 1 393 } 394 } 395 if dtype == DT_REG { 396 if cd_is_conf(name) == 1 { 397 let pl: i64 = cd_join(path, dir, name) 398 let sl: i64 = cd_norm(stem, name) 399 if nf >= CD_MAXFILES { truncated = 1 } else { 400 if ao + pl + sl + 4 >= CD_ARENA { truncated = 1 } else { 401 pathoff[nf] = ao 402 var c2: i64 = 0 403 while c2 <= pl { arena[ao + c2] = path[c2]; c2 = c2 + 1 } 404 ao = ao + pl + 1 405 stemoff[nf] = ao 406 c2 = 0 407 while c2 <= sl { arena[ao + c2] = stem[c2]; c2 = c2 + 1 } 408 ao = ao + sl + 1 409 taken[nf] = 0 410 nf = nf + 1 411 } 412 } 413 } 414 } 415 } 416 if reclen <= 0 { off = nread } else { off = off + reclen } 417 } 418 } 419 } 420 sys_close(dfd) 421 } 422 } 423 424 var o: i64 = 0 425 o = cd_cat(out, o, "=== nx_confdup -- config files declaring the SAME CONCERN twice ===\n" as *u8) 426 o = cd_cat(out, o, "root=" as *u8) 427 o = cd_cat(out, o, root) 428 o = cd_cat(out, o, " dirs=" as *u8) 429 o = cd_udec(out, o, dirs) 430 o = cd_cat(out, o, " conf_files=" as *u8) 431 o = cd_udec(out, o, nf) 432 if truncated == 1 { o = cd_cat(out, o, " TRUNCATED=1 (raise CD_MAXFILES/CD_ARENA; NOT a silent cap)" as *u8) } 433 o = cd_ch(out, o, 10) 434 435 var groups: i64 = 0 436 var basegroups: i64 = 0 437 var i2: i64 = 0 438 while i2 < nf { 439 if taken[i2] == 0 { 440 let si: *u8 = ((arena as i64) + stemoff[i2]) as *u8 441 var members: i64 = 0 442 var j: i64 = i2 + 1 443 while j < nf { 444 if taken[j] == 0 { 445 let sj: *u8 = ((arena as i64) + stemoff[j]) as *u8 446 if cd_streq(si, sj) == 1 { members = members + 1 } 447 } 448 j = j + 1 449 } 450 if members > 0 { 451 groups = groups + 1 452 // are the BASENAMES identical too? then it is the two-roots-one-name shape 453 let pi: *u8 = ((arena as i64) + pathoff[i2]) as *u8 454 var samebase: i64 = 0 455 j = i2 + 1 456 while j < nf { 457 if taken[j] == 0 { 458 let sj2: *u8 = ((arena as i64) + stemoff[j]) as *u8 459 if cd_streq(si, sj2) == 1 { 460 let pj: *u8 = ((arena as i64) + pathoff[j]) as *u8 461 if cd_streq(cd_basename(pi), cd_basename(pj)) == 1 { samebase = 1 } 462 } 463 } 464 j = j + 1 465 } 466 if samebase == 1 { 467 basegroups = basegroups + 1 468 o = cd_cat(out, o, "COLLIDE-BASE stem=" as *u8) 469 } else { 470 o = cd_cat(out, o, "COLLIDE-STEM stem=" as *u8) 471 } 472 o = cd_cat(out, o, si) 473 o = cd_ch(out, o, 10) 474 o = cd_cat(out, o, " " as *u8) 475 o = cd_cat(out, o, pi) 476 o = cd_ch(out, o, 10) 477 taken[i2] = 1 478 j = i2 + 1 479 while j < nf { 480 if taken[j] == 0 { 481 let sj3: *u8 = ((arena as i64) + stemoff[j]) as *u8 482 if cd_streq(si, sj3) == 1 { 483 o = cd_cat(out, o, " " as *u8) 484 o = cd_cat(out, o, ((arena as i64) + pathoff[j]) as *u8) 485 o = cd_ch(out, o, 10) 486 taken[j] = 1 487 } 488 } 489 j = j + 1 490 } 491 } 492 } 493 i2 = i2 + 1 494 } 495 496 // ---- COLLIDE-ROW pass: same key twice inside ONE file (see cd_rowdups header) ---- 497 let fbuf: *u8 = sys_mmap(CD_FBUF) 498 let karena: *u8 = sys_mmap(CD_FBUF) 499 let keyoff: *i64 = sys_mmap(8 * CD_MAXROWS) as *i64 500 let keyline: *i64 = sys_mmap(8 * CD_MAXROWS) as *i64 501 let keytaken: *i64 = sys_mmap(8 * CD_MAXROWS) as *i64 502 let dupbox: *i64 = sys_mmap(16) as *i64 503 dupbox[0] = 0 504 dupbox[1] = 0 505 var fi: i64 = 0 506 while fi < nf { 507 o = cd_rowdups(((arena as i64) + pathoff[fi]) as *u8, fbuf, keyoff, keyline, keytaken, karena, out, o, dupbox) 508 fi = fi + 1 509 } 510 511 o = cd_cat(out, o, "\nNX-CONFDUP collisions=" as *u8) 512 o = cd_udec(out, o, groups) 513 o = cd_cat(out, o, " same_basename=" as *u8) 514 o = cd_udec(out, o, basegroups) 515 o = cd_cat(out, o, " row_dups=" as *u8) 516 o = cd_udec(out, o, dupbox[0]) 517 o = cd_cat(out, o, " multirow_files_skipped=" as *u8) 518 o = cd_udec(out, o, dupbox[1]) 519 o = cd_cat(out, o, " scanned=" as *u8) 520 o = cd_udec(out, o, nf) 521 var bad: i64 = groups 522 if dupbox[0] > 0 { bad = bad + dupbox[0] } 523 if bad == 0 { 524 o = cd_cat(out, o, " verdict=GREEN (no config concern is declared twice, and no file declares a key twice)\n" as *u8) 525 } else { 526 o = cd_cat(out, o, " verdict=RED (each group is a place a config row can be SILENTLY INERT, and each COLLIDE-ROW is two answers to a question that must have one)\n" as *u8) 527 } 528 sys_write(1, out, o) 529 if bad == 0 { return 0 } 530 return 1 531}