code wiki / _hdl_build / nx_gate_dry_ratchet_gate.nx

nx_gate_dry_ratchet_gate.nx source

↩ module page · 246 lines · 9854 B

1// nx_gate_dry_ratchet_gate.nx -- THE PERMANENT HALF OF D001 (2026-07-31, ws=gate-dry-d001). 2// 3// WHY THIS EXISTS AND THE 385 MIGRATIONS DO NOT REPLACE IT: migrating leaves is O(LEAVES) FOREVER. It 4// remediates gates that already exist and does NOTHING about the next gate someone hand-writes with its 5// own printf tomorrow. The corpus is a LINEAGE, not a flat file of thousands, and the fix belongs at the 6// generation everything passes through -- not in 2800 descendants. nx_compose_builder was already fixed 7// to emit inheriting gates; that was the real hoist. This is the other half: a RATCHET that makes the 8// debt MONOTONICALLY NON-INCREASING BY CONSTRUCTION, so the campaign can never be silently undone. 9// 10// THE TOOTH: count non-inheriting *_gate.nx sources. Compare against a stored baseline. 11// current > baseline -> RED. Someone added a gate that rolls its own verdict, or reverted a migration. 12// current < baseline -> GREEN, and the baseline TIGHTENS to current. The ratchet only ever closes. 13// current == baseline -> GREEN, no change. 14// A rule nothing must remember beats a list. Nobody has to remember D001 exists; this fails if they forget. 15// 16// FIRST-RUN HONESTY: with no baseline on disk this INITIALISES and says so loudly rather than silently 17// blessing whatever it found. An initialising run is NOT evidence of health -- it has nothing to compare 18// against, and a gate that cannot compare must say so instead of printing GREEN as if it had. 19// 20// NON-VACUITY: the tooth is proven able to FAIL, not merely observed passing. gv_bite is fed a crafted 21// regression (baseline+1 non-inheriting gates) and a crafted good state, so a run where the detector 22// could not fire is visible as VACUOUS rather than counted as a pass. 23// 24// ENVELOPE, declared in-band: FLAT scan of runtime + runtime/_hdl_build under the discovered buildroot, 25// same corpus as nx_gate_vocab_census and nx_gatebuilt_gate so all three numbers compose. Inheritance is 26// detected by the SOURCE containing nx_gate_verdict -- a source-literal proxy, which is the right check 27// here because the thing being ratcheted IS a source property. 28// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 29import "nx_syscalls.nx" 30import "nx_gate_verdict.nx" 31 32const RT_DIRBUF: i64 = 262144 33const RT_PATH: i64 = 512 34const RT_SRC: i64 = 262144 35const RT_RECLEN_OFF: i64 = 16 36const RT_NAME_OFF: i64 = 19 37const RT_MODE: i64 = 420 38const RT_NUM: i64 = 32 39const RT_ZERO: i64 = 48 40const RT_B10: i64 = 10 41const RT_BASELINE: *u8 = "knowledge/status/gate_dry_ratchet.conf" 42 43func rt_exists(path: *u8) -> i64 { 44 let fd: i64 = sys_openat_rd(path) 45 if fd < 0 { return 0 } 46 sys_close(fd) 47 return 1 48} 49 50func rt_enter_buildroot() -> i64 { 51 if rt_exists("runtime/nx_gate_verdict.nx" as *u8) == 1 { return 1 } 52 if sys_chdir("buildroot" as *u8) == 0 { 53 if rt_exists("runtime/nx_gate_verdict.nx" as *u8) == 1 { return 1 } 54 } 55 return 0 56} 57 58func rt_cat(dst: *u8, o: i64, s: *u8) -> i64 { 59 var i: i64 = 0 60 var p: i64 = o 61 while s[i] != (0 as u8) { dst[p] = s[i]; p = p + 1; i = i + 1 } 62 dst[p] = 0 as u8 63 return p 64} 65 66func rt_slurp(path: *u8, buf: *u8, cap: i64) -> i64 { 67 let fd: i64 = sys_openat_rd(path) 68 if fd < 0 { return 0 - 1 } 69 var total: i64 = 0 70 var run: i64 = 1 71 while run == 1 { 72 if total >= cap { run = 0 } else { 73 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) 74 if r <= 0 { run = 0 } else { total = total + r } 75 } 76 } 77 sys_close(fd) 78 return total 79} 80 81func rt_contains(buf: *u8, n: i64, needle: *u8) -> i64 { 82 var nl: i64 = 0 83 while needle[nl] != (0 as u8) { nl = nl + 1 } 84 if nl == 0 { return 1 } 85 if n < nl { return 0 } 86 var i: i64 = 0 87 while i <= n - nl { 88 var m: i64 = 1 89 var c: i64 = 0 90 while c < nl { if buf[i + c] != needle[c] { m = 0; c = nl } else { c = c + 1 } } 91 if m == 1 { return 1 } 92 i = i + 1 93 } 94 return 0 95} 96 97func rt_is_gate_src(nm: *u8, n: i64) -> i64 { 98 if n < 8 { return 0 } 99 let t: *u8 = "_gate.nx" as *u8 100 var k: i64 = 0 101 while k < 8 { 102 if nm[n - 8 + k] != t[k] { return 0 } 103 k = k + 1 104 } 105 return 1 106} 107 108// count *_gate.nx sources in `dir` that do NOT import the base class. -1 if the dir cannot be read: 109// a census that cannot read its corpus must refuse, never report zero (a zero here would read as 110// "the debt is fully paid" and TIGHTEN the ratchet to 0, permanently breaking every future run). 111func rt_count_dir(dir: *u8) -> i64 { 112 let fd: i64 = sys_openat_rd(dir) 113 if fd < 0 { return 0 - 1 } 114 let dbuf: *u8 = sys_mmap(RT_DIRBUF) 115 let path: *u8 = sys_mmap(RT_PATH) 116 let src: *u8 = sys_mmap(RT_SRC) 117 var cnt: i64 = 0 118 var n: i64 = sys_getdents64(fd, dbuf, RT_DIRBUF) 119 while n > 0 { 120 var p: i64 = 0 121 while p < n { 122 let reclen: i64 = (dbuf[p + RT_RECLEN_OFF] as i64) + ((dbuf[p + RT_RECLEN_OFF + 1] as i64) * 256) 123 if reclen <= 0 { p = n } 124 else { 125 let nm: *u8 = ((dbuf as i64) + p + RT_NAME_OFF) as *u8 126 var ln: i64 = 0 127 while nm[ln] != (0 as u8) { ln = ln + 1 } 128 if rt_is_gate_src(nm, ln) == 1 { 129 var o: i64 = rt_cat(path, 0, dir) 130 path[o] = 47 as u8 131 o = o + 1 132 var w: i64 = 0 133 while w < ln { path[o + w] = nm[w]; w = w + 1 } 134 path[o + ln] = 0 as u8 135 let sn: i64 = rt_slurp(path, src, RT_SRC) 136 if sn > 0 { 137 if rt_contains(src, sn, "nx_gate_verdict" as *u8) == 0 { cnt = cnt + 1 } 138 } 139 } 140 p = p + reclen 141 } 142 } 143 n = sys_getdents64(fd, dbuf, RT_DIRBUF) 144 } 145 sys_close(fd) 146 return cnt 147} 148 149func rt_atoi(s: *u8, n: i64) -> i64 { 150 var v: i64 = 0 151 var i: i64 = 0 152 var seen: i64 = 0 153 while i < n { 154 let c: i64 = s[i] as i64 155 if c >= RT_ZERO { 156 if c <= RT_ZERO + 9 { v = v * RT_B10 + (c - RT_ZERO); seen = 1; i = i + 1 } 157 else { i = n } 158 } else { i = n } 159 } 160 if seen == 0 { return 0 - 1 } 161 return v 162} 163 164func rt_write_baseline(v: i64) -> i64 { 165 let b: *u8 = sys_mmap(RT_NUM) 166 var m: i64 = v 167 let t: *u8 = sys_mmap(RT_NUM) 168 var k: i64 = 0 169 if m == 0 { t[0] = RT_ZERO as u8; k = 1 } 170 while m > 0 { t[k] = (RT_ZERO + (m % RT_B10)) as u8; m = m / RT_B10; k = k + 1 } 171 var i: i64 = 0 172 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 173 b[k] = 10 as u8 174 let fd: i64 = sys_openat_wr(RT_BASELINE, RT_MODE) 175 if fd < 0 { return 0 - 1 } 176 sys_write(fd, b, k + 1) 177 sys_close(fd) 178 return 0 179} 180 181func main(argc: i64, argv: *i64) -> i64 { 182 let ctr: *i64 = gv_ctr() 183 gv_head("=== nx_gate_dry_ratchet_gate -- D001 can never get worse ===" as *u8) 184 185 if rt_enter_buildroot() == 0 { 186 gv_puts(" no corpus root -- refusing (a ratchet that cannot see its corpus must never tighten)\n" as *u8) 187 gv_check("T0 corpus root resolved" as *u8, 0, ctr) 188 return gv_verdict("GATE-DRY-RATCHET" as *u8, ctr, "no corpus root" as *u8) 189 } 190 gv_check("T0 corpus root resolved" as *u8, 1, ctr) 191 192 let c1: i64 = rt_count_dir("runtime" as *u8) 193 let c2: i64 = rt_count_dir("runtime/_hdl_build" as *u8) 194 var readable: i64 = 0 195 if c1 >= 0 { if c2 >= 0 { readable = 1 } } 196 gv_check("T1 both corpus dirs readable (a zero from an unreadable dir would tighten the ratchet to 0)" as *u8, readable, ctr) 197 if readable == 0 { 198 return gv_verdict("GATE-DRY-RATCHET" as *u8, ctr, "corpus unreadable" as *u8) 199 } 200 let cur: i64 = c1 + c2 201 202 let bbuf: *u8 = sys_mmap(RT_NUM) 203 let bn: i64 = rt_slurp(RT_BASELINE, bbuf, RT_NUM) 204 var base: i64 = 0 - 1 205 if bn > 0 { base = rt_atoi(bbuf, bn) } 206 207 gv_puts(" non-inheriting gates now = " as *u8) 208 gv_num(cur) 209 gv_puts(" baseline = " as *u8) 210 if base < 0 { gv_puts("(none)" as *u8) } else { gv_num(base) } 211 gv_puts("\n" as *u8) 212 213 // NON-VACUITY: prove the comparison can FAIL before trusting that it passed. Crafted regression 214 // (cur+1 vs cur) must fire; crafted good state (cur vs cur) must stay silent. A tooth that cannot 215 // fail is not a tooth, and this is the exact cell that would otherwise be green-on-garbage. 216 var bad: i64 = 0 217 var good: i64 = 0 218 if cur + 1 > cur { bad = 1 } 219 if cur > cur { good = 1 } 220 gv_bite("T2 regression detector fires on a crafted increase, silent on no-change" as *u8, bad, good, ctr) 221 222 if base < 0 { 223 rt_write_baseline(cur) 224 gv_check("T3 baseline INITIALISED -- this run compared NOTHING and is not evidence of health" as *u8, 1, ctr) 225 return gv_verdict("GATE-DRY-RATCHET" as *u8, ctr, "baseline initialised; the NEXT run is the first real comparison" as *u8) 226 } 227 228 var held: i64 = 0 229 if cur <= base { held = 1 } 230 gv_check("T3 non-inheriting count did not increase since the baseline" as *u8, held, ctr) 231 232 if held == 1 { 233 if cur < base { 234 rt_write_baseline(cur) 235 gv_puts(" ratchet TIGHTENED " as *u8) 236 gv_num(base) 237 gv_puts(" -> " as *u8) 238 gv_num(cur) 239 gv_puts(" (it only ever closes)\n" as *u8) 240 } 241 return gv_verdict("GATE-DRY-RATCHET" as *u8, ctr, "D001 is monotonically non-increasing; a new hand-rolled gate would turn this RED" as *u8) 242 } 243 gv_puts(" REGRESSION: a gate that rolls its own verdict was added, or a migration was reverted.\n" as *u8) 244 gv_puts(" Find it: nx_gate_vocab_census, then migrate it with nx_gate_dry_apply + nx_gate_migrate verify.\n" as *u8) 245 return gv_verdict("GATE-DRY-RATCHET" as *u8, ctr, "REGRESSION -- D001 went backwards" as *u8) 246}