nx_confdup.nx
buildroot/runtime/nx_confdup.nx
about
nx_confdup.nx -- find CONFIG FILES THAT DECLARE THE SAME CONCERN TWICE.
WHY THIS EXISTS (measured 2026-07-30, media-vault lane): /api/promote reads
`knowledge/organ_kinds.conf` (PLURAL, values hyphenated `one-shot`) while
nx_organkind reads `knowledge/status/organ_kind.conf` (SINGULAR, values
unhyphenated `oneshot`). I added a row to the singular file, promote kept
working, and I concluded the row was the fix -- it was INERT. Undeclared
names fall through to a name-substring heuristic, so the wrong file "worked"
by luck until a name that failed the heuristic (nx_mvault_walk) became
unshippable by any sanctioned route.
★★★LAW: A SILENTLY-INERT CONFIG ROW IS WORSE THAN A MISSING ONE -- it
manufactures false confidence. The failure is invisible precisely because
the other source of truth keeps answering.
This is a RECURRING class in this ecosystem, not a one-off:
* organ_kind.conf vs organ_kinds.conf (this session)
* nx_fs root=nishihost vs /api/build buildroot/runtime -- TWO ROOTS ONE NAME
* the ruler reading the wrong evidence root (rollup saw 181 of 1164 logs)
So it gets a DETECTOR rather than another banked warning.
WHAT IT FLAGS -- two independent signals, reported separately because they
have different remedies:
COLLIDE-STEM two config files whose names normalize to the SAME stem
(plural/singular, hyphen/underscore, trailing digits).
Remedy: one file, or make one provably derived.
COLLIDE-BASE the SAME basename living under two different directories.
Remedy: name the roots apart, or collapse them.
Normalization is deliberately AGGRESSIVE (drop ext, lowercase, '-'->'_',
strip trailing digits/underscores, strip ONE trailing plural 's') because a
false positive costs a glance while a false negative costs a session.
nx_confdup scan [root] (default root: knowledge)
Read-only. license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 1 imports · 0 importers
imports: nx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 40 | const CD_QCAP: i64 = 1048576 // dir-queue arena |
| 41 | const CD_DIRBUF: i64 = 262144 // getdents buffer |
| 42 | const CD_ARENA: i64 = 1048576 // path + stem text arena |
| 43 | const CD_MAXFILES: i64 = 8192 // bounded, and the cap is REPORTED not silent |
| 44 | const CD_PATH: i64 = 4096 |
| 45 | const CD_STEM: i64 = 256 |
| 46 | const CD_OUT: i64 = 262144 |
functions
| 48 | func cd_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 50 | func cd_cat(dst: *u8, off: i64, s: *u8) -> i64 |
| 57 | func cd_ch(dst: *u8, off: i64, c: i64) -> i64 { dst[off] = c as u8; return off + 1 } |
| 59 | func cd_udec(dst: *u8, off: i64, v: i64) -> i64 |
| 73 | func cd_join(dst: *u8, dir: *u8, name: *u8) -> i64 |
| 82 | func cd_basename(p: *u8) -> *u8 |
| 94 | func cd_is_conf(name: *u8) -> i64 |
| 108 | func cd_norm(dst: *u8, name: *u8) -> i64 |
| 149 | func cd_streq(a: *u8, b: *u8) -> i64 called by 1: main |
| 159 | func main(argc: i64, argv: *i64) -> i64 |