code wiki / _hdl_build / _ark_gate_test.nx

_ark_gate_test.nx source

↩ module page · 287 lines · 12755 B

1// _ark_gate_test.nx -- ARK-GATE-001 BOTH-ARMS gate (NO-FAKE-GREEN). 2// Proves the gate-aware nx_ark_push on a THROWAWAY /tmp git repo + bare origin 3// -- the real /mnt/c/Users/elder/nishi-core repo is NEVER touched. The gated 4// ark_push ELF is run with argv overrides (repo/branch/marker/state/log all in 5// /tmp). Three arms, each asserted by capturing origin's HEAD before/after: 6// ARM-A no fresh ACHIEVED marker -> HOLD: origin HEAD UNCHANGED + log HOLD 7// ARM-B fresh ACHIEVED marker -> PUSH: origin HEAD ADVANCED + verdict GREEN 8// ARM-C same marker, re-run -> HOLD (already-pushed): origin HEAD UNCHANGED 9// Also stages a dirty file before ARM-A to prove un-gated work stays LOCAL. 10// Emits ARKGATEGATE <p>/3 verdict=GREEN|RED -> knowledge/status/ark_gate_test.log 11// with a ||MARK= anchor so reconcile flips ARK-GATE-001 -> DONE on evidence. 12// license_tier: ORIGINAL 13import "nx_syscalls.nx" 14 15const T_ELF: *u8 = "/tmp/_apk_gated.elf" // the gated ark_push (built before the gate runs) 16const T_GIT: *u8 = "/usr/bin/git" 17const T_ROOT: *u8 = "/tmp/_agt_work" // scratch work tree 18const T_ORIGIN: *u8 = "/tmp/_agt_origin.git" // scratch bare origin 19const T_BRANCH: *u8 = "main" 20const T_MARKER: *u8 = "/tmp/_agt_ark_gate.marker" 21const T_STATE: *u8 = "/tmp/_agt_ark_push.state" 22const T_LOG: *u8 = "/tmp/_agt_ark_push.log" 23const T_HEADTXT:*u8 = "/tmp/_agt_head.txt" // origin HEAD capture 24const T_OUTLOG: *u8 = "knowledge/status/ark_gate_test.log" 25const MODE: i64 = 420 26 27func t_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 28func t_f(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 29func t_n(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(fd,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 } 30 31func t_write(path: *u8, s: *u8) -> i64 { 32 let fd: i64 = sys_openat_wr(path, MODE) 33 if fd < 0 { return 0 } 34 t_f(fd, s) 35 sys_close(fd) 36 return 1 37} 38 39func t_read(path: *u8, buf: *u8, cap: i64) -> i64 { 40 let fd: i64 = sys_openat_rd(path) 41 if fd < 0 { return 0 } 42 var n: i64 = 0 43 var r: i64 = sys_read(fd, buf, cap - 1) 44 while r > 0 { n = n + r; if n >= cap - 1 { r = 0 } else { r = sys_read(fd, buf + n, cap - 1 - n) } } 45 sys_close(fd) 46 buf[n] = 0 as u8 47 return n 48} 49 50// run a NULL-terminated argv; child stdout/err -> outpath (append, or /dev/null 51// if outpath null). Returns WEXITSTATUS. 52func t_run(argv: *i64, outpath: *u8) -> i64 { 53 let pid: i64 = sys_fork() 54 if pid == 0 { 55 var op: *u8 = "/dev/null" as *u8 56 if outpath != (0 as *u8) { op = outpath } 57 let ofd: i64 = sys_openat_wr(op, MODE) 58 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } 59 let envp: *i64 = sys_mmap(64) as *i64 60 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 61 envp[1] = "HOME=/tmp" as *u8 as i64 62 envp[2] = "GIT_AUTHOR_NAME=agt" as *u8 as i64 63 envp[3] = "GIT_AUTHOR_EMAIL=agt@local" as *u8 as i64 64 envp[4] = "GIT_COMMITTER_NAME=agt" as *u8 as i64 65 envp[5] = "GIT_COMMITTER_EMAIL=agt@local" as *u8 as i64 66 envp[6] = 0 67 sys_execve(argv[0] as *u8, argv, envp) 68 sys_exit(127) 69 } 70 let st: *i64 = sys_mmap(16) as *i64 71 sys_wait4(pid, st, 0) 72 return (st[0] >> 8) & 0xff 73} 74 75// git -C <repo> <a> <b> <c> <d> (NULL-pad the unused tail) 76func t_git(repo: *u8, a: *u8, b: *u8, c: *u8, d: *u8) -> i64 { 77 let argv: *i64 = sys_mmap(96) as *i64 78 var k: i64 = 0 79 argv[k] = T_GIT as i64; k = k + 1 80 argv[k] = "-C" as *u8 as i64; k = k + 1 81 argv[k] = repo as i64; k = k + 1 82 if a != (0 as *u8) { argv[k] = a as i64; k = k + 1 } 83 if b != (0 as *u8) { argv[k] = b as i64; k = k + 1 } 84 if c != (0 as *u8) { argv[k] = c as i64; k = k + 1 } 85 if d != (0 as *u8) { argv[k] = d as i64; k = k + 1 } 86 argv[k] = 0 87 return t_run(argv, 0 as *u8) 88} 89 90// capture origin's HEAD (branch tip) into a fresh buffer; returns the sha string. 91// Empty (no commits yet) -> empty string. Uses rev-parse refs/heads/<branch>. 92func t_origin_head(out: *u8) -> i64 { 93 let argv: *i64 = sys_mmap(96) as *i64 94 argv[0] = T_GIT as i64 95 argv[1] = "-C" as *u8 as i64 96 argv[2] = T_ORIGIN as i64 97 argv[3] = "rev-parse" as *u8 as i64 98 argv[4] = "refs/heads/main" as *u8 as i64 99 argv[5] = 0 100 t_run(argv, T_HEADTXT) 101 return t_read(T_HEADTXT, out, 256) 102} 103 104func t_streq(a: *u8, an: i64, b: *u8, bn: i64) -> i64 { 105 if an != bn { return 0 } 106 var i: i64 = 0 107 while i < an { if a[i] != b[i] { return 0 } i = i + 1 } 108 return 1 109} 110 111// run the gated ark_push ELF with ALL scratch overrides. argv: 112// ELF repo branch marker state log 113func t_arkpush() -> i64 { 114 let argv: *i64 = sys_mmap(96) as *i64 115 argv[0] = T_ELF as i64 116 argv[1] = T_ROOT as i64 117 argv[2] = T_BRANCH as i64 118 argv[3] = T_MARKER as i64 119 argv[4] = T_STATE as i64 120 argv[5] = T_LOG as i64 121 argv[6] = 0 122 return t_run(argv, 0 as *u8) 123} 124 125// does T_LOG's LAST line contain pat? (whole-file substring is enough -- each 126// arm truncates+re-reads via append; we read the full log and look for pat) 127func t_log_has(pat: *u8) -> i64 { 128 let buf: *u8 = sys_mmap(65536) 129 let n: i64 = t_read(T_LOG, buf, 65536) 130 var pl: i64 = 0 131 while pat[pl] != (0 as u8) { pl = pl + 1 } 132 var i: i64 = 0 133 while i + pl <= n { 134 var k: i64 = 0 135 var hit: i64 = 1 136 while k < pl { if buf[i + k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 137 if hit == 1 { return 1 } 138 i = i + 1 139 } 140 return 0 141} 142 143func t_chk(label: *u8, ok: i64, bad: *i64) -> i64 { 144 t_p("AGT-" as *u8); t_p(label) 145 if ok == 1 { t_p(" ok\n" as *u8) } else { t_p(" BAD\n" as *u8); bad[0] = bad[0] + 1 } 146 return 0 147} 148 149// build the gated nx_ark_push from CURRENT source to T_ELF (compile + assemble, 150// NO run -- running it would touch the real repo). Self-contained so the gate 151// is reproducible via the standard lane. Returns 1 on success. 152func t_build_gated() -> i64 { 153 // _offc/nx_cc_sovereign.elf runtime/_hdl_build/nx_ark_push.nx > /tmp/nx_ark_push.s 154 let sfd: i64 = sys_openat_wr("/tmp/nx_ark_push.s" as *u8, MODE) 155 let cc: *i64 = sys_mmap(64) as *i64 156 cc[0] = "_offc/nx_cc_sovereign.elf" as *u8 as i64 157 cc[1] = "runtime/_hdl_build/nx_ark_push.nx" as *u8 as i64 158 cc[2] = 0 159 let pidc: i64 = sys_fork() 160 if pidc == 0 { 161 if sfd >= 0 { sys_dup3(sfd, 1, 0) } 162 let dn: i64 = sys_openat_wr("/dev/null" as *u8, MODE) 163 if dn >= 0 { sys_dup3(dn, 2, 0) } 164 let envp: *i64 = sys_mmap(16) as *i64 165 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 166 sys_execve("_offc/nx_cc_sovereign.elf" as *u8, cc, envp) 167 sys_exit(127) 168 } 169 let st: *i64 = sys_mmap(16) as *i64 170 sys_wait4(pidc, st, 0) 171 if sfd >= 0 { sys_close(sfd) } 172 // _offc/nxasm_x86_main.elf /tmp/nx_ark_push.s /tmp/_apk_gated.elf 173 let aa: *i64 = sys_mmap(64) as *i64 174 aa[0] = "_offc/nxasm_x86_main.elf" as *u8 as i64 175 aa[1] = "/tmp/nx_ark_push.s" as *u8 as i64 176 aa[2] = T_ELF as i64 177 aa[3] = 0 178 let rca: i64 = t_run(aa, 0 as *u8) 179 if rca != 0 { return 0 } 180 let probe: i64 = sys_openat_rd(T_ELF) 181 if probe < 0 { return 0 } 182 sys_close(probe) 183 return 1 184} 185 186func main() -> i64 { 187 let bad: *i64 = sys_mmap(16) as *i64 188 bad[0] = 0 189 t_p("=== ARK-GATE-001 both-arms gate (scratch /tmp origin; real repo untouched) ===\n" as *u8) 190 191 // build the gated ark_push from CURRENT source (self-contained, lane-runnable) 192 if t_build_gated() == 0 { t_p("ARKGATEGATE verdict=RED reason=gated-elf-build-failed\n" as *u8); sys_exit(9); return 9 } 193 194 // ---- fresh scratch world (idempotent: rm -rf both dirs, recreate) ---- 195 let rm1: *i64 = sys_mmap(64) as *i64 196 rm1[0] = "/bin/rm" as *u8 as i64; rm1[1] = "-rf" as *u8 as i64; rm1[2] = T_ROOT as i64; rm1[3] = T_ORIGIN as i64; rm1[4] = 0 197 t_run(rm1, 0 as *u8) 198 // also clear scratch marker/state/log from any prior run 199 let rm2: *i64 = sys_mmap(64) as *i64 200 rm2[0] = "/bin/rm" as *u8 as i64; rm2[1] = "-f" as *u8 as i64; rm2[2] = T_MARKER as i64; rm2[3] = T_STATE as i64; rm2[4] = T_LOG as i64; rm2[5] = 0 201 t_run(rm2, 0 as *u8) 202 203 sys_mkdir(T_ROOT, 0x1ed) 204 // bare origin 205 let initbare: *i64 = sys_mmap(64) as *i64 206 initbare[0] = T_GIT as i64; initbare[1] = "init" as *u8 as i64; initbare[2] = "--bare" as *u8 as i64; initbare[3] = "-b" as *u8 as i64; initbare[4] = "main" as *u8 as i64; initbare[5] = T_ORIGIN as i64; initbare[6] = 0 207 t_run(initbare, 0 as *u8) 208 // work tree 209 t_git(T_ROOT, "init" as *u8, "-b" as *u8, "main" as *u8, 0 as *u8) 210 t_git(T_ROOT, "remote" as *u8, "add" as *u8, "origin" as *u8, T_ORIGIN) 211 // configure a commit identity IN THE REPO (not env): nx_ark_push's ap_run 212 // execs git with an EMPTY environment, so the commit author must come from 213 // repo config, exactly like the real repo (which has user.name/email set). 214 t_git(T_ROOT, "config" as *u8, "user.name" as *u8, "agt" as *u8, 0 as *u8) 215 t_git(T_ROOT, "config" as *u8, "user.email" as *u8, "agt@local" as *u8, 0 as *u8) 216 // seed commit so refs/heads/main exists on both sides 217 t_write("/tmp/_agt_work/seed.txt" as *u8, "seed\n" as *u8) 218 t_git(T_ROOT, "add" as *u8, "-A" as *u8, 0 as *u8, 0 as *u8) 219 t_git(T_ROOT, "commit" as *u8, "-q" as *u8, "-m" as *u8, "seed" as *u8) 220 t_git(T_ROOT, "push" as *u8, "-q" as *u8, "origin" as *u8, "main" as *u8) 221 222 let h0: *u8 = sys_mmap(256) 223 let n0: i64 = t_origin_head(h0) 224 t_p("AGT-SEED origin-head-len=" as *u8); t_n(1, n0); t_p("\n" as *u8) 225 226 // ---- ARM-A: no fresh marker -> HOLD; stage a dirty file first ---- 227 // dirty/un-gated work in the tree: it must stay LOCAL (never reach origin). 228 t_write("/tmp/_agt_work/ungated.txt" as *u8, "mid-work un-gated change\n" as *u8) 229 let rcA: i64 = t_arkpush() 230 let hA: *u8 = sys_mmap(256) 231 let nA: i64 = t_origin_head(hA) 232 var okA: i64 = 0 233 if rcA == 0 { if t_streq(h0, n0, hA, nA) == 1 { if t_log_has("verdict=HOLD reason=no-fresh-achieved-marker" as *u8) == 1 { okA = 1 } } } 234 t_chk("ARM-A-NO-MARKER-SKIP" as *u8, okA, bad) 235 236 // ---- ARM-B: fresh ACHIEVED marker -> PUSH; origin HEAD must ADVANCE ---- 237 let mk: *u8 = sys_mmap(256) 238 var mo: i64 = 0 239 let s1: *u8 = "ARKGATE epoch=" as *u8 240 var si: i64 = 0 241 while s1[si] != (0 as u8) { mk[mo] = s1[si]; mo = mo + 1; si = si + 1 } 242 var ev: i64 = sys_now_realtime_sec() 243 let dt: *u8 = sys_mmap(28) 244 var dk: i64 = 0 245 if ev == 0 { dt[0] = 48 as u8; dk = 1 } 246 while ev > 0 { dt[dk] = (48 + (ev % 10)) as u8; ev = ev / 10; dk = dk + 1 } 247 var dq: i64 = 0 248 while dq < dk { mk[mo] = dt[dk - 1 - dq]; mo = mo + 1; dq = dq + 1 } 249 let s2: *u8 = " verdict=ACHIEVED flipped=1 source=reconcile\n" as *u8 250 si = 0 251 while s2[si] != (0 as u8) { mk[mo] = s2[si]; mo = mo + 1; si = si + 1 } 252 mk[mo] = 0 as u8 253 t_write(T_MARKER, mk) 254 255 let rcB: i64 = t_arkpush() 256 let hB: *u8 = sys_mmap(256) 257 let nB: i64 = t_origin_head(hB) 258 var okB: i64 = 0 259 // advanced = origin head changed from the seed AND ark_push logged GREEN 260 if rcB == 0 { if t_streq(h0, n0, hB, nB) == 0 { if t_log_has("verdict=GREEN" as *u8) == 1 { okB = 1 } } } 261 t_chk("ARM-B-FRESH-MARKER-PUSH" as *u8, okB, bad) 262 263 // ---- ARM-C: same marker, re-run -> HOLD (already-pushed); HEAD unchanged ---- 264 // first make a NEW local change so a push WOULD move origin if the gate failed. 265 t_write("/tmp/_agt_work/ungated2.txt" as *u8, "another un-gated change\n" as *u8) 266 let rcC: i64 = t_arkpush() 267 let hC: *u8 = sys_mmap(256) 268 let nC: i64 = t_origin_head(hC) 269 var okC: i64 = 0 270 if rcC == 0 { if t_streq(hB, nB, hC, nC) == 1 { if t_log_has("reason=already-pushed-this-verdict" as *u8) == 1 { okC = 1 } } } 271 t_chk("ARM-C-IDEMPOTENT-HOLD" as *u8, okC, bad) 272 273 // ---- verdict + reconcile MARK ---- 274 let lg: i64 = sys_openat_append(T_OUTLOG, MODE) 275 var rcg: i64 = 0 276 if bad[0] == 0 { 277 t_p("ARKGATEGATE 3/3 verdict=GREEN\n" as *u8) 278 if lg >= 0 { t_f(lg, "ARKGATEGATE 3/3 verdict=GREEN epoch=" as *u8); t_n(lg, sys_now_realtime_sec()); t_f(lg, "\n" as *u8) } 279 } else { 280 t_p("ARKGATEGATE verdict=RED bad=" as *u8); t_n(1, bad[0]); t_p("\n" as *u8) 281 if lg >= 0 { t_f(lg, "ARKGATEGATE verdict=RED bad=" as *u8); t_n(lg, bad[0]); t_f(lg, "\n" as *u8) } 282 rcg = bad[0] 283 } 284 if lg >= 0 { sys_close(lg) } 285 sys_exit(rcg) 286 return rcg 287}