code wiki / (root) / nx_hashdiverge.nx

nx_hashdiverge.nx source

↩ module page · 326 lines · 13728 B

1// nx_hashdiverge.nx -- CONTENT-EXACT CROSS-TREE DIVERGENCE. The comparer half of nx_treehash. 2// 3// WHY IT EXISTS (measured 2026-08-06, tree-canon-single-writer lane): 4// nx_treediverge compares nx_treediff manifests, which carry SIZE. Its own usage line says the 5// verdicts are "(by BYTES, a FLOOR)" and it is right. The hourly treediverge beat therefore 6// reported a size floor as if it were the fork, for days, and one of the files it could not see 7// was an ESCAPED nx_gate_bite MUTANT in buildroot: _hdl_build/nx_media_extract.nx held 8// `if hit != 0` where the SSOT held `if hit == 0` -- one byte, 4389 bytes on BOTH sides, so every 9// plain-http: URL was silently dropped from media harvest and no size gauge could ever notice. 10// => A MUTATION-CLASS DEFECT IS SIZE-PRESERVING BY CONSTRUCTION. A size screen is not merely 11// approximate against it, it is STRUCTURALLY BLIND to the whole class. Of 18060 files present in 12// both trees, only 5 differed at equal size -- and 1 of those 5 was the mutant. 13// 14// TWO IMPROVEMENTS OVER THE ORGAN IT SUCCEEDS, both learned by being bitten in the same session: 15// 1. DETAIL GOES TO A FILE, ENVELOPE GOES TO STDOUT. Running nx_treediverge over these trees 16// returned 163,936 characters and was cut off at capture_cap mid-row, so the envelope -- the 17// only line carrying the counts -- was the part that got dropped. A comparer whose SUMMARY is 18// the first casualty of its own verbosity cannot be wired to a beat. Rows go to [outfile]. 19// 2. THE SAME-SIZE CLASS IS COUNTED SEPARATELY. It is the only class a size screen cannot reach, 20// so it is the number that says whether the cheap gauge upstream is still trustworthy. 21// 22// CLASSES: IDENT (sha equal) / DIFFSZ (sha differs, size differs -- a size screen sees these) / 23// SAMESZ (sha differs, size EQUAL -- the blind spot) / A-ONLY / B-ONLY. 24// An UNREADABLE row from nx_treehash is 64 '-', which is not hex and never equals itself, so an 25// unreadable file always classifies as divergent rather than silently matching another unreadable. 26// 27// DIALECT: plain-if, <=6 params, consts above use. Tables are mmap'd through static POINTERS -- 28// a BSS static ARRAY silently crashes the module at startup (banked gotcha). 29// license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26). 30import "nx_syscalls.nx" 31 32const HD_SLOTS: i64 = 65536 // power of two; ~18k rows = 28% load 33const HD_MASK: i64 = 65535 34const HD_SHA_CHARS: i64 = 64 35const HD_OUTBUF: i64 = 8388608 36const HD_NUMBUF: i64 = 64 37const HD_FNV_OFF: i64 = 1469598103934665603 38const HD_FNV_PRIME: i64 = 1099511628211 39const HD_SAMPLE_CAP: i64 = 40 // stdout sample rows; the FULL list always goes to [outfile] 40 41static hd_key: *i64 // rel pointer per slot (0 = empty) 42static hd_sha: *i64 43static hd_sz: *i64 44static hd_seen: *i64 45static hd_c: *i64 // [0]=ident [1]=samesz [2]=diffsz [3]=aonly [4]=bonly [5]=arows [6]=brows 46static hd_out: *u8 47static hd_out_n: *i64 48static hd_num: *u8 49static hd_rev: *u8 50static hd_shown: *i64 51 52func hd_puts(s: *u8) -> i64 { 53 var n: i64 = 0 54 while s[n] != (0 as u8) { n = n + 1 } 55 sys_write(1, s, n) 56 return 0 57} 58func hd_putn(v: i64) -> i64 { 59 var m: i64 = v 60 if m == 0 { hd_num[0] = 48 as u8; sys_write(1, hd_num, 1); return 0 } 61 if m < 0 { hd_puts("-" as *u8); m = 0 - m } 62 var k: i64 = 0 63 while m > 0 { hd_rev[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 64 var i: i64 = 0 65 while i < k { hd_num[i] = hd_rev[k - 1 - i]; i = i + 1 } 66 sys_write(1, hd_num, k) 67 return 0 68} 69func hd_slen(s: *u8) -> i64 { 70 var n: i64 = 0 71 while s[n] != (0 as u8) { n = n + 1 } 72 return n 73} 74func hd_streq(a: *u8, b: *u8) -> i64 { 75 var i: i64 = 0 76 var go: i64 = 1 77 var eq: i64 = 1 78 while go == 1 { 79 if a[i] != b[i] { eq = 0; go = 0 } else { 80 if a[i] == (0 as u8) { go = 0 } else { i = i + 1 } 81 } 82 } 83 return eq 84} 85// FNV-1a over the relpath. 86func hd_hash(s: *u8) -> i64 { 87 var h: i64 = HD_FNV_OFF 88 var i: i64 = 0 89 while s[i] != (0 as u8) { 90 h = h ^ (s[i] as i64) 91 h = h * HD_FNV_PRIME 92 i = i + 1 93 } 94 if h < 0 { h = 0 - h } 95 return h 96} 97// 64-char sha compare (fixed width, so no strlen walk). 98func hd_shaeq(a: *u8, b: *u8) -> i64 { 99 var i: i64 = 0 100 while i < HD_SHA_CHARS { if a[i] != b[i] { return 0 } i = i + 1 } 101 return 1 102} 103 104func hd_put(rel: *u8, sha: *u8, sz: *u8) -> i64 { 105 var s: i64 = hd_hash(rel) & HD_MASK 106 var go: i64 = 1 107 while go == 1 { 108 if hd_key[s] == 0 { 109 hd_key[s] = rel as i64 110 hd_sha[s] = sha as i64 111 hd_sz[s] = sz as i64 112 hd_seen[s] = 0 113 go = 0 114 } else { 115 if hd_streq(hd_key[s] as *u8, rel) == 1 { go = 0 } else { s = (s + 1) & HD_MASK } 116 } 117 } 118 return 0 119} 120// returns slot index, or -1 when absent 121func hd_find(rel: *u8) -> i64 { 122 var s: i64 = hd_hash(rel) & HD_MASK 123 var go: i64 = 1 124 while go == 1 { 125 if hd_key[s] == 0 { return 0 - 1 } 126 if hd_streq(hd_key[s] as *u8, rel) == 1 { return s } 127 s = (s + 1) & HD_MASK 128 } 129 return 0 - 1 130} 131 132func hd_emit(cls: *u8, rel: *u8, x: *u8, y: *u8) -> i64 { 133 var o: i64 = hd_out_n[0] 134 let need: i64 = 8 + hd_slen(rel) + 1 + HD_SHA_CHARS + 1 + HD_SHA_CHARS + 4 135 if o + need >= HD_OUTBUF { return 0 } 136 var i: i64 = 0 137 while cls[i] != (0 as u8) { hd_out[o] = cls[i]; o = o + 1; i = i + 1 } 138 hd_out[o] = 32 as u8; o = o + 1 139 i = 0 140 while rel[i] != (0 as u8) { hd_out[o] = rel[i]; o = o + 1; i = i + 1 } 141 if (x as i64) != 0 { 142 hd_out[o] = 32 as u8; o = o + 1 143 i = 0 144 while i < HD_SHA_CHARS { hd_out[o] = x[i]; o = o + 1; i = i + 1 } 145 } 146 if (y as i64) != 0 { 147 hd_out[o] = 32 as u8; o = o + 1 148 i = 0 149 while i < HD_SHA_CHARS { hd_out[o] = y[i]; o = o + 1; i = i + 1 } 150 } 151 hd_out[o] = 10 as u8; o = o + 1 152 hd_out_n[0] = o 153 return 0 154} 155// SAMESZ is the class a size screen cannot reach, so it is the one worth printing to stdout. 156func hd_sample(cls: *u8, rel: *u8) -> i64 { 157 if hd_shown[0] >= HD_SAMPLE_CAP { return 0 } 158 hd_puts(" " as *u8); hd_puts(cls); hd_puts(" " as *u8); hd_puts(rel); hd_puts("\n" as *u8) 159 hd_shown[0] = hd_shown[0] + 1 160 return 0 161} 162 163// Parse a treehash manifest IN PLACE: rows are "<64hex> <bytes> <relpath>\n". Terminates each field 164// by overwriting its separator, then calls hd_put (side A) or classifies (side B). 165// side 0 = load into the table, side 1 = stream and classify. 166func hd_scan(buf: *u8, n: i64, side: i64) -> i64 { 167 var p: i64 = 0 168 while p < n { 169 let base: i64 = buf as i64 170 let sha: *u8 = (base + p) as *u8 171 if p + HD_SHA_CHARS + 2 > n { return 0 } 172 buf[p + HD_SHA_CHARS] = 0 as u8 173 var q: i64 = p + HD_SHA_CHARS + 1 174 let szs: *u8 = (base + q) as *u8 175 var gq: i64 = 1 176 while gq == 1 { 177 if q >= n { gq = 0 } else { 178 if buf[q] == (32 as u8) { gq = 0 } else { q = q + 1 } 179 } 180 } 181 if q >= n { return 0 } 182 buf[q] = 0 as u8 183 var r: i64 = q + 1 184 let rel: *u8 = (base + r) as *u8 185 var gr: i64 = 1 186 while gr == 1 { 187 if r >= n { gr = 0 } else { 188 if buf[r] == (10 as u8) { gr = 0 } else { r = r + 1 } 189 } 190 } 191 if r >= n { return 0 } 192 buf[r] = 0 as u8 193 if side == 0 { 194 hd_c[5] = hd_c[5] + 1 195 hd_put(rel, sha, szs) 196 } 197 if side == 1 { 198 hd_c[6] = hd_c[6] + 1 199 let s: i64 = hd_find(rel) 200 if s < 0 { 201 hd_c[4] = hd_c[4] + 1 202 hd_emit("B-ONLY" as *u8, rel, sha, 0 as *u8) 203 } else { 204 hd_seen[s] = 1 205 let asha: *u8 = hd_sha[s] as *u8 206 let asz: *u8 = hd_sz[s] as *u8 207 if hd_shaeq(asha, sha) == 1 { hd_c[0] = hd_c[0] + 1 } else { 208 if hd_streq(asz, szs) == 1 { 209 hd_c[1] = hd_c[1] + 1 210 hd_emit("SAMESZ" as *u8, rel, asha, sha) 211 hd_sample("SAMESZ" as *u8, rel) 212 } else { 213 hd_c[2] = hd_c[2] + 1 214 hd_emit("DIFFSZ" as *u8, rel, asha, sha) 215 } 216 } 217 } 218 } 219 p = r + 1 220 } 221 return 0 222} 223 224func main(argc: i64, argv: *i64) -> i64 { 225 hd_num = sys_mmap(HD_NUMBUF) 226 hd_rev = sys_mmap(HD_NUMBUF) 227 if argc < 4 { 228 hd_puts("usage: nx_hashdiverge <manifestA> <manifestB> <outfile>\n" as *u8) 229 hd_puts(" manifests come from: nx_treehash <dir> <outfile> (one per tree)\n" as *u8) 230 hd_puts(" CONTENT-exact. Classes: IDENT / SAMESZ (the size-screen blind spot) / DIFFSZ /\n" as *u8) 231 hd_puts(" A-ONLY / B-ONLY. Rows go to <outfile>; stdout carries only the envelope + a sample,\n" as *u8) 232 hd_puts(" so the counts can never be the part that a payload cap truncates.\n" as *u8) 233 sys_exit(2) 234 return 2 235 } 236 hd_key = sys_mmap(HD_SLOTS * 8) as *i64 237 hd_sha = sys_mmap(HD_SLOTS * 8) as *i64 238 hd_sz = sys_mmap(HD_SLOTS * 8) as *i64 239 hd_seen = sys_mmap(HD_SLOTS * 8) as *i64 240 hd_c = sys_mmap(128) as *i64 241 hd_out_n = sys_mmap(16) as *i64 242 hd_shown = sys_mmap(16) as *i64 243 hd_out = sys_mmap(HD_OUTBUF) 244 hd_out_n[0] = 0 245 hd_shown[0] = 0 246 var z: i64 = 0 247 while z < 8 { hd_c[z] = 0; z = z + 1 } 248 249 let la: *i64 = sys_mmap(16) as *i64 250 let ba: *u8 = sys_read_file(argv[1] as *u8, la) 251 if (ba as i64) == 0 { 252 hd_puts("# HASHDIVERGE RED -- cannot read manifestA\n" as *u8) 253 sys_exit(3); return 3 254 } 255 let lb: *i64 = sys_mmap(16) as *i64 256 let bb: *u8 = sys_read_file(argv[2] as *u8, lb) 257 if (bb as i64) == 0 { 258 hd_puts("# HASHDIVERGE RED -- cannot read manifestB\n" as *u8) 259 sys_exit(3); return 3 260 } 261 hd_puts("=== nx_hashdiverge -- CONTENT-exact cross-tree divergence ===\n" as *u8) 262 hd_scan(ba, la[0], 0) 263 hd_scan(bb, lb[0], 1) 264 // leftovers in the table were never matched by B 265 var s: i64 = 0 266 while s < HD_SLOTS { 267 if hd_key[s] != 0 { 268 if hd_seen[s] == 0 { 269 hd_c[3] = hd_c[3] + 1 270 hd_emit("A-ONLY" as *u8, hd_key[s] as *u8, hd_sha[s] as *u8, 0 as *u8) 271 } 272 } 273 s = s + 1 274 } 275 let fd: i64 = sys_openat_wr(argv[3] as *u8, 0x1a4) 276 if fd < 0 { 277 hd_puts("# HASHDIVERGE RED -- cannot open outfile\n" as *u8) 278 sys_exit(3); return 3 279 } 280 sys_write(fd, hd_out, hd_out_n[0]) 281 sys_close(fd) 282 let inboth: i64 = hd_c[0] + hd_c[1] + hd_c[2] 283 let divergent: i64 = hd_c[1] + hd_c[2] + hd_c[3] + hd_c[4] 284 hd_puts("# HASHDIVERGE arows=" as *u8); hd_putn(hd_c[5]) 285 hd_puts(" brows=" as *u8); hd_putn(hd_c[6]) 286 hd_puts(" inboth=" as *u8); hd_putn(inboth) 287 hd_puts(" ident=" as *u8); hd_putn(hd_c[0]) 288 hd_puts(" samesz=" as *u8); hd_putn(hd_c[1]) 289 hd_puts(" diffsz=" as *u8); hd_putn(hd_c[2]) 290 hd_puts(" aonly=" as *u8); hd_putn(hd_c[3]) 291 hd_puts(" bonly=" as *u8); hd_putn(hd_c[4]) 292 hd_puts(" divergent=" as *u8); hd_putn(divergent) 293 hd_puts("\n# rows written: " as *u8); hd_puts(argv[3] as *u8) 294 hd_puts(" bytes=" as *u8); hd_putn(hd_out_n[0]); hd_puts("\n" as *u8) 295 // A CONVERGENCE PROOF OVER THE EMPTY SET IS THE MOST DANGEROUS GREEN THERE IS: it is textually 296 // identical to a real proof, and it is produced by having looked at NOTHING. 297 // MEASURED 2026-08-16: nx_treehash hashes *.nx ONLY, so pointing it at a tree of .matrix/.verdict 298 // files yields a manifest with ZERO rows and its own verdict=COMPLETE. Feeding two such manifests 299 // to this organ printed "verdict=CONVERGED (every path present in both trees is byte-identical)" 300 // -- a confident no-divergence answer about two trees from which not one file was ever read. The 301 // producer was behaving correctly and so was the arithmetic (0 divergent of 0 compared); the defect 302 // is that ZERO COMPARISONS AND ZERO DISAGREEMENTS ARE INDISTINGUISHABLE IN A TWO-STATE VERDICT. 303 // AN INSTRUMENT THAT CANNOT SEE MUST ABSTAIN, NOT ACQUIT -- so empty input gets its own state. 304 // Exit 3 matches this organ's existing RED-exit convention; CONVERGED and DIVERGENT keep exit 0 so 305 // no existing caller's contract moves (rule 19). That DIVERGENT also exits 0 is a separate, 306 // pre-existing weakness -- named here rather than silently changed underneath a caller. 307 var unproven: i64 = 0 308 if hd_c[5] == 0 { unproven = 1 } 309 if hd_c[6] == 0 { unproven = 1 } 310 if unproven == 1 { 311 hd_puts("# verdict=UNPROVEN -- a manifest is EMPTY, so NOTHING was compared. This is not convergence.\n" as *u8) 312 hd_puts("# CHECK THE PRODUCER'S SCOPE FIRST: nx_treehash hashes *.nx ONLY, so a tree of .matrix,\n" as *u8) 313 hd_puts("# .conf or .verdict files legitimately yields zero rows -- the manifest is empty because\n" as *u8) 314 hd_puts("# the filter excluded everything, NOT because the two trees agree.\n" as *u8) 315 sys_exit(3) 316 return 3 317 } 318 if divergent == 0 { hd_puts("# verdict=CONVERGED (every path present in both trees is byte-identical)\n" as *u8) } 319 if divergent != 0 { 320 hd_puts("# verdict=DIVERGENT -- direction is a PER-FILE measurement, never a per-tree policy.\n" as *u8) 321 hd_puts("# SAMESZ is the class no size screen can reach: a mutation-class defect preserves size\n" as *u8) 322 hd_puts("# by construction. Body-diff every SAMESZ row before assuming which side is ahead.\n" as *u8) 323 } 324 sys_exit(0) 325 return 0 326}