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}