code wiki / (root) / nx_pacetbl_probe.nx

nx_pacetbl_probe.nx source

↩ module page · 129 lines · 6238 B

1// nx_pacetbl_probe.nx -- READ-ONLY discriminator for the nx_crawl_pace_gate RED (debt filed 2026-09-01). 2// license_tier: ORIGINAL 3// 4// THE ONE QUESTION THIS ANSWERS, AND WHY IT NEEDED AN ORGAN. nx_crawl_pace_gate fails 10/60 teeth, all with 5// got=0, because its fixture read-back goes through pace_slot and pace_slot returns -1 for an absent host on a 6// full table. Four mechanisms were hypothesised and all four were refuted by control, so the remaining split 7// is exactly two families and NO amount of source reading separates them: 8// 9// A. THE WRITE DID NOT LAND -- the host's hash is nowhere in the table. 10// B. THE READ CANNOT FIND IT -- the hash IS in the table, at some slot, and pace_slot still returns -1 11// or lands on the wrong slot. That is an open-addressing probe-chain 12// defect: pace_slot_or_evict places a record at an ARBITRARY victim slot 13// unrelated to hh % PACE_SLOTS, and linear probing from the home slot 14// stops at the first empty record. 15// 16// The discriminator is a DIRECT LINEAR SCAN of all PACE_SLOTS records for the hash, compared against what 17// pace_slot answers for the same hash. If the scan finds it and pace_slot does not, the answer is B and the 18// defect is in retrieval, not in the write -- which inverts the remedy completely. 19// 20// WRITES NOTHING. It loads the table into an anonymous copy (pace_load_tbl already decouples from the file's 21// mapping) and never calls pace_save_tbl, so running it against the live production table is safe and cannot 22// perturb the very state it is measuring -- the diagnostic-that-joins-the-outage defect. 23// 24// The two hosts are the gate's own fixture literals, so this probe and the gate are asking about the SAME 25// subject. If they are ever edited apart, this probe is measuring a different thing and says so by finding 26// neither. 27 28import "nx_syscalls.nx" 29import "nx_crawl_pace.nx" 30 31const PP_H1: *u8 = "r13throttled.example.test" 32const PP_H2: *u8 = "r13polite.example.test" 33 34func pp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 35func pp_num(v: i64) -> i64 { 36 let b: *u8 = sys_mmap(32) 37 var m: i64 = v 38 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 39 let t: *u8 = sys_mmap(32) 40 var k: i64 = 0 41 if m == 0 { t[0] = 48 as u8; k = 1 } 42 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 43 var j: i64 = 0 44 while j < k { b[j] = t[k - 1 - j]; j = j + 1 } 45 sys_write(1, b, k) 46 return 0 47} 48func pp_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 49 50// The direct scan pace_slot does not do: every slot, no probe chain, no early stop. 51func pp_scan_for(tbl: *u8, hh: i64) -> i64 { 52 var i: i64 = 0 53 while i < PACE_SLOTS { 54 if pace_ld(tbl, i * PACE_REC) == hh { return i } 55 i = i + 1 56 } 57 return 0 - 1 58} 59 60func pp_report(tbl: *u8, label: *u8, host: *u8) -> i64 { 61 let hl: i64 = pp_len(host) 62 let hh: i64 = pace_hash(host, hl) 63 let home: i64 = hh % PACE_SLOTS 64 let scan: i64 = pp_scan_for(tbl, hh) 65 let probe: i64 = pace_slot(tbl, hh) 66 67 pp_puts(" " as *u8); pp_puts(label); pp_puts(" host=" as *u8); pp_puts(host); pp_puts("\n" as *u8) 68 pp_puts(" hash=" as *u8); pp_num(hh) 69 pp_puts(" home_slot=" as *u8); pp_num(home) 70 pp_puts(" direct_scan=" as *u8); pp_num(scan) 71 pp_puts(" pace_slot=" as *u8); pp_num(probe) 72 pp_puts("\n" as *u8) 73 74 if scan < 0 { 75 pp_puts(" VERDICT-A: the hash is ABSENT from every slot -- THE WRITE DID NOT LAND.\n" as *u8) 76 return 1 77 } 78 // present: read its record so the caller can see whether the VALUES are also right 79 let off: i64 = scan * PACE_REC 80 pp_puts(" record: next_allowed=" as *u8); pp_num(pace_ld(tbl, off + PACE_OFF_NEXT_ALLOWED)) 81 pp_puts(" consec=" as *u8); pp_num(pace_ld(tbl, off + PACE_OFF_CONSEC)) 82 pp_puts(" crawl_delay=" as *u8); pp_num(pace_ld(tbl, off + PACE_OFF_CRAWL_DELAY)) 83 pp_puts(" flags=" as *u8); pp_num(pace_ld(tbl, off + PACE_OFF_FLAGS)) 84 pp_puts("\n" as *u8) 85 if probe == scan { 86 pp_puts(" PRESENT and RETRIEVABLE: pace_slot agrees with the direct scan.\n" as *u8) 87 return 0 88 } 89 pp_puts(" VERDICT-B: THE RECORD IS PRESENT AT A SLOT pace_slot DOES NOT REACH -- a retrieval defect,\n" as *u8) 90 pp_puts(" not a write defect. Linear probing from the home slot stops before this slot,\n" as *u8) 91 pp_puts(" so an evicted-in record placed at an arbitrary victim index is ORPHANED.\n" as *u8) 92 return 2 93} 94 95func main() -> i64 { 96 pp_puts("=== nx_pacetbl_probe -- does the pace table CONTAIN the crawl_pace_gate fixture hosts? ===\n" as *u8) 97 let tbl: *u8 = pace_load_tbl() 98 if (tbl as i64) == 0 { 99 pp_puts(" table unreadable -- no conclusion available in either direction\n" as *u8) 100 sys_exit(3) 101 return 3 102 } 103 104 // occupancy first: every surviving story needs a FULL table, so report it rather than assume it 105 var occ: i64 = 0 106 var zero_na: i64 = 0 107 var i: i64 = 0 108 while i < PACE_SLOTS { 109 if pace_ld(tbl, i * PACE_REC) != 0 { 110 occ = occ + 1 111 if pace_ld(tbl, i * PACE_REC + PACE_OFF_NEXT_ALLOWED) == 0 { zero_na = zero_na + 1 } 112 } 113 i = i + 1 114 } 115 pp_puts(" occupied=" as *u8); pp_num(occ) 116 pp_puts(" of " as *u8); pp_num(PACE_SLOTS) 117 pp_puts(" empty=" as *u8); pp_num(PACE_SLOTS - occ) 118 pp_puts(" occupied_with_next_allowed_zero=" as *u8); pp_num(zero_na) 119 pp_puts("\n" as *u8) 120 pp_puts(" (empty=0 means linear probing can never stop early, so an orphan requires a different story;\n" as *u8) 121 pp_puts(" occupied_with_next_allowed_zero is the eviction-victim pool -- those are evicted FIRST.)\n" as *u8) 122 123 let a: i64 = pp_report(tbl, "F1" as *u8, PP_H1) 124 let b: i64 = pp_report(tbl, "F2" as *u8, PP_H2) 125 126 pp_puts("PROBE-DONE f1=" as *u8); pp_num(a); pp_puts(" f2=" as *u8); pp_num(b) 127 pp_puts(" (0=present-and-retrievable 1=absent-write-did-not-land 2=present-but-unreachable)\n" as *u8) 128 return 0 129}