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}