nx_glbnorm_gate.nx
buildroot/runtime/nx_glbnorm_gate.nx
about
nx_glbnorm_gate.nx -- proves that every NORMAL nx_gltf_export writes is a UNIT vector per glTF 2.0,
and proves it BY DECODING THE ACTUAL binary32 BYTES OUT OF THE BIN CHUNK OF A FILE THE SUBJECT WROTE.
WHY THIS GATE HAD TO EXIST. Until 2026-08-25 every .glb this estate published was invalid. The number
came from OUTSIDE: KhronosGroup/glTF-Validator 2.0.0-dev.3.10 under node, over all 18 .glb files pulled
from https://nishifamily.com by curl, uncapped -- files=18 clean=0 with_errors=18 threw=0 sum=18, and
33,682 errors, of which ACCESSOR_VECTOR3_NON_UNIT x 33,656. SIX of our own gates ran GREEN over that
same code the whole time. They were not weak gates; they were gates that measured our code against our
own expectations, and the expectation itself was the bug ("renderers re-normalise" -- a comment, never a
measurement). This gate exists so the estate can see that class WITHOUT an external referee: it decodes
the emitted bytes and applies a tolerance DERIVED FROM THE REFEREE'S OWN SOURCE LINE
if (Math.abs(Math.sqrt(r) - 1) > 0.00674) -> ACCESSOR_VECTOR3_NON_UNIT
as GN_TOL_FP. A green here is a claim about the SPEC, not about our taste.
THE ANTI-VACUITY CONTROL IS THE WHOLE POINT. gg_prefix_f32 below is a VERBATIM TRANSCRIPTION of the
emission this fix replaced. T2 shows it FAILS the identical predicate the fixed path passes, so the
tooth can tell a fix from a no-op. T2b proves the transcription is faithful by sweeping it against the
still-live gl_i2f32 rather than trusting that it was copied correctly.
WHY THIS FILE IS ALL SMALL FUNCTIONS AND main() HOLDS ALMOST NOTHING. NOT STYLE -- A MEASURED LANDMINE.
The first draft did the whole job inline in main(), which reached roughly 85 local bindings, and the
compiler then MISCOMPILED SOME OF THEM WITH NO DIAGNOSTIC AT ALL. Measured, from that build's own
output, with the constants and the inline product printed beside the binding:
DUMP GG_NV = 145 ... GG_VEC3_F32_BYTES = 12 ... GG_NV * GG_VEC3_F32_BYTES inline = 1740
DUMP gg_pb = 1756 <- let gg_pb: i64 = GG_NV * GG_VEC3_F32_BYTES
DUMP gg_nb = 5244 <- let gg_nb: i64 = GG_NV * GG_VEC3_F32_BYTES (the SAME expression)
Two `let`s of one constant expression, in one function, disagreeing with each other AND with that
expression evaluated inline one line away. Everything computed from a RUNTIME value in the same
function (jlen 724, binoff 752, binlen 6384, fsz 7136) stayed correct and self-consistent throughout.
The cost of that landmine was a full RED that looked exactly like a broken fix: the gate read the
normal block 8 bytes late and reported 132 of 145 normals non-unit, while the writer had been correct
all along -- the real block sat at 752+1740, exactly where it belongs.
***A MISCOMPILED LOCAL IS A SILENT WRONG ANSWER THAT INDICTS THE SUBJECT INSTEAD OF THE INSTRUMENT.***
So every function here stays small, and the BIN layout is derived ONCE, in gg_layout, from PARAMETERS
rather than from constant-expression locals.
THE GATE READS u32s WITH ITS OWN gg_ru32 AND ENCODES EXPECTED POSITIONS WITH vm_int_to_f32 RATHER THAN
THE SUBJECT'S gl_i2f32 -- ON PURPOSE, and for the reason nx_vnormals_gate states: an oracle that borrows
its subject's helpers agrees with it by construction and stops being able to detect a change in them.
The side effect is a free measurement: T8 passing means gl_i2f32 and vm_int_to_f32(v,1) are the same
dependencies 6 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nxnx_vecmath.nxnx_vnormals_lib.nxnx_glbnorm_lib.nxnx_gltf_export.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
| 61 | const GG_DIR_MODE: i64 = 493 // 0755, spelled in decimal because this dialect has no octal |
| 62 | const GG_DIR: *u8 = "/tmp/nx_glbnorm_gate" |
| 63 | const GG_PLAIN: *u8 = "/tmp/nx_glbnorm_gate/plain.glb" |
| 64 | const GG_COLOR: *u8 = "/tmp/nx_glbnorm_gate/color.glb" |
| 66 | const GG_V3: i64 = 3 |
| 67 | const GG_VEC3_F32_BYTES: i64 = 12 |
| 68 | const GG_U32_BYTES: i64 = 4 |
| 69 | const GG_TRI_I: i64 = 3 |
| 70 | const GG_ALPHA_OPAQUE: i64 = 255 |
| 71 | const GG_NO_COLOUR_BLOCK: i64 = 0 // write_glb has no COLOR_0 block between normals and indices |
| 72 | const GG_HAS_COLOUR_BLOCK: i64 = 1 // write_glb_colored does |
| 76 | const GG_GLB_HDR_BYTES: i64 = 12 |
| 77 | const GG_CHUNK_HDR_BYTES: i64 = 8 |
| 78 | const GG_OFF_MAGIC: i64 = 0 |
| 79 | const GG_OFF_VERSION: i64 = 4 |
| 80 | const GG_OFF_JSON_LEN: i64 = 12 |
| 81 | const GG_OFF_JSON_TYPE: i64 = 16 |
| 82 | const GG_MAGIC_GLTF: i64 = 0x46546C67 |
| 83 | const GG_MAGIC_JSON: i64 = 0x4E4F534A |
| 84 | const GG_MAGIC_BIN: i64 = 0x004E4942 |
| 85 | const GG_GLB_VERSION: i64 = 2 |
| 86 | const GG_CONTAINER_CHECKS: i64 = 4 |
| 92 | const GG_N: i64 = 12 |
| 93 | const GG_STEP: i64 = 128 |
| 94 | const GG_NV_GRID: i64 = 144 // GG_N * GG_N |
| 95 | const GG_NV: i64 = 145 // + the orphan |
| 96 | const GG_ORPHAN: i64 = 144 // GG_NV - 1 |
| 97 | const GG_NF: i64 = 242 // 2 * (GG_N - 1)^2 |
| 102 | const GG_HA: i64 = 7 |
| 103 | const GG_HB: i64 = 13 |
| 104 | const GG_HC: i64 = 11 |
| 105 | const GG_HD: i64 = 37 |
| 106 | const GG_HE: i64 = 5 |
| 107 | const GG_HF: i64 = 29 |
| 108 | const GG_JX: i64 = 3 |
| 109 | const GG_JXS: i64 = 11 |
| 110 | const GG_JZ: i64 = 4 |
| 111 | const GG_JZS: i64 = 7 |
| 112 | const GG_COL_MOD: i64 = 256 |
| 113 | const GG_MIN_DISTINCT: i64 = 20 // a FLOOR on fixture variety; the measured value is printed |
| 118 | const GG_RAW_LO: i64 = 995 |
| 119 | const GG_RAW_HI: i64 = 1000 |
| 121 | const GG_SWEEP_LO: i64 = 0 - 40000 // gl_i2f32 transcription sweep, wide enough to cross the sign, |
| 122 | const GG_SWEEP_HI: i64 = 40000 // the 2^e boundaries and the fx1024 range positions live in |
| 123 | const GG_OVER_ENVELOPE: i64 = 1048576 // 2^20, far outside GN_IN_MAX, to exercise gn_reduce |
| 124 | const GG_RULER_OVER: i64 = 4096 // > GN_RULER_MAX, so the ruler must abstain rather than acquit |
| 125 | const GG_EXP_OF_ONE: i64 = 104 // biased exponent of gn_f32(1) = 2^-23: 127 - 23. Underflow guard. |
| 126 | const GG_EXP_SHIFT: i64 = 23 |
| 127 | const GG_EXP_MASK: i64 = 255 |
| 130 | const GG_R_BINOFF: i64 = 0 |
| 131 | const GG_R_NORMOFF: i64 = 1 |
| 132 | const GG_R_COLOFF: i64 = 2 |
| 133 | const GG_R_IDXOFF: i64 = 3 |
| 134 | const GG_R_BINLEN: i64 = 4 |
| 135 | const GG_R_CONT: i64 = 5 |
| 136 | const GG_R_WORST: i64 = 6 |
| 137 | const GG_R_BAD: i64 = 7 |
| 138 | const GG_R_UNMEAS: i64 = 8 |
| 139 | const GG_R_WORSTX: i64 = 9 |
| 140 | const GG_R_BADX: i64 = 10 |
| 141 | const GG_R_RT: i64 = 11 |
| 142 | const GG_R_N: i64 = 12 |
| 145 | const GG_F_RAWLO: i64 = 0 |
| 146 | const GG_F_RAWHI: i64 = 1 |
| 147 | const GG_F_DISTINCT: i64 = 2 |
| 148 | const GG_F_PREFAIL: i64 = 3 |
| 149 | const GG_F_N: i64 = 4 |
| 413 | const GG_KB_ONE: i64 = 1065353216 |
| 414 | const GG_KD_ONE: *u8 = "1" |
| 415 | const GG_KB_HALF: i64 = 1056964608 |
| 416 | const GG_KD_HALF: *u8 = "0.5" |
| 417 | const GG_KB_EIGHTH: i64 = 1040187392 |
| 418 | const GG_KD_EIGHTH: *u8 = "0.125" |
| 419 | const GG_KB_DANCE: i64 = 1098776576 |
| 420 | const GG_KD_DANCE: *u8 = "15.875" |
| 421 | const GG_KB_SEEDSAN: i64 = 3286131474 |
| 422 | const GG_KD_SEEDSAN: *u8 = "-444.77398681640625" |
| 423 | const GG_KD_TRUNC: *u8 = "-444" |
| 424 | const GG_KB_NEGONE: i64 = 3212836864 |
| 425 | const GG_KD_NEGONE: *u8 = "-1" |
| 428 | const GG_KB_TINY: i64 = 869711765 |
| 429 | const GG_KD_TINY: *u8 = "0.00000010000000116860974230803549289703369140625" |
| 430 | const GG_KB_HUGE: i64 = 2139095039 |
| 431 | const GG_KD_HUGE: *u8 = "340282346638528859811704183484516925440" |
| 432 | const GG_KAT_COUNT: i64 = 8 |
| 433 | const GG_DEC_SWEEP_LO: i64 = 0 - 20000 |
| 434 | const GG_DEC_SWEEP_HI: i64 = 20000 |
| 435 | const GG_DEC_BUF: i64 = 384 |
functions
| 151 | func gg_pv(label: *u8, v: i64) -> i64 { gv_puts(label); gv_num(v); gv_puts("\n" as *u8); return 0 } |
| 152 | func gg_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } called by 1: main |
| 155 | func gg_both(a: i64, b: i64) -> i64 { if a == 1 { if b == 1 { return 1 } } return 0 } called by 1: main |
| 156 | func gg_ge(a: i64, b: i64) -> i64 { if a >= b { return 1 } return 0 } called by 1: main |
| 157 | func gg_le(a: i64, b: i64) -> i64 { if a <= b { return 1 } return 0 } called by 1: main |
| 161 | func gg_ru32(b: *u8, at: i64) -> i64 |
| 167 | func gg_prefix_f32(v: i64) -> i64 |
| 182 | func gg_h(i: i64, j: i64) -> i64 { return ((i*GG_HA + j*GG_HB) % GG_HC) * GG_HD - ((i*j) % GG_HE) * GG_HF } called by 1: gg_build_verts |
| 184 | func gg_set3(a: *i64, i: i64, x: i64, y: i64, z: i64) -> i64 { a[i*GG_V3+0]=x; a[i*GG_V3+1]=y; a[i*GG_V3+2]=z; return 0 } |
| 186 | func gg_build_verts(v: *i64) -> i64 |
| 201 | func gg_build_faces(f: *i64) -> i64 |
| 218 | func gg_build_cols(c: *i64) -> i64 |
| 231 | func gg_layout(glb: *u8, nv: i64, ncol: i64, out: *i64) -> i64 |
| 249 | func gg_expect_binlen(nv: i64, nf: i64, ncol: i64) -> i64 called by 1: main |
| 255 | func gg_measure(glb: *u8, nv: i64, normoff: i64, out: *i64) -> i64 |
| 288 | func gg_pos_ok(glb: *u8, vbuf: *i64, nv: i64, binoff: i64) -> i64 |
| 297 | func gg_idx_ok(glb: *u8, fbuf: *i64, nf: i64, idxoff: i64) -> i64 |
| 306 | func gg_col_ok(glb: *u8, cbuf: *i64, nv: i64, coloff: i64) -> i64 called by 1: main |
| 320 | func gg_orphan_ok(glb: *u8, normoff: i64) -> i64 |
| 328 | func gg_distinct(nrm: *i64, n: i64) -> i64 called by 1: gg_fixture |
| 350 | func gg_fixture(nrm: *i64, nv: i64, out: *i64) -> i64 |
| 368 | func gg_transcription_ok() -> i64 |
| 378 | func gg_shifts(nrm: *i64, nv: i64, un: *i64) -> i64 |
| 389 | func gg_report(tag: *u8, r: *i64) -> i64 |
| 404 | func gg_err_at(glb: *u8, normoff: i64) -> i64 |
| 437 | func gg_dec_is(bits: i64, want: *u8, buf: *u8) -> i64 |
| 450 | func gg_kats(buf: *u8) -> i64 |
| 469 | func gg_key_ok() -> i64 |
| 478 | func gg_dec_int_ok(buf: *u8, ibuf: *u8) -> i64 |
| 494 | func main() -> i64 |