code wiki / (root) / nx_refusal_shape_lib.nx

nx_refusal_shape_lib.nx source

↩ module page · 1087 lines · 49186 B

1// nx_refusal_shape_lib.nx -- DOES THIS REFUSAL NAME ITS SUBJECT AND ITS REMEDY? 2// 3// FM11 of /compare/failmodes: the MECHANIZABLE half of the tenth cell, LOUD-AND-CORRECT. The 4// taxonomy lane established that EMITTER QUALITY and CALLER RESPONSE are two INDEPENDENT AXES and 5// proved it with one instance that scores on both at once: nx_fs read declares its truncation cap 6// AND its remedy in its last line (emitter CORRECT) while the reader filtered that line out and 7// filed it as a critical defect (caller IGNORED). A single axis keyed on "did the caller obey" 8// would file that instance under LOUD-AND-IGNORED and exclude the very case that motivated the 9// cell. 10// 11// THIS LIBRARY OWNS THE EMITTER HALF ONLY, AND THAT SCOPE IS DELIBERATE. 12// The caller half -- did anyone act on the refusal -- is CENSUS-ONLY over the call record and is 13// explicitly OUT OF SCOPE here, because a refusal that callers routinely retry through is 14// LOUD-AND-IGNORED however well written it is. No function in this file may be read as evidence 15// about a caller. 16// 17// WHY A LIBRARY AND NOT A SECOND ORGAN. The census verb lives in nx_failclass (fc_refusal_shape) 18// and the gate lives in nx_refusal_shape_gate, and the two MUST agree about what "shaped" means. 19// They agree BY CONSTRUCTION rather than by discipline: there is exactly ONE classifier 20// (rs_bucket), ONE block builder and ONE vocabulary loader, and both consumers import them. 21// 22// THE UNIT IS THE COMPOSED EMISSION, NOT THE STRING LITERAL, AND THAT IS THE WHOLE DESIGN. 23// Measured on the canonical guard before a line was written: nx_tools_api.nx builds the truncation 24// envelope from THREE pieces -- 25// b = ta_cat(jb, b, " [NX-TRUNCATED OUTPUT-IS-PARTIAL capture_cap=" as *u8) 26// b = ta_catn(jb, b, xcap) 27// b = ta_cat(jb, b, " -- narrow the query or re-run with _async=1]" as *u8) 28// -- so the refusal marker and its remedy live in DIFFERENT LITERALS with a non-literal line 29// between them. A per-literal checker would flag a KNOWN-GOOD GUARD, and 30// * A SHAPE CHECKER THAT FLAGS A KNOWN-GOOD GUARD IS WORSE THAN NONE, BECAUSE SOMEONE WILL ACT ON 31// IT. * 32// So a MESSAGE here is a BLOCK: a run of consecutive source lines carrying string literals, 33// tolerating up to max_gap_lines literal-free lines, broken HARD by a blank line, a comment line, 34// or a line whose first non-space character closes a scope. The brace break is what stops a block 35// borrowing an unrelated neighbour's remedy, and it is bite-proven in the gate. 36// 37// COMMENTS ARE NOT SCANNED. A source-scanning detector finds the PROSE DESCRIBING the defect and 38// accuses the author who documented it -- prose is source bytes too. Comment lines are hard breaks 39// and no literal is ever harvested from one. 40// 41// IT EMITS NUMBERS AND NO VERDICT. The distribution is published BEFORE any bar is chosen, because 42// a detector that fires on everything discriminates nothing and an uncalibrated classifier that 43// votes is a false-alarm generator with an authoritative name. The bar lives in the RATCHET 44// (rs_ratchet), which is a set of NAMES, self-baselining, and prints its bar in every verdict. 45// 46// UNKNOWN IS ITS OWN BUCKET. A block too short to carry either signal has not been judged, it has 47// been skipped: an axis that cannot see must abstain, not acquit. UNKNOWN is never an offender and 48// never folded into a known bucket. 49// 50// DECLARED IMPRECISION, so the next reader does not trust this as exact: 51// 1. A literal that spans a source line (a bare newline inside a string) is read as two. 52// 2. A line that both closes a scope and emits (`} else { fw("...") }`) breaks before its own 53// literal, so that literal starts the next block. 54// 3. SUBJECT is witnessed structurally (a key= pair, a path segment, a <placeholder>), never 55// semantically. It answers "is this bound to a particular", not "is it the RIGHT particular". 56// 4. The population is source literals, so a refusal assembled entirely from runtime values 57// carries no marker and is invisible here. Every per-bucket count is a FLOOR. 58// 59// license_tier: ORIGINAL 60// module: nishi-core.quality.refusal_shape 61// capability: REFUSAL_EMITTER_SHAPE 62import "nx_syscalls.nx" 63import "nx_gatekit_lib.nx" 64 65// ---- byte names: a raw code inside a comparison is a literal no reader recognises and no grep 66// ---- finds, which is the same defect as spelling a string as character codes. 67const RS_TAB: i64 = 9 68const RS_NL: i64 = 10 69const RS_SP: i64 = 32 70const RS_QUOTE: i64 = 34 71const RS_HASH: i64 = 35 72const RS_MINUS: i64 = 45 73const RS_SLASH: i64 = 47 74const RS_COLON: i64 = 58 75const RS_SEMI: i64 = 59 76const RS_LT: i64 = 60 77const RS_EQ: i64 = 61 78const RS_GT: i64 = 62 79const RS_USCORE: i64 = 95 80const RS_BSLASH: i64 = 92 81const RS_BAR: i64 = 124 82const RS_RBRACE: i64 = 125 83const RS_DOT: i64 = 46 84const RS_MODE_0644: i64 = 420 85 86// ---- buckets. The partition is over the POPULATION (blocks carrying a refusal marker) and its 87// ---- members are printed with their sum, because a partition you cannot reconcile is a leak. 88const RS_B_SHAPED: i64 = 0 89const RS_B_NOREMEDY: i64 = 1 90const RS_B_NOSUBJECT: i64 = 2 91const RS_B_BARE: i64 = 3 92const RS_B_UNKNOWN: i64 = 4 93// TWO BUCKETS THAT EXIST BECAUSE THE FIRST RUN'S HITS WERE READ INSTEAD OF SUPPRESSED. 94// v1 flagged 920 permil of the population as offenders, and reading the flagged rows -- not 95// assuming them -- found two whole kinds of thing in there that are not refusals a caller reads: 96// TEST-ASSERTION: a gate tooth or selftest line. Signature: it states BOTH outcomes at once 97// ("verdict=GREEN ... verdict=RED", "PASS ... FAIL"). A refusal never announces its own success 98// alternative in the same breath, so this discriminates without naming any file or organ. 99// USAGE-BANNER: a call-grammar banner. It IS its own remedy -- it states the correct form -- so 100// scoring it NO-REMEDY indicts the most helpful message an organ emits. 101// Both are PARTITION MEMBERS (they sum) and neither is an OFFENDER. Naming them is the point: a 102// detector that suppresses its false positives instead of classifying them has thrown away its own 103// taxonomy, and a headline computed over the un-split population is a number nobody should plan 104// against. 105const RS_B_TEST: i64 = 5 106const RS_B_USAGE: i64 = 6 107const RS_NBUCKET: i64 = 7 108 109// ---- results vector. A caller ASSERTS the scan rather than parsing its printout: a gate that 110// ---- reads its subject's stdout is testing the formatter as much as the ruler. 111const RS_R_FILES: i64 = 0 112const RS_R_BYTES: i64 = 1 113const RS_R_BLOCKS: i64 = 2 114const RS_R_POP: i64 = 3 115const RS_R_BUCKET0: i64 = 4 // .. RS_R_BUCKET0 + RS_NBUCKET - 1 116const RS_R_TRUNC: i64 = 11 117const RS_R_UNREADABLE: i64 = 12 118const RS_R_WANTBUCKET: i64 = 13 119const RS_R_WANTLINE: i64 = 14 120const RS_R_WANTHITS: i64 = 15 121const RS_R_OFFENDERS: i64 = 16 122const RS_RLEN: i64 = 20 123 124// ---- vocabulary block. ONE i64 array so every consumer passes ONE pointer: a twelve-parameter 125// ---- signature is where a caller starts passing the wrong array. 126const RS_MAXVOC: i64 = 512 127const RS_V_NMARK: i64 = 0 128const RS_V_NREM: i64 = 1 129const RS_V_MAXGAP: i64 = 2 130const RS_V_MINMSG: i64 = 3 131const RS_V_REJECT: i64 = 4 132const RS_V_OVER: i64 = 5 // vocabulary cap reached -> the vocabulary is a FLOOR 133const RS_V_NEXCL: i64 = 6 // declared self-fixture exclusions 134const RS_V_EXCOUNT: i64 = 7 // files actually skipped by those exclusions 135const RS_V_BASE: i64 = 8 // markers: (off,len) pairs from here 136const RS_V_REMBASE: i64 = 1032 // RS_V_BASE + 2 * RS_MAXVOC 137const RS_V_MKHIT: i64 = 2056 // RS_V_REMBASE + 2 * RS_MAXVOC: per-marker first-fired tally 138const RS_V_RMHIT: i64 = 2568 // RS_V_MKHIT + RS_MAXVOC: per-remedy first-fired tally 139const RS_V_EXBASE: i64 = 3080 // RS_V_RMHIT + RS_MAXVOC: exclusion (off,len) pairs 140const RS_V_PASSBASE: i64 = 4104 // RS_V_EXBASE + 2 * RS_MAXVOC: positive-outcome tokens 141const RS_V_FAILBASE: i64 = 5128 // RS_V_PASSBASE + 2 * RS_MAXVOC: negative-outcome tokens 142const RS_V_SRBASE: i64 = 6152 // RS_V_FAILBASE + 2 * RS_MAXVOC: self-remedy (usage) phrases 143const RS_V_NPASS: i64 = 7176 // scalars appended after the tables, never wedged before them 144const RS_V_NFAIL: i64 = 7177 145const RS_V_NSR: i64 = 7178 146const RS_V_LEN: i64 = 7184 147 148// Corpus bounds. Each ANNOUNCES when it binds; gk_corpus_scan REFUSES rather than under-reporting, 149// which is why these are generous rather than tuned. Measured 2026-08-20: the compile corpus is 150// 22,923 files, so RS_MAXF has 1.4x headroom and a breach is a refusal, never a silent prefix. 151const RS_MAXF: i64 = 32768 152const RS_STRIDE: i64 = 256 153const RS_SLACK: i64 = 65536 154const RS_PREVIEW: i64 = 160 155const RS_ROW_MAX: i64 = 1024 156const RS_OUT_PER_FILE: i64 = 8192 // worklist headroom per source file; a bind ANNOUNCES 157const RS_NUMBUF: i64 = 64 158const RS_PATHBUF: i64 = 4096 159const RS_SMALL: i64 = 16 160 161func rs_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 162func rs_err(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 } 163 164func rs_putn(v: i64) -> i64 { 165 let b: *u8 = sys_mmap(RS_NUMBUF) 166 let o: i64 = gk_catn(b, 0, v) 167 sys_write(1, b, o) 168 sys_munmap(b, RS_NUMBUF) 169 return 0 170} 171 172func rs_lower(c: i64) -> i64 { 173 if c >= 65 { if c <= 90 { return c + 32 } } 174 return c 175} 176 177// A word character AFTER case folding: a-z 0-9 underscore. Everything else is a boundary. 178func rs_wordch(c: i64) -> i64 { 179 if c >= 97 { if c <= 122 { return 1 } } 180 if c >= 48 { if c <= 57 { return 1 } } 181 if c == RS_USCORE { return 1 } 182 return 0 183} 184 185// Case-folded, WORD-BOUNDED search of nee[0,nlen) inside hay[hs,he). Returns the offset or -1. 186// The needle must already be lower case -- only the haystack is folded. 187// The inner compare uses a SEPARATE `scanning` flag rather than clobbering `j`: a loop that breaks 188// by writing its own cursor destroys the position it would have to report. 189func rs_find_word(hay: *u8, hs: i64, he: i64, nee: *u8, nlen: i64) -> i64 { 190 if nlen <= 0 { return 0 - 1 } 191 var i: i64 = hs 192 while i + nlen <= he { 193 var j: i64 = 0 194 var ok: i64 = 1 195 var scanning: i64 = 1 196 while scanning == 1 { 197 if j >= nlen { scanning = 0 } else { 198 if rs_lower(hay[i + j] as i64) != (nee[j] as i64) { ok = 0; scanning = 0 } else { j = j + 1 } 199 } 200 } 201 if ok == 1 { 202 var lb: i64 = 1 203 if i > hs { if rs_wordch(rs_lower(hay[i - 1] as i64)) == 1 { lb = 0 } } 204 var rb: i64 = 1 205 if i + nlen < he { if rs_wordch(rs_lower(hay[i + nlen] as i64)) == 1 { rb = 0 } } 206 if lb == 1 { if rb == 1 { return i } } 207 } 208 i = i + 1 209 } 210 return 0 - 1 211} 212 213// --------------------------------------------------------------------------------------------- 214// SUBJECT: is the refusal bound to a concrete PARTICULAR the reader can act on? 215// 216// Three INDEPENDENT structural witnesses, any one of which suffices. They are structural on 217// purpose: "does this name the RIGHT subject" is semantic and belongs to the LOUD-BUT-WRONG-SUBJECT 218// class, which has no mechanical detector anywhere and does not acquire one by being asserted here. 219// 1. a key=value pair -- an identifier character immediately before an `=` that is not `==`. 220// Comparison operators cannot match: `>=` `<=` `!=` all carry a non-word character before the 221// `=`, and `==` carries an `=`. 222// 2. a path segment -- a `/` with word characters on both sides. 223// 3. a placeholder -- `<name>` naming the particular the caller must supply. 224// --------------------------------------------------------------------------------------------- 225func rs_has_particular(buf: *u8, s: i64, e: i64) -> i64 { 226 var i: i64 = s 227 while i < e { 228 let c: i64 = buf[i] as i64 229 if c == RS_EQ { 230 if i > s { 231 if rs_wordch(rs_lower(buf[i - 1] as i64)) == 1 { 232 var nxt: i64 = 0 233 if i + 1 < e { nxt = buf[i + 1] as i64 } 234 if nxt != RS_EQ { return 1 } 235 } 236 } 237 } 238 if c == RS_SLASH { 239 if i > s { 240 if i + 1 < e { 241 if rs_wordch(rs_lower(buf[i - 1] as i64)) == 1 { 242 if rs_wordch(rs_lower(buf[i + 1] as i64)) == 1 { return 1 } 243 } 244 } 245 } 246 } 247 if c == RS_LT { 248 if i + 1 < e { 249 if rs_wordch(rs_lower(buf[i + 1] as i64)) == 1 { 250 var j: i64 = i + 1 251 var scanning: i64 = 1 252 while scanning == 1 { 253 if j >= e { scanning = 0 } else { 254 let d: i64 = buf[j] as i64 255 if d == RS_GT { return 1 } 256 var cont: i64 = 0 257 if rs_wordch(rs_lower(d)) == 1 { cont = 1 } 258 if d == RS_MINUS { cont = 1 } 259 if cont == 1 { j = j + 1 } else { scanning = 0 } 260 } 261 } 262 } 263 } 264 } 265 i = i + 1 266 } 267 return 0 268} 269 270// --------------------------------------------------------------------------------------------- 271// THE RULER. Pure: same inputs, same bucket, no I/O, no globals. The gate tests THIS directly, so 272// a change to the decision cannot hide behind a formatter. 273// UNKNOWN is decided FIRST and is its own bucket: a block too short to carry either signal has not 274// been judged. 275// --------------------------------------------------------------------------------------------- 276// ORDER IS PART OF THE CONTRACT AND IS STATED HERE RATHER THAN LEFT TO THE READER: 277// 1. TEST-ASSERTION -- a tooth that also carries a usage word is still a tooth. 278// 2. USAGE-BANNER -- the call grammar IS the remedy. 279// 3. UNKNOWN -- too short to carry either signal: abstain, do not acquit. 280// 4. the subject / remedy ruler. 281func rs_bucket(blen: i64, minmsg: i64, subj: i64, rem: i64, testassert: i64, usage: i64) -> i64 { 282 if testassert == 1 { return RS_B_TEST } 283 if usage == 1 { return RS_B_USAGE } 284 if blen < minmsg { return RS_B_UNKNOWN } 285 if subj == 1 { 286 if rem == 1 { return RS_B_SHAPED } 287 return RS_B_NOREMEDY 288 } 289 if rem == 1 { return RS_B_NOSUBJECT } 290 return RS_B_BARE 291} 292 293func rs_bucket_name(b: i64) -> *u8 { 294 if b == RS_B_SHAPED { return "SHAPED" as *u8 } 295 if b == RS_B_NOREMEDY { return "NO-REMEDY" as *u8 } 296 if b == RS_B_NOSUBJECT { return "NO-SUBJECT" as *u8 } 297 if b == RS_B_BARE { return "BARE" as *u8 } 298 if b == RS_B_UNKNOWN { return "UNKNOWN" as *u8 } 299 if b == RS_B_TEST { return "TEST-ASSERTION" as *u8 } 300 if b == RS_B_USAGE { return "USAGE-BANNER" as *u8 } 301 return "UNRECOGNISED" as *u8 302} 303 304// An OFFENDER is a judged refusal that is not SHAPED. UNKNOWN can never be one: folding an 305// unobservable into a known bucket is how a census invents findings. TEST-ASSERTION and 306// USAGE-BANNER can never be one either: neither is a refusal a caller reads, and flagging them 307// would send a campaign at the wrong subject with the numbers apparently behind it. 308func rs_is_offender(b: i64) -> i64 { 309 if b == RS_B_SHAPED { return 0 } 310 if b == RS_B_UNKNOWN { return 0 } 311 if b == RS_B_TEST { return 0 } 312 if b == RS_B_USAGE { return 0 } 313 return 1 314} 315 316// --------------------------------------------------------------------------------------------- 317// VOCABULARY, loaded from data. Adding a marker or a remedy phrase is a conf edit, never a rebuild. 318// Grammar: marker|<lower-case word or phrase> 319// remedy|<lower-case word or phrase> 320// param|<name>|<integer> 321// Comment rows start with `;` or the hash byte. A row it cannot parse is COUNTED, never ignored. 322// --------------------------------------------------------------------------------------------- 323func rs_range_eq(buf: *u8, s: i64, e: i64, lit: *u8) -> i64 { 324 let n: i64 = gk_len(lit) 325 if e - s != n { return 0 } 326 var i: i64 = 0 327 while i < n { 328 if buf[s + i] != lit[i] { return 0 } 329 i = i + 1 330 } 331 return 1 332} 333 334func rs_find_byte(buf: *u8, s: i64, e: i64, b: i64) -> i64 { 335 var i: i64 = s 336 while i < e { 337 if (buf[i] as i64) == b { return i } 338 i = i + 1 339 } 340 return 0 - 1 341} 342 343func rs_atoi(buf: *u8, s: i64, e: i64) -> i64 { 344 var v: i64 = 0 345 var any: i64 = 0 346 var i: i64 = s 347 while i < e { 348 let c: i64 = buf[i] as i64 349 if c >= 48 { 350 if c <= 57 { v = v * 10 + (c - 48); any = 1 } 351 } 352 i = i + 1 353 } 354 if any == 0 { return 0 - 1 } 355 return v 356} 357 358// Returns the conf buffer (its bytes back the vocabulary offsets), or 0 when the conf cannot be 359// used. It REFUSES on a conf that yields no markers, no remedies, or an unset parameter rather 360// than publishing a distribution of noise. 361func rs_conf_load(confp: *u8, clen: *i64, voc: *i64) -> *u8 { 362 var z: i64 = 0 363 while z < RS_V_LEN { voc[z] = 0; z = z + 1 } 364 voc[RS_V_MAXGAP] = 0 - 1 365 voc[RS_V_MINMSG] = 0 - 1 366 let conf: *u8 = sys_read_file(confp, clen) 367 if clen[0] <= 0 { 368 rs_err("nx_refusal_shape: cannot read the vocabulary conf at " as *u8) 369 rs_err(confp) 370 rs_err(" -- pass the path to knowledge/refusal_shape.conf\n" as *u8) 371 return 0 as *u8 372 } 373 var p: i64 = 0 374 while p < clen[0] { 375 let eol: i64 = gk_eol(conf, p, clen[0]) 376 if eol > p { 377 let f: i64 = conf[p] as i64 378 var skip: i64 = 0 379 if f == RS_SEMI { skip = 1 } 380 if f == RS_HASH { skip = 1 } 381 if skip == 0 { 382 let b1: i64 = rs_find_byte(conf, p, eol, RS_BAR) 383 if b1 < 0 { voc[RS_V_REJECT] = voc[RS_V_REJECT] + 1 } else { 384 var handled: i64 = 0 385 if rs_range_eq(conf, p, b1, "marker" as *u8) == 1 { 386 handled = 1 387 if voc[RS_V_NMARK] >= RS_MAXVOC { voc[RS_V_OVER] = voc[RS_V_OVER] + 1 } else { 388 let k: i64 = voc[RS_V_NMARK] 389 voc[RS_V_BASE + k * 2] = b1 + 1 390 voc[RS_V_BASE + k * 2 + 1] = eol - b1 - 1 391 voc[RS_V_NMARK] = k + 1 392 } 393 } 394 if rs_range_eq(conf, p, b1, "remedy" as *u8) == 1 { 395 handled = 1 396 if voc[RS_V_NREM] >= RS_MAXVOC { voc[RS_V_OVER] = voc[RS_V_OVER] + 1 } else { 397 let k2: i64 = voc[RS_V_NREM] 398 voc[RS_V_REMBASE + k2 * 2] = b1 + 1 399 voc[RS_V_REMBASE + k2 * 2 + 1] = eol - b1 - 1 400 voc[RS_V_NREM] = k2 + 1 401 } 402 } 403 if rs_range_eq(conf, p, b1, "passtoken" as *u8) == 1 { 404 handled = 1 405 if voc[RS_V_NPASS] >= RS_MAXVOC { voc[RS_V_OVER] = voc[RS_V_OVER] + 1 } else { 406 let kp: i64 = voc[RS_V_NPASS] 407 voc[RS_V_PASSBASE + kp * 2] = b1 + 1 408 voc[RS_V_PASSBASE + kp * 2 + 1] = eol - b1 - 1 409 voc[RS_V_NPASS] = kp + 1 410 } 411 } 412 if rs_range_eq(conf, p, b1, "failtoken" as *u8) == 1 { 413 handled = 1 414 if voc[RS_V_NFAIL] >= RS_MAXVOC { voc[RS_V_OVER] = voc[RS_V_OVER] + 1 } else { 415 let kf: i64 = voc[RS_V_NFAIL] 416 voc[RS_V_FAILBASE + kf * 2] = b1 + 1 417 voc[RS_V_FAILBASE + kf * 2 + 1] = eol - b1 - 1 418 voc[RS_V_NFAIL] = kf + 1 419 } 420 } 421 if rs_range_eq(conf, p, b1, "selfremedy" as *u8) == 1 { 422 handled = 1 423 if voc[RS_V_NSR] >= RS_MAXVOC { voc[RS_V_OVER] = voc[RS_V_OVER] + 1 } else { 424 let ks: i64 = voc[RS_V_NSR] 425 voc[RS_V_SRBASE + ks * 2] = b1 + 1 426 voc[RS_V_SRBASE + ks * 2 + 1] = eol - b1 - 1 427 voc[RS_V_NSR] = ks + 1 428 } 429 } 430 if rs_range_eq(conf, p, b1, "exclude_file" as *u8) == 1 { 431 handled = 1 432 if voc[RS_V_NEXCL] >= RS_MAXVOC { voc[RS_V_OVER] = voc[RS_V_OVER] + 1 } else { 433 let k3: i64 = voc[RS_V_NEXCL] 434 voc[RS_V_EXBASE + k3 * 2] = b1 + 1 435 voc[RS_V_EXBASE + k3 * 2 + 1] = eol - b1 - 1 436 voc[RS_V_NEXCL] = k3 + 1 437 } 438 } 439 if rs_range_eq(conf, p, b1, "param" as *u8) == 1 { 440 handled = 1 441 let b2: i64 = rs_find_byte(conf, b1 + 1, eol, RS_BAR) 442 if b2 < 0 { voc[RS_V_REJECT] = voc[RS_V_REJECT] + 1 } else { 443 let v: i64 = rs_atoi(conf, b2 + 1, eol) 444 var known: i64 = 0 445 if rs_range_eq(conf, b1 + 1, b2, "max_gap_lines" as *u8) == 1 { voc[RS_V_MAXGAP] = v; known = 1 } 446 if rs_range_eq(conf, b1 + 1, b2, "min_msg_bytes" as *u8) == 1 { voc[RS_V_MINMSG] = v; known = 1 } 447 if known == 0 { voc[RS_V_REJECT] = voc[RS_V_REJECT] + 1 } 448 } 449 } 450 if handled == 0 { voc[RS_V_REJECT] = voc[RS_V_REJECT] + 1 } 451 } 452 } 453 } 454 p = eol + 1 455 } 456 if voc[RS_V_NMARK] <= 0 { 457 rs_err("nx_refusal_shape: the conf yielded ZERO refusal markers -- refusing to publish a distribution of noise. Add marker| rows to the conf.\n" as *u8) 458 return 0 as *u8 459 } 460 if voc[RS_V_NREM] <= 0 { 461 rs_err("nx_refusal_shape: the conf yielded ZERO remedy phrases -- every refusal would score NO-REMEDY. Add remedy| rows to the conf.\n" as *u8) 462 return 0 as *u8 463 } 464 if voc[RS_V_NPASS] <= 0 { 465 rs_err("nx_refusal_shape: the conf yielded ZERO positive-outcome tokens -- the TEST-ASSERTION bucket could never fire and every gate tooth would be counted as a refusal. Add passtoken| rows to the conf.\n" as *u8) 466 return 0 as *u8 467 } 468 if voc[RS_V_NFAIL] <= 0 { 469 rs_err("nx_refusal_shape: the conf yielded ZERO negative-outcome tokens -- the TEST-ASSERTION bucket could never fire. Add failtoken| rows to the conf.\n" as *u8) 470 return 0 as *u8 471 } 472 if voc[RS_V_NSR] <= 0 { 473 rs_err("nx_refusal_shape: the conf yielded ZERO self-remedy phrases -- every usage banner would be scored NO-REMEDY. Add selfremedy| rows to the conf.\n" as *u8) 474 return 0 as *u8 475 } 476 if voc[RS_V_MAXGAP] < 0 { 477 rs_err("nx_refusal_shape: the conf sets no max_gap_lines -- add param|max_gap_lines|<n> to the conf.\n" as *u8) 478 return 0 as *u8 479 } 480 if voc[RS_V_MINMSG] < 0 { 481 rs_err("nx_refusal_shape: the conf sets no min_msg_bytes -- add param|min_msg_bytes|<n> to the conf.\n" as *u8) 482 return 0 as *u8 483 } 484 return conf 485} 486 487// Index of the first vocabulary entry that matches, or -1. The INDEX is returned rather than a 488// boolean because the reason must travel with the count: the worklist prints which marker fired. 489func rs_vocab_hit(conf: *u8, voc: *i64, base: i64, n: i64, hay: *u8, hs: i64, he: i64) -> i64 { 490 var i: i64 = 0 491 while i < n { 492 let w: i64 = rs_find_word(hay, hs, he, ((conf as i64) + voc[base + i * 2]) as *u8, voc[base + i * 2 + 1]) 493 if w >= 0 { return i } 494 i = i + 1 495 } 496 return 0 - 1 497} 498 499// Harvest the string literals on ONE source line into the block buffer. Tracks in-string state, so 500// a `//` INSIDE a literal (every url in the tree) does not end the line and a `"` inside a comment 501// does not open one. Every escape becomes a SPACE: an escaped newline must not glue the last word 502// of one sentence to the first word of the next, or a word-bounded matcher reads a word nobody 503// wrote. Returns the new block offset. 504func rs_line_lits(buf: *u8, s: i64, e: i64, blk: *u8, bo0: i64, blkcap: i64) -> i64 { 505 var bo: i64 = bo0 506 var i: i64 = s 507 var ins: i64 = 0 508 var scanning: i64 = 1 509 while scanning == 1 { 510 if i >= e { scanning = 0 } else { 511 let c: i64 = buf[i] as i64 512 if ins == 0 { 513 if c == RS_QUOTE { 514 ins = 1 515 if bo + 1 < blkcap { blk[bo] = RS_SP as u8; bo = bo + 1 } 516 } else { 517 if c == RS_SLASH { 518 if i + 1 < e { 519 if (buf[i + 1] as i64) == RS_SLASH { scanning = 0 } 520 } 521 } 522 } 523 } else { 524 if c == RS_BSLASH { 525 i = i + 1 526 if bo + 1 < blkcap { blk[bo] = RS_SP as u8; bo = bo + 1 } 527 } else { 528 if c == RS_QUOTE { ins = 0 } else { 529 if bo + 1 < blkcap { blk[bo] = buf[i]; bo = bo + 1 } 530 } 531 } 532 } 533 i = i + 1 534 } 535 } 536 return bo 537} 538 539// Classify ONE assembled block and, when it is in the population, write its worklist row. 540// `want` is an OPTIONAL lower-case witness string: when the block carries it, its bucket and line 541// are reported in the results vector so a caller can ask about ONE known guard instead of parsing 542// a whole distribution. Pass 0 to skip. 543func rs_block_emit(blk: *u8, bo: i64, bline: i64, path: *u8, conf: *u8, voc: *i64, res: *i64, out: *u8, outcap: i64, oo: *i64, want: *u8) -> i64 { 544 res[RS_R_BLOCKS] = res[RS_R_BLOCKS] + 1 545 let mi: i64 = rs_vocab_hit(conf, voc, RS_V_BASE, voc[RS_V_NMARK], blk, 0, bo) 546 if mi < 0 { return 0 } 547 res[RS_R_POP] = res[RS_R_POP] + 1 548 voc[RS_V_MKHIT + mi] = voc[RS_V_MKHIT + mi] + 1 549 550 var rem: i64 = 0 551 let ri: i64 = rs_vocab_hit(conf, voc, RS_V_REMBASE, voc[RS_V_NREM], blk, 0, bo) 552 if ri >= 0 { rem = 1; voc[RS_V_RMHIT + ri] = voc[RS_V_RMHIT + ri] + 1 } 553 let subj: i64 = rs_has_particular(blk, 0, bo) 554 // A message that states BOTH outcomes at once is a test assertion, not a refusal. 555 var testassert: i64 = 0 556 if rs_vocab_hit(conf, voc, RS_V_PASSBASE, voc[RS_V_NPASS], blk, 0, bo) >= 0 { 557 if rs_vocab_hit(conf, voc, RS_V_FAILBASE, voc[RS_V_NFAIL], blk, 0, bo) >= 0 { testassert = 1 } 558 } 559 var usage: i64 = 0 560 if rs_vocab_hit(conf, voc, RS_V_SRBASE, voc[RS_V_NSR], blk, 0, bo) >= 0 { usage = 1 } 561 let b: i64 = rs_bucket(bo, voc[RS_V_MINMSG], subj, rem, testassert, usage) 562 res[RS_R_BUCKET0 + b] = res[RS_R_BUCKET0 + b] + 1 563 if rs_is_offender(b) == 1 { res[RS_R_OFFENDERS] = res[RS_R_OFFENDERS] + 1 } 564 565 if (want as i64) != 0 { 566 if rs_find_word(blk, 0, bo, want, gk_len(want)) >= 0 { 567 res[RS_R_WANTBUCKET] = b 568 res[RS_R_WANTLINE] = bline 569 res[RS_R_WANTHITS] = res[RS_R_WANTHITS] + 1 570 } 571 } 572 573 if (out as i64) == 0 { return 0 } 574 if oo[0] + RS_ROW_MAX >= outcap { res[RS_R_TRUNC] = 1; return 0 } 575 var o: i64 = oo[0] 576 o = gk_cat(out, o, rs_bucket_name(b)) 577 out[o] = RS_TAB as u8; o = o + 1 578 o = gk_cat(out, o, path) 579 out[o] = RS_COLON as u8; o = o + 1 580 o = gk_catn(out, o, bline) 581 out[o] = RS_TAB as u8; o = o + 1 582 var k: i64 = 0 583 while k < voc[RS_V_BASE + mi * 2 + 1] { 584 out[o] = conf[voc[RS_V_BASE + mi * 2] + k] 585 o = o + 1 586 k = k + 1 587 } 588 out[o] = RS_TAB as u8; o = o + 1 589 var pe: i64 = RS_PREVIEW 590 if pe > bo { pe = bo } 591 var q: i64 = 0 592 while q < pe { 593 // Never let a raw control byte into the worklist: a forged newline turns one row into two. 594 let cb: i64 = blk[q] as i64 595 if cb < RS_SP { out[o] = RS_DOT as u8 } else { out[o] = blk[q] } 596 o = o + 1 597 q = q + 1 598 } 599 out[o] = RS_NL as u8; o = o + 1 600 oo[0] = o 601 return 0 602} 603 604// Scan ONE source file: build blocks, classify each, tally into `res`, append worklist rows. 605// The read mapping is RELEASED per file. A census that walks 20,000 sources and leaks each 606// mapping is a resource bug even when its numbers are right. 607func rs_file_scan(path: *u8, conf: *u8, voc: *i64, res: *i64, out: *u8, outcap: i64, oo: *i64, want: *u8) -> i64 { 608 let flen: *i64 = sys_mmap(RS_SMALL) as *i64 609 let buf: *u8 = sys_read_file(path, flen) 610 if flen[0] <= 0 { 611 res[RS_R_UNREADABLE] = res[RS_R_UNREADABLE] + 1 612 sys_munmap(flen as *u8, RS_SMALL) 613 return 0 614 } 615 res[RS_R_FILES] = res[RS_R_FILES] + 1 616 res[RS_R_BYTES] = res[RS_R_BYTES] + flen[0] 617 618 // The block buffer is sized FROM THE FILE: block text is a subset of the file's own bytes, so 619 // this is a bound, not a guess. 620 let blkcap: i64 = flen[0] + RS_SLACK 621 let blk: *u8 = sys_mmap(blkcap) 622 var bo: i64 = 0 623 var bline: i64 = 0 624 var gap: i64 = 0 625 var line: i64 = 1 626 var p: i64 = 0 627 var running: i64 = 1 628 while running == 1 { 629 var eol: i64 = flen[0] 630 var hard: i64 = 0 631 if p >= flen[0] { hard = 1; running = 0 } else { 632 eol = gk_eol(buf, p, flen[0]) 633 var f: i64 = p 634 var sc: i64 = 1 635 while sc == 1 { 636 if f >= eol { sc = 0 } else { 637 let wc: i64 = buf[f] as i64 638 if wc == RS_SP { f = f + 1 } else { 639 if wc == RS_TAB { f = f + 1 } else { sc = 0 } 640 } 641 } 642 } 643 if f >= eol { hard = 1 } 644 if hard == 0 { 645 if (buf[f] as i64) == RS_RBRACE { hard = 1 } 646 if (buf[f] as i64) == RS_SLASH { 647 if f + 1 < eol { 648 if (buf[f + 1] as i64) == RS_SLASH { hard = 1 } 649 } 650 } 651 } 652 if hard == 0 { 653 let before: i64 = bo 654 bo = rs_line_lits(buf, f, eol, blk, bo, blkcap) 655 if bo > before { 656 if bline == 0 { bline = line } 657 gap = 0 658 } else { 659 gap = gap + 1 660 if gap > voc[RS_V_MAXGAP] { hard = 1 } 661 } 662 } 663 } 664 if hard == 1 { 665 if bo > 0 { rs_block_emit(blk, bo, bline, path, conf, voc, res, out, outcap, oo, want) } 666 bo = 0 667 bline = 0 668 gap = 0 669 } 670 line = line + 1 671 p = eol + 1 672 } 673 sys_munmap(blk, blkcap) 674 sys_free_file(buf, flen[0]) 675 sys_munmap(flen as *u8, RS_SMALL) 676 return 0 677} 678 679// Does `path` end with "/" followed by this declared basename? 680func rs_path_is(path: *u8, conf: *u8, off: i64, len: i64) -> i64 { 681 let pl: i64 = gk_len(path) 682 if pl < len + 1 { return 0 } 683 let s: i64 = pl - len 684 if (path[s - 1] as i64) != RS_SLASH { return 0 } 685 var i: i64 = 0 686 while i < len { 687 if path[s + i] != conf[off + i] { return 0 } 688 i = i + 1 689 } 690 return 1 691} 692 693// A DECLARED, COUNTED, NAMED exclusion -- never a silent suppression. 694// This detector scans source, so it finds ITS OWN test fixtures and accuses its author first. The 695// answer is a list of exactly the files that exist to TEST this detector, declared in the conf, 696// counted here and NAMED in the report. It is not a place to hide findings: excluding a real organ 697// would remove its refusals from the population and the report would say so out loud. 698func rs_excluded(path: *u8, conf: *u8, voc: *i64) -> i64 { 699 var i: i64 = 0 700 while i < voc[RS_V_NEXCL] { 701 if rs_path_is(path, conf, voc[RS_V_EXBASE + i * 2], voc[RS_V_EXBASE + i * 2 + 1]) == 1 { return 1 } 702 i = i + 1 703 } 704 return 0 705} 706 707// Scan an EXPLICIT list of source paths (stride-packed, absolute). Shared by the corpus census and 708// by the gate, so the gate exercises the same walker the census uses rather than a test-only twin. 709func rs_scan_list(names: *u8, n: i64, conf: *u8, voc: *i64, res: *i64, out: *u8, outcap: i64, oo: *i64, want: *u8) -> i64 { 710 var i: i64 = 0 711 while i < n { 712 let pth: *u8 = ((names as i64) + i * RS_STRIDE) as *u8 713 if rs_excluded(pth, conf, voc) == 1 { voc[RS_V_EXCOUNT] = voc[RS_V_EXCOUNT] + 1 } else { 714 rs_file_scan(pth, conf, voc, res, out, outcap, oo, want) 715 } 716 i = i + 1 717 } 718 return 0 719} 720 721func rs_res_new() -> *i64 { 722 let r: *i64 = sys_mmap(RS_RLEN * 8) as *i64 723 var i: i64 = 0 724 while i < RS_RLEN { r[i] = 0; i = i + 1 } 725 r[RS_R_WANTBUCKET] = 0 - 1 726 r[RS_R_WANTLINE] = 0 - 1 727 return r 728} 729 730// Print the distribution. NUMBERS AND NO VERDICT, and the partition PRINTS ITS OWN SUM. 731func rs_report(conf: *u8, res: *i64, voc: *i64, outp: *u8, wrote: i64) -> i64 { 732 rs_puts("nx_refusal_shape census -- EMITTER SHAPE ONLY (the caller-heeded axis is census-only over the call record and is NOT measured here)\n" as *u8) 733 rs_puts("markers=" as *u8); rs_putn(voc[RS_V_NMARK]) 734 rs_puts(" remedies=" as *u8); rs_putn(voc[RS_V_NREM]) 735 rs_puts(" passtokens=" as *u8); rs_putn(voc[RS_V_NPASS]) 736 rs_puts(" failtokens=" as *u8); rs_putn(voc[RS_V_NFAIL]) 737 rs_puts(" selfremedies=" as *u8); rs_putn(voc[RS_V_NSR]) 738 rs_puts(" max_gap_lines=" as *u8); rs_putn(voc[RS_V_MAXGAP]) 739 rs_puts(" min_msg_bytes=" as *u8); rs_putn(voc[RS_V_MINMSG]) 740 rs_puts(" conf_rows_rejected=" as *u8); rs_putn(voc[RS_V_REJECT]) 741 rs_puts("\n" as *u8) 742 rs_puts("declared_self_fixture_exclusions=" as *u8); rs_putn(voc[RS_V_NEXCL]) 743 rs_puts(" files_skipped_by_them=" as *u8); rs_putn(voc[RS_V_EXCOUNT]) 744 rs_puts("\n" as *u8) 745 var xi: i64 = 0 746 while xi < voc[RS_V_NEXCL] { 747 rs_puts("EXCLUDED-BY-DECLARATION " as *u8) 748 sys_write(1, ((conf as i64) + voc[RS_V_EXBASE + xi * 2]) as *u8, voc[RS_V_EXBASE + xi * 2 + 1]) 749 rs_puts(" -- this file exists to TEST this detector; its literals are fixtures and tooth names, not emitted refusals\n" as *u8) 750 xi = xi + 1 751 } 752 rs_puts("files=" as *u8); rs_putn(res[RS_R_FILES]) 753 rs_puts(" unreadable=" as *u8); rs_putn(res[RS_R_UNREADABLE]) 754 rs_puts(" bytes=" as *u8); rs_putn(res[RS_R_BYTES]) 755 rs_puts(" blocks=" as *u8); rs_putn(res[RS_R_BLOCKS]) 756 rs_puts(" population=" as *u8); rs_putn(res[RS_R_POP]) 757 rs_puts("\n" as *u8) 758 rs_puts("-- PARTITION over the population (every refusal block lands in exactly one) --\n" as *u8) 759 var sum: i64 = 0 760 var b: i64 = 0 761 while b < RS_NBUCKET { 762 rs_puts("BUCKET " as *u8) 763 rs_puts(rs_bucket_name(b)) 764 rs_puts(" = " as *u8) 765 rs_putn(res[RS_R_BUCKET0 + b]) 766 rs_puts("\n" as *u8) 767 sum = sum + res[RS_R_BUCKET0 + b] 768 b = b + 1 769 } 770 rs_puts("PARTITION SUM = " as *u8); rs_putn(sum) 771 rs_puts(" population = " as *u8); rs_putn(res[RS_R_POP]) 772 if sum == res[RS_R_POP] { rs_puts(" RECONCILES\n" as *u8) } else { rs_puts(" DOES NOT RECONCILE -- do not publish these numbers\n" as *u8) } 773 rs_puts("offenders (judged and not SHAPED; UNKNOWN is never an offender) = " as *u8) 774 rs_putn(res[RS_R_OFFENDERS]); rs_puts("\n" as *u8) 775 if voc[RS_V_OVER] > 0 { 776 rs_puts("VOCABULARY-TABLE-FULL rows_dropped=" as *u8); rs_putn(voc[RS_V_OVER]) 777 rs_puts(" -- the counts above are a FLOOR, not a total\n" as *u8) 778 } 779 // THE VOCABULARY'S OWN DISTRIBUTION, published so the bar is chosen on evidence. A phrase that 780 // fires on nearly the whole population discriminates nothing and must be cut, and a phrase that 781 // never fires is dead weight -- neither is visible from a bucket count alone. 782 // ⚠ FIRST-FIRED, NOT A TOTAL: a block is charged to the first entry that matched, so these are 783 // order-dependent shares of the population and they SUM to it. They are not per-phrase recall. 784 rs_puts("-- VOCABULARY first-fired shares (order-dependent; they SUM to the population) --\n" as *u8) 785 var mi2: i64 = 0 786 while mi2 < voc[RS_V_NMARK] { 787 rs_puts("MARKER " as *u8) 788 sys_write(1, ((conf as i64) + voc[RS_V_BASE + mi2 * 2]) as *u8, voc[RS_V_BASE + mi2 * 2 + 1]) 789 rs_puts(" = " as *u8); rs_putn(voc[RS_V_MKHIT + mi2]); rs_puts("\n" as *u8) 790 mi2 = mi2 + 1 791 } 792 var ri2: i64 = 0 793 while ri2 < voc[RS_V_NREM] { 794 rs_puts("REMEDY " as *u8) 795 sys_write(1, ((conf as i64) + voc[RS_V_REMBASE + ri2 * 2]) as *u8, voc[RS_V_REMBASE + ri2 * 2 + 1]) 796 rs_puts(" = " as *u8); rs_putn(voc[RS_V_RMHIT + ri2]); rs_puts("\n" as *u8) 797 ri2 = ri2 + 1 798 } 799 if (outp as i64) != 0 { 800 rs_puts("worklist=" as *u8); rs_puts(outp) 801 rs_puts(" bytes=" as *u8); rs_putn(wrote) 802 if res[RS_R_TRUNC] == 1 { rs_puts(" WORKLIST-TRUNCATED=1 -- the COUNTS above are complete, the LIST is not" as *u8) } 803 rs_puts("\n" as *u8) 804 } 805 rs_puts("note: NUMBERS AND NO VERDICT. The bar lives in the ratchet (a set of NAMES that self-baselines and prints its bar), because a detector that fires on everything discriminates nothing.\n" as *u8) 806 return 0 807} 808 809// THE CENSUS. Full compile corpus, never a sample: gk_corpus_scan enumerates _hdl_build over 810// runtime exactly as the builder resolves them, and REFUSES rather than reporting a prefix. 811func rs_scan(confp: *u8, outp: *u8, res: *i64, want: *u8) -> i64 { 812 let clen: *i64 = sys_mmap(RS_SMALL) as *i64 813 let voc: *i64 = sys_mmap(RS_V_LEN * 8) as *i64 814 let conf: *u8 = rs_conf_load(confp, clen, voc) 815 if (conf as i64) == 0 { return 3 } 816 817 let names: *u8 = sys_mmap(RS_MAXF * RS_STRIDE) 818 let n: i64 = gk_corpus_scan(names, RS_STRIDE, RS_MAXF) 819 if n < 0 { 820 rs_err("nx_refusal_shape: the corpus enumerator REFUSED rc=" as *u8) 821 rs_puts("CORPUS-REFUSED rc=" as *u8); rs_putn(n) 822 rs_puts(" (-1 root unresolvable on this host, -2 file cap reached, -3 a path exceeds the stride) -- no census is published from a partial enumeration\n" as *u8) 823 return 3 824 } 825 let outcap: i64 = n * RS_OUT_PER_FILE + RS_SLACK 826 let out: *u8 = sys_mmap(outcap) 827 let oo: *i64 = sys_mmap(RS_SMALL) as *i64 828 oo[0] = 0 829 830 rs_scan_list(names, n, conf, voc, res, out, outcap, oo, want) 831 832 var wrote: i64 = 0 833 let fd: i64 = sys_openat_wr(outp, RS_MODE_0644) 834 if fd >= 0 { wrote = sys_write(fd, out, oo[0]); sys_close(fd) } 835 if fd < 0 { rs_puts("WORKLIST-WRITE-FAILED path=" as *u8); rs_puts(outp); rs_puts("\n" as *u8) } 836 rs_puts("corpus_files_enumerated=" as *u8); rs_putn(n); rs_puts("\n" as *u8) 837 rs_report(conf, res, voc, outp, wrote) 838 return 0 839} 840 841// --------------------------------------------------------------------------------------------- 842// THE RATCHET -- a SET OF NAMES, never a count. 843// 844// A count-only ratchet on a shared tree reports a regression without saying whose. This one names 845// every new offender, prints its bar in EVERY verdict, and SELF-BASELINES on first sight so 846// adopting it cannot turn the fleet red on day one -- a detector that is permanently red is one 847// everyone learns to ignore. 848// 849// It is deliberately wrong in the direction of DOING NOTHING: on a rise it REFUSES and does NOT 850// touch the baseline, because a ratchet that rewrites its baseline on a rise launders itself green 851// and can never fire twice. 852// --------------------------------------------------------------------------------------------- 853const RS_RATCHET_ALLOW: i64 = 0 854const RS_RATCHET_REFUSE: i64 = 1 855const RS_RATCHET_UNMEAS: i64 = 3 856 857// The bucket NAME back to its id. This exists so the ratchet and the census cannot disagree about 858// what an offender is. 859// 🔴 MEASURED 2026-08-20, IN THIS ORGAN, BY RUNNING IT: the first version of rs_row_name tested two 860// bucket names BY HAND -- "SHAPED" and "UNKNOWN" -- and when the TEST-ASSERTION and USAGE-BANNER 861// buckets were added it silently kept counting them as offenders. The census said 9,039 offenders 862// and the ratchet baselined 13,286 in the same run, over the same file. Neither number is wrong on 863// its own terms; there were simply TWO RULERS for one concept, kept in step by discipline until the 864// day a bucket was added. ★ WHEN TWO PIECES OF CODE MUST AGREE, MAKE DISAGREEMENT IMPOSSIBLE BY 865// CONSTRUCTION -- the ratchet now asks rs_is_offender, the same function the census asks. 866// An unrecognised label is its OWN outcome, never folded into a known one: the bucket a stranger 867// lands in becomes a number somebody plans against. 868func rs_bucket_id(buf: *u8, s: i64, e: i64) -> i64 { 869 var b: i64 = 0 870 while b < RS_NBUCKET { 871 if rs_range_eq(buf, s, e, rs_bucket_name(b)) == 1 { return b } 872 b = b + 1 873 } 874 return 0 - 1 875} 876 877// Returns 1 (an offender, span filled), 0 (a judged non-offender), -2 (malformed row), or 878// -3 (an unrecognised bucket label). 879func rs_row_name(buf: *u8, s: i64, e: i64, span: *i64) -> i64 { 880 // row = <BUCKET> TAB <file:line> TAB <marker> TAB <preview> 881 let t1: i64 = rs_find_byte(buf, s, e, RS_TAB) 882 if t1 < 0 { return 0 - 2 } 883 let t2: i64 = rs_find_byte(buf, t1 + 1, e, RS_TAB) 884 var ne: i64 = e 885 if t2 >= 0 { ne = t2 } 886 let b: i64 = rs_bucket_id(buf, s, t1) 887 if b < 0 { return 0 - 3 } 888 if rs_is_offender(b) == 0 { return 0 } 889 span[0] = t1 + 1 890 span[1] = ne 891 return 1 892} 893 894// MEMBERSHIP BY HASH SET, NOT BY RESCAN. 895// ⚠ MEASURED 2026-08-20 ON THE REAL BASELINE: the first version answered "is this name in the 896// baseline" by walking the whole baseline for EVERY current name -- 9,039 names against a 897// 1.1 MB baseline is ~10 GB of byte scanning and it took roughly 90 seconds per run. It was 898// CORRECT and it was still a bug: this runs on a beat, and a change that burns the array is a 899// defect even when its answer is right. Budget the complexity before writing, not after. 900// The table is sized FROM THE INPUT (the next power of two above 4x the name count), so there is 901// no capacity to guess and no cap to reach in silence. 902const RS_FNV_OFFSET: i64 = 2166136261 903const RS_FNV_PRIME: i64 = 16777619 904const RS_HASH_MASK32: i64 = 4294967295 905const RS_HASH_LOAD: i64 = 4 // 4x headroom keeps linear probing short 906 907func rs_name_hash(buf: *u8, s: i64, e: i64) -> i64 { 908 var h: i64 = RS_FNV_OFFSET 909 var i: i64 = s 910 while i < e { 911 h = h ^ (buf[i] as i64) 912 h = (h * RS_FNV_PRIME) & RS_HASH_MASK32 913 i = i + 1 914 } 915 return h 916} 917 918func rs_name_eq(a: *u8, as0: i64, ae: i64, b: *u8, bs: i64, be: i64) -> i64 { 919 if ae - as0 != be - bs { return 0 } 920 var i: i64 = 0 921 while i < ae - as0 { 922 if a[as0 + i] != b[bs + i] { return 0 } 923 i = i + 1 924 } 925 return 1 926} 927 928// Fill the table from the baseline's lines. Returns the number of names inserted. 929func rs_name_index(bl: *u8, blen: i64, hs: *i64, he: *i64, hcap: i64) -> i64 { 930 var i: i64 = 0 931 while i < hcap { hs[i] = 0 - 1; he[i] = 0 - 1; i = i + 1 } 932 var n: i64 = 0 933 var p: i64 = 0 934 while p < blen { 935 let eol: i64 = gk_eol(bl, p, blen) 936 if eol > p { 937 var slot: i64 = rs_name_hash(bl, p, eol) & (hcap - 1) 938 var placing: i64 = 1 939 while placing == 1 { 940 if hs[slot] < 0 { hs[slot] = p; he[slot] = eol; n = n + 1; placing = 0 } else { 941 if rs_name_eq(bl, hs[slot], he[slot], bl, p, eol) == 1 { placing = 0 } else { 942 slot = slot + 1 943 if slot >= hcap { slot = 0 } 944 } 945 } 946 } 947 } 948 p = eol + 1 949 } 950 return n 951} 952 953func rs_name_in(bl: *u8, hs: *i64, he: *i64, hcap: i64, needle: *u8, ns: i64, ne: i64) -> i64 { 954 var slot: i64 = rs_name_hash(needle, ns, ne) & (hcap - 1) 955 var probing: i64 = 1 956 while probing == 1 { 957 if hs[slot] < 0 { return 0 } 958 if rs_name_eq(bl, hs[slot], he[slot], needle, ns, ne) == 1 { return 1 } 959 slot = slot + 1 960 if slot >= hcap { slot = 0 } 961 } 962 return 0 963} 964 965func rs_ratchet(worklistp: *u8, baselinep: *u8) -> i64 { 966 let wlen: *i64 = sys_mmap(RS_SMALL) as *i64 967 let wl: *u8 = sys_read_file(worklistp, wlen) 968 if wlen[0] <= 0 { 969 rs_puts("RATCHET UNMEASURED -- no worklist at " as *u8); rs_puts(worklistp) 970 rs_puts(" . Run the census first: nx_failclass refusal <conf> <worklist>\n" as *u8) 971 return RS_RATCHET_UNMEAS 972 } 973 // Current offender set, as offsets into the worklist buffer. Capacity derived from the file: 974 // the shortest possible row is well over 8 bytes, so wlen/8 can never be exceeded. 975 let cap: i64 = wlen[0] / 8 + 8 976 let cs: *i64 = sys_mmap(cap * 8) as *i64 977 let ce: *i64 = sys_mmap(cap * 8) as *i64 978 let span: *i64 = sys_mmap(RS_SMALL) as *i64 979 var ncur: i64 = 0 980 var nmalformed: i64 = 0 981 var nunrecognised: i64 = 0 982 var nrows: i64 = 0 983 var p: i64 = 0 984 while p < wlen[0] { 985 let eol: i64 = gk_eol(wl, p, wlen[0]) 986 if eol > p { 987 nrows = nrows + 1 988 let r: i64 = rs_row_name(wl, p, eol, span) 989 if r == 1 { 990 if ncur < cap { 991 cs[ncur] = span[0] 992 ce[ncur] = span[1] 993 ncur = ncur + 1 994 } 995 } 996 if r == 0 - 2 { nmalformed = nmalformed + 1 } 997 if r == 0 - 3 { nunrecognised = nunrecognised + 1 } 998 } 999 p = eol + 1 1000 } 1001 rs_puts("RATCHET-INPUT rows=" as *u8); rs_putn(nrows) 1002 rs_puts(" offenders=" as *u8); rs_putn(ncur) 1003 rs_puts(" malformed_rows=" as *u8); rs_putn(nmalformed) 1004 rs_puts(" unrecognised_bucket_labels=" as *u8); rs_putn(nunrecognised) 1005 rs_puts(" (offenders here MUST equal the census offenders line -- both ask rs_is_offender)\n" as *u8) 1006 1007 let blen: *i64 = sys_mmap(RS_SMALL) as *i64 1008 let bl: *u8 = sys_read_file(baselinep, blen) 1009 if blen[0] <= 0 { 1010 // SELF-BASELINE. Adopting the ratchet is non-breaking BY CONSTRUCTION. 1011 let fd0: i64 = sys_openat_wr(baselinep, RS_MODE_0644) 1012 var w0: i64 = 0 1013 if fd0 >= 0 { 1014 var i0: i64 = 0 1015 while i0 < ncur { 1016 w0 = w0 + sys_write(fd0, ((wl as i64) + cs[i0]) as *u8, ce[i0] - cs[i0]) 1017 let nlb: *u8 = sys_mmap(RS_SMALL) 1018 nlb[0] = RS_NL as u8 1019 sys_write(fd0, nlb, 1) 1020 sys_munmap(nlb, RS_SMALL) 1021 i0 = i0 + 1 1022 } 1023 sys_close(fd0) 1024 } 1025 rs_puts("RATCHET SELF-BASELINED baseline=" as *u8); rs_putn(0) 1026 rs_puts(" current=" as *u8); rs_putn(ncur) 1027 rs_puts(" new=0 file=" as *u8); rs_puts(baselinep) 1028 rs_puts("\n" as *u8) 1029 return RS_RATCHET_ALLOW 1030 } 1031 1032 var nbase: i64 = 0 1033 var q: i64 = 0 1034 while q < blen[0] { 1035 let e2: i64 = gk_eol(bl, q, blen[0]) 1036 if e2 > q { nbase = nbase + 1 } 1037 q = e2 + 1 1038 } 1039 1040 var hcap: i64 = 1 1041 while hcap < nbase * RS_HASH_LOAD + RS_HASH_LOAD { hcap = hcap * 2 } 1042 let hs: *i64 = sys_mmap(hcap * 8) as *i64 1043 let he: *i64 = sys_mmap(hcap * 8) as *i64 1044 let nidx: i64 = rs_name_index(bl, blen[0], hs, he, hcap) 1045 1046 var nnew: i64 = 0 1047 var i: i64 = 0 1048 while i < ncur { 1049 if rs_name_in(bl, hs, he, hcap, wl, cs[i], ce[i]) == 0 { 1050 nnew = nnew + 1 1051 rs_puts("RATCHET-NEW " as *u8) 1052 sys_write(1, ((wl as i64) + cs[i]) as *u8, ce[i] - cs[i]) 1053 rs_puts("\n" as *u8) 1054 } 1055 i = i + 1 1056 } 1057 1058 rs_puts("RATCHET baseline=" as *u8); rs_putn(nbase) 1059 rs_puts(" distinct_baseline_names=" as *u8); rs_putn(nidx) 1060 rs_puts(" current=" as *u8); rs_putn(ncur) 1061 rs_puts(" new=" as *u8); rs_putn(nnew) 1062 if nnew > 0 { 1063 rs_puts(" REFUSE -- the baseline is NOT rewritten: a ratchet that rewrites on a rise launders itself green. Fix the named refusals, or accept them deliberately by editing " as *u8) 1064 rs_puts(baselinep) 1065 rs_puts("\n" as *u8) 1066 return RS_RATCHET_REFUSE 1067 } 1068 if ncur < nbase { 1069 let fd1: i64 = sys_openat_wr(baselinep, RS_MODE_0644) 1070 if fd1 >= 0 { 1071 var i1: i64 = 0 1072 while i1 < ncur { 1073 sys_write(fd1, ((wl as i64) + cs[i1]) as *u8, ce[i1] - cs[i1]) 1074 let nlb1: *u8 = sys_mmap(RS_SMALL) 1075 nlb1[0] = RS_NL as u8 1076 sys_write(fd1, nlb1, 1) 1077 sys_munmap(nlb1, RS_SMALL) 1078 i1 = i1 + 1 1079 } 1080 sys_close(fd1) 1081 } 1082 rs_puts(" TIGHTENED -- the baseline now records the smaller set\n" as *u8) 1083 return RS_RATCHET_ALLOW 1084 } 1085 rs_puts(" ALLOW\n" as *u8) 1086 return RS_RATCHET_ALLOW 1087}