code wiki / (root) / nx_failclass.nx

nx_failclass.nx source

↩ module page · 718 lines · 34355 B

1// nx_failclass.nx -- CLASSIFY THE ESTATE'S OWN FAILURE RECORD AGAINST A PUBLISHED TAXONOMY. 2// 3// The estate banks ~70,100 distilled law rows and every one of them is failure-shaped, but they 4// were never CLASSIFIED and never COUNTED -- so nobody could say which failure class bites most, 5// which is chronic, or whether any class is extinct. Anecdotes without a taxonomy. 6// 7// This organ answers that question over the WHOLE population, never a sample: 8// nx_failclass scan <signatures.conf> <learnings.jsonl> <worklist.out> 9// nx_failclass selftest 10// 11// AND, since 2026-08-20, THE FM11 RUNG OF /compare/failmodes -- a second census over a DIFFERENT 12// population, the refusals this estate actually EMITS: 13// nx_failclass refusal <refusal_shape.conf> <worklist.out> 14// nx_failclass refusal_ratchet <worklist> <baseline> 15// The ruler lives in nx_refusal_shape_lib so this verb and nx_refusal_shape_gate cannot disagree, 16// and it scores the EMITTER only: whether the message names its subject and its remedy. Whether a 17// caller HEEDED it is a second, independent axis, census-only over the call record, and no counter 18// in this organ may be read as evidence about it. 19// 20// THREE PROPERTIES IT REFUSES TO GIVE UP: 21// 22// 1. u2605 IT IS A CENSUS, NOT A GATE. An uncalibrated classifier must report NUMBERS, never 23// verdicts. There is deliberately no verdict= line and no RED. Exit is 0 for a completed 24// scan, 3 for an unreadable input, 2 for usage. A reader who wants a bar must set it 25// themselves against numbers they can see. 26// 27// 2. u2605 THE REASON TRAVELS WITH THE COUNT. Every counted row is also WRITTEN to the worklist 28// with the exact signature that classified it. A count without a worklist is not actionable, 29// and a worklist without the reason is still one step short -- the reason is already in hand 30// at measure time and is normally thrown away. 31// 32// 3. u2605 UNCLASSIFIED IS ITS OWN BUCKET AND MULTI IS ITS OWN AXIS. A row matching no signature 33// is never folded into a known class; a row matching two classes genuinely names two, so it is 34// counted once in the MULTI partition member AND once per class on the overlapping per-class 35// axis, which is labelled as an axis and never summed into the partition. 36// The partition PRINTS ITS OWN SUM: single + multi + unclassified must equal rows. 37// 38// WORD BOUNDARIES ARE LOAD-BEARING, NOT COSMETIC. Measured 2026-08-20 over 70,100 rows, the bare 39// substring "cap" hits 7,802 rows and the WORD cap/caps/capped hits 2,992 -- the residue is 40// "capability", "capture", "capsearch", "capacity". A substring matcher would have published a 41// LIMIT incidence inflated by 160 percent and nothing downstream could have seen it. 42// 43// license_tier: ORIGINAL 44// module: nishi-core.quality.failclass 45// capability: FAILURE_TAXONOMY_CENSUS 46import "nx_estate_path.nx" 47import "nx_itoa_lib.nx" 48import "nx_refusal_shape_lib.nx" 49import "nx_verdictlog_lib.nx" 50 51const FC_MODE_0644: i64 = 420 52 53// Structural bounds. Each one ANNOUNCES when it binds -- a cap reached in silence becomes a 54// measurement nobody knows is partial. 55const FC_MAX_CLASS: i64 = 32 56const FC_MAX_MONTH: i64 = 64 57const FC_MONTH_LEN: i64 = 7 58const FC_TEXT_PREVIEW: i64 = 160 59 60func fw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 61func fe(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 } 62 63func fc_strlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 64 65func fc_streq(a: *u8, b: *u8) -> i64 { 66 var i: i64 = 0 67 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 68 if b[i] != (0 as u8) { return 0 } 69 return 1 70} 71 72// Case-folded, WORD-BOUNDED search of nee[0,nlen) inside hay[hs,he). Returns the offset or -1. 73// 74// THE IMPLEMENTATION MOVED TO nx_refusal_shape_lib (rs_find_word) ON 2026-08-20 AND THERE IS NOW 75// EXACTLY ONE OF IT. The FM11 rung needed the same word-bounded matcher, and a second copy is the 76// duplicate-ruler defect: two matchers that must agree, kept in step by discipline instead of by 77// construction. The call sites here are untouched and the behaviour is the same bytes -- this is a 78// delegation, not a rewrite, so `selftest` remains the proof that it did not change. 79// ★ WHEN TWO ORGANS MUST AGREE, MAKE DISAGREEMENT IMPOSSIBLE BY CONSTRUCTION. 80func fc_find_word(hay: *u8, hs: i64, he: i64, nee: *u8, nlen: i64) -> i64 { 81 return rs_find_word(hay, hs, he, nee, nlen) 82} 83 84// Plain (NOT word-bounded) forward search -- used only for structural JSON field markers such as 85// the literal "text":" , never for classification. 86func fc_find_raw(hay: *u8, hs: i64, he: i64, nee: *u8, nlen: i64) -> i64 { 87 if nlen <= 0 { return 0 - 1 } 88 var i: i64 = hs 89 while i + nlen <= he { 90 var j: i64 = 0 91 var ok: i64 = 1 92 var scanning: i64 = 1 93 while scanning == 1 { 94 if j >= nlen { scanning = 0 } else { 95 if hay[i + j] != nee[j] { ok = 0; scanning = 0 } else { j = j + 1 } 96 } 97 } 98 if ok == 1 { return i } 99 i = i + 1 100 } 101 return 0 - 1 102} 103 104// --------------------------------------------------------------------------------------------- 105// Class-name registry. Names are discovered FROM THE CONF, never hardcoded here -- adding a class 106// is a data edit, not a rebuild. 107// --------------------------------------------------------------------------------------------- 108func fc_class_id(names: *u8, nlens: *i64, ncls: *i64, s: *u8, slen: i64) -> i64 { 109 var i: i64 = 0 110 while i < ncls[0] { 111 if nlens[i] == slen { 112 var j: i64 = 0 113 var ok: i64 = 1 114 var scanning: i64 = 1 115 while scanning == 1 { 116 if j >= slen { scanning = 0 } else { 117 if names[i * 32 + j] != s[j] { ok = 0; scanning = 0 } else { j = j + 1 } 118 } 119 } 120 if ok == 1 { return i } 121 } 122 i = i + 1 123 } 124 if ncls[0] >= FC_MAX_CLASS { return 0 - 1 } 125 let id: i64 = ncls[0] 126 var k: i64 = 0 127 while k < slen { if k < 32 { names[id * 32 + k] = s[k] } k = k + 1 } 128 nlens[id] = slen 129 ncls[0] = id + 1 130 return id 131} 132 133func fc_put_class(names: *u8, nlens: *i64, id: i64) -> i64 { 134 sys_write(1, ((names as i64) + id * 32) as *u8, nlens[id]) 135 return 0 136} 137 138func fc_buf_class(dst: *u8, off: i64, names: *u8, nlens: *i64, id: i64) -> i64 { 139 var k: i64 = 0 140 while k < nlens[id] { dst[off + k] = names[id * 32 + k]; k = k + 1 } 141 return off + nlens[id] 142} 143 144func fc_buf_bytes(dst: *u8, off: i64, src: *u8, s: i64, e: i64) -> i64 { 145 var o: i64 = off 146 var i: i64 = s 147 while i < e { 148 let c: i64 = src[i] as i64 149 // Never let a raw control byte into the worklist: a forged newline turns one row into two. 150 if c < 32 { dst[o] = 46 as u8 } else { dst[o] = src[i] } 151 o = o + 1 152 i = i + 1 153 } 154 return o 155} 156 157func fc_buf_lit(dst: *u8, off: i64, s: *u8) -> i64 { 158 var o: i64 = off 159 var i: i64 = 0 160 while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } 161 return o 162} 163 164// --------------------------------------------------------------------------------------------- 165// Month registry -- the record date, taken from the slug's trailing YYYY-MM-DD. A slug without 166// one is NO-DATE, which is its own month bucket for the same reason UNCLASSIFIED is its own 167// class bucket. 168// --------------------------------------------------------------------------------------------- 169func fc_month_id(mons: *u8, nmon: *i64, src: *u8, s: i64, e: i64) -> i64 { 170 var mb: *u8 = sys_mmap(16) 171 var have: i64 = 0 172 if e - s >= 10 { 173 let p: i64 = e - 10 174 var digits_ok: i64 = 1 175 if src[p + 4] != (45 as u8) { digits_ok = 0 } 176 if src[p + 7] != (45 as u8) { digits_ok = 0 } 177 var q: i64 = 0 178 while q < 10 { 179 if q != 4 { if q != 7 { 180 let c: i64 = src[p + q] as i64 181 if c < 48 { digits_ok = 0 } 182 if c > 57 { digits_ok = 0 } 183 } } 184 q = q + 1 185 } 186 if digits_ok == 1 { 187 var k: i64 = 0 188 while k < FC_MONTH_LEN { mb[k] = src[p + k]; k = k + 1 } 189 have = 1 190 } 191 } 192 if have == 0 { 193 mb[0] = 78 as u8; mb[1] = 79 as u8; mb[2] = 45 as u8 194 mb[3] = 68 as u8; mb[4] = 65 as u8; mb[5] = 84 as u8; mb[6] = 69 as u8 195 } 196 var i: i64 = 0 197 while i < nmon[0] { 198 var j: i64 = 0 199 var ok: i64 = 1 200 var scanning: i64 = 1 201 while scanning == 1 { 202 if j >= FC_MONTH_LEN { scanning = 0 } else { 203 if mons[i * FC_MONTH_LEN + j] != mb[j] { ok = 0; scanning = 0 } else { j = j + 1 } 204 } 205 } 206 if ok == 1 { sys_munmap(mb, 16); return i } 207 i = i + 1 208 } 209 if nmon[0] >= FC_MAX_MONTH { sys_munmap(mb, 16); return 0 - 1 } 210 let id: i64 = nmon[0] 211 var k2: i64 = 0 212 while k2 < FC_MONTH_LEN { mons[id * FC_MONTH_LEN + k2] = mb[k2]; k2 = k2 + 1 } 213 nmon[0] = id + 1 214 sys_munmap(mb, 16) 215 return id 216} 217 218// --------------------------------------------------------------------------------------------- 219// selftest -- the bite teeth. u2605 A DETECTOR THAT HAS ONLY EVER SEEN CORRECT DATA HAS NOT BEEN 220// SHOWN TO FIRE, and its inverse: a matcher that fires on everything discriminates nothing. Both 221// directions are asserted here, and the NEGATIVE CONTROLS are named so a census can find them. 222// --------------------------------------------------------------------------------------------- 223func fc_expect(name: *u8, got: i64, want_hit: i64, passed: *i64, total: *i64) -> i64 { 224 total[0] = total[0] + 1 225 var hit: i64 = 0 226 if got >= 0 { hit = 1 } 227 if hit == want_hit { 228 passed[0] = passed[0] + 1 229 fw(" PASS " as *u8); fw(name); fw("\n" as *u8) 230 } else { 231 fw(" FAIL " as *u8); fw(name) 232 fw(" got_offset=" as *u8); nxi_out(got) 233 fw(" want_hit=" as *u8); nxi_out(want_hit); fw("\n" as *u8) 234 } 235 return 0 236} 237 238func fc_eq(name: *u8, got: i64, want: i64, passed: *i64, total: *i64) -> i64 { 239 total[0] = total[0] + 1 240 if got == want { 241 passed[0] = passed[0] + 1 242 fw(" PASS " as *u8); fw(name); fw(" = " as *u8); nxi_out(got); fw("\n" as *u8) 243 } else { 244 fw(" FAIL " as *u8); fw(name) 245 fw(" got=" as *u8); nxi_out(got) 246 fw(" want=" as *u8); nxi_out(want); fw("\n" as *u8) 247 } 248 return 0 249} 250 251func fc_writef(path: *u8, s: *u8) -> i64 { 252 let fd: i64 = sys_openat_wr(path, FC_MODE_0644) 253 if fd < 0 { return 0 - 1 } 254 sys_write(fd, s, fc_strlen(s)) 255 sys_close(fd) 256 return 0 257} 258 259// END-TO-END tooth over a fixture the organ ASSEMBLES AT RUNTIME. Scratch lives under /tmp so it 260// can never collide with a production corpus, and it is rewritten every run so the tooth is 261// idempotent -- a gate that is not idempotent reports on its first run and lies about every run 262// after. The fixture is built so the trivial wrong implementations CANNOT pass it: one row must 263// classify SILENT, one must classify LIMIT, one must be MULTI, and one is a NEGATIVE CONTROL that 264// contains the near-miss words "capability" and "absence" and must stay UNCLASSIFIED. 265func fc_e2e(passed: *i64, total: *i64) -> i64 { 266 fw("nx_failclass selftest -- end to end over a runtime fixture\n" as *u8) 267 sys_mkdir("/tmp/nx_failclass" as *u8, 493) 268 let cf: *u8 = "/tmp/nx_failclass/sig.conf" as *u8 269 let xf: *u8 = "/tmp/nx_failclass/corpus.jsonl" as *u8 270 let of: *u8 = "/tmp/nx_failclass/worklist.tsv" as *u8 271 272 if fc_writef(cf, "SILENT|silent|w\nLIMIT|cap|w\n" as *u8) < 0 { 273 fw(" FAIL e2e-fixture-unwritable\n" as *u8) 274 total[0] = total[0] + 1 275 return 1 276 } 277 fc_writef(xf, "{\"slug\":\"a-2026-08-01\",\"text\":\"it failed silent and nobody saw\"}\n{\"slug\":\"b-2026-08-01\",\"text\":\"the read cap was reached\"}\n{\"slug\":\"c-2026-08-01\",\"text\":\"a silent truncation at the cap\"}\n{\"slug\":\"d-2026-08-01\",\"text\":\"a capability whose absence was proven\"}\n" as *u8) 278 279 let res: *i64 = sys_mmap((FC_MAX_CLASS + 8) * 8) as *i64 280 var i: i64 = 0 281 while i < FC_MAX_CLASS + 8 { res[i] = 0 - 999; i = i + 1 } 282 283 fc_scan(cf, xf, of, res) 284 285 // ASSERT THE FIXTURE REACHED THE CONDITION before asserting the outcome: if the scan did not 286 // read four rows, every count below is about something other than the fixture. 287 fc_eq("e2e-fixture-rows-were-actually-read" as *u8, res[0], 4, passed, total) 288 fc_eq("e2e-single-SILENT" as *u8, res[3], 1, passed, total) 289 fc_eq("e2e-single-LIMIT" as *u8, res[4], 1, passed, total) 290 fc_eq("e2e-MULTI-row-counted-once-in-multi" as *u8, res[2], 1, passed, total) 291 fc_eq("neg-control-capability-and-absence-stay-UNCLASSIFIED" as *u8, res[1], 1, passed, total) 292 return 0 293} 294 295func fc_selftest() -> i64 { 296 let passed: *i64 = sys_mmap(16) as *i64 297 let total: *i64 = sys_mmap(16) as *i64 298 passed[0] = 0 299 total[0] = 0 300 fw("nx_failclass selftest -- word-boundary matcher\n" as *u8) 301 302 let cap: *u8 = "cap" as *u8 303 let h1: *u8 = "the read cap was reached" as *u8 304 fc_expect("fires-on-the-real-word-cap" as *u8, 305 fc_find_word(h1, 0, fc_strlen(h1), cap, 3), 1, passed, total) 306 307 let h2: *u8 = "a capability nobody wired" as *u8 308 fc_expect("neg-control-capability-is-not-cap" as *u8, 309 fc_find_word(h2, 0, fc_strlen(h2), cap, 3), 0, passed, total) 310 311 let h3: *u8 = "capture the artifact before retry" as *u8 312 fc_expect("neg-control-capture-is-not-cap" as *u8, 313 fc_find_word(h3, 0, fc_strlen(h3), cap, 3), 0, passed, total) 314 315 let h4: *u8 = "the buffer was capped at 4 MiB" as *u8 316 fc_expect("neg-control-capped-is-not-the-word-cap" as *u8, 317 fc_find_word(h4, 0, fc_strlen(h4), cap, 3), 0, passed, total) 318 319 let capped: *u8 = "capped" as *u8 320 fc_expect("fires-on-capped-when-capped-is-the-signature" as *u8, 321 fc_find_word(h4, 0, fc_strlen(h4), capped, 6), 1, passed, total) 322 323 let ind: *u8 = "indistinguishable from" as *u8 324 let h5: *u8 = "A STALE COPY IS INDISTINGUISHABLE FROM A BROKEN ONE" as *u8 325 fc_expect("phrase-matches-case-folded" as *u8, 326 fc_find_word(h5, 0, fc_strlen(h5), ind, 22), 1, passed, total) 327 328 let h6: *u8 = "tall vertical line never compressed never short torso" as *u8 329 fc_expect("neg-control-content-row-matches-no-limit-signature" as *u8, 330 fc_find_word(h6, 0, fc_strlen(h6), cap, 3), 0, passed, total) 331 332 let unw: *u8 = "unwired" as *u8 333 fc_expect("neg-control-content-row-matches-no-absent-signature" as *u8, 334 fc_find_word(h6, 0, fc_strlen(h6), unw, 7), 0, passed, total) 335 336 // A signature must not match across a field boundary it was never given. 337 let h7: *u8 = "silent" as *u8 338 fc_expect("bounded-window-excludes-text-outside-it" as *u8, 339 fc_find_word(h5, 0, 4, h7, 6), 0, passed, total) 340 341 fc_e2e(passed, total) 342 343 fw("passed " as *u8); nxi_out(passed[0]) 344 fw("/" as *u8); nxi_out(total[0]); fw("\n" as *u8) 345 if passed[0] == total[0] { return 0 } 346 return 1 347} 348 349// --------------------------------------------------------------------------------------------- 350// `res` is an OPTIONAL results vector so a caller can ASSERT the scan, not merely read its 351// printout. Pass 0 to skip. Layout: res[0]=rows, res[1]=unclassified, res[2]=multi, 352// res[3+c]=single count for class c. 353// 354// u2605 THIS PARAMETER EXISTS BECAUSE THE UNIT TEETH COULD NOT SEE A TOTAL FAILURE. The first 355// build of this organ passed 9/9 on its matcher while the scan path resolved every signature 356// pointer into the WRONG BUFFER, so SILENT and LIMIT both censused as ZERO over 70,100 rows. The 357// matcher was perfect and the wiring was broken, and no tooth in the organ could tell. What 358// caught it was an INDEPENDENT instrument disagreeing -- a plain substring count that said 2,163 359// rows contain "silent". A gate cannot test a conjunct its subject has no evidence for. 360func fc_scan(confp: *u8, corpp: *u8, outp: *u8, res: *i64) -> i64 { 361 let clen: *i64 = sys_mmap(16) as *i64 362 let conf: *u8 = sys_read_file(confp, clen) 363 if clen[0] <= 0 { fe("nx_failclass: cannot read signature conf\n" as *u8); return 3 } 364 365 let xlen: *i64 = sys_mmap(16) as *i64 366 let corp: *u8 = sys_read_file(corpp, xlen) 367 if xlen[0] <= 0 { fe("nx_failclass: cannot read corpus\n" as *u8); return 3 } 368 369 // Signature table capacity derived FROM THE CONF, not guessed: the shortest possible row is 370 // about 8 bytes, so conf_len/8 can never be exceeded by the rows the conf actually holds. 371 let sigcap: i64 = clen[0] / 8 + 8 372 let sig_off: *i64 = sys_mmap(sigcap * 8) as *i64 373 let sig_len: *i64 = sys_mmap(sigcap * 8) as *i64 374 let sig_cls: *i64 = sys_mmap(sigcap * 8) as *i64 375 let nsig: *i64 = sys_mmap(16) as *i64 376 nsig[0] = 0 377 378 let cnames: *u8 = sys_mmap(FC_MAX_CLASS * 32) 379 let cnlens: *i64 = sys_mmap(FC_MAX_CLASS * 8) as *i64 380 let ncls: *i64 = sys_mmap(16) as *i64 381 ncls[0] = 0 382 383 // ---- parse the conf: CLASS|signature|kind, comments start with the hash byte 35 ---------- 384 var p: i64 = 0 385 var conf_bad: i64 = 0 386 while p < clen[0] { 387 var q: i64 = p 388 var eol: i64 = clen[0] 389 var looking: i64 = 1 390 while looking == 1 { 391 if q >= clen[0] { eol = clen[0]; looking = 0 } else { 392 if conf[q] == (10 as u8) { eol = q; looking = 0 } else { q = q + 1 } 393 } 394 } 395 if eol > p { 396 if conf[p] != (35 as u8) { 397 let bar1: *u8 = "|" as *u8 398 let b1: i64 = fc_find_raw(conf, p, eol, bar1, 1) 399 if b1 > p { 400 let b2: i64 = fc_find_raw(conf, b1 + 1, eol, bar1, 1) 401 if b2 > b1 { 402 let cid: i64 = fc_class_id(cnames, cnlens, ncls, ((conf as i64) + p) as *u8, b1 - p) 403 if cid < 0 { conf_bad = conf_bad + 1 } else { 404 if nsig[0] < sigcap { 405 sig_off[nsig[0]] = b1 + 1 406 sig_len[nsig[0]] = b2 - b1 - 1 407 sig_cls[nsig[0]] = cid 408 nsig[0] = nsig[0] + 1 409 } else { conf_bad = conf_bad + 1 } 410 } 411 } else { conf_bad = conf_bad + 1 } 412 } else { conf_bad = conf_bad + 1 } 413 } 414 } 415 p = eol + 1 416 } 417 418 if nsig[0] == 0 { fe("nx_failclass: signature conf yielded ZERO signatures -- refusing to publish a distribution of noise\n" as *u8); return 3 } 419 420 // ---- counters --------------------------------------------------------------------------- 421 let single: *i64 = sys_mmap(FC_MAX_CLASS * 8) as *i64 422 let anyc: *i64 = sys_mmap(FC_MAX_CLASS * 8) as *i64 423 let mons: *u8 = sys_mmap(FC_MAX_MONTH * FC_MONTH_LEN) 424 let nmon: *i64 = sys_mmap(16) as *i64 425 nmon[0] = 0 426 let mcnt: *i64 = sys_mmap(FC_MAX_MONTH * FC_MAX_CLASS * 8) as *i64 427 let mrows: *i64 = sys_mmap(FC_MAX_MONTH * 8) as *i64 428 var z: i64 = 0 429 while z < FC_MAX_CLASS { single[z] = 0; anyc[z] = 0; z = z + 1 } 430 z = 0 431 while z < FC_MAX_MONTH * FC_MAX_CLASS { mcnt[z] = 0; z = z + 1 } 432 z = 0 433 while z < FC_MAX_MONTH { mrows[z] = 0; z = z + 1 } 434 435 let hits: *i64 = sys_mmap(FC_MAX_CLASS * 8) as *i64 436 let firstsig: *i64 = sys_mmap(FC_MAX_CLASS * 8) as *i64 437 438 // Worklist buffer sized FROM THE INPUT: every emitted line is a prefix of the row that 439 // produced it plus a short tag, so 1x the corpus plus a per-row tag allowance is a bound, 440 // not a guess. If it ever binds, it ANNOUNCES -- it does not silently stop. 441 let outcap: i64 = xlen[0] + xlen[0] / 2 + 65536 442 let out: *u8 = sys_mmap(outcap) 443 var oo: i64 = 0 444 var out_trunc: i64 = 0 445 446 var rows: i64 = 0 447 var multi: i64 = 0 448 var unclass: i64 = 0 449 var month_overflow: i64 = 0 450 451 let tmark: *u8 = "\"text\":\"" as *u8 452 let smark: *u8 = "\"slug\":\"" as *u8 453 let tab: *u8 = "\t" as *u8 454 let nl: *u8 = "\n" as *u8 455 456 var lp: i64 = 0 457 while lp < xlen[0] { 458 var q2: i64 = lp 459 var le2: i64 = xlen[0] 460 var look2: i64 = 1 461 while look2 == 1 { 462 if q2 >= xlen[0] { le2 = xlen[0]; look2 = 0 } else { 463 if corp[q2] == (10 as u8) { le2 = q2; look2 = 0 } else { q2 = q2 + 1 } 464 } 465 } 466 if le2 > lp { 467 rows = rows + 1 468 // text field: from the marker to the closing quote-brace that ends every row 469 let tm: i64 = fc_find_raw(corp, lp, le2, tmark, 8) 470 var ts: i64 = lp 471 var te: i64 = le2 472 if tm >= 0 { ts = tm + 8; te = le2 - 2 } 473 if te < ts { te = ts } 474 // slug field 475 let sm: i64 = fc_find_raw(corp, lp, le2, smark, 8) 476 var ss: i64 = lp 477 var se: i64 = lp 478 if sm >= 0 { 479 ss = sm + 8 480 var r: i64 = ss 481 var look3: i64 = 1 482 while look3 == 1 { 483 if r >= le2 { se = le2; look3 = 0 } else { 484 if corp[r] == (34 as u8) { se = r; look3 = 0 } else { r = r + 1 } 485 } 486 } 487 } 488 489 var c: i64 = 0 490 while c < FC_MAX_CLASS { hits[c] = 0; firstsig[c] = 0 - 1; c = c + 1 } 491 492 var s: i64 = 0 493 while s < nsig[0] { 494 let cid: i64 = sig_cls[s] 495 if hits[cid] == 0 { 496 let where: i64 = fc_find_word(corp, ts, te, ((conf as i64) + sig_off[s]) as *u8, sig_len[s]) 497 if where >= 0 { hits[cid] = 1; firstsig[cid] = s } 498 } 499 s = s + 1 500 } 501 502 var nclshit: i64 = 0 503 c = 0 504 while c < ncls[0] { if hits[c] == 1 { nclshit = nclshit + 1 } c = c + 1 } 505 506 let mid: i64 = fc_month_id(mons, nmon, corp, ss, se) 507 if mid < 0 { month_overflow = month_overflow + 1 } else { mrows[mid] = mrows[mid] + 1 } 508 509 if nclshit == 0 { unclass = unclass + 1 } else { 510 if nclshit == 1 { multi = multi + 0 } else { multi = multi + 1 } 511 c = 0 512 while c < ncls[0] { 513 if hits[c] == 1 { 514 anyc[c] = anyc[c] + 1 515 if nclshit == 1 { single[c] = single[c] + 1 } 516 if mid >= 0 { mcnt[mid * FC_MAX_CLASS + c] = mcnt[mid * FC_MAX_CLASS + c] + 1 } 517 // ---- the reason travels with the count ---- 518 if oo + 512 < outcap { 519 oo = fc_buf_class(out, oo, cnames, cnlens, c) 520 oo = fc_buf_lit(out, oo, tab) 521 let sg: i64 = firstsig[c] 522 oo = fc_buf_bytes(out, oo, conf, sig_off[sg], sig_off[sg] + sig_len[sg]) 523 oo = fc_buf_lit(out, oo, tab) 524 oo = fc_buf_bytes(out, oo, corp, ss, se) 525 oo = fc_buf_lit(out, oo, tab) 526 var pe: i64 = ts + FC_TEXT_PREVIEW 527 if pe > te { pe = te } 528 oo = fc_buf_bytes(out, oo, corp, ts, pe) 529 oo = fc_buf_lit(out, oo, nl) 530 } else { out_trunc = 1 } 531 } 532 c = c + 1 533 } 534 } 535 } 536 lp = le2 + 1 537 } 538 539 let fd: i64 = sys_openat_wr(outp, FC_MODE_0644) 540 var wrote: i64 = 0 541 if fd >= 0 { wrote = sys_write(fd, out, oo); sys_close(fd) } 542 543 // ---- report ----------------------------------------------------------------------------- 544 fw("nx_failclass census\n" as *u8) 545 fw("signatures=" as *u8); nxi_out(nsig[0]) 546 fw(" classes=" as *u8); nxi_out(ncls[0]) 547 fw(" conf_rows_rejected=" as *u8); nxi_out(conf_bad) 548 fw(" corpus_bytes=" as *u8); nxi_out(xlen[0]) 549 fw(" rows=" as *u8); nxi_out(rows); fw("\n" as *u8) 550 551 fw("-- PARTITION (every row lands in exactly one member) --\n" as *u8) 552 var sum: i64 = 0 553 var c3: i64 = 0 554 while c3 < ncls[0] { 555 fw("SINGLE " as *u8); fc_put_class(cnames, cnlens, c3) 556 fw(" = " as *u8); nxi_out(single[c3]); fw("\n" as *u8) 557 sum = sum + single[c3] 558 c3 = c3 + 1 559 } 560 fw("MULTI (matched more than one class) = " as *u8); nxi_out(multi); fw("\n" as *u8) 561 fw("UNCLASSIFIED (matched no signature) = " as *u8); nxi_out(unclass); fw("\n" as *u8) 562 sum = sum + multi + unclass 563 fw("PARTITION SUM = " as *u8); nxi_out(sum) 564 fw(" rows = " as *u8); nxi_out(rows) 565 if sum == rows { fw(" RECONCILES\n" as *u8) } else { fw(" DOES NOT RECONCILE -- do not publish these numbers\n" as *u8) } 566 567 fw("-- AXIS (overlapping: a row naming two classes is counted in BOTH; NEVER sum this) --\n" as *u8) 568 c3 = 0 569 while c3 < ncls[0] { 570 fw("ANY " as *u8); fc_put_class(cnames, cnlens, c3) 571 fw(" = " as *u8); nxi_out(anyc[c3]); fw("\n" as *u8) 572 c3 = c3 + 1 573 } 574 575 fw("-- TREND by record month (a level cannot express a trajectory) --\n" as *u8) 576 var m: i64 = 0 577 while m < nmon[0] { 578 sys_write(1, ((mons as i64) + m * FC_MONTH_LEN) as *u8, FC_MONTH_LEN) 579 fw(" rows=" as *u8); nxi_out(mrows[m]) 580 c3 = 0 581 while c3 < ncls[0] { 582 fw(" " as *u8); fc_put_class(cnames, cnlens, c3) 583 fw("=" as *u8); nxi_out(mcnt[m * FC_MAX_CLASS + c3]) 584 c3 = c3 + 1 585 } 586 fw("\n" as *u8) 587 m = m + 1 588 } 589 if month_overflow > 0 { 590 fw("MONTH-TABLE-FULL rows_unbucketed=" as *u8); nxi_out(month_overflow) 591 fw(" -- the trend above is a FLOOR, not a total\n" as *u8) 592 } 593 594 fw("worklist=" as *u8); fw(outp) 595 fw(" bytes=" as *u8); nxi_out(wrote) 596 fw(" of=" as *u8); nxi_out(oo) 597 if fd < 0 { fw(" WORKLIST-WRITE-FAILED" as *u8) } 598 if out_trunc == 1 { fw(" WORKLIST-TRUNCATED=1 -- the counts above are complete, the LIST is not" as *u8) } 599 fw("\n" as *u8) 600 fw("note: this organ emits NUMBERS and NO verdict -- an uncalibrated classifier that votes is a false-alarm generator with an authoritative name\n" as *u8) 601 602 if (res as i64) != 0 { 603 res[0] = rows 604 res[1] = unclass 605 res[2] = multi 606 var rc: i64 = 0 607 while rc < ncls[0] { res[3 + rc] = single[rc]; rc = rc + 1 } 608 } 609 return 0 610} 611 612// --------------------------------------------------------------------------------------------- 613// FM11 -- THE EMITTER HALF OF THE TENTH CELL, LOUD-AND-CORRECT. 614// 615// The classifier above answers "which failure class does this law row describe". THIS answers a 616// different question over a DIFFERENT population: of the refusals this estate actually emits, how 617// many name their SUBJECT and their REMEDY. It is deliberately a sibling verb rather than a second 618// organ, and the ruler itself lives in nx_refusal_shape_lib so this verb and its gate cannot drift 619// apart. 620// 621// ⚠ EMITTER AXIS ONLY, AND THAT BOUNDARY IS LOAD-BEARING. Emitter quality and caller response are 622// two INDEPENDENT axes -- nx_fs read declares its truncation cap AND its remedy (emitter CORRECT) 623// while the reader filtered that line out and filed a critical defect (caller IGNORED). The 624// caller-heeded half is census-only over the call record and is NOT measured here; a refusal that 625// callers routinely retry through is LOUD-AND-IGNORED however well written it is. 626// 627// ⚠ IT DOES NOT MOVE THE 70,100-ROW LAW CENSUS AND NOBODY SHOULD RE-RUN THAT EXPECTING MOVEMENT. 628// That instrument classifies failure-shaped law rows; this one scores emitted messages. Different 629// population, different question, deliberately separate counters. 630func fc_refusal_shape(confp: *u8, outp: *u8) -> i64 { 631 let res: *i64 = rs_res_new() 632 return rs_scan(confp, outp, res, 0 as *u8) 633} 634 635// ONE BEAT, ONE PROCESS, IN ORDER. The census writes the worklist and the ratchet reads it, so two 636// clock rows would make the ORDER a race: the ratchet would judge whichever worklist happened to be 637// on disk, which on a slow array is yesterday's. Exit carries the RATCHET's verdict 638// (0 ALLOW / 1 REFUSE / 3 UNMEASURED) because that is the part a watcher acts on; the census's 639// numbers are on stdout above it, unchanged and still verdict-free. 640func fc_refusal_beat(confp: *u8, outp: *u8, basep: *u8) -> i64 { 641 let res: *i64 = rs_res_new() 642 let rc: i64 = rs_scan(confp, outp, res, 0 as *u8) 643 if rc != 0 { return rc } 644 return rs_ratchet(outp, basep) 645} 646 647// FM3 -- ALERT QUALITY SCORED AS A CLASSIFIER. Every scoring decision lives in nx_verdictlog_lib so 648// this verb and nx_verdictlog_gate cannot drift about what a verdict is or what an offender is. 649// NUMBERS AND NO VERDICT: exit 0 whenever the census RAN, because an uncalibrated classifier that 650// votes is a false-alarm generator with an authoritative name. The bar lives in the ratchet. 651func fc_alert_score(confp: *u8, dirp: *u8, outp: *u8) -> i64 { 652 let tot: *i64 = sys_mmap(VL_T_LEN * 8) as *i64 653 return vl_alert_scan(confp, dirp, outp, tot) 654} 655 656// ONE BEAT, ONE PROCESS, IN ORDER -- the same argument the refusal beat already makes: the census 657// WRITES the worklist and the ratchet READS it, so two clock rows would make the ORDER a race and 658// the ratchet would judge whichever worklist happened to be on disk. Exit carries the RATCHET's 659// verdict (0 ALLOW / 1 REFUSE / 3 UNMEASURED) because that is the part a watcher acts on. 660func fc_alert_beat(confp: *u8, dirp: *u8, outp: *u8, basep: *u8) -> i64 { 661 let tot: *i64 = sys_mmap(VL_T_LEN * 8) as *i64 662 let rc: i64 = vl_alert_scan(confp, dirp, outp, tot) 663 if rc != 0 { return rc } 664 return vl_alert_ratchet(outp, basep) 665} 666 667// FM4 -- GRAY FAILURE, TWO VANTAGES COMPARED, NEVER ONE. Exit 0 when every declared subject was 668// adjudicated, 3 when the ruler could not be loaded or no subject is declared -- an organ that 669// examined nothing must not return the same code as one that examined everything and found no 670// disagreement. A DISAGREE is a finding to publish, not an exit code to trip a beat on, so it does 671// NOT change the exit: this verb reports, it does not adjudicate. 672func fc_two_vantage(confp: *u8, outp: *u8) -> i64 { 673 let res: *i64 = sys_mmap(VL_NVOUT * 8) as *i64 674 return vl_vantage_scan(confp, outp, res) 675} 676 677func main(argc: i64, argv: *i64) -> i64 { 678 if argc < 2 { 679 fw("usage: nx_failclass scan <signatures.conf> <corpus.jsonl> <worklist.out> | selftest | refusal <refusal_shape.conf> <worklist.out> | refusal_ratchet <worklist> <baseline> | refusal_beat <refusal_shape.conf> <worklist.out> <baseline> | alert_score <verdictlog.conf> <status-dir> <worklist.out> | alert_ratchet <worklist> <baseline> | alert_beat <verdictlog.conf> <status-dir> <worklist.out> <baseline> | two_vantage <verdictlog.conf> <worklist.out>\n" as *u8) 680 return 2 681 } 682 let cmd: *u8 = argv[1] as *u8 683 if fc_streq(cmd, "selftest" as *u8) == 1 { return fc_selftest() } 684 if fc_streq(cmd, "scan" as *u8) == 1 { 685 if argc < 5 { fe("nx_failclass scan: missing arguments. Pass the three it needs, in order: nx_failclass scan knowledge/failclass.conf <corpus.jsonl> /tmp/failclass_worklist.tsv\n" as *u8); return 2 } 686 return fc_scan(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8, 0 as *i64) 687 } 688 if fc_streq(cmd, "refusal" as *u8) == 1 { 689 if argc < 4 { fe("nx_failclass refusal: missing arguments. Pass the vocabulary and the worklist: nx_failclass refusal buildroot/knowledge/refusal_shape.conf /tmp/refusal_worklist.tsv\n" as *u8); return 2 } 690 return fc_refusal_shape(argv[2] as *u8, argv[3] as *u8) 691 } 692 if fc_streq(cmd, "refusal_beat" as *u8) == 1 { 693 if argc < 5 { fe("nx_failclass refusal_beat: missing arguments. Pass the vocabulary, the worklist and the baseline: nx_failclass refusal_beat buildroot/knowledge/refusal_shape.conf knowledge/status/refusal_shape_worklist.tsv knowledge/status/refusal_shape.baseline\n" as *u8); return 2 } 694 return fc_refusal_beat(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8) 695 } 696 if fc_streq(cmd, "refusal_ratchet" as *u8) == 1 { 697 if argc < 4 { fe("nx_failclass refusal_ratchet: missing arguments. Pass the census worklist and the baseline: nx_failclass refusal_ratchet /tmp/refusal_worklist.tsv knowledge/status/refusal_shape.baseline\n" as *u8); return 2 } 698 return rs_ratchet(argv[2] as *u8, argv[3] as *u8) 699 } 700 if fc_streq(cmd, "alert_score" as *u8) == 1 { 701 if argc < 5 { fe("nx_failclass alert_score: missing arguments. Pass the ruler, the directory of verdict streams and the worklist, in order: nx_failclass alert_score buildroot/knowledge/verdictlog.conf knowledge/status/ /tmp/alert_score_worklist.tsv\n" as *u8); return 2 } 702 return fc_alert_score(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8) 703 } 704 if fc_streq(cmd, "alert_beat" as *u8) == 1 { 705 if argc < 6 { fe("nx_failclass alert_beat: missing arguments. Pass the ruler, the directory, the worklist and the baseline: nx_failclass alert_beat buildroot/knowledge/verdictlog.conf knowledge/status/ knowledge/status/alert_score_worklist.tsv knowledge/status/alert_score.baseline\n" as *u8); return 2 } 706 return fc_alert_beat(argv[2] as *u8, argv[3] as *u8, argv[4] as *u8, argv[5] as *u8) 707 } 708 if fc_streq(cmd, "alert_ratchet" as *u8) == 1 { 709 if argc < 4 { fe("nx_failclass alert_ratchet: missing arguments. Pass the census worklist and the baseline: nx_failclass alert_ratchet /tmp/alert_score_worklist.tsv knowledge/status/alert_score.baseline\n" as *u8); return 2 } 710 return vl_alert_ratchet(argv[2] as *u8, argv[3] as *u8) 711 } 712 if fc_streq(cmd, "two_vantage" as *u8) == 1 { 713 if argc < 4 { fe("nx_failclass two_vantage: missing arguments. Pass the ruler and the worklist: nx_failclass two_vantage buildroot/knowledge/verdictlog.conf /tmp/two_vantage.tsv\n" as *u8); return 2 } 714 return fc_two_vantage(argv[2] as *u8, argv[3] as *u8) 715 } 716 fe("nx_failclass: unknown mode. Use one of scan, selftest, refusal, refusal_ratchet, refusal_beat, alert_score, alert_ratchet, alert_beat, two_vantage -- run nx_failclass with no arguments to see the full call grammar.\n" as *u8) 717 return 2 718}