code wiki / _hdl_build / nx_charjudge_gate.nx

nx_charjudge_gate.nx source

↩ module page · 404 lines · 18460 B

1// nx_charjudge_gate.nx -- the LIAR-KILLER battery for the v2 character judge, all fixtures SYNTHETIC + 2// deterministic (no file deps): a composed "character" (smooth bg + textured figure + border contours + 3// face patch with an eye pair), its eyeless twin, a flat blob, an aligned-16px patch-scramble of the 4// composed image, and the smooth+noise+palette COLLAGE that games v1. Teeth assert the HUMAN ordering, 5// per-axis kills, determinism, and the conf override path. Designing this battery already killed two 6// contour-axis designs before they shipped (plain run-length: dense noise is fully connected; scramble 7// seams are long lines) -- that is the gate doing its job at design time. license_tier: ORIGINAL expect_exit: 0 8import "nx_syscalls.nx" 9import "nx_charjudge_lib.nx" 10import "nx_gate_verdict.nx" 11const CHG_W: i64 = 256 12const CHG_H: i64 = 256 13const CHG_LCG_A: i64 = 1103515245 14const CHG_LCG_C: i64 = 12345 15const CHG_LCG_M: i64 = 2147483647 16// ★A SECOND RESOLUTION. Every tooth in this battery ran at 256x256 -- which is EXACTLY ref_w, the width the 17// eye geometry was calibrated at, where the scale factor is 1.0 and the pyramid is irrelevant. Both defects 18// found on 2026-08-15 lived entirely outside this single resolution, which is how 8/8 GREEN coexisted with a 19// judge that scored an actual face close-up 0 and a faceless voxel landscape 1000. A battery that has only 20// ever seen one resolution cannot report on a scale bug, and its greenness is not evidence about one. 21const CHG_W2: i64 = 512 22const CHG_H2: i64 = 512 23 24func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 25func gn(v: i64) -> i64 { 26 let b: *u8 = sys_mmap(32) 27 var x: i64 = v; var ng: i64 = 0 28 if x < 0 { ng = 1; x = 0-x } 29 var i: i64 = 31 30 if x == 0 { b[i]=48 as u8; i=i-1 } 31 while x > 0 { b[i]=(48+x%10) as u8; x=x/10; i=i-1 } 32 if ng == 1 { b[i]=45 as u8; i=i-1 } 33 sys_write(1,(b as i64+i+1) as *u8,31-i) 34 return 0 35} 36func chg_px(r: i64, g: i64, b: i64) -> i64 { 37 var r2: i64 = r 38 var g2: i64 = g 39 var b2: i64 = b 40 if r2 < 0 { r2 = 0 } 41 if r2 > 255 { r2 = 255 } 42 if g2 < 0 { g2 = 0 } 43 if g2 > 255 { g2 = 255 } 44 if b2 < 0 { b2 = 0 } 45 if b2 > 255 { b2 = 255 } 46 return r2 + g2*256 + b2*CHJ_MAGIC_65536 47} 48// composed character fixture. eyes=1 draws the eye pair. 49func chg_composed(fb: *i64, eyes: i64) -> i64 { 50 var s: i64 = 777 51 var y: i64 = 0 52 while y < CHG_H { 53 var x: i64 = 0 54 while x < CHG_W { 55 fb[y*CHG_W+x] = chg_px(40 + y, 80 + y/2, 250 - y/2) 56 x = x + 1 57 } 58 y = y + 1 59 } 60 y = 48 61 while y < 224 { 62 var x: i64 = 64 63 while x < 192 { 64 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 65 let nz: i64 = (s>>15) % 51 - 25 66 fb[y*CHG_W+x] = chg_px(60 + y%128 + nz, 50 + (x+y)%150 + nz, 90 + x%120 + nz) 67 x = x + 1 68 } 69 y = y + 1 70 } 71 // face patch (flat skin) so the eyes are the only compact dark blobs there 72 y = 64 73 while y < 120 { 74 var x: i64 = 96 75 while x < 176 { 76 fb[y*CHG_W+x] = chg_px(230, 200, 180) 77 x = x + 1 78 } 79 y = y + 1 80 } 81 if eyes == 1 { 82 var ey: i64 = 0-3 83 while ey <= 3 { 84 var ex: i64 = 0-3 85 while ex <= 3 { 86 if ex*ex + ey*ey <= 9 { 87 fb[(89+ey)*CHG_W + 113+ex] = chg_px(25, 20, 20) 88 fb[(89+ey)*CHG_W + 145+ex] = chg_px(25, 20, 20) 89 } 90 ex = ex + 1 91 } 92 ey = ey + 1 93 } 94 } 95 // 2px dark border = the long contours 96 y = 48 97 while y < 224 { 98 fb[y*CHG_W+64] = chg_px(30,30,40) 99 fb[y*CHG_W+65] = chg_px(30,30,40) 100 fb[y*CHG_W+190] = chg_px(30,30,40) 101 fb[y*CHG_W+191] = chg_px(30,30,40) 102 y = y + 1 103 } 104 var x5: i64 = 64 105 while x5 < 192 { 106 fb[48*CHG_W+x5] = chg_px(30,30,40) 107 fb[49*CHG_W+x5] = chg_px(30,30,40) 108 fb[222*CHG_W+x5] = chg_px(30,30,40) 109 fb[223*CHG_W+x5] = chg_px(30,30,40) 110 x5 = x5 + 1 111 } 112 return 0 113} 114func chg_blob(fb: *i64) -> i64 { 115 var y: i64 = 0 116 while y < CHG_H { 117 var x: i64 = 0 118 while x < CHG_W { 119 fb[y*CHG_W+x] = chg_px(40 + y, 80 + y/2, 250 - y/2) 120 x = x + 1 121 } 122 y = y + 1 123 } 124 y = 48 125 while y < 224 { 126 var x: i64 = 64 127 while x < 192 { 128 fb[y*CHG_W+x] = chg_px(200, 170, 150) 129 x = x + 1 130 } 131 y = y + 1 132 } 133 y = 48 134 while y < 224 { 135 fb[y*CHG_W+64] = chg_px(30,30,40) 136 fb[y*CHG_W+191] = chg_px(30,30,40) 137 y = y + 1 138 } 139 var x6: i64 = 64 140 while x6 < 192 { 141 fb[48*CHG_W+x6] = chg_px(30,30,40) 142 fb[223*CHG_W+x6] = chg_px(30,30,40) 143 x6 = x6 + 1 144 } 145 return 0 146} 147func chg_scramble(src: *i64, dst: *i64) -> i64 { 148 let P: i64 = 16 149 let nx: i64 = CHG_W/P 150 let ny: i64 = CHG_H/P 151 let n: i64 = nx*ny 152 let perm: *i64 = sys_mmap(n*8) as *i64 153 var i: i64 = 0 154 while i < n { perm[i] = i; i = i + 1 } 155 var s: i64 = 80404 156 i = n - 1 157 while i > 0 { 158 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 159 let j: i64 = s % (i+1) 160 let t: i64 = perm[i]; perm[i] = perm[j]; perm[j] = t 161 i = i - 1 162 } 163 i = 0 164 while i < n { 165 let src2: i64 = perm[i] 166 let dx: i64 = (i%nx)*P 167 let dy: i64 = (i/nx)*P 168 let sx: i64 = (src2%nx)*P 169 let sy: i64 = (src2/nx)*P 170 var yy: i64 = 0 171 while yy < P { 172 var xx: i64 = 0 173 while xx < P { dst[(dy+yy)*CHG_W + dx+xx] = src[(sy+yy)*CHG_W + sx+xx]; xx = xx + 1 } 174 yy = yy + 1 175 } 176 i = i + 1 177 } 178 return 0 179} 180func chg_collage(fb: *i64) -> i64 { 181 var s: i64 = 4242 182 var y: i64 = 0 183 while y < CHG_H { 184 var x: i64 = 0 185 while x < CHG_W { 186 if x < CHG_W/2 { fb[y*CHG_W+x] = chg_px(120, 140, 160) } 187 if x >= CHG_W/2 { 188 // high bits: an LCG's LOW bits have period 2^k -- s%256 gave PERIODIC stripes, i.e. 189 // real coherent contours, and the "noise" collage wasn't noise (gate T4 caught it) 190 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 191 let r: i64 = (s>>15) % 256 192 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 193 let g: i64 = (s>>15) % 256 194 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 195 let b: i64 = (s>>15) % 256 196 fb[y*CHG_W+x] = chg_px(r, g, b) 197 } 198 x = x + 1 199 } 200 y = y + 1 201 } 202 return 0 203} 204// nearest-neighbour 2x upscale: the SAME subject, twice the width. Nearest-neighbour on purpose -- it adds 205// no new detail, so any score change is the judge reacting to size rather than to content. 206func chg_upscale2(src: *i64, dst: *i64) -> i64 { 207 var y: i64 = 0 208 while y < CHG_H2 { 209 var x: i64 = 0 210 while x < CHG_W2 { 211 dst[y*CHG_W2+x] = src[(y/2)*CHG_W + x/2] 212 x = x + 1 213 } 214 y = y + 1 215 } 216 return 0 217} 218// the blob fixture with SURFACE TEXTURE laid over the figure, inside its border. This is the case the 219// contour axis inverted on: two real A/B pairs of the same body measured an 8.9x and 6.8x drop in the 220// headline purely for having been textured. 221func chg_textured_blob(fb: *i64) -> i64 { 222 chg_blob(fb) 223 var s: i64 = 9091 224 var y: i64 = 49 225 while y < 223 { 226 var x: i64 = 65 227 while x < 191 { 228 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 229 let nz: i64 = (s>>15) % 61 - 30 230 fb[y*CHG_W+x] = chg_px(200+nz, 170+nz, 150+nz) 231 x = x + 1 232 } 233 y = y + 1 234 } 235 return 0 236} 237// NEGATIVE CONTROL for the smoothing change. Same texture, but NO figure and NO border contours. Smoothing 238// before edge extraction must not turn "has texture" into "has contours": if the fix were achieved by making 239// the axis permissive rather than by making it correct, this fixture would rise with the textured blob and 240// this tooth is what says so. 241func chg_texture_only(fb: *i64) -> i64 { 242 var s: i64 = 5150 243 var y: i64 = 0 244 while y < CHG_H { 245 var x: i64 = 0 246 while x < CHG_W { 247 s = (s*CHG_LCG_A + CHG_LCG_C) & CHG_LCG_M 248 let nz: i64 = (s>>15) % 61 - 30 249 fb[y*CHG_W+x] = chg_px(200+nz, 170+nz, 150+nz) 250 x = x + 1 251 } 252 y = y + 1 253 } 254 return 0 255} 256// ★MIGRATED ONTO THE BASE CLASS 2026-08-15. This gate hand-rolled its own PASS/FAIL printer and its own 257// failure counter -- the D001 debt: declared and executed tooth counts were two independent numbers, so a 258// tooth that silently stopped running lowered BOTH and the gate still read green. gv_check increments the 259// denominator itself, so declared == executed BY CONSTRUCTION, and gv_verdict carries the result in the 260// EXIT CODE -- without which /api/gate_run derives its verdict from a passing exit and serves a RED gate as 261// GREEN. The build lane refused this file until it emitted that anchor, and it was right to. 262 263func main() -> i64 { 264 gw("=== nx_charjudge_gate -- liar-killer battery for the v2 character judge ===\n" as *u8) 265 let ctr: *i64 = gv_ctr() 266 let npx: i64 = CHG_W*CHG_H 267 let fA: *i64 = sys_mmap(npx*8) as *i64 268 let fB: *i64 = sys_mmap(npx*8) as *i64 269 let fS: *i64 = sys_mmap(npx*8) as *i64 270 let fC: *i64 = sys_mmap(npx*8) as *i64 271 let fE: *i64 = sys_mmap(npx*8) as *i64 272 chg_composed(fA, 1) 273 chg_blob(fB) 274 chg_scramble(fA, fS) 275 chg_collage(fC) 276 chg_composed(fE, 0) 277 let oA: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 278 let oA2: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 279 let oB: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 280 let oS: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 281 let oC: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 282 let oE: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 283 // HERMETIC CONF. This battery's own header promised "all fixtures SYNTHETIC + deterministic (no file 284 // deps)" and then read the PRODUCTION conf, so every tooth silently depended on a file another lane 285 // edits -- and today that file legitimately changed face_required, which would have turned two teeth red 286 // for a reason having nothing to do with the code. It writes its own conf now: face_required=1 so the 287 // detector is genuinely exercised here, every other value from the code defaults. 288 let gfd: i64 = sys_openat_wr("/tmp/chj_gate.conf" as *u8, MODE_0644) 289 let gstr: *u8 = "face_required=1\n" as *u8 290 var gcn: i64 = 0 291 while gstr[gcn] != (0 as u8) { gcn = gcn + 1 } 292 sys_write(gfd, gstr, gcn) 293 sys_close(gfd) 294 let cp: *u8 = "/tmp/chj_gate.conf" as *u8 295 chj_judge(fA, CHG_W, CHG_H, cp, oA) 296 chj_judge(fA, CHG_W, CHG_H, cp, oA2) 297 chj_judge(fB, CHG_W, CHG_H, cp, oB) 298 chj_judge(fS, CHG_W, CHG_H, cp, oS) 299 chj_judge(fC, CHG_W, CHG_H, cp, oC) 300 chj_judge(fE, CHG_W, CHG_H, cp, oE) 301 let fU: *i64 = sys_mmap(CHG_W2*CHG_H2*8) as *i64 302 let fT: *i64 = sys_mmap(npx*8) as *i64 303 let fTO: *i64 = sys_mmap(npx*8) as *i64 304 chg_upscale2(fA, fU) 305 chg_textured_blob(fT) 306 chg_texture_only(fTO) 307 let oU: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 308 let oT: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 309 let oTO: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 310 chj_judge(fU, CHG_W2, CHG_H2, cp, oU) 311 chj_judge(fT, CHG_W, CHG_H, cp, oT) 312 chj_judge(fTO, CHG_W, CHG_H, cp, oTO) 313 gw("composed: head=" as *u8); gn(oA[4]); gw(" comp=" as *u8); gn(oA[0]); gw(" pal=" as *u8); gn(oA[1]); gw(" con=" as *u8); gn(oA[2]); gw(" face=" as *u8); gn(oA[3]); gw("\n" as *u8) 314 gw("blob: head=" as *u8); gn(oB[4]); gw(" comp=" as *u8); gn(oB[0]); gw(" face=" as *u8); gn(oB[3]); gw("\n" as *u8) 315 gw("scramble: head=" as *u8); gn(oS[4]); gw(" comp=" as *u8); gn(oS[0]); gw(" con=" as *u8); gn(oS[2]); gw("\n" as *u8) 316 gw("collage: head=" as *u8); gn(oC[4]); gw(" comp=" as *u8); gn(oC[0]); gw(" con=" as *u8); gn(oC[2]); gw("\n" as *u8) 317 gw("eyeless: head=" as *u8); gn(oE[4]); gw(" face=" as *u8); gn(oE[3]); gw("\n" as *u8) 318 var t: i64 = 0 319 if oA[4] >= 600 { t = 1 } 320 gv_check("T1 composed character scores >= 600" as *u8, t, ctr) 321 t = 0 322 if oB[3] == 0 { if oB[4] == 0 { t = 1 } } 323 gv_check("T2 faceless flat blob floors to 0 (face axis)" as *u8, t, ctr) 324 // a 16px-grid scramble GENUINELY contains long straight seam lines, so contour cannot floor it to 325 // zero -- the honest claim is a deep drop below the composed original (real-content scrambles 326 // measured 20/1000 on the kk portrait; this synthetic bg has gentler seams) 327 t = 0 328 if oS[4] < 300 { if oS[4]*3 < oA[4] { t = 1 } } 329 gv_check("T3 16px scramble drops below 300 and below composed/3" as *u8, t, ctr) 330 t = 0 331 if oC[2] < 100 { if oC[4] < 100 { t = 1 } } 332 gv_check("T4 smooth+noise+palette collage floors (contour kills the v1 gameable vector)" as *u8, t, ctr) 333 t = 0 334 if oE[3] == 0 { if oE[4] == 0 { if oA[3] == 1000 { t = 1 } } } 335 gv_check("T5 face axis isolated: eyeless twin 0, eyed twin 1000" as *u8, t, ctr) 336 t = 0 337 if oA[4] == oA2[4] { t = 1 } 338 gv_check("T6 deterministic (same fixture twice, same headline)" as *u8, t, ctr) 339 // T7: conf override path -- face_required=0 lifts the eyeless twin off the floor 340 let cfd: i64 = sys_openat_wr("/tmp/chj_face_off.conf" as *u8, 0x1a4) 341 let cstr: *u8 = "face_required=0\n" as *u8 342 var cn: i64 = 0 343 while cstr[cn] != (0 as u8) { cn = cn + 1 } 344 sys_write(cfd, cstr, cn) 345 sys_close(cfd) 346 let oE2: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 347 chj_judge(fE, CHG_W, CHG_H, "/tmp/chj_face_off.conf" as *u8, oE2) 348 t = 0 349 if oE2[4] > 0 { t = 1 } 350 gv_check("T7 conf override proven live (face_required=0 lifts the eyeless twin)" as *u8, t, ctr) 351 t = 0 352 if oA[4] > oB[4] { if oA[4] > oS[4] { if oA[4] > oC[4] { t = 1 } } } 353 gv_check("T8 strict ordering: composed above blob, scramble, collage" as *u8, t, ctr) 354 // PRINT THE VALUES, NOT JUST PASS/FAIL. Both defects fixed on 2026-08-15 were found in a diagnostic 355 // dump and never in a verdict vector; a tooth that reports only a boolean cannot say why it moved. 356 gw("upscale2x: head=" as *u8); gn(oU[4]); gw(" face=" as *u8); gn(oU[3]); gw(" lv=" as *u8); gn(oU[10]); gw(" cands=" as *u8); gn(oU[11]); gw("\n" as *u8) 357 gw("blob_plain: con=" as *u8); gn(oB[2]); gw(" coh=" as *u8); gn(oB[8]); gw(" edges=" as *u8); gn(oB[9]); gw("\n" as *u8) 358 gw("blob_tex: con=" as *u8); gn(oT[2]); gw(" coh=" as *u8); gn(oT[8]); gw(" edges=" as *u8); gn(oT[9]); gw("\n" as *u8) 359 gw("tex_only: con=" as *u8); gn(oTO[2]); gw(" coh=" as *u8); gn(oTO[8]); gw(" edges=" as *u8); gn(oTO[9]); gw("\n" as *u8) 360 // T9 -- THE SCALE DEFECT. The same subject at twice the width. The probe radius was scaled by the 361 // CURRENT pyramid level's width, so it shrank in lockstep with the image the pyramid was halving and 362 // every level re-ran an identical scan: a face larger than the calibration frame was unreachable at 363 // any depth. Asserting BOTH twins pins the claim to scale rather than to the fixture. 364 t = 0 365 if oU[3] == 1000 { if oA[3] == 1000 { t = 1 } } 366 gv_check("T9 scale invariance: the SAME composed character at 2x width still scores face 1000" as *u8, t, ctr) 367 // T10 -- THE TEXTURE INVERSION, the defect that made this judge unusable as a referee: it graded the 368 // estate's own shipped PBR skin bake as a large regression. The bar is a RATIO of the untextured twin 369 // rather than an absolute number, so it stays meaningful if the fixtures are ever retuned. 370 // T10 -- THE TEXTURE INVERSION, as a BITE-PROVEN cell rather than as two independent booleans. A pass 371 // requires BOTH directions at once: the axis must still FIRE on a genuinely contourless texture field 372 // AND stay silent on a textured figure that really does have contours. Two separate teeth could each be 373 // satisfied by an axis that simply became permissive; only the paired form proves DISCRIMINATION. 374 var tbad: i64 = 0 375 if oTO[2] < 100 { tbad = 1 } 376 var tgood: i64 = 0 377 if oT[2]*2 < oB[2] { tgood = 1 } 378 gv_bite("T10 neg-control-texture: contour floors a contourless texture field but NOT a textured figure" as *u8, tbad, tgood, ctr) 379 // T11 -- THE DEMOTION IS REAL AND COMPLETE. Under the PRODUCTION conf (face_required=0) the composed 380 // character and its eyeless twin must score the SAME nonzero headline. If they differ, the face axis is 381 // still leaking into the verdict and the demotion is cosmetic; if it is zero, the headline collapsed for 382 // some other reason. This is the tooth that catches a future seat re-arming an uncalibrated axis. 383 let oPA: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 384 let oPE: *i64 = sys_mmap(CHJ_NOUT*8) as *i64 385 chj_judge(fA, CHG_W, CHG_H, "knowledge/charjudge.conf" as *u8, oPA) 386 chj_judge(fE, CHG_W, CHG_H, "knowledge/charjudge.conf" as *u8, oPE) 387 gw("prod_conf: composed_head=" as *u8); gn(oPA[4]); gw(" eyeless_head=" as *u8); gn(oPE[4]); gw(" face_reported=" as *u8); gn(oPA[3]); gw("\n" as *u8) 388 // The first form of this tooth asserted the two headlines were EQUAL and failed 986 vs 963 -- correctly, 389 // because the eyeless twin is a DIFFERENT IMAGE and its composition, palette and contour legitimately 390 // differ. Equality was never the property. The property is that the face axis, while still computed and 391 // still reporting 0 on this fixture, no longer selects the minimum. 392 t = 0 393 var pmin: i64 = oPE[0] 394 if oPE[1] < pmin { pmin = oPE[1] } 395 if oPE[2] < pmin { pmin = oPE[2] } 396 if oPE[4] == pmin { if oPE[4] > 0 { if oPE[3] == 0 { t = 1 } } } 397 gv_check("T11 face axis demoted in production: eyeless twin scores MIN(comp,pal,contour), nonzero, with face still computed and reported as 0" as *u8, t, ctr) 398 // The count is no longer hand-recited. "GREEN 11/11" was a second, independently-authored number beside 399 // the counter that actually ran -- the duplicate-ruler defect, and the reason a gate can print a total 400 // it never reached. gv_verdict prints the counter it incremented. 401 let rc: i64 = gv_verdict("CHARJUDGE-GATE" as *u8, ctr, "v2 character judge -- composition x palette x contour x face, with the 2026-08-15 texture-inversion and scale-cancellation fixes each under a named negative control" as *u8) 402 sys_exit(rc) 403 return rc 404}