code wiki / (root) / nx_closurehash_gate.nx

nx_closurehash_gate.nx source

↩ module page · 206 lines · 9963 B

1// nx_closurehash_gate.nx -- PROVE nx_closurehash ACTUALLY READS THE CLOSURE (2026-08-07). 2// 3// The organ claims something strong: that its one hash covers every source the compiler will read. That 4// claim has an obvious trivial-wrong implementation -- hash ONLY the target file and print a number -- 5// and such a fake would pass every naive test: it resolves, it is deterministic, it emits 64 hex chars. 6// *A TEST THAT THE TRIVIAL WRONG IMPLEMENTATION ALSO PASSES MEASURES NOTHING.* 7// 8// T3 is the tooth that decides it: mutate ONE BYTE of a TRANSITIVE dependency (depth 2, never named by 9// the target) and require the closure hash to CHANGE. A target-only hasher cannot pass that, and neither 10// can a one-level hasher. 11// 12// Fixtures are built on disk under /tmp so the walk is exercised for real -- a fixture held in memory 13// would test the parser and skip the resolver, which is where the last two defects actually were 14// (a scanner matching its own source text, and a resolver blind to a third search path). 15// 16// nx_closurehash_gate [elf] elf default ./_build/nx_closurehash.sov.elf 17// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 18import "nx_tool_run.nx" 19import "nx_gate_verdict.nx" 20 21const CG_ROOT: *u8 = "/tmp/chgate" 22const CG_RT: *u8 = "/tmp/chgate/runtime" 23const CG_CAP: i64 = 65536 24const CG_MODE: i64 = 493 25 26func cgp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 27func cgn(v: i64) -> i64 { 28 var m: i64 = v 29 if m < 0 { cgp("-" as *u8); m = 0 - m } 30 let t: *u8 = sys_mmap(32) 31 var k: i64 = 0 32 if m == 0 { t[0] = 48 as u8; k = 1 } 33 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 34 let b: *u8 = sys_mmap(32) 35 var i: i64 = 0 36 while i < k { b[i] = t[k-1-i]; i = i + 1 } 37 sys_write(1, b, k) 38 return 0 39} 40func cgwrite(path: *u8, body: *u8) -> i64 { 41 let fd: i64 = sys_openat_wr(path, 420) 42 if fd < 0 { return 0 - 1 } 43 var n: i64 = 0 44 while body[n] != (0 as u8) { n = n + 1 } 45 sys_write(fd, body, n) 46 sys_close(fd) 47 return n 48} 49func cgcat(d: *u8, o: i64, s: *u8) -> i64 { var x: i64=o; var i: i64=0; while s[i]!=(0 as u8){d[x]=s[i];x=x+1;i=i+1} return x } 50// pull the 64-hex value after "closure_sha=" out of a capture. Scans every byte: the value is never at 51// offset 0, and a merged stdout+stderr capture moves it again. 52func cg_sha(buf: *u8, n: i64, out: *u8) -> i64 { 53 let k: *u8 = "closure_sha=" as *u8 54 var i: i64 = 0 55 while i + 12 <= n { 56 var m: i64 = 0 57 var hit: i64 = 1 58 while m < 12 { if buf[i+m] != k[m] { hit = 0; m = 12 } else { m = m + 1 } } 59 if hit == 1 { 60 var s: i64 = i + 12 61 var o: i64 = 0 62 while o < 64 { if s + o < n { out[o] = buf[s+o]; o = o + 1 } else { o = 64 } } 63 out[64] = 0 as u8 64 return 1 65 } 66 i = i + 1 67 } 68 return 0 69} 70func cg_num_after(buf: *u8, n: i64, key: *u8) -> i64 { 71 var ln: i64 = 0 72 while key[ln] != (0 as u8) { ln = ln + 1 } 73 var i: i64 = 0 74 while i + ln < n { 75 var m: i64 = 0 76 var hit: i64 = 1 77 while m < ln { if buf[i+m] != key[m] { hit = 0; m = ln } else { m = m + 1 } } 78 if hit == 1 { 79 var s: i64 = i + ln 80 var v: i64 = 0 81 var got: i64 = 0 82 while s < n { 83 let c: i64 = buf[s] as i64 84 if c >= 48 { if c <= 57 { v = v*10 + (c-48); got = 1; s = s + 1 } else { s = n } } else { s = n } 85 } 86 if got == 1 { return v } 87 return 0 - 1 88 } 89 i = i + 1 90 } 91 return 0 - 1 92} 93func cg_same(a: *u8, b: *u8) -> i64 { 94 var i: i64 = 0 95 while i < 64 { if a[i] != b[i] { return 0 } i = i + 1 } 96 return 1 97} 98 99// 1 if `p` can be stat'd. Used to RESOLVE THE SUBJECT rather than assume it. 100func cg_exists(p: *u8) -> i64 { 101 let sb: *u8 = sys_mmap(256) 102 let r: i64 = sys_fstatat(p, sb) 103 sys_munmap(sb, 256) 104 if r < 0 { return 0 } 105 return 1 106} 107 108func main(argc: i64, argv: *i64) -> i64 { 109 // SUBJECT RESOLUTION (fixed 2026-08-08). The default used to be the AUTHOR'S LAPTOP ABSOLUTE 110 // PATH, so this gate scored 4/4 on that one machine and could only ever fork-fail (rc=127) 111 // anywhere else -- and it did, 1/4 RED, the first time it ever ran on the NAS. 112 // *A GATE THAT HARDCODES ITS AUTHOR'S ABSOLUTE PATH IS PROVEN ONLY ON ITS AUTHOR'S MACHINE.* 113 // Probe deployed locations first, build tree last; literals are hoisted to `let` because an 114 // inline-cast string literal indexed directly never compares equal (banked NishiLang trap). 115 let c1: *u8 = "./_offc/nx_closurehash.elf" as *u8 116 let c2: *u8 = "../nx_closurehash.elf" as *u8 117 let c3: *u8 = "./nx_closurehash.elf" as *u8 118 let c4: *u8 = "./_build/nx_closurehash.sov.elf" as *u8 119 let c5: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_build/nx_closurehash.sov.elf" as *u8 120 var elf: *u8 = c5 121 var found: i64 = 0 122 if cg_exists(c5) == 1 { found = 1 } 123 if cg_exists(c4) == 1 { elf = c4; found = 1 } 124 if cg_exists(c3) == 1 { elf = c3; found = 1 } 125 if cg_exists(c2) == 1 { elf = c2; found = 1 } 126 if cg_exists(c1) == 1 { elf = c1; found = 1 } 127 if argc >= 2 { let e: *u8 = argv[1] as *u8; if e[0] != (0 as u8) { elf = e; found = cg_exists(e) } } 128 // PRINT THE SUBJECT. An absent subject must be visible as "I could not look", never silently 129 // scored as four failing teeth -- rc=127 read as a RED verdict is a diagnosis nobody made. 130 cgp("# subject=" as *u8); cgp(elf) 131 if found == 0 { cgp(" *** ABSENT -- teeth below measure NOTHING ***" as *u8) } 132 cgp("\n" as *u8) 133 134 sys_mkdir(CG_ROOT, CG_MODE) 135 sys_mkdir(CG_RT, CG_MODE) 136 137 let pa: *u8 = sys_mmap(256); var x: i64 = cgcat(pa, 0, CG_RT); x = cgcat(pa, x, "/cg_a.nx" as *u8); pa[x] = 0 as u8 138 let pb: *u8 = sys_mmap(256); var y: i64 = cgcat(pb, 0, CG_RT); y = cgcat(pb, y, "/cg_b.nx" as *u8); pb[y] = 0 as u8 139 let pc: *u8 = sys_mmap(256); var z: i64 = cgcat(pc, 0, CG_RT); z = cgcat(pc, z, "/cg_c.nx" as *u8); pc[z] = 0 as u8 140 141 // depth-2 chain: a -> b -> c. `c` is NEVER named by `a`, which is the whole point of T3. 142 cgwrite(pa, "import \"cg_b.nx\"\nfunc cg_a() -> i64 { return 1 }\n" as *u8) 143 cgwrite(pb, "import \"cg_c.nx\"\nfunc cg_b() -> i64 { return 2 }\n" as *u8) 144 cgwrite(pc, "func cg_c() -> i64 { return 3 }\n" as *u8) 145 146 let out: *u8 = sys_mmap(CG_CAP) 147 let ol: *i64 = sys_mmap(16) as *i64 148 let av: *i64 = sys_mmap(64) as *i64 149 let h1: *u8 = sys_mmap(80) 150 let h2: *u8 = sys_mmap(80) 151 let h3: *u8 = sys_mmap(80) 152 var pass: i64 = 0 153 var total: i64 = 0 154 155 // ---- T1: the depth-2 closure resolves completely and counts 3 files 156 av[0] = elf as i64; av[1] = "cg_a" as *u8 as i64; av[2] = CG_ROOT as i64; av[3] = 0 157 ol[0] = 0 158 tr_run_capture_to(elf, av, out, CG_CAP, ol, 60000) 159 let f1: i64 = cg_num_after(out, ol[0], "files=" as *u8) 160 let u1: i64 = cg_num_after(out, ol[0], "unresolved=" as *u8) 161 let g1: i64 = cg_sha(out, ol[0], h1) 162 total = total + 1 163 var t1: i64 = 0 164 if f1 == 3 { if u1 == 0 { if g1 == 1 { t1 = 1 } } } 165 if t1 == 1 { pass = pass + 1; cgp("T1 GREEN depth-2 closure complete (files=3 unresolved=0)\n" as *u8) } else { cgp("T1 RED files=" as *u8); cgn(f1); cgp(" unresolved=" as *u8); cgn(u1); cgp("\n" as *u8) } 166 167 // ---- T2: DETERMINISM -- unchanged tree must give the identical hash 168 ol[0] = 0 169 tr_run_capture_to(elf, av, out, CG_CAP, ol, 60000) 170 cg_sha(out, ol[0], h2) 171 total = total + 1 172 if cg_same(h1, h2) == 1 { pass = pass + 1; cgp("T2 GREEN deterministic across runs\n" as *u8) } else { cgp("T2 RED hash changed with no edit\n" as *u8) } 173 174 // ---- T3: ANTI-VACUITY. One byte in the DEPTH-2 dependency must move the closure hash. 175 // A hasher that reads only the target -- or only its direct imports -- passes T1, T2 and T4 and 176 // FAILS HERE. This is the tooth that makes the organ's claim mean anything. 177 cgwrite(pc, "func cg_c() -> i64 { return 4 }\n" as *u8) 178 ol[0] = 0 179 tr_run_capture_to(elf, av, out, CG_CAP, ol, 60000) 180 cg_sha(out, ol[0], h3) 181 total = total + 1 182 if cg_same(h1, h3) == 0 { pass = pass + 1; cgp("T3 GREEN one byte at DEPTH 2 changed the closure hash -- a target-only hasher cannot pass this\n" as *u8) } else { cgp("T3 RED transitive edit did NOT change the hash: the closure is not being read\n" as *u8) } 183 184 // ---- T4: a missing import must be COUNTED and must exit non-zero, never silently dropped. 185 // *A PROVENANCE RECORD THAT DROPS WHAT IT COULD NOT FIND IS A FORGERY* -- and a clean exit on an 186 // incomplete closure is exactly how a caller gating a promote would be misled. 187 cgwrite(pb, "import \"cg_missing_zz.nx\"\nfunc cg_b() -> i64 { return 2 }\n" as *u8) 188 ol[0] = 0 189 let rc: i64 = tr_run_capture_to(elf, av, out, CG_CAP, ol, 60000) 190 let u4: i64 = cg_num_after(out, ol[0], "unresolved=" as *u8) 191 total = total + 1 192 var t4: i64 = 0 193 if u4 >= 1 { if rc != 0 { t4 = 1 } } 194 if t4 == 1 { pass = pass + 1; cgp("T4 GREEN missing import COUNTED (unresolved=" as *u8); cgn(u4); cgp(") and exit non-zero\n" as *u8) } else { cgp("T4 RED unresolved=" as *u8); cgn(u4); cgp(" rc=" as *u8); cgn(rc); cgp(" -- an incomplete closure must not exit clean\n" as *u8) } 195 196 cgp("nx_closurehash_gate " as *u8); cgn(pass); cgp("/" as *u8); cgn(total); cgp("\n" as *u8) 197 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 198 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 199 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 200 let ctr__dry: *i64 = gv_ctr() 201 ctr__dry[0] = pass 202 ctr__dry[1] = total 203 let rc__dry: i64 = gv_verdict("CLOSUREHASH-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 204 sys_exit(rc__dry) 205 return rc__dry 206}