code wiki / _hdl_build / nx_ws_repair_gate.nx

nx_ws_repair_gate.nx source

↩ module page · 215 lines · 8696 B

1// nx_ws_repair_gate.nx -- the REFEREE for WMS-R9 (ledger->registry crash repair). 2// 3// Proves the reflog-recovery trick AND that the test can detect failure (no false green): 4// T1 rebuild-from-loss : build a reflog of known transitions for RP_NWS workstreams, then 5// ledger_rebuild into a FRESH (= wiped/lost) registry prefix. EVERY ws's 6// recovered state field MUST equal the reflog's final state; rebuilt==NWS; 7// flagged==0. (the registry was reconstructed entirely from history.) 8// T2 corruption-repair : overwrite ws:0 with a WRONG state (a stale/torn registry record), re-run 9// ledger_rebuild -> the record is REPAIRED back to the reflog's authoritative 10// state. (proves rebuild REPAIRS, not merely populates.) 11// T3 no-phantom (NEG) : a ws-id that NEVER appears in the reflog MUST NOT exist in the rebuilt 12// registry (rebuild never fabricates a ref it didn't see). 13// T4 torn-surfaced(NEG): a reflog containing a deliberately-broken record -> ledger_rebuild FLAGS 14// it (flagged>=1), never silently mis-applies. flagged==0 => RED. 15// Each artifact uses a per-run unique /tmp path (disposable; no clearing). Self-log via fa_appendz 16// (lock-framed, dogfoods R0b). Evidence -> knowledge/status/ws_repair_gate.log. GREEN(0) iff T1..T4. 17// Sovereign: nx_ws_repair + nx_ws_ledger + nx_workstream_store + nx_framed_append + nx_syscalls. 18// license_tier: ORIGINAL 19import "nx_ws_repair.nx" 20import "nx_ws_ledger.nx" 21import "nx_workstream_store.nx" 22import "nx_framed_append.nx" 23import "nx_syscalls.nx" 24 25const RP_NWS: i64 = 8 // test workstreams ws-id 0..7 26const RP_MAXWS: i64 = 64 27const RP_REC: i64 = 256 28const RP_LOG: *u8 = "knowledge/status/ws_repair_gate.log" 29 30// deterministic expected FINAL state for ws w (spread over the 5 ledger states 0..4) 31func rp_expect(w: i64) -> i64 { return (w * 2 + 1) % 5 } 32 33// stdout-only writers 34func so(s: *u8) -> i64 { 35 var n: i64 = 0 36 while s[n] != (0 as u8) { n = n + 1 } 37 sys_write(1, s, n) 38 return 0 39} 40func son(v: i64) -> i64 { 41 let bb: *u8 = sys_mmap(28) 42 var m: i64 = v 43 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 44 let t: *u8 = sys_mmap(28) 45 var k: i64 = 0 46 if m == 0 { t[0] = 48 as u8; k = 1 } 47 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 48 var i: i64 = 0 49 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 50 sys_write(1, bb, k) 51 return 0 52} 53 54// buffer assembly 55func rp_cat(dst: *u8, off: i64, s: *u8) -> i64 { 56 var i: i64 = 0 57 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 58 return off + i 59} 60func rp_catn(dst: *u8, off: i64, v: i64) -> i64 { 61 var m: i64 = v 62 var o: i64 = off 63 if m == 0 { dst[o] = 48 as u8; return o + 1 } 64 let t: *u8 = sys_mmap(28) 65 var k: i64 = 0 66 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 67 var i: i64 = 0 68 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 69 return o + k 70} 71 72// NUL-term decimal -> int (digits only) 73func rp_atoi(s: *u8) -> i64 { 74 var v: i64 = 0 75 var i: i64 = 0 76 while s[i] != (0 as u8) { 77 let c: i64 = s[i] as i64 78 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } } 79 i = i + 1 80 } 81 return v 82} 83 84// build "/tmp/<tag><epoch><suffix>" into out (NUL-term) 85func rp_path(out: *u8, tag: *u8, epoch: i64, suffix: *u8) -> i64 { 86 var o: i64 = 0 87 o = rp_cat(out, o, "/tmp/" as *u8) 88 o = rp_cat(out, o, tag) 89 o = rp_catn(out, o, epoch) 90 o = rp_cat(out, o, suffix) 91 out[o] = 0 as u8 92 return o 93} 94 95// append a raw NUL-term line (its own '\n' included) to path via O_APPEND 96func rp_rawline(path: *u8, s: *u8) -> i64 { 97 var n: i64 = 0 98 while s[n] != (0 as u8) { n = n + 1 } 99 let fd: i64 = sys_openat_append(path, 0x1a4) 100 if fd < 0 { return 0 - 1 } 101 sys_write(fd, s, n) 102 sys_close(fd) 103 return 0 104} 105 106// state field (field 2) int of the registry record for ws under prefix; -1 if not present. 107func rp_get_state(prefix: *u8, ws: i64) -> i64 { 108 let key: *u8 = sys_mmap(128) 109 var o: i64 = 0 110 o = rp_cat(key, o, "ws:" as *u8) 111 o = rp_catn(key, o, ws) 112 key[o] = 0 as u8 113 let pq: *i64 = sys_mmap(16) as *i64 114 let lq: *i64 = sys_mmap(16) as *i64 115 if ws_get_p(prefix, key, pq, lq) != WS_FOUND { return 0 - 1 } 116 let sb: *u8 = sys_mmap(64) 117 ws_field(pq[0] as *u8, lq[0], 2, sb) 118 return rp_atoi(sb) 119} 120 121func main() -> i64 { 122 let epoch: i64 = sys_now_realtime_sec() 123 let ledp: *u8 = sys_mmap(128) 124 let regp: *u8 = sys_mmap(128) 125 let tornp: *u8 = sys_mmap(128) 126 let trp: *u8 = sys_mmap(128) 127 rp_path(ledp, "rpled" as *u8, epoch, ".log" as *u8) 128 rp_path(regp, "rpreg" as *u8, epoch, "-" as *u8) 129 rp_path(tornp, "rptorn" as *u8, epoch, ".log" as *u8) 130 rp_path(trp, "rptr" as *u8, epoch, "-" as *u8) 131 132 // ---- build the reflog: 3 transitions/ws, final = rp_expect(w) (last-writer-wins) ---- 133 var w: i64 = 0 134 while w < RP_NWS { 135 ledger_append(ledp, w, 0, 1, 7, 0, w * 10 + 0) 136 ledger_append(ledp, w, 1, 2, 7, 1, w * 10 + 1) 137 ledger_append(ledp, w, 2, rp_expect(w), 7, 2, w * 10 + 2) 138 w = w + 1 139 } 140 141 // ---- T1: rebuild into a FRESH (lost) registry; verify states ---- 142 let outs: *i64 = sys_mmap(32) as *i64 143 ledger_rebuild(ledp, regp, RP_MAXWS, outs) 144 let rebuilt: i64 = outs[3] 145 let flagged1: i64 = outs[2] 146 var correct: i64 = 0 147 w = 0 148 while w < RP_NWS { 149 if rp_get_state(regp, w) == rp_expect(w) { correct = correct + 1 } 150 w = w + 1 151 } 152 var t1: i64 = 0 153 if correct == RP_NWS { if rebuilt == RP_NWS { if flagged1 == 0 { t1 = 1 } } } 154 155 // ---- T2: corrupt ws:0 -> wrong state 99, re-rebuild -> repaired to expected ---- 156 ws_put_p(regp, "ws:0" as *u8, "0\tE-CORRUPT\t99\t0\t-\t-\t-" as *u8) 157 let corrupted: i64 = rp_get_state(regp, 0) 158 ledger_rebuild(ledp, regp, RP_MAXWS, outs) 159 let repaired: i64 = rp_get_state(regp, 0) 160 var t2: i64 = 0 161 if corrupted == 99 { if repaired == rp_expect(0) { t2 = 1 } } 162 163 // ---- T3 NEG: ws never in the reflog must be absent (no phantom) ---- 164 var t3: i64 = 0 165 if rp_get_state(regp, 20) == (0 - 1) { t3 = 1 } 166 167 // ---- T4 NEG: torn reflog -> flagged>=1 ---- 168 ledger_append(tornp, 0, 0, 1, 7, 0, 0) 169 ledger_append(tornp, 1, 0, 3, 7, 0, 1) 170 rp_rawline(tornp, "WSX ws=2 old=0 new=1 actor=7 reason=0 ev=0 epoch=0 BROKEN\n" as *u8) 171 let outs2: *i64 = sys_mmap(32) as *i64 172 ledger_rebuild(tornp, trp, RP_MAXWS, outs2) 173 let flagged4: i64 = outs2[2] 174 var t4: i64 = 0 175 if flagged4 >= 1 { t4 = 1 } 176 177 var green: i64 = 1 178 if t1 == 0 { green = 0 } 179 if t2 == 0 { green = 0 } 180 if t3 == 0 { green = 0 } 181 if t4 == 0 { green = 0 } 182 183 so("WMS-R9 ledger->registry crash-repair gate (reflog recovery)\n" as *u8) 184 so(" T1 rebuild-from-loss rebuilt=" as *u8); son(rebuilt) 185 so(" correct=" as *u8); son(correct); so("/" as *u8); son(RP_NWS) 186 so(" flagged=" as *u8); son(flagged1) 187 if t1 == 1 { so(" PASS\n" as *u8) } else { so(" FAIL\n" as *u8) } 188 so(" T2 corruption-repair corrupted=" as *u8); son(corrupted) 189 so(" repaired=" as *u8); son(repaired); so(" want=" as *u8); son(rp_expect(0)) 190 if t2 == 1 { so(" PASS\n" as *u8) } else { so(" FAIL\n" as *u8) } 191 so(" T3 no-phantom (NEG) ws20_present=" as *u8); son(rp_get_state(regp, 20)) 192 if t3 == 1 { so(" PASS\n" as *u8) } else { so(" FAIL\n" as *u8) } 193 so(" T4 torn-surfaced (NEG) flagged=" as *u8); son(flagged4) 194 if t4 == 1 { so(" PASS(corruption-surfaced)\n" as *u8) } else { so(" FAIL(swallowed)\n" as *u8) } 195 so("verdict=" as *u8) 196 if green == 1 { so("GREEN\n" as *u8) } else { so("RED\n" as *u8) } 197 198 // single assembled evidence record -> fa_appendz (dogfoods R0b lock-framed primitive) 199 let rec: *u8 = sys_mmap(RP_REC + 16) 200 var o: i64 = 0 201 o = rp_cat(rec, o, "WMS-R9-REPAIR epoch=" as *u8); o = rp_catn(rec, o, epoch) 202 o = rp_cat(rec, o, " rebuilt=" as *u8); o = rp_catn(rec, o, rebuilt) 203 o = rp_cat(rec, o, " correct=" as *u8); o = rp_catn(rec, o, correct) 204 o = rp_cat(rec, o, " t1=" as *u8); o = rp_catn(rec, o, t1) 205 o = rp_cat(rec, o, " t2=" as *u8); o = rp_catn(rec, o, t2) 206 o = rp_cat(rec, o, " t3=" as *u8); o = rp_catn(rec, o, t3) 207 o = rp_cat(rec, o, " t4=" as *u8); o = rp_catn(rec, o, t4) 208 o = rp_cat(rec, o, " verdict=" as *u8) 209 if green == 1 { o = rp_cat(rec, o, "GREEN" as *u8) } else { o = rp_cat(rec, o, "RED" as *u8) } 210 rec[o] = 0 as u8 211 fa_appendz(RP_LOG, rec, RP_REC) 212 213 if green == 1 { return 0 } 214 return 1 215}