code wiki / (root) / nx_research_journal_gate.nx

nx_research_journal_gate.nx source

↩ module page · 315 lines · 16602 B

1// nx_research_journal_gate.nx -- the REFEREE for RES-R6 (crash-resumable journal). 2// 3// Proves nx_research_journal makes a research run RESUME from a crash point by 4// REPLAYING the journal and SKIPPING already-completed phases -- AND proves the 5// test can actually DETECT failure (the mandatory NEGATIVE CONTROL: the old, 6// journal-less path re-runs everything, skipping nothing). 7// 8// GOOD lane (journaled): 9// run 1 ("crash"): execute FETCH (3 sources) + phase_done, EXTRACT (3 10// sources) + phase_done, then VERIFY begin + 1 source_done, then STOP -- 11// a crash MID-VERIFY (VERIFY never gets its phase_done). work_run1 counts 12// every phase actually executed. 13// run 2 ("restart"): rj_replay(GOOD) -> resume high-water = EXTRACT (VERIFY 14// is NOT complete, so it is correctly NOT skipped). For FETCH..SYNTH: 15// rj_should_skip -> FETCH+EXTRACT SKIPPED (counted), VERIFY+SYNTH executed. 16// PROOF of resume = good_skipped >= 2 AND completed phases re-executed = 0. 17// 18// BAD lane (NEGATIVE CONTROL -- old path, NO journal): 19// same crash, but restart has no journal -> rj_replay on the absent path 20// returns 0 -> resume high-water 0 -> EVERY phase re-executes -> bad_skipped 21// == 0 and bad_refetched == FULL_RUN. If bad_skipped != 0 the detector is 22// worthless -> RED (no false green). 23// 24// TAMPER x2: 25// (a) oversized record (rec_len+1 > cap=8) must be REJECTED with -2. 26// (b) a TORN "RJ ... VERIFY ... DONE" fragment hand-written as a SEQUENCE of 27// separate sys_write()s (no single-write framing) into a victim journal 28// that ALSO holds a real FETCH+EXTRACT phase_done -> rj_replay must NOT 29// let the torn VERIFY-DONE advance the resume point past EXTRACT. Proves 30// a crash that tore the last line can never cause skipped-but-incomplete 31// work. 32// 33// Output: rows -> stdout + evidence log knowledge/status/research_journal_gate.log, 34// then a final verdict line. Exit 0 GREEN / 1 RED. Sovereign: only nx_syscalls 35// + nx_framed_append + nx_research_journal. 36// license_tier: ORIGINAL 37import "nx_syscalls.nx" 38import "nx_framed_append.nx" 39import "nx_research_journal.nx" 40 41const NSRC: i64 = 3 // sources per FETCH/EXTRACT/VERIFY phase 42const SID: i64 = 7 // the research session id under test 43const FULL_RUN: i64 = 4 // FETCH+EXTRACT+VERIFY+SYNTH = 4 phase executions 44// journal scratch paths are now PID-NAMESPACED at runtime (built in main: gpath/bpath/tpath) so concurrent 45// gate runs are isolated -- the fixed /tmp/rj_*.jrnl constants were the intermittent-RED flake under parallel sweeps. 46 47// dual-sink writer: stdout (fd 1) AND the evidence log fd (lifted from WMS-R0 gate) 48func gp(logfd: i64, s: *u8) -> i64 { 49 var n: i64 = 0 50 while s[n] != (0 as u8) { n = n + 1 } 51 sys_write(1, s, n) 52 if logfd > 0 { sys_write(logfd, s, n) } 53 return 0 54} 55func gn(logfd: i64, v: i64) -> i64 { 56 let bb: *u8 = sys_mmap(28) 57 var m: i64 = v 58 if m < 0 { sys_write(1, "-\x00" as *u8, 1); if logfd > 0 { sys_write(logfd, "-\x00" as *u8, 1) } m = 0 - m } 59 let t: *u8 = sys_mmap(28) 60 var k: i64 = 0 61 if m == 0 { t[0] = 48 as u8; k = 1 } 62 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 63 var i: i64 = 0 64 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 65 sys_write(1, bb, k) 66 if logfd > 0 { sys_write(logfd, bb, k) } 67 return 0 68} 69 70// build "/tmp/<base>_<pid><suffix>" into out -- PID-NAMESPACED scratch journal so CONCURRENT gate runs never 71// share /tmp state (the de-flake: the fixed /tmp/rj_*.jrnl paths corrupted each other under parallel sweeps). 72func g_pidp(base: *u8, suffix: *u8, pid: i64, out: *u8) -> i64 { 73 var o: i64 = 0 74 while base[o] != (0 as u8) { out[o] = base[o]; o = o + 1 } 75 out[o] = 95 as u8; o = o + 1 // '_' 76 let t: *u8 = sys_mmap(28) 77 var m: i64 = pid 78 var k: i64 = 0 79 if m == 0 { t[0] = 48 as u8; k = 1 } 80 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 81 var i: i64 = 0 82 while i < k { out[o] = t[k - 1 - i]; o = o + 1; i = i + 1 } 83 var s: i64 = 0 84 while suffix[s] != (0 as u8) { out[o] = suffix[s]; o = o + 1; s = s + 1 } 85 out[o] = 0 as u8 86 return o 87} 88 89// getpid (__syscall 39) is BROKEN on this backend (-25 for EVERY process, and -25 rendered no digits at all -> 90// /tmp/rj_good_.jrnl shared by every run = the flake). Claim a process-unique POSITIVE i64 token ATOMICALLY via 91// O_EXCL: first creator of /tmp/nxuq_<n> owns <n>; EEXIST retries n+1. Seeded by monotonic us. Collision-free. 92func g_uniq() -> i64 { 93 var n: i64 = sys_now_us() 94 if n < 0 { n = 0 - n } 95 var tries: i64 = 0 96 while tries < 1000000 { 97 let p: *u8 = sys_mmap(64) 98 var o: i64 = 0 99 let pre: *u8 = "/tmp/nxuq_\x00" as *u8 100 while pre[o] != (0 as u8) { p[o] = pre[o]; o = o + 1 } 101 let t: *u8 = sys_mmap(28); var m: i64 = n; var k: i64 = 0 102 if m == 0 { t[0] = 48 as u8; k = 1 } 103 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 104 var i: i64 = 0 105 while i < k { p[o] = t[k - 1 - i]; o = o + 1; i = i + 1 } 106 p[o] = 0 as u8 107 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, p, 0xc1, 0x1a4, 0, 0) // O_CREAT|O_EXCL|O_WRONLY 108 if fd >= 0 { sys_close(fd); return n } 109 n = n + 1 110 tries = tries + 1 111 } 112 return n 113} 114 115// Execute one phase = the "real research work" signal. Logs phase_begin, NSRC 116// source_done records, then phase_done. Returns 1 (one phase of work executed). 117func do_phase(path: *u8, phase: i64) -> i64 { 118 rj_phase_begin(path, SID, phase) 119 var s: i64 = 0 120 while s < NSRC { 121 rj_source_done(path, SID, phase, s) 122 s = s + 1 123 } 124 rj_phase_done(path, SID, phase) 125 return 1 126} 127 128// SYNTH is a single-step phase (no per-source loop). 129func do_synth(path: *u8) -> i64 { 130 rj_phase_begin(path, SID, RJ_SYNTH) 131 rj_phase_done(path, SID, RJ_SYNTH) 132 return 1 133} 134 135func main() -> i64 { 136 let logfd: i64 = sys_openat_append("knowledge/status/research_journal_gate.log\x00" as *u8, 0x1a4) 137 gp(logfd, "RESEARCH-JOURNAL epoch=\x00" as *u8) 138 gn(logfd, sys_now_realtime_sec()) 139 gp(logfd, " sid=\x00" as *u8); gn(logfd, SID) 140 gp(logfd, " nsrc=\x00" as *u8); gn(logfd, NSRC) 141 gp(logfd, "\n\x00" as *u8) 142 143 // UNIQUE-NAMESPACED journal scratch paths so CONCURRENT gate runs are isolated (the de-flake; getpid is broken) 144 let pid: i64 = g_uniq() 145 let gpath: *u8 = sys_mmap(128); g_pidp("/tmp/rj_good\x00" as *u8, ".jrnl\x00" as *u8, pid, gpath) 146 let bpath: *u8 = sys_mmap(128); g_pidp("/tmp/rj_bad\x00" as *u8, ".jrnl\x00" as *u8, pid, bpath) 147 let tpath: *u8 = sys_mmap(128); g_pidp("/tmp/rj_torn\x00" as *u8, ".jrnl\x00" as *u8, pid, tpath) 148 let tampath: *u8 = sys_mmap(128); g_pidp("/tmp/rj_tamper\x00" as *u8, ".log\x00" as *u8, pid, tampath) 149 150 // fresh journals each run (truncate the good + torn paths; the bad path stays absent) 151 let gtr: i64 = sys_openat_wr(gpath, 0x1a4) 152 if gtr > 0 { sys_close(gtr) } 153 let ttr: i64 = sys_openat_wr(tpath, 0x1a4) 154 if ttr > 0 { sys_close(ttr) } 155 156 // ============ GOOD LANE: journaled, crash-resumable ============ 157 // --- run 1: execute through EXTRACT, then CRASH mid-VERIFY --- 158 var work_run1: i64 = 0 159 work_run1 = work_run1 + do_phase(gpath, RJ_FETCH) // FETCH complete 160 work_run1 = work_run1 + do_phase(gpath, RJ_EXTRACT) // EXTRACT complete 161 // start VERIFY but crash before phase_done: BEGIN + 1 source_done only 162 rj_phase_begin(gpath, SID, RJ_VERIFY) 163 rj_source_done(gpath, SID, RJ_VERIFY, 0) 164 work_run1 = work_run1 + 1 // VERIFY partially executed 165 // (process "crashes" here -- no SYNTH, no VERIFY phase_done) 166 167 // --- run 2: restart, REPLAY, resume from crash point --- 168 let out: *i64 = sys_mmap(32) as *i64 169 let resume_from: i64 = rj_replay(gpath, SID, out) // expect EXTRACT(=2) 170 let good_wf: i64 = out[2] 171 var good_skipped: i64 = 0 172 var good_refetched: i64 = 0 // re-executions of an ALREADY-COMPLETE phase (must be 0) 173 var good_executed: i64 = 0 174 var ph: i64 = RJ_FETCH 175 while ph <= RJ_SYNTH { 176 if rj_should_skip(ph, resume_from) == 1 { 177 good_skipped = good_skipped + 1 178 } else { 179 // execute the phase (resume work) 180 if ph == RJ_SYNTH { do_synth(gpath) } else { do_phase(gpath, ph) } 181 good_executed = good_executed + 1 182 // if this phase was already complete per replay, that is a re-fetch BUG 183 if ph <= resume_from { good_refetched = good_refetched + 1 } 184 } 185 ph = ph + 1 186 } 187 // after resume, the whole run is complete iff SYNTH ran (highest phase done) 188 let out2: *i64 = sys_mmap(32) as *i64 189 let final_hw: i64 = rj_replay(gpath, SID, out2) 190 var good_finished: i64 = 0 191 if final_hw == RJ_SYNTH { good_finished = 1 } 192 193 // ============ BAD LANE: NEGATIVE CONTROL (no journal) ============ 194 // run 1 "crashes" the same way -- but NOTHING is journaled. On restart the 195 // replay reads an ABSENT journal -> high-water 0 -> resume nothing -> the old 196 // path re-runs EVERY phase. 197 let outb: *i64 = sys_mmap(32) as *i64 198 let bad_resume: i64 = rj_replay(bpath, SID, outb) // expect 0 (absent file) 199 var bad_skipped: i64 = 0 200 var bad_refetched: i64 = 0 201 ph = RJ_FETCH 202 while ph <= RJ_SYNTH { 203 if rj_should_skip(ph, bad_resume) == 1 { 204 bad_skipped = bad_skipped + 1 205 } else { 206 bad_refetched = bad_refetched + 1 // every phase re-executed from scratch 207 } 208 ph = ph + 1 209 } 210 211 // ============ TAMPER (a): oversized record rejected with -2 ============ 212 let trec: *u8 = sys_mmap(64) 213 var ti: i64 = 0 214 while ti < 32 { trec[ti] = 65 as u8; ti = ti + 1 } // 32 'A's, way over cap=8 215 let tamper_oversize: i64 = fa_append(tampath, trec, 32, 8) 216 217 // ============ TAMPER (b): torn VERIFY-DONE must NOT advance resume ============ 218 // Write a REAL FETCH+EXTRACT (atomic) into the torn journal, then hand-write a 219 // TORN "RJ ... VERIFY ... ev=DONE" line as a SEQUENCE of separate writes with 220 // NO single-write framing (the exact bug WMS-R0 fixes). A crash could leave 221 // such a fragment. rj_replay must IGNORE it (not well-formed / interleavable) 222 // so the resume high-water stays at EXTRACT, never jumping to VERIFY. 223 do_phase(tpath, RJ_FETCH) 224 do_phase(tpath, RJ_EXTRACT) 225 // torn fragment: open O_APPEND, emit the record as many little writes, and -- 226 // critically -- do NOT terminate it with the framed single '\n'+sentinel in a 227 // single write; we deliberately interleave a second record-head, producing a 228 // line that fails rj_wellformed. 229 let tfd: i64 = sys_openat_append(tpath, 0x1a4) 230 if tfd > 0 { 231 sys_write(tfd, "RJ sid=\x00" as *u8, 7) 232 sys_write(tfd, "7\x00" as *u8, 1) 233 sys_write(tfd, " ph=\x00" as *u8, 4) 234 sys_write(tfd, "3\x00" as *u8, 1) // VERIFY 235 sys_write(tfd, " src=\x00" as *u8, 5) 236 sys_write(tfd, "-1\x00" as *u8, 2) 237 sys_write(tfd, " ev=\x00" as *u8, 4) 238 // INTERLEAVE a second record head before the END sentinel -> torn line 239 sys_write(tfd, "2 RJ sid=7 ph=3 src=-1 ev=2 ts=0\x00" as *u8, 32) 240 sys_write(tfd, "\n\x00" as *u8, 1) // newline WITHOUT a clean " END" tail 241 sys_close(tfd) 242 } 243 let out_t: *i64 = sys_mmap(32) as *i64 244 let torn_resume: i64 = rj_replay(tpath, SID, out_t) // must stay at EXTRACT(=2) 245 var tamper_torn_advanced: i64 = 0 246 if torn_resume > RJ_EXTRACT { tamper_torn_advanced = 1 } // 1 = FOOLED (bad) 247 248 // ---------------- rows ---------------- 249 gp(logfd, "RJ row=good_resume_from got=\x00" as *u8); gn(logfd, resume_from) 250 gp(logfd, " want=\x00" as *u8); gn(logfd, RJ_EXTRACT) 251 if resume_from == RJ_EXTRACT { gp(logfd, " verdict=PASS\n\x00" as *u8) } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } 252 253 gp(logfd, "RJ row=good_skipped got=\x00" as *u8); gn(logfd, good_skipped) 254 gp(logfd, " want>=2 (FETCH+EXTRACT) refetched=\x00" as *u8); gn(logfd, good_refetched) 255 if good_skipped >= 2 { if good_refetched == 0 { gp(logfd, " verdict=PASS\n\x00" as *u8) } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } 256 257 gp(logfd, "RJ row=good_executed_on_resume got=\x00" as *u8); gn(logfd, good_executed) 258 gp(logfd, " want=2 (VERIFY+SYNTH) finished=\x00" as *u8); gn(logfd, good_finished) 259 if good_executed == 2 { if good_finished == 1 { gp(logfd, " verdict=PASS\n\x00" as *u8) } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } 260 gp(logfd, "RJ row=good_wf_lines got=\x00" as *u8); gn(logfd, good_wf); gp(logfd, "\n\x00" as *u8) 261 262 gp(logfd, "RJ row=neg_control bad_resume=\x00" as *u8); gn(logfd, bad_resume) 263 gp(logfd, " bad_skipped=\x00" as *u8); gn(logfd, bad_skipped) 264 gp(logfd, " bad_refetched=\x00" as *u8); gn(logfd, bad_refetched) 265 gp(logfd, " want_skipped=0 want_refetched=\x00" as *u8); gn(logfd, FULL_RUN) 266 if bad_skipped == 0 { if bad_refetched == FULL_RUN { gp(logfd, " verdict=PASS(no-journal-redoes-all)\n\x00" as *u8) } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } } else { gp(logfd, " verdict=FAIL(skipped-without-journal)\n\x00" as *u8) } 267 268 gp(logfd, "RJ row=tamper_oversize got=\x00" as *u8); gn(logfd, tamper_oversize) 269 gp(logfd, " want=-2\x00" as *u8) 270 if tamper_oversize == (0 - 2) { gp(logfd, " verdict=PASS\n\x00" as *u8) } else { gp(logfd, " verdict=FAIL\n\x00" as *u8) } 271 272 gp(logfd, "RJ row=tamper_torn torn_resume=\x00" as *u8); gn(logfd, torn_resume) 273 gp(logfd, " want<=\x00" as *u8); gn(logfd, RJ_EXTRACT) 274 if tamper_torn_advanced == 0 { gp(logfd, " verdict=PASS(torn-ignored)\n\x00" as *u8) } else { gp(logfd, " verdict=FAIL(torn-advanced-resume)\n\x00" as *u8) } 275 276 // ---------------- verdict ---------------- 277 var green: i64 = 1 278 if resume_from != RJ_EXTRACT { green = 0 } // resume point computed correctly 279 if good_skipped < 2 { green = 0 } // FETCH+EXTRACT skipped on resume 280 if good_refetched != 0 { green = 0 } // completed phases NOT re-executed 281 if good_executed != 2 { green = 0 } // VERIFY+SYNTH actually run 282 if good_finished != 1 { green = 0 } // run completes after resume 283 if bad_skipped != 0 { green = 0 } // NEG: no-journal skips nothing 284 if bad_refetched != FULL_RUN { green = 0 } // NEG: no-journal redoes all 285 if tamper_oversize != (0 - 2) { green = 0 } // oversized rejected 286 if tamper_torn_advanced != 0 { green = 0 } // torn line did NOT advance resume 287 288 gp(logfd, "RESEARCH-JOURNAL verdict=\x00" as *u8) 289 if green == 1 { gp(logfd, "GREEN\x00" as *u8) } else { gp(logfd, "RED\x00" as *u8) } 290 gp(logfd, " resume_from=\x00" as *u8); gn(logfd, resume_from) 291 gp(logfd, " good_skipped=\x00" as *u8); gn(logfd, good_skipped) 292 gp(logfd, " good_refetched=\x00" as *u8); gn(logfd, good_refetched) 293 gp(logfd, " good_executed=\x00" as *u8); gn(logfd, good_executed) 294 gp(logfd, " good_finished=\x00" as *u8); gn(logfd, good_finished) 295 gp(logfd, " bad_skipped=\x00" as *u8); gn(logfd, bad_skipped) 296 gp(logfd, " bad_refetched=\x00" as *u8); gn(logfd, bad_refetched) 297 gp(logfd, " tamper_oversize=\x00" as *u8); gn(logfd, tamper_oversize) 298 gp(logfd, " tamper_torn_advanced=\x00" as *u8); gn(logfd, tamper_torn_advanced) 299 if green == 0 { 300 gp(logfd, " reason=\x00" as *u8) 301 if resume_from != RJ_EXTRACT { gp(logfd, "wrong-resume-point \x00" as *u8) } 302 if good_skipped < 2 { gp(logfd, "did-not-skip-completed \x00" as *u8) } 303 if good_refetched != 0 { gp(logfd, "REFETCHED-COMPLETED \x00" as *u8) } 304 if good_executed != 2 { gp(logfd, "wrong-resume-execution \x00" as *u8) } 305 if good_finished != 1 { gp(logfd, "did-not-finish \x00" as *u8) } 306 if bad_skipped != 0 { gp(logfd, "neg-control-skipped-without-journal \x00" as *u8) } 307 if bad_refetched != FULL_RUN { gp(logfd, "neg-control-did-not-rerun-all \x00" as *u8) } 308 if tamper_oversize != (0 - 2) { gp(logfd, "oversize-not-rejected \x00" as *u8) } 309 if tamper_torn_advanced != 0 { gp(logfd, "torn-line-advanced-resume \x00" as *u8) } 310 } 311 gp(logfd, "\n\x00" as *u8) 312 if logfd > 0 { sys_close(logfd) } 313 if green == 1 { return 0 } 314 return 1 315}