code wiki / (root) / nx_rigfloor.nx

nx_rigfloor.nx source

↩ module page · 675 lines · 33090 B

1// nx_rigfloor.nx -- DERIVE THE ASSET FLOOR FROM THE REFERENCE CORPUS INSTEAD OF DECLARING IT. 2// 3// WHY THIS EXISTS. nx_asset_floor_gate is THE ruler for "is this character game-ready", and it 4// carried five HAND-PICKED bounds: AF_VERT_MIN 4000, AF_TRI_MIN 8000, AF_TRI_MAX 120000, 5// AF_JOINT_MIN 40, AF_MIDLINE 3000. A picked bound structurally cannot answer the question the 6// operator actually asks -- can we emit something that MATCHES THE MODELS WE HAVE -- because it 7// is not a function of the models we have. This organ makes the bound a MEASUREMENT of the 8// reference population, so the gate stops ASSERTING a floor and starts REPORTING one. 9// 10// THIS IS NOT A SECOND RULER. nx_asset_floor_gate remains the one ruler; this is the PRODUCER 11// that derives its bounds into a conf the gate reads. Producer and consumer cannot disagree 12// because there is exactly one of each. nx_spendgate ranked nx_asset_floor_gate as the top 13// incumbent for this intent -- extending it is the entire point, and a rival floor organ would 14// be the duplicate-ruler defect. 15// 16// ROSTER-DRIVEN, NOT DIRECTORY-WALKING, ON PURPOSE. sys_getdents64 exists and a bounded walker 17// is proven in nx_wiki_content_loader. It is still the wrong primitive HERE: the corpus IS the 18// thing under measurement, so a silently-truncated listing would not merely lose a file, it would 19// corrupt the derived bound itself and then publish that bound as a fact. A roster is complete by 20// construction and its coverage is DECIDABLE -- rows declared vs rows read, both printed, and if 21// they do not sum then no floor is written at all. 22// 23// METHOD: OBSERVED RANGE OVER THE FULL POPULATION. No percentile index is chosen, because 24// choosing one would reintroduce exactly the picked constant this organ exists to remove. The 25// band is [min, max] of what the reference assets actually are. 26// 27// WHY n>=2 IS NOT A MAGIC NUMBER. A range needs two observations to HAVE WIDTH. At n=1 the band 28// is a point and would refuse every asset except that one file -- a bar that is not a bar. So the 29// organ abstains at n<2. That is the arity of the operation, not a threshold anyone selected. 30// 31// EXITS: 0 OK | 1 OUT-OF-BAND | 2 usage | 3 UNOBSERVABLE (no corpus / degenerate / partial read) 32// A partial roster read NEVER degrades to a floor: it abstains, because a floor derived from an 33// unknown fraction of the corpus is worse than no floor -- it looks authoritative and is not. 34// 35// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 36import "nx_syscalls.nx" 37 38// ---- exit contract ---- 39const RF_EXIT_OK: i64 = 0 40const RF_EXIT_OUT_OF_BAND: i64 = 1 41const RF_EXIT_USAGE: i64 = 2 42const RF_EXIT_UNOBSERVABLE: i64 = 3 43 44// ---- NXA container layout (mirrors nx_asset_floor_gate; one format, one reader shape) ---- 45const RF_HDR_BYTES: i64 = 32 46const RF_SECHDR_BYTES: i64 = 32 47const RF_SEC_OFF_FIELD: i64 = 8 48const RF_NSEC_FIELD: i64 = 16 49const RF_MAGIC_BYTES: i64 = 8 50const RF_TAG_BYTES: i64 = 4 51const RF_WORD_BYTES: i64 = 8 52const RF_PERMIL: i64 = 1000 53const RF_BYTE_RADIX: i64 = 256 54const RF_BYTE_MASK: i64 = 255 55 56// ---- ASCII ---- 57const RF_ASCII_ZERO: i64 = 48 58const RF_ASCII_NINE: i64 = 57 59const RF_ASCII_MINUS: i64 = 45 60const RF_ASCII_EQ: i64 = 61 61const RF_ASCII_NL: i64 = 10 62const RF_ASCII_CR: i64 = 13 63const RF_ASCII_HASH: i64 = 35 64const RF_VERB_DERIVE: i64 = 100 65const RF_VERB_SCORE: i64 = 115 66const RF_DECIMAL: i64 = 10 67 68// ---- buffers. Every cap here REFUSES rather than truncating (no silent caps). ---- 69const RF_CONF_CAP: i64 = 65536 70const RF_SCRATCH_CAP: i64 = 64 71const RF_PATH_CAP: i64 = 4096 72const RF_MAX_ROWS: i64 = 4096 73const RF_STDOUT: i64 = 1 74const RF_PAIR: i64 = 2 75 76// ---- CONTAINERS. Two live in this estate and BOTH START WITH 'N', so one byte cannot tell them 77// apart -- the estate's own readers check only b[0] and therefore cannot either. NXANIM01 is an 78// exact EIGHT-byte check, so testing it FIRST is unambiguous and everything else beginning with 79// 'N' falls through to NXMSH2. 80const RF_CONTAINER_UNKNOWN: i64 = 0 81const RF_CONTAINER_NXA: i64 = 1 // NXANIM01 -- our shipped rigged asset 82const RF_CONTAINER_MSH: i64 = 2 // NXMSH2 -- what nx_gltf2mesh emits from a glTF donor 83// The magic is EIGHT bytes, "NXMSH2\0\0". Every shipping consumer validates byte 0 and byte 5 -- 84// checking byte 0 alone is NOT enough here, because 'NXANIM01' also begins with 'N'. NXANIM01 is 85// tested exhaustively first, so byte 5 is belt-and-braces against any other N-file. 86const RF_MSH_MAGIC_BYTE: i64 = 78 // 'N' at offset 0 87const RF_MSH_VER_OFF: i64 = 5 88const RF_MSH_VER_BYTE: i64 = 50 // '2' at offset 5 -- the version discriminator 89const RF_MSH_NLAYER_FIELD: i64 = 8 // u32 LE layer count 90const RF_MSH_NTRI_FIELD: i64 = 12 // u32 LE triangle count 91const RF_MSH_HDR_BYTES: i64 = 16 // FIXED header; the FULL header is 16 + nlay*24 92const RF_MSH_LAYROW_BYTES: i64 = 24 93const RF_U32_BYTES: i64 = 4 94// A triangle record is 84 BYTES = 21 float32: nine position (three vertices x xyz), nine normal, 95// and THREE per-triangle colour. Positions are unindexed -- there is no shared-vertex table and no 96// joint section anywhere in the format. That is a property of the container, not of any one asset. 97const RF_MSH_TRIREC_BYTES: i64 = 84 98 99// ---- AXES. Seven exist; WHICH ONES A CONTAINER CAN ANSWER IS A PROPERTY OF THE CONTAINER, and an 100// axis a container cannot answer is reported UNAVAILABLE rather than filled with a plausible 101// substitute. Deriving "verts" for NXMSH2 as tris*3 would be arithmetic dressed as a measurement: 102// it would be the SAME number for every mesh in existence and would silently add a zero-width axis 103// that dilutes axes_with_width. Name a bucket for what was measured, never for what you wish it was. 104const RF_AXES: i64 = 7 105const RF_AX_VERTS: i64 = 0 106const RF_AX_TRIS: i64 = 1 107const RF_AX_JOINTS: i64 = 2 108const RF_AX_TPV_PERMIL: i64 = 3 109const RF_AX_VPJ: i64 = 4 110const RF_AX_PARTS: i64 = 5 111const RF_AX_TPP: i64 = 6 // triangles per named part -- the NXMSH2 shape signal 112 113func rf_out(s: *u8) -> i64 { 114 var n: i64 = 0 115 while s[n] != 0 { n = n + 1 } 116 sys_write(RF_STDOUT, s, n) 117 return 0 118} 119// little-endian i64 read, byte by byte -- the container is not alignment-guaranteed 120func rf_rd64(b: *u8, off: i64) -> i64 { 121 var v: i64 = 0 122 var i: i64 = RF_WORD_BYTES - 1 123 while i >= 0 { v = v*RF_BYTE_RADIX + ((b[off + i] & RF_BYTE_MASK) as i64); i = i - 1 } 124 return v 125} 126func rf_tag_eq(b: *u8, off: i64, t: *u8) -> i64 { 127 var i: i64 = 0 128 while i < RF_TAG_BYTES { if b[off + i] != t[i] { return 0 } i = i + 1 } 129 return 1 130} 131// section byte-offset by tag, or -1. Walks EVERY declared section: the table is small and a 132// partial scan would report a capability absent that the asset actually carries. 133func rf_find(b: *u8, nsec: i64, t: *u8) -> i64 { 134 var s: i64 = 0 135 while s < nsec { 136 let e: i64 = RF_HDR_BYTES + s*RF_SECHDR_BYTES 137 if rf_tag_eq(b, e, t) == 1 { return rf_rd64(b, e + RF_SEC_OFF_FIELD) } 138 s = s + 1 139 } 140 return 0 - 1 141} 142 143// ---- text emission into a buffer (build the line, then ONE write) ---- 144func rf_bputs(buf: *u8, off: i64, s: *u8) -> i64 { 145 var o: i64 = off 146 var i: i64 = 0 147 while s[i] != 0 { buf[o] = s[i]; o = o + 1; i = i + 1 } 148 return o 149} 150// scratch is passed IN, never allocated here -- this runs per row per axis 151func rf_bputnum(buf: *u8, off: i64, v: i64, scratch: *u8) -> i64 { 152 var o: i64 = off 153 var n: i64 = v 154 if n < 0 { buf[o] = RF_ASCII_MINUS; o = o + 1; n = 0 - n } 155 var k: i64 = 0 156 if n == 0 { scratch[k] = RF_ASCII_ZERO; k = 1 } 157 while n > 0 { 158 let q: i64 = n / RF_DECIMAL 159 scratch[k] = RF_ASCII_ZERO + (n - q*RF_DECIMAL) 160 n = q 161 k = k + 1 162 } 163 while k > 0 { k = k - 1; buf[o] = scratch[k]; o = o + 1 } 164 return o 165} 166func rf_kv(buf: *u8, off: i64, key: *u8, v: i64, scratch: *u8) -> i64 { 167 var o: i64 = rf_bputs(buf, off, key) 168 buf[o] = RF_ASCII_EQ 169 o = o + 1 170 o = rf_bputnum(buf, o, v, scratch) 171 buf[o] = RF_ASCII_NL 172 return o + 1 173} 174// Emit one axis band ONLY if the corpus's container could answer it. An axis the container cannot 175// answer must be ABSENT from the conf, never present-with-a-plausible-value: rf_score treats an 176// absent bound as UNOBSERVABLE, so omission propagates the honest answer instead of inventing one. 177func rf_emit_band(cb: *u8, off: i64, kmin: *u8, kmax: *u8, idx: i64, 178 lo: *i64, hi: *i64, corpus_av: *i64, scratch: *u8) -> i64 { 179 if corpus_av[idx] == 0 { return off } 180 var o: i64 = rf_kv(cb, off, kmin, lo[idx], scratch) 181 o = rf_kv(cb, o, kmax, hi[idx], scratch) 182 return o 183} 184func rf_say(label: *u8, v: i64, scratch: *u8, line: *u8) -> i64 { 185 var o: i64 = rf_bputs(line, 0, label) 186 o = rf_bputnum(line, o, v, scratch) 187 line[o] = RF_ASCII_NL 188 sys_write(RF_STDOUT, line, o + 1) 189 return 0 190} 191 192// ---- conf reading. LINE-ANCHORED: a key matched mid-line would read a number out of a comment 193// and answer confidently with the wrong bound. Returns `miss` when absent, so an ABSENT key stays 194// distinguishable from a key whose value is legitimately zero. 195func rf_conf_i64(buf: *u8, n: i64, key: *u8, miss: i64) -> i64 { 196 var i: i64 = 0 197 var at_line_start: i64 = 1 198 var found: i64 = 0 199 var result: i64 = miss 200 while i < n { 201 if found == 0 { if at_line_start == 1 { 202 var k: i64 = 0 203 var matched: i64 = 1 204 while key[k] != 0 { 205 if i + k >= n { matched = 0 } 206 if matched == 1 { if buf[i + k] != key[k] { matched = 0 } } 207 k = k + 1 208 } 209 if matched == 1 { if i + k < n { if buf[i + k] == RF_ASCII_EQ { 210 var o: i64 = i + k + 1 211 var neg: i64 = 0 212 if o < n { if buf[o] == RF_ASCII_MINUS { neg = 1; o = o + 1 } } 213 var v: i64 = 0 214 var digits: i64 = 0 215 var scanning: i64 = 1 216 while scanning == 1 { 217 if o >= n { scanning = 0 } 218 if scanning == 1 { 219 let c: i64 = (buf[o] & RF_BYTE_MASK) as i64 220 if c < RF_ASCII_ZERO { scanning = 0 } 221 if c > RF_ASCII_NINE { scanning = 0 } 222 if scanning == 1 { 223 v = v*RF_DECIMAL + (c - RF_ASCII_ZERO) 224 digits = digits + 1 225 o = o + 1 226 } 227 } 228 } 229 if digits > 0 { 230 if neg == 1 { v = 0 - v } 231 result = v 232 found = 1 233 } 234 } } } 235 } } 236 if buf[i] == RF_ASCII_NL { at_line_start = 1 } else { at_line_start = 0 } 237 i = i + 1 238 } 239 return result 240} 241 242// little-endian u32 -- NXMSH2 uses 32-bit counts where NXANIM01 uses 64 243func rf_rd32(b: *u8, off: i64) -> i64 { 244 var v: i64 = 0 245 var i: i64 = RF_U32_BYTES - 1 246 while i >= 0 { v = v*RF_BYTE_RADIX + ((b[off + i] & RF_BYTE_MASK) as i64); i = i - 1 } 247 return v 248} 249 250// ---- one asset measurement ---- 251// Fills out[] on the seven axes and avail[] with 1 for each axis this CONTAINER can answer. 252// Returns the container kind (RF_CONTAINER_NXA / _MSH), or RF_CONTAINER_UNKNOWN when the asset is 253// unmeasurable -- and it SAYS WHICH, because an unmeasurable asset and a failing asset demand 254// opposite actions. 255func rf_measure(path: *u8, out: *i64, avail: *i64) -> i64 { 256 var a: i64 = 0 257 while a < RF_AXES { out[a] = 0; avail[a] = 0; a = a + 1 } 258 let lp: *i64 = sys_mmap(RF_WORD_BYTES*RF_PAIR) as *i64 259 let b: *u8 = sys_read_file(path, lp) 260 if (b as i64) == 0 { rf_out(" UNREADABLE (cannot open)\n" as *u8); return RF_CONTAINER_UNKNOWN } 261 let n: i64 = lp[0] 262 if n < RF_HDR_BYTES { rf_out(" UNREADABLE (shorter than a container header)\n" as *u8); return RF_CONTAINER_UNKNOWN } 263 264 // NXANIM01 FIRST, on all eight bytes. Both containers start with 'N', so the exact check must 265 // run before the one-byte one or every NXA would be misread as an NXMSH2. 266 let mg: *u8 = "NXANIM01" as *u8 267 var magic: i64 = 1 268 var i: i64 = 0 269 while i < RF_MAGIC_BYTES { if b[i] != mg[i] { magic = 0 } i = i + 1 } 270 271 if magic == 1 { 272 let nsec: i64 = rf_rd64(b, RF_NSEC_FIELD) 273 let ov: i64 = rf_find(b, nsec, "VERT" as *u8) 274 let ot: i64 = rf_find(b, nsec, "TRIS" as *u8) 275 let ok: i64 = rf_find(b, nsec, "SKEL" as *u8) 276 if ov < 0 { rf_out(" UNREADABLE (no VERT section)\n" as *u8); return RF_CONTAINER_UNKNOWN } 277 if ot < 0 { rf_out(" UNREADABLE (no TRIS section)\n" as *u8); return RF_CONTAINER_UNKNOWN } 278 if ok < 0 { rf_out(" UNREADABLE (no SKEL section -- not a RIGGED asset)\n" as *u8); return RF_CONTAINER_UNKNOWN } 279 let nv: i64 = rf_rd64(b, ov) 280 let nt: i64 = rf_rd64(b, ot) 281 let nj: i64 = rf_rd64(b, ok) 282 if nv <= 0 { rf_out(" UNREADABLE (VERT count is zero)\n" as *u8); return RF_CONTAINER_UNKNOWN } 283 if nj <= 0 { rf_out(" UNREADABLE (SKEL count is zero)\n" as *u8); return RF_CONTAINER_UNKNOWN } 284 out[RF_AX_VERTS] = nv 285 out[RF_AX_TRIS] = nt 286 out[RF_AX_JOINTS] = nj 287 // SCALE-FREE axes: these answer "does it match", because a larger or smaller character is 288 // still the same KIND of character. Raw counts are scale-bound. 289 out[RF_AX_TPV_PERMIL] = nt*RF_PERMIL/nv 290 out[RF_AX_VPJ] = nv/nj 291 avail[RF_AX_VERTS] = 1 292 avail[RF_AX_TRIS] = 1 293 avail[RF_AX_JOINTS] = 1 294 avail[RF_AX_TPV_PERMIL] = 1 295 avail[RF_AX_VPJ] = 1 296 return RF_CONTAINER_NXA 297 } 298 299 var msh: i64 = 0 300 if (b[0] & RF_BYTE_MASK) == RF_MSH_MAGIC_BYTE { 301 if (b[RF_MSH_VER_OFF] & RF_BYTE_MASK) == RF_MSH_VER_BYTE { msh = 1 } 302 } 303 if msh == 1 { 304 let nl: i64 = rf_rd32(b, RF_MSH_NLAYER_FIELD) 305 let nt: i64 = rf_rd32(b, RF_MSH_NTRI_FIELD) 306 if nt <= 0 { rf_out(" UNREADABLE (NXMSH2 triangle count is zero)\n" as *u8); return RF_CONTAINER_UNKNOWN } 307 // SIZE CHECK against the format's own arithmetic: 16 + nlay*24 + nt*84. A truncated file 308 // whose header still parses would otherwise report a confident count for data that is not 309 // there -- the same class as a partial scan publishing its prefix as a total. 310 let need: i64 = RF_MSH_HDR_BYTES + nl*RF_MSH_LAYROW_BYTES + nt*RF_MSH_TRIREC_BYTES 311 if n < need { 312 rf_out(" UNREADABLE (NXMSH2 shorter than its own declared header+records)\n" as *u8) 313 return RF_CONTAINER_UNKNOWN 314 } 315 // ONLY WHAT THE CONTAINER ACTUALLY HOLDS. NXMSH2 has no vertex table and no joint section, 316 // so verts/joints/ratios stay UNAVAILABLE. Deriving verts as a fixed multiple of tris would 317 // be identical for every mesh ever written -- a zero-width axis masquerading as a 318 // measurement, which would also dilute axes_with_width. 319 out[RF_AX_TRIS] = nt 320 avail[RF_AX_TRIS] = 1 321 // A ZERO-LAYER MESH IS VALID, NOT BROKEN. nx_bvhfk is a shipping producer that writes 322 // nlay=0. Rejecting it would call a conformant file unreadable; dividing by it would be a 323 // SIGFPE. The honest answer is that the parts axes are simply UNAVAILABLE for such a file. 324 if nl > 0 { 325 out[RF_AX_PARTS] = nl 326 out[RF_AX_TPP] = nt/nl 327 avail[RF_AX_PARTS] = 1 328 avail[RF_AX_TPP] = 1 329 } 330 return RF_CONTAINER_MSH 331 } 332 333 rf_out(" UNREADABLE (magic is neither NXANIM01 nor NXMSH2)\n" as *u8) 334 return RF_CONTAINER_UNKNOWN 335} 336 337func rf_usage() -> i64 { 338 rf_out("usage: nx_rigfloor derive <roster> <out.conf> -- measure the FULL roster, write the floor\n" as *u8) 339 rf_out(" nx_rigfloor score <asset.nxa> <floor.conf> -- score one asset against that floor\n" as *u8) 340 rf_out(" roster: one NXA path per line; blank lines and # comments ignored.\n" as *u8) 341 rf_out(" exits: 0 OK | 1 OUT-OF-BAND | 2 usage | 3 UNOBSERVABLE\n" as *u8) 342 return RF_EXIT_USAGE 343} 344 345func rf_derive(roster: *u8, outconf: *u8) -> i64 { 346 let line: *u8 = sys_mmap(RF_PATH_CAP) 347 let scratch: *u8 = sys_mmap(RF_SCRATCH_CAP) 348 let pathbuf: *u8 = sys_mmap(RF_PATH_CAP) 349 let m: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 350 let lo: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 351 let hi: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 352 let av: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 353 let corpus_av: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 354 var ai: i64 = 0 355 while ai < RF_AXES { corpus_av[ai] = 0; ai = ai + 1 } 356 var container: i64 = RF_CONTAINER_UNKNOWN 357 var mixed: i64 = 0 358 359 let rp: *i64 = sys_mmap(RF_WORD_BYTES*RF_PAIR) as *i64 360 let rb: *u8 = sys_read_file(roster, rp) 361 if (rb as i64) == 0 { 362 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=roster-unreadable path=" as *u8) 363 rf_out(roster) 364 rf_out("\n" as *u8) 365 return RF_EXIT_UNOBSERVABLE 366 } 367 let rn: i64 = rp[0] 368 rf_out("NX-RIGFLOOR derive\n roster: " as *u8) 369 rf_out(roster) 370 rf_out("\n" as *u8) 371 372 var declared: i64 = 0 373 var read_ok: i64 = 0 374 var failed: i64 = 0 375 var capped: i64 = 0 376 var first: i64 = 1 377 var i: i64 = 0 378 while i < rn { 379 var e: i64 = i 380 var eol: i64 = 0 381 while eol == 0 { 382 if e >= rn { eol = 1 } 383 if eol == 0 { if rb[e] == RF_ASCII_NL { eol = 1 } else { e = e + 1 } } 384 } 385 var len: i64 = e - i 386 if len > 0 { if rb[i + len - 1] == RF_ASCII_CR { len = len - 1 } } 387 var skip: i64 = 0 388 if len <= 0 { skip = 1 } 389 if skip == 0 { if rb[i] == RF_ASCII_HASH { skip = 1 } } 390 if skip == 0 { if len >= RF_PATH_CAP { skip = 1; capped = 1 } } 391 if skip == 0 { 392 declared = declared + 1 393 if declared > RF_MAX_ROWS { capped = 1 } 394 if capped == 0 { 395 var c: i64 = 0 396 while c < len { pathbuf[c] = rb[i + c]; c = c + 1 } 397 pathbuf[len] = 0 398 rf_out(" - " as *u8) 399 rf_out(pathbuf) 400 rf_out("\n" as *u8) 401 let kind: i64 = rf_measure(pathbuf, m, av) 402 if kind != RF_CONTAINER_UNKNOWN { 403 read_ok = read_ok + 1 404 // A MIXED-CONTAINER CORPUS IS NOT A CORPUS. NXANIM01 answers verts/joints; 405 // NXMSH2 answers parts and cannot answer either. Ranging across both would put 406 // two different quantities in one band and publish it as a bound. 407 if container == RF_CONTAINER_UNKNOWN { container = kind } 408 if container != kind { mixed = 1 } 409 if kind == RF_CONTAINER_NXA { 410 rf_say(" container=NXANIM01 verts=" as *u8, m[RF_AX_VERTS], scratch, line) 411 rf_say(" tris=" as *u8, m[RF_AX_TRIS], scratch, line) 412 rf_say(" joints=" as *u8, m[RF_AX_JOINTS], scratch, line) 413 rf_say(" tris_per_vert_permil=" as *u8, m[RF_AX_TPV_PERMIL], scratch, line) 414 rf_say(" verts_per_joint=" as *u8, m[RF_AX_VPJ], scratch, line) 415 } 416 if kind == RF_CONTAINER_MSH { 417 rf_say(" container=NXMSH2 tris=" as *u8, m[RF_AX_TRIS], scratch, line) 418 rf_say(" parts=" as *u8, m[RF_AX_PARTS], scratch, line) 419 rf_say(" tris_per_part=" as *u8, m[RF_AX_TPP], scratch, line) 420 } 421 var a: i64 = 0 422 while a < RF_AXES { 423 if av[a] == 1 { 424 // an axis enters the corpus set the first time ANY row answers it, and 425 // its band opens on that row -- not on row zero, which may not have it 426 if corpus_av[a] == 0 { corpus_av[a] = 1; lo[a] = m[a]; hi[a] = m[a] } 427 else { 428 if m[a] < lo[a] { lo[a] = m[a] } 429 if m[a] > hi[a] { hi[a] = m[a] } 430 } 431 } 432 a = a + 1 433 } 434 first = 0 435 } else { failed = failed + 1 } 436 } 437 } 438 i = e + 1 439 } 440 441 rf_say(" n_declared=" as *u8, declared, scratch, line) 442 rf_say(" n_read=" as *u8, read_ok, scratch, line) 443 rf_say(" n_failed=" as *u8, failed, scratch, line) 444 // A PARTITION IS A CLAIM: CHECK THE PARTS SUM. 445 var sums: i64 = 0 446 if read_ok + failed == declared { sums = 1 } 447 rf_say(" partition_reconciles=" as *u8, sums, scratch, line) 448 449 if capped == 1 { 450 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=roster-hit-the-row-or-path-cap -- RAISE IT DELIBERATELY, a floor is NOT written from a prefix\n" as *u8) 451 return RF_EXIT_UNOBSERVABLE 452 } 453 if sums == 0 { 454 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=partition-does-not-reconcile\n" as *u8) 455 return RF_EXIT_UNOBSERVABLE 456 } 457 if failed > 0 { 458 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=corpus-incomplete -- a floor derived from an unknown fraction of the corpus looks authoritative and is not\n" as *u8) 459 return RF_EXIT_UNOBSERVABLE 460 } 461 if read_ok < RF_PAIR { 462 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=degenerate-band-too-few -- a range needs two observations to have width; at n<2 the band is a point and would refuse every asset but one\n" as *u8) 463 return RF_EXIT_UNOBSERVABLE 464 } 465 // n>=2 IS NECESSARY BUT NOT SUFFICIENT, AND REAL DATA REFUTED THE FIRST VERSION OF THIS GUARD. 466 // Measured 2026-08-22 over the estate's own corpus: eleven rigged NXA files that are BYTE 467 // IDENTICAL in geometry (verts=14164 tris=28092 joints=104, every one) satisfy n>=2 and still 468 // produce a ZERO-WIDTH band on every axis -- a bar that admits only the reference itself and 469 // rejects every other asset in existence. The invariant is not the COUNT of observations, it 470 // is the WIDTH those observations produce. Counting was the wrong SHAPE, not the wrong number. 471 if mixed == 1 { 472 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=mixed-container-corpus -- this roster holds both NXANIM01 and NXMSH2 rows. They answer DIFFERENT axes, so a band across them would range over two different quantities and publish the result as one bound. Split the roster by container.\n" as *u8) 473 return RF_EXIT_UNOBSERVABLE 474 } 475 var axes_available: i64 = 0 476 var axes_with_width: i64 = 0 477 var w: i64 = 0 478 while w < RF_AXES { 479 if corpus_av[w] == 1 { 480 axes_available = axes_available + 1 481 if hi[w] > lo[w] { axes_with_width = axes_with_width + 1 } 482 } 483 w = w + 1 484 } 485 rf_say(" axes_available=" as *u8, axes_available, scratch, line) 486 rf_say(" axes_with_width=" as *u8, axes_with_width, scratch, line) 487 if axes_with_width == 0 { 488 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=degenerate-corpus-every-asset-identical -- n>=2 but ZERO axes have width. This corpus is ONE distinct shape repeated, so it cannot express a range and a floor from it would admit only itself. This is a CORPUS defect, not an organ defect: acquire distinct reference assets.\n" as *u8) 489 return RF_EXIT_UNOBSERVABLE 490 } 491 492 let cb: *u8 = sys_mmap(RF_CONF_CAP) 493 var o: i64 = 0 494 o = rf_bputs(cb, o, "# nx_rigfloor derived asset floor -- GENERATED ARTEFACT, DO NOT HAND-EDIT\n" as *u8) 495 o = rf_bputs(cb, o, "# Every bound below is a MEASUREMENT of the reference corpus, never a chosen number.\n" as *u8) 496 o = rf_bputs(cb, o, "# method: observed range over the FULL population. No percentile index is selected,\n" as *u8) 497 o = rf_bputs(cb, o, "# because selecting one would reintroduce the picked constant this file exists to remove.\n" as *u8) 498 o = rf_bputs(cb, o, "method=observed-range-full-population\n" as *u8) 499 o = rf_bputs(cb, o, "roster=" as *u8) 500 o = rf_bputs(cb, o, roster) 501 cb[o] = RF_ASCII_NL 502 o = o + 1 503 o = rf_kv(cb, o, "n_declared" as *u8, declared, scratch) 504 o = rf_kv(cb, o, "n_read" as *u8, read_ok, scratch) 505 o = rf_kv(cb, o, "n_failed" as *u8, failed, scratch) 506 o = rf_kv(cb, o, "corpus_complete" as *u8, 1, scratch) 507 // published so a consumer can see HOW MANY axes actually discriminate. A floor whose bands are 508 // mostly zero-width is technically valid and nearly useless; that has to be visible, not buried. 509 o = rf_kv(cb, o, "axes_total" as *u8, RF_AXES, scratch) 510 o = rf_kv(cb, o, "axes_available" as *u8, axes_available, scratch) 511 o = rf_kv(cb, o, "axes_with_width" as *u8, axes_with_width, scratch) 512 // the container is part of the claim: a floor derived from NXMSH2 donors says nothing about 513 // joints, and a consumer must be able to see that WITHOUT inferring it from missing keys 514 o = rf_kv(cb, o, "container" as *u8, container, scratch) 515 o = rf_emit_band(cb, o, "verts_min" as *u8, "verts_max" as *u8, RF_AX_VERTS, lo, hi, corpus_av, scratch) 516 o = rf_emit_band(cb, o, "tris_min" as *u8, "tris_max" as *u8, RF_AX_TRIS, lo, hi, corpus_av, scratch) 517 o = rf_emit_band(cb, o, "joints_min" as *u8, "joints_max" as *u8, RF_AX_JOINTS, lo, hi, corpus_av, scratch) 518 o = rf_emit_band(cb, o, "tris_per_vert_permil_min" as *u8, "tris_per_vert_permil_max" as *u8, RF_AX_TPV_PERMIL, lo, hi, corpus_av, scratch) 519 o = rf_emit_band(cb, o, "verts_per_joint_min" as *u8, "verts_per_joint_max" as *u8, RF_AX_VPJ, lo, hi, corpus_av, scratch) 520 o = rf_emit_band(cb, o, "parts_min" as *u8, "parts_max" as *u8, RF_AX_PARTS, lo, hi, corpus_av, scratch) 521 o = rf_emit_band(cb, o, "tris_per_part_min" as *u8, "tris_per_part_max" as *u8, RF_AX_TPP, lo, hi, corpus_av, scratch) 522 523 let fd: i64 = sys_openat_wr(outconf, MODE_0644) 524 if fd < 0 { 525 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=cannot-open-out-conf path=" as *u8) 526 rf_out(outconf) 527 rf_out("\n" as *u8) 528 return RF_EXIT_UNOBSERVABLE 529 } 530 let wr: i64 = sys_write(fd, cb, o) 531 sys_close(fd) 532 // ANNOUNCE THE WRITE: an unannounced publish makes "did it land" a hunt instead of a number. 533 rf_say(" conf_bytes_built=" as *u8, o, scratch, line) 534 rf_say(" conf_bytes_written=" as *u8, wr, scratch, line) 535 if wr != o { 536 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=short-write\n" as *u8) 537 return RF_EXIT_UNOBSERVABLE 538 } 539 rf_out(" wrote: " as *u8) 540 rf_out(outconf) 541 rf_out("\n" as *u8) 542 rf_out("NX-RIGFLOOR verdict=DERIVED\n" as *u8) 543 return RF_EXIT_OK 544} 545 546// An ABSENT bound is UNOBSERVABLE, never a pass. The caller passes an EMPTY band (lo=1, hi=0) as 547// the miss sentinel, so a key missing from the conf reports bound-absent instead of silently 548// scoring IN-BAND -- which would be a gate passing on the empty set. 549func rf_band(name: *u8, v: i64, lo: i64, hi: i64, scratch: *u8, line: *u8, bad: *i64) -> i64 { 550 var o: i64 = rf_bputs(line, 0, " " as *u8) 551 o = rf_bputs(line, o, name) 552 o = rf_bputs(line, o, " value=" as *u8) 553 o = rf_bputnum(line, o, v, scratch) 554 o = rf_bputs(line, o, " band=[" as *u8) 555 o = rf_bputnum(line, o, lo, scratch) 556 o = rf_bputs(line, o, "," as *u8) 557 o = rf_bputnum(line, o, hi, scratch) 558 o = rf_bputs(line, o, "] " as *u8) 559 if lo > hi { 560 o = rf_bputs(line, o, "UNOBSERVABLE-bound-absent\n" as *u8) 561 bad[1] = bad[1] + 1 562 } else { 563 var ok: i64 = 0 564 if v >= lo { if v <= hi { ok = 1 } } 565 if ok == 1 { o = rf_bputs(line, o, "IN-BAND\n" as *u8) } 566 if ok == 0 { 567 o = rf_bputs(line, o, "OUT-OF-BAND\n" as *u8) 568 bad[0] = bad[0] + 1 569 } 570 } 571 sys_write(RF_STDOUT, line, o) 572 return 0 573} 574 575func rf_score(asset: *u8, conf: *u8) -> i64 { 576 let line: *u8 = sys_mmap(RF_PATH_CAP) 577 let scratch: *u8 = sys_mmap(RF_SCRATCH_CAP) 578 let m: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 579 let av: *i64 = sys_mmap(RF_WORD_BYTES*RF_AXES) as *i64 580 let bad: *i64 = sys_mmap(RF_WORD_BYTES*RF_PAIR) as *i64 581 bad[0] = 0 582 bad[1] = 0 583 584 let cp: *i64 = sys_mmap(RF_WORD_BYTES*RF_PAIR) as *i64 585 let cbuf: *u8 = sys_read_file(conf, cp) 586 if (cbuf as i64) == 0 { 587 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=floor-conf-unreadable path=" as *u8) 588 rf_out(conf) 589 rf_out("\n" as *u8) 590 return RF_EXIT_UNOBSERVABLE 591 } 592 let cn: i64 = cp[0] 593 // A FLOOR DERIVED FROM A PARTIAL CORPUS MUST NOT BE USED AS ONE. 594 let complete: i64 = rf_conf_i64(cbuf, cn, "corpus_complete" as *u8, 0 - 1) 595 if complete != 1 { 596 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=floor-conf-not-corpus-complete\n" as *u8) 597 return RF_EXIT_UNOBSERVABLE 598 } 599 rf_out("NX-RIGFLOOR score\n asset: " as *u8) 600 rf_out(asset) 601 rf_out("\n floor: " as *u8) 602 rf_out(conf) 603 rf_out("\n" as *u8) 604 let n_read: i64 = rf_conf_i64(cbuf, cn, "n_read" as *u8, 0 - 1) 605 rf_say(" corpus_n=" as *u8, n_read, scratch, line) 606 607 let kind: i64 = rf_measure(asset, m, av) 608 if kind == RF_CONTAINER_UNKNOWN { 609 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=asset-unmeasurable\n" as *u8) 610 return RF_EXIT_UNOBSERVABLE 611 } 612 // SCORING ACROSS CONTAINERS IS A SUBJECT MISMATCH, NOT A STRICTER TEST. A floor derived from 613 // NXMSH2 donors has no joints band at all; scoring an NXANIM01 asset against it would silently 614 // grade a rigged character on three form axes and report the result as a pass. 615 let conf_container: i64 = rf_conf_i64(cbuf, cn, "container" as *u8, 0 - 1) 616 rf_say(" asset_container=" as *u8, kind, scratch, line) 617 rf_say(" floor_container=" as *u8, conf_container, scratch, line) 618 if conf_container != kind { 619 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=container-mismatch -- this asset and this floor are different container kinds and answer different axes. Derive a floor from a corpus of the same kind.\n" as *u8) 620 return RF_EXIT_UNOBSERVABLE 621 } 622 if av[RF_AX_VERTS] == 1 { rf_band("verts " as *u8, m[RF_AX_VERTS], 623 rf_conf_i64(cbuf, cn, "verts_min" as *u8, 1), 624 rf_conf_i64(cbuf, cn, "verts_max" as *u8, 0), scratch, line, bad) } 625 if av[RF_AX_TRIS] == 1 { rf_band("tris " as *u8, m[RF_AX_TRIS], 626 rf_conf_i64(cbuf, cn, "tris_min" as *u8, 1), 627 rf_conf_i64(cbuf, cn, "tris_max" as *u8, 0), scratch, line, bad) } 628 if av[RF_AX_JOINTS] == 1 { rf_band("joints " as *u8, m[RF_AX_JOINTS], 629 rf_conf_i64(cbuf, cn, "joints_min" as *u8, 1), 630 rf_conf_i64(cbuf, cn, "joints_max" as *u8, 0), scratch, line, bad) } 631 if av[RF_AX_TPV_PERMIL] == 1 { rf_band("tris_per_vert_permil" as *u8, m[RF_AX_TPV_PERMIL], 632 rf_conf_i64(cbuf, cn, "tris_per_vert_permil_min" as *u8, 1), 633 rf_conf_i64(cbuf, cn, "tris_per_vert_permil_max" as *u8, 0), scratch, line, bad) } 634 if av[RF_AX_VPJ] == 1 { rf_band("verts_per_joint " as *u8, m[RF_AX_VPJ], 635 rf_conf_i64(cbuf, cn, "verts_per_joint_min" as *u8, 1), 636 rf_conf_i64(cbuf, cn, "verts_per_joint_max" as *u8, 0), scratch, line, bad) } 637 if av[RF_AX_PARTS] == 1 { rf_band("parts " as *u8, m[RF_AX_PARTS], 638 rf_conf_i64(cbuf, cn, "parts_min" as *u8, 1), 639 rf_conf_i64(cbuf, cn, "parts_max" as *u8, 0), scratch, line, bad) } 640 if av[RF_AX_TPP] == 1 { rf_band("tris_per_part " as *u8, m[RF_AX_TPP], 641 rf_conf_i64(cbuf, cn, "tris_per_part_min" as *u8, 1), 642 rf_conf_i64(cbuf, cn, "tris_per_part_max" as *u8, 0), scratch, line, bad) } 643 644 rf_say(" axes_total=" as *u8, RF_AXES, scratch, line) 645 rf_say(" axes_out_of_band=" as *u8, bad[0], scratch, line) 646 rf_say(" axes_unobservable=" as *u8, bad[1], scratch, line) 647 if bad[1] > 0 { 648 rf_out("NX-RIGFLOOR verdict=UNOBSERVABLE reason=at-least-one-bound-absent-from-the-floor-conf\n" as *u8) 649 return RF_EXIT_UNOBSERVABLE 650 } 651 if bad[0] > 0 { 652 rf_out("NX-RIGFLOOR verdict=OUT-OF-BAND\n" as *u8) 653 return RF_EXIT_OUT_OF_BAND 654 } 655 rf_out("NX-RIGFLOOR verdict=IN-BAND\n" as *u8) 656 return RF_EXIT_OK 657} 658 659func main(argc: i64, argv: *i64) -> i64 { 660 if argc < 4 { 661 let u: i64 = rf_usage() 662 sys_exit(u) 663 return u 664 } 665 let verb: *u8 = argv[1] as *u8 666 let a2: *u8 = argv[2] as *u8 667 let a3: *u8 = argv[3] as *u8 668 var rc: i64 = RF_EXIT_USAGE 669 var known: i64 = 0 670 if verb[0] == RF_VERB_DERIVE { rc = rf_derive(a2, a3); known = 1 } 671 if verb[0] == RF_VERB_SCORE { rc = rf_score(a2, a3); known = 1 } 672 if known == 0 { rc = rf_usage() } 673 sys_exit(rc) 674 return rc 675}