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}