code wiki / (root) / nx_deployjrnl_gate.nx

nx_deployjrnl_gate.nx source

↩ module page · 220 lines · 12812 B

1// nx_deployjrnl_gate.nx -- THE REFEREE for the deploy-audit reconstruction. 2// 3// It tests the SHIPPING ruler, not a paraphrase of it: every judgement nx_deployjrnl makes lives in 4// nx_deployjrnl_lib as a pure function, and this gate imports that same lib. There is one copy, so a 5// tooth here cannot pass while the organ does something else. 6// 7// Fixtures are assembled at RUNTIME under /tmp/<gate>/ and never share a path with a production 8// beat: a gate whose RED tracks its fixture rather than its code has been measured in this estate, 9// and a detector that scans source will otherwise find its own test data. 10// 11// license_tier: ORIGINAL 12import "nx_syscalls.nx" 13import "nx_gate_verdict.nx" 14import "nx_deployjrnl_lib.nx" 15 16const G_DIR: *u8 = "/tmp/nx_deployjrnl_gate" as *u8 17const G_BANK: *u8 = "/tmp/nx_deployjrnl_gate/bank" as *u8 18const G_MODE_DIR: i64 = 511 19const G_A_LIVE: *u8 = "/tmp/nx_deployjrnl_gate/a.elf" as *u8 20const G_A_PREV: *u8 = "/tmp/nx_deployjrnl_gate/a.elf.prev" as *u8 21const G_B_LIVE: *u8 = "/tmp/nx_deployjrnl_gate/b.elf" as *u8 22const G_B_PREV: *u8 = "/tmp/nx_deployjrnl_gate/b.elf.prev" as *u8 23const G_BANKED: *u8 = "/tmp/nx_deployjrnl_gate/bank/a.elf.pre-tag" as *u8 24const G_MISSING: *u8 = "/tmp/nx_deployjrnl_gate/definitely-absent" as *u8 25 26func g_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 27func g_not(a: i64) -> i64 { if a == 1 { return 0 } return 1 } 28func g_and(a: i64, b: i64) -> i64 { if a == 1 { if b == 1 { return 1 } } return 0 } 29func g_ge0(v: i64) -> i64 { if v >= 0 { return 1 } return 0 } 30 31func g_write(path: *u8, s: *u8) -> i64 { 32 let fd: i64 = sys_openat_wr(path, DJ_MODE_644) 33 if fd < 0 { return 0 } 34 sys_write(fd, s, dj_slen(s)) 35 sys_close(fd) 36 return 1 37} 38 39func main(argc: i64, argv: *i64) -> i64 { 40 let ctr: *i64 = gv_ctr() 41 gv_head("NX-DEPLOYJRNL-GATE -- deploy reconstruction + rollback survivability" as *u8) 42 43 // ---- SETUP. Created at setup, not teardown: a teardown does not run when a run crashes. ---- 44 sys_mkdir(G_DIR, G_MODE_DIR) 45 sys_mkdir(G_BANK, G_MODE_DIR) 46 // a.elf.prev content is ALSO in the bank -> banked rollback. 47 // b.elf.prev content is banked NOWHERE -> the hazard that actually fired on 2026-08-25. 48 var setup: i64 = 1 49 if g_write(G_A_LIVE, "AAAA-live" as *u8) == 0 { setup = 0 } 50 if g_write(G_A_PREV, "AAAA-rollback" as *u8) == 0 { setup = 0 } 51 if g_write(G_BANKED, "AAAA-rollback" as *u8) == 0 { setup = 0 } 52 if g_write(G_B_LIVE, "BBBB-live" as *u8) == 0 { setup = 0 } 53 if g_write(G_B_PREV, "BBBB-rollback" as *u8) == 0 { setup = 0 } 54 gv_check("fixture-assembled" as *u8, setup, ctr) 55 56 // ---- 1. THE PURE TRANSITION RULER ---- 57 gv_check("transition-first-sight-is-BASELINE" as *u8, 58 g_eq(dj_transition(0, 0, 0, 0), DJ_T_NEW), ctr) 59 gv_check("transition-nothing-moved-is-UNCHANGED" as *u8, 60 g_eq(dj_transition(1, 0, 0, 0), DJ_T_UNCHANGED), ctr) 61 // ONE deploy satisfies the invariant: the new prev IS the live we recorded last scan. 62 gv_check("transition-single-deploy-is-WITNESSED" as *u8, 63 g_eq(dj_transition(1, 1, 1, 1), DJ_T_DEPLOY_WITNESSED), ctr) 64 // The invariant BROKEN means a binary passed through the serving slot we never hashed. 65 gv_check("transition-broken-invariant-is-MULTI-UNWITNESSED" as *u8, 66 g_eq(dj_transition(1, 1, 0, 1), DJ_T_DEPLOY_MULTI), ctr) 67 gv_check("transition-prev-moved-alone-is-PREV-ONLY" as *u8, 68 g_eq(dj_transition(1, 0, 0, 1), DJ_T_PREV_ONLY), ctr) 69 70 // THE TOOTH THAT MATTERS MOST FOR HONESTY: a multi-deploy must NEVER be reported as a clean 71 // single deploy. If this ever passes silently the instrument is claiming to have witnessed 72 // something it did not. 73 gv_bite("bite-unwitnessed-multi-deploy-not-laundered-as-witnessed" as *u8, 74 g_eq(dj_transition(1, 1, 0, 1), DJ_T_DEPLOY_MULTI), 75 g_eq(dj_transition(1, 1, 1, 1), DJ_T_DEPLOY_MULTI), ctr) 76 77 // ---- 2. RISK + THE HAZARD PREDICATE ---- 78 gv_check("risk-no-prev-is-NO-ROLLBACK" as *u8, g_eq(dj_risk(0, 0), DJ_R_NO_ROLLBACK), ctr) 79 gv_check("risk-prev-banked-is-OK" as *u8, g_eq(dj_risk(1, 1), DJ_R_OK), ctr) 80 gv_check("risk-prev-unbanked-is-AT-RISK" as *u8, g_eq(dj_risk(1, 0), DJ_R_AT_RISK), ctr) 81 82 // THE HAZARD THAT FIRED LIVE: a second deploy destroying the only rollback. 83 gv_bite("bite-second-deploy-would-destroy-only-rollback" as *u8, 84 dj_would_destroy_only_rollback(1, 0), 85 dj_would_destroy_only_rollback(1, 1), ctr) 86 // neg-control: with NO rollback slot there is nothing to destroy, so the alarm must stay silent. 87 // A guard that refuses everything passes every deny-test; this is the input that must be ALLOWED. 88 gv_check("neg-control-absent-prev-raises-no-destroy-alarm" as *u8, 89 g_eq(dj_would_destroy_only_rollback(0, 0), 0), ctr) 90 91 // ---- 3. VERDICT ORDERING ---- 92 // UNPROVEN outranks everything: an axis that could not look must not be spelled like a clean run. 93 gv_check("verdict-unobserved-is-UNPROVEN-not-GREEN" as *u8, 94 g_eq(dj_verdict(0, 0, 0, 0, 0), DJ_V_UNPROVEN), ctr) 95 gv_check("verdict-irreplaceable-eviction-is-RED" as *u8, 96 g_eq(dj_verdict(1, 0, 0, 0, 1), DJ_V_RED), ctr) 97 gv_check("verdict-unwitnessed-deploy-is-RED" as *u8, 98 g_eq(dj_verdict(0, 1, 0, 0, 1), DJ_V_RED), ctr) 99 gv_check("verdict-atrisk-above-floor-is-AMBER" as *u8, 100 g_eq(dj_verdict(0, 0, 5, 3, 1), DJ_V_AMBER), ctr) 101 // AT-RISK at or below the floor is the NORMAL post-deploy state. If this went RED the board 102 // would be permanently red and everyone would learn to ignore it. 103 gv_check("verdict-atrisk-at-floor-is-GREEN-not-permanently-red" as *u8, 104 g_eq(dj_verdict(0, 0, 3, 3, 1), DJ_V_GREEN), ctr) 105 106 // ---- 4. THE RATCHET, ALL THREE DIRECTIONS ---- 107 gv_check("ratchet-tightens-on-a-fall" as *u8, g_eq(dj_new_floor(1, 3), 1), ctr) 108 gv_check("ratchet-holds-when-flat" as *u8, g_eq(dj_new_floor(3, 3), 3), ctr) 109 // The direction that matters: a ratchet that raised its own floor on a rise would launder every 110 // regression green -- the exact failure a ratchet exists to prevent. 111 gv_check("ratchet-refuses-to-rewrite-baseline-on-a-rise" as *u8, g_eq(dj_new_floor(9, 3), 3), ctr) 112 gv_bite("bite-ratchet-holds-on-rise-and-moves-on-fall" as *u8, 113 g_eq(dj_new_floor(9, 3), 3), 114 g_eq(dj_new_floor(1, 3), 3), ctr) 115 116 // ---- 5. HASHING: FAIL LOUD, NEVER THE SHA OF NOTHING ---- 117 let h1: *u8 = sys_mmap(DJ_HEXSLOT) 118 let h2: *u8 = sys_mmap(DJ_HEXSLOT) 119 let hm: *u8 = sys_mmap(DJ_HEXSLOT) 120 let na: i64 = dj_hash_file(G_A_PREV, h1) 121 let nb: i64 = dj_hash_file(G_BANKED, h2) 122 let nm: i64 = dj_hash_file(G_MISSING, hm) 123 gv_check("fixture-reached-condition-both-files-hashed" as *u8, 124 g_and(g_ge0(na), g_ge0(nb)), ctr) 125 gv_check("identical-content-hashes-identical" as *u8, dj_streq(h1, h2), ctr) 126 // A hasher that digested an empty buffer would return a real-looking 64-hex answer for a file 127 // that does not exist. That is a lying instrument, and it is the shape a bank lookup would 128 // silently trust. 129 gv_check("neg-control-missing-file-returns-minus-one-not-a-digest" as *u8, g_eq(nm, 0 - 1), ctr) 130 131 // ---- 5b. THE STAT PRIMITIVE ---- 132 // THIS TOOTH EXISTS BECAUSE ITS ABSENCE LET A REAL DEFECT SHIP. On 2026-08-25 this gate read 133 // GREEN 42/42 while dj_stat was failing for EVERY path in the estate: a syscall number split by 134 // target left both consts in the build, the wrong one won, and on x86_64 it dispatched getcwd. 135 // Every tooth here tested a PURE ruler; nothing tested the one impure primitive, so the gate had 136 // no opinion about the only thing that was broken. A GREEN GATE THAT HAS NEVER EXERCISED THE 137 // SYSCALL IT DEPENDS ON IS NOT EVIDENCE ABOUT THAT SYSCALL. 138 let stt: *i64 = sys_mmap(DJ_LENSLOT * 2) as *i64 139 let statok: i64 = dj_stat(G_A_LIVE, stt) 140 gv_check("stat-succeeds-on-a-real-file" as *u8, g_eq(statok, 1), ctr) 141 // The size is asserted against the fixture this gate WROTE, so it cannot pass on a stale buffer. 142 gv_check("stat-returns-the-true-size" as *u8, g_eq(stt[1], 9), ctr) 143 gv_check("stat-returns-a-nonzero-mtime" as *u8, g_ge0(stt[0] - 1), ctr) 144 let stmiss: i64 = dj_stat(G_MISSING, stt) 145 gv_check("neg-control-stat-fails-on-an-absent-file" as *u8, g_eq(stmiss, 0), ctr) 146 gv_bite("bite-stat-discriminates-present-from-absent" as *u8, 147 g_eq(statok, 1), g_eq(stmiss, 1), ctr) 148 149 // ---- 6. BANK MEMBERSHIP IS BY CONTENT, NOT BY NAME ---- 150 let bank: *u8 = sys_mmap(DJ_HEXSLOT * 4) 151 dj_cat(bank, 0, h2) 152 let e0: *u8 = ((bank as i64) + 0) as *u8 153 e0[DJ_HEX_BYTES] = 0 as u8 154 let hb: *u8 = sys_mmap(DJ_HEXSLOT) 155 let nbp: i64 = dj_hash_file(G_B_PREV, hb) 156 gv_check("fixture-reached-condition-b-prev-hashed" as *u8, g_ge0(nbp), ctr) 157 // a.elf.prev is banked under a DIFFERENT filename (a.elf.pre-tag) and must still be found. 158 gv_check("bank-hit-despite-unpredicted-filename-tag" as *u8, dj_bank_has(bank, 1, h1), ctr) 159 gv_check("neg-control-unbanked-content-is-not-found" as *u8, g_eq(dj_bank_has(bank, 1, hb), 0), ctr) 160 gv_bite("bite-bank-membership-discriminates" as *u8, 161 dj_bank_has(bank, 1, h1), 162 dj_bank_has(bank, 1, hb), ctr) 163 164 // ---- 7. THE DIRECTORY WALK ---- 165 let nm1: *u8 = sys_mmap(64 * DJ_NAME_SLOT) 166 let cn: *i64 = sys_mmap(DJ_LENSLOT) as *i64 167 let sk: *i64 = sys_mmap(DJ_LENSLOT) as *i64 168 let wrc: i64 = dj_walk(G_DIR, ".elf.prev" as *u8, nm1, DJ_NAME_SLOT, 64, cn, sk) 169 gv_check("walk-returns-OK-on-a-real-directory" as *u8, g_eq(wrc, DJ_W_OK), ctr) 170 gv_subjects("walk-found-rollback-slots" as *u8, cn[0], ctr) 171 gv_check("walk-found-both-planted-prev-files" as *u8, g_eq(cn[0], 2), ctr) 172 173 // A suffix that matches nothing must return OK with zero rows -- that is a real, proven empty. 174 let wrc0: i64 = dj_walk(G_DIR, ".no-such-suffix" as *u8, nm1, DJ_NAME_SLOT, 64, cn, sk) 175 gv_check("walk-genuine-empty-is-OK-with-zero-rows" as *u8, 176 g_and(g_eq(wrc0, DJ_W_OK), g_eq(cn[0], 0)), ctr) 177 178 // THE CONFIDENT-EMPTY DEFECT. Pointed at a FILE, getdents returns negative. If that were folded 179 // into end-of-directory the walk would report a serene "zero entries" for a path it never read, 180 // and every absence conclusion downstream would be false. 181 let wrcf: i64 = dj_walk(G_A_LIVE, "" as *u8, nm1, DJ_NAME_SLOT, 64, cn, sk) 182 gv_check("neg-control-walking-a-file-does-not-return-OK" as *u8, g_not(g_eq(wrcf, DJ_W_OK)), ctr) 183 gv_bite("bite-error-and-empty-directory-are-distinguishable" as *u8, 184 g_not(g_eq(wrcf, DJ_W_OK)), 185 g_not(g_eq(wrc0, DJ_W_OK)), ctr) 186 187 // The cap must REFUSE, not silently return a prefix: a cap reached in silence becomes a 188 // measurement nobody knows is partial. 189 let wrcc: i64 = dj_walk(G_DIR, ".elf.prev" as *u8, nm1, DJ_NAME_SLOT, 1, cn, sk) 190 gv_check("walk-refuses-at-cap-instead-of-truncating-silently" as *u8, g_eq(wrcc, DJ_W_CAP), ctr) 191 192 // ---- 8. STATUS ROUND-TRIP (the producer parses its own format) ---- 193 let line: *u8 = sys_mmap(DJ_PATH_SLOT) 194 var o: i64 = dj_cat(line, 0, "ROW target=a.elf live=" as *u8) 195 o = dj_cat(line, o, h1) 196 o = dj_cat(line, o, " lbytes=13 lmtime=1787680659 prev=" as *u8) 197 o = dj_cat(line, o, hb) 198 o = dj_cat(line, o, " pbytes=13 banked=0 risk=1\n" as *u8) 199 let fv: *u8 = sys_mmap(DJ_HEXSLOT) 200 gv_check("roundtrip-target-parses" as *u8, 201 g_eq(dj_field(line, 0, o, "target=" as *u8, fv, DJ_HEXSLOT), 1), ctr) 202 gv_check("roundtrip-target-value-correct" as *u8, dj_streq(fv, "a.elf" as *u8), ctr) 203 dj_field(line, 0, o, "live=" as *u8, fv, DJ_HEXSLOT) 204 gv_check("roundtrip-live-sha-survives" as *u8, dj_streq(fv, h1), ctr) 205 dj_field(line, 0, o, "prev=" as *u8, fv, DJ_HEXSLOT) 206 gv_check("roundtrip-prev-sha-survives" as *u8, dj_streq(fv, hb), ctr) 207 // ANCHORING. "live=" must not match inside "lbytes=" or any longer key, and a key that is not 208 // present must report absent rather than returning the neighbouring field's value. 209 gv_check("neg-control-absent-key-reports-absent" as *u8, 210 g_eq(dj_field(line, 0, o, "nosuchkey=" as *u8, fv, DJ_HEXSLOT), 0), ctr) 211 212 // ---- 9. NUMERIC PARSE REFUSES GARBAGE ---- 213 gv_check("atoi-parses-a-number" as *u8, g_eq(dj_atoi("42" as *u8), 42), ctr) 214 // A corrupt ratchet conf read as 0 would refuse every future scan. Refuse the parse instead. 215 gv_check("neg-control-atoi-refuses-non-numeric" as *u8, g_eq(dj_atoi("x9" as *u8), 0 - 1), ctr) 216 gv_check("neg-control-atoi-refuses-empty" as *u8, g_eq(dj_atoi("" as *u8), 0 - 1), ctr) 217 218 return gv_verdict("DEPLOYJRNL-GATE" as *u8, ctr, 219 "reconstruction rulers, rollback-destruction hazard, walk coverage, ratchet direction" as *u8) 220}