code wiki / _hdl_build / nx_cc_promote.nx

nx_cc_promote.nx source

↩ module page · 292 lines · 13037 B

1// SUPERSEDED-BY: nx_bless_compiler.nx (2026-06-14). The canonical deploy organ now carries BOTH an 2// opt-in NEUTRAL mode (bless a behavior-neutral capability fix on no-regression) AND a pre-deploy 3// cc_equiv guard (corpus+self-host equivalence) -- folding in this organ's equiv validation. Kept 4// (not deleted) for history/the EXDEV same-device-rename + byte-landing-check learnings; not a live 5// path. Safe to remove in a confirmed cleanup pass once no lineage row references it. 6// 7// nx_cc_promote.nx -- the EQUIV-GATED COMPILER-DEPLOY organ (the missing safe path to land a 8// compiler-source change like the flock syscall-map fix). Today "deploying" a new nx_cc means a 9// hand-cp of the binary ~7400 organs depend on, in a hot daemon env, with no gate -- the highest 10// blast-radius op in the system. This organ makes the team's Engineer own that deploy: it can 11// ONLY swap the live compiler when nx_cc_equiv_gate (the differential-behavior oracle) is GREEN, 12// it backs up first, and it AUTO-ROLLS-BACK if the post-deploy probe fails. So a regressing 13// compiler can never go live, and a bad swap self-heals. 14// 15// PIPELINE: 16// 1. build CHALLENGER from current source: baseline-cc CC_SRC -> .s ; nxasm .s -> CC_CHAL 17// 2. run nx_cc_equiv_gate (it uses CC_CHAL) -> read its log tail -> require the LAST summary 18// line to be "selfhost=1 ... verdict=GREEN" (10/10 corpus + self-host). 19// 3a. DRY-RUN (default, no arg): report READY/BLOCKED, swap NOTHING. (safe; proves the pipeline) 20// 3b. DEPLOY (argv[1]=="DEPLOY"): IFF equiv GREEN -> cp baseline->CC_BAK ; renameat CHAL over 21// baseline (atomic; an already-open compiler fd survives, new execs get the new one) -> 22// POST-VERIFY: build _derefcast_minrepro with the NEW baseline + run -> exit 0. If post-OK 23// keep + log DEPLOYED; ELSE renameat CC_BAK back (rollback) + log ROLLED-BACK + nonzero exit. 24// 25// SOVEREIGN: pure NishiLang fork/dup3/execve/wait4 (sovereign exit-judging law -- no shell, no $?). 26// Imports the LIB-only (just nx_syscalls here). Additive: a DRY-RUN run mutates nothing. Durable 27// log: knowledge/status/cc_promote.log. license_tier: ORIGINAL 28// 29// module: nishi-core.engineer.cc_promote 30// depends: nishi-core.sys.syscalls 31// capability: EQUIV_GATED_COMPILER_DEPLOY 32import "nx_syscalls.nx" 33const CC_MAGIC_65536: i64 = 65536 34const CC_MAGIC_262144: i64 = 262144 35 36const CC_BASELINE: *u8 = "_offc/nx_compile_x86_native.elf" 37const CC_BAK: *u8 = "_offc/nx_compile_x86_native.elf.bak-preflock" 38const CC_CHAL: *u8 = "/tmp/cc_challenger.elf" 39const CC_CHAL_S: *u8 = "/tmp/cc_chal.s" 40const CC_TMPE: *u8 = "/tmp/_ccp_asm_out.elf" 41const CC_SRC: *u8 = "runtime/nx_compile_x86.nx" 42const CC_NXASM: *u8 = "_offc/nxasm_x86_main.elf" 43const CC_SBR: *u8 = "./_offc/nx_sov_build_run.elf" 44const CC_EQLOG: *u8 = "knowledge/status/cc_equiv_gate.log" 45const CC_DEPLOG: *u8 = "knowledge/status/cc_promote.log" 46const CC_PROBE: *u8 = "runtime/_derefcast_minrepro.nx" 47const CC_PROBE_S: *u8 = "/tmp/_ccp_probe.s" 48const CC_PROBE_E: *u8 = "/tmp/_ccp_probe.elf" 49const CC_STAGE: *u8 = "_offc/.cc_deploy_stage.elf" // stage on baseline's OWN device (drvfs); /tmp->_offc renameat=EXDEV 50const CC_MODE: i64 = 420 // 0644 51const CC_BUF: i64 = 1048576 52 53func cp_w(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 } 54func cp_wn(fd: i64, v: i64) -> i64 { 55 let t: *u8=sys_mmap(28); var m: i64=v; if m<0 {m=0-m; sys_write(fd,"-" as *u8,1)} 56 var k: i64=0; if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1} 57 let b: *u8=sys_mmap(28); var i: i64=0; while i<k {b[i]=t[k-1-i]; i=i+1}; sys_write(fd,b,k); return 0 58} 59 60// fork; child redirects stdout->out_fd (if >=0) + stderr->/dev/null; execve; parent waits -> exit code. 61func cp_run(path: *u8, argv: *i64, out_fd: i64) -> i64 { 62 let pid: i64 = sys_fork() 63 if pid == 0 { 64 if out_fd >= 0 { sys_dup3(out_fd, 1, 0) } 65 let dn: i64 = sys_openat_wr("/dev/null" as *u8, CC_MODE) 66 if dn >= 0 { sys_dup3(dn, 2, 0) } 67 let envp: *i64 = sys_mmap(16) as *i64 68 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64 69 envp[1] = 0 70 sys_execve(path, argv, envp) 71 sys_exit(127) 72 } 73 let st: *i64 = sys_mmap(16) as *i64 74 sys_wait4(pid, st, 0) 75 return wait_exit_code(st[0]) 76} 77 78func cp_exists(path: *u8) -> i64 { 79 let fd: i64 = sys_openat_rd(path) 80 if fd < 0 { return 0 } 81 sys_close(fd); return 1 82} 83 84func cp_read_tail(path: *u8, buf: *u8, cap: i64) -> i64 { 85 let fd: i64 = sys_openat_rd(path) 86 if fd < 0 { return 0 } 87 let sz: i64 = sys_lseek(fd, 0, 2) // SEEK_END 88 var off: i64 = 0 89 if sz > cap - 1 { off = sz - (cap - 1) } 90 sys_lseek(fd, off, 0) // SEEK_SET 91 var n: i64 = 0 92 var r: i64 = sys_read(fd, buf, cap - 1) 93 while r > 0 { n = n + r; if n >= cap - 1 { r = 0 } else { r = sys_read(fd, buf + n, cap - 1 - n) } } 94 sys_close(fd) 95 return n 96} 97 98// substring search in buf[0,n) 99func cp_contains(buf: *u8, n: i64, needle: *u8) -> i64 { 100 var nl: i64 = 0; while needle[nl] != (0 as u8) { nl = nl + 1 } 101 if nl == 0 { return 1 } 102 var i: i64 = 0 103 while i + nl <= n { 104 var k: i64 = 0; var ok: i64 = 1 105 while k < nl { if buf[i+k] != needle[k] { ok = 0; k = nl } else { k = k + 1 } } 106 if ok == 1 { return 1 } 107 i = i + 1 108 } 109 return 0 110} 111 112// the LAST line of buf must contain both needles (the equiv summary line). 113func cp_lastline_has2(buf: *u8, n: i64, a: *u8, b: *u8) -> i64 { 114 var s: i64 = n 115 if s > 0 { if buf[s-1] == (10 as u8) { s = s - 1 } } // drop trailing newline 116 var ls: i64 = s 117 while ls > 0 { if buf[ls-1] == (10 as u8) { ls = 0 - ls } else { ls = ls - 1 } } 118 if ls < 0 { ls = 0 - ls } 119 let len: i64 = s - ls 120 if len <= 0 { return 0 } 121 if cp_contains(buf + ls, len, a) == 1 { if cp_contains(buf + ls, len, b) == 1 { return 1 } } 122 return 0 123} 124 125func cp_copy(src: *u8, dst: *u8) -> i64 { 126 let buf: *u8 = sys_mmap(CC_BUF) 127 let rf: i64 = sys_openat_rd(src) 128 if rf < 0 { return 0 } 129 var n: i64 = 0 130 var r: i64 = sys_read(rf, buf, CC_BUF - 1) 131 while r > 0 { n = n + r; if n >= CC_BUF - 1 { r = 0 } else { r = sys_read(rf, buf + n, CC_BUF - 1 - n) } } 132 sys_close(rf) 133 let wf: i64 = sys_openat_wr(dst, CC_MODE) 134 if wf < 0 { return 0 } 135 var off: i64 = 0 136 while off < n { let w: i64 = sys_write(wf, buf + off, n - off); if w <= 0 { off = n } else { off = off + w } } 137 sys_close(wf) 138 return 1 139} 140 141// byte-compare two files; 1 equal, 0 different/unreadable. Confirms the swap actually landed. 142func cp_files_equal(pa: *u8, pb: *u8) -> i64 { 143 let fa: i64 = sys_openat_rd(pa) 144 let fb: i64 = sys_openat_rd(pb) 145 if fa < 0 { if fb < 0 { return 1 } } 146 if fa < 0 { if fb >= 0 { sys_close(fb) } return 0 } 147 if fb < 0 { sys_close(fa); return 0 } 148 let ba: *u8 = sys_mmap(CC_MAGIC_65536) 149 let bb: *u8 = sys_mmap(CC_MAGIC_65536) 150 var equal: i64 = 1 151 var more: i64 = 1 152 while more == 1 { 153 let na: i64 = sys_read(fa, ba, CC_MAGIC_65536) 154 let nb: i64 = sys_read(fb, bb, CC_MAGIC_65536) 155 if na != nb { equal = 0; more = 0 } 156 if more == 1 { 157 if na <= 0 { more = 0 } 158 var i: i64 = 0 159 while i < na { if ba[i] != bb[i] { equal = 0; i = na; more = 0 } i = i + 1 } 160 } 161 } 162 sys_close(fa); sys_close(fb) 163 return equal 164} 165 166// build /tmp/cc_challenger.elf from current source with the deployed baseline. 0 ok / nonzero stage. 167func cp_build_challenger() -> i64 { 168 let sfd: i64 = sys_openat_wr(CC_CHAL_S, CC_MODE) 169 if sfd < 0 { return 1 } 170 let a1: *i64 = sys_mmap(32) as *i64 171 a1[0] = CC_BASELINE as i64; a1[1] = CC_SRC as i64; a1[2] = 0 172 let rc1: i64 = cp_run(CC_BASELINE, a1, sfd) 173 sys_close(sfd) 174 if rc1 != 0 { return 2 } 175 let a2: *i64 = sys_mmap(32) as *i64 176 a2[0] = CC_NXASM as i64; a2[1] = CC_CHAL_S as i64; a2[2] = CC_TMPE as i64; a2[3] = 0 177 let rc2: i64 = cp_run(CC_NXASM, a2, 0 - 1) 178 if rc2 != 0 { return 3 } 179 sys_renameat(CC_TMPE, CC_CHAL) 180 if cp_exists(CC_CHAL) == 0 { return 4 } 181 return 0 182} 183 184// run nx_cc_equiv_gate (uses CC_CHAL); 1 iff its last log line is selfhost=1 + verdict=GREEN. 185func cp_equiv_green() -> i64 { 186 let a: *i64 = sys_mmap(32) as *i64 187 a[0] = CC_SBR as i64; a[1] = "nx_cc_equiv_gate" as *u8 as i64; a[2] = 0 188 cp_run(CC_SBR, a, 0 - 1) 189 let buf: *u8 = sys_mmap(CC_MAGIC_262144) 190 let n: i64 = cp_read_tail(CC_EQLOG, buf, CC_MAGIC_262144) 191 if n <= 0 { return 0 } 192 return cp_lastline_has2(buf, n, "selfhost=1" as *u8, "verdict=GREEN" as *u8) 193} 194 195// POST-DEPLOY probe: build _derefcast with the (now-new) baseline + run -> 1 iff exit 0. 196func cp_post_probe() -> i64 { 197 let sfd: i64 = sys_openat_wr(CC_PROBE_S, CC_MODE) 198 if sfd < 0 { return 0 } 199 let a1: *i64 = sys_mmap(32) as *i64 200 a1[0] = CC_BASELINE as i64; a1[1] = CC_PROBE as i64; a1[2] = 0 201 let rc1: i64 = cp_run(CC_BASELINE, a1, sfd) 202 sys_close(sfd) 203 if rc1 != 0 { return 0 } 204 let a2: *i64 = sys_mmap(32) as *i64 205 a2[0] = CC_NXASM as i64; a2[1] = CC_PROBE_S as i64; a2[2] = CC_PROBE_E as i64; a2[3] = 0 206 if cp_run(CC_NXASM, a2, 0 - 1) != 0 { return 0 } 207 let a3: *i64 = sys_mmap(32) as *i64 208 a3[0] = CC_PROBE_E as i64; a3[1] = 0 209 let rc3: i64 = cp_run(CC_PROBE_E, a3, 0 - 1) 210 if rc3 == 0 { return 1 } 211 return 0 212} 213 214func cp_log(verdict: *u8, mode: *u8, green: i64) -> i64 { 215 let fd: i64 = sys_openat_append(CC_DEPLOG, CC_MODE) 216 if fd < 0 { return 0 } 217 cp_w(fd, "CC-PROMOTE authored=organ mode=" as *u8); cp_w(fd, mode) 218 cp_w(fd, " equiv_green=" as *u8); cp_wn(fd, green) 219 cp_w(fd, " epoch=" as *u8); cp_wn(fd, sys_now_realtime_sec()) 220 cp_w(fd, " verdict=" as *u8); cp_w(fd, verdict); cp_w(fd, "\n" as *u8) 221 sys_close(fd) 222 return 0 223} 224 225func main(argc: i64, argv: *i64) -> i64 { 226 var deploy: i64 = 0 227 if argc >= 2 { 228 let arg: *u8 = argv[1] as *u8 229 // "DEPLOY" == D,E,P,L,O,Y 230 if arg[0] == (68 as u8) { if arg[1] == (69 as u8) { if arg[2] == (80 as u8) { deploy = 1 } } } 231 } 232 var mode: *u8 = "DRYRUN" as *u8 233 if deploy == 1 { mode = "DEPLOY" as *u8 } 234 cp_w(1, "CC-PROMOTE mode=" as *u8); cp_w(1, mode); cp_w(1, "\n" as *u8) 235 236 let brc: i64 = cp_build_challenger() 237 if brc != 0 { 238 cp_w(1, "CC-PROMOTE verdict=RED reason=challenger-build-failed stage="); cp_wn(1, brc); cp_w(1, "\n" as *u8) 239 cp_log("RED-BUILD" as *u8, mode, 0) 240 sys_exit(2); return 2 241 } 242 let green: i64 = cp_equiv_green() 243 cp_w(1, "CC-PROMOTE equiv_green=" as *u8); cp_wn(1, green); cp_w(1, "\n" as *u8) 244 if green != 1 { 245 cp_w(1, "CC-PROMOTE verdict=BLOCKED reason=equiv-not-green (no swap)\n" as *u8) 246 cp_log("BLOCKED" as *u8, mode, green) 247 sys_exit(3); return 3 248 } 249 250 if deploy == 0 { 251 cp_w(1, "CC-PROMOTE verdict=READY equiv=GREEN swap=SKIPPED(dry-run) -- rerun with DEPLOY to swap\n" as *u8) 252 cp_log("READY" as *u8, mode, green) 253 sys_exit(0); return 0 254 } 255 256 // DEPLOY: backup -> STAGE the challenger onto baseline's OWN device (cp; /tmp->_offc renameat is 257 // EXDEV cross-device = silent fail) -> atomic same-device renameat (CHECK its return) -> confirm 258 // the bytes actually LANDED (deployed == challenger; the prior false-green probed the still-old 259 // compiler) -> post-probe builds+runs the probe with the NEW compiler -> keep or auto-rollback. 260 if cp_copy(CC_BASELINE, CC_BAK) == 0 { 261 cp_w(1, "CC-PROMOTE verdict=RED reason=backup-failed (no swap)\n" as *u8) 262 cp_log("RED-BAK" as *u8, mode, green) 263 sys_exit(4); return 4 264 } 265 if cp_copy(CC_CHAL, CC_STAGE) == 0 { 266 cp_w(1, "CC-PROMOTE verdict=RED reason=stage-copy-failed (no swap)\n" as *u8) 267 cp_log("RED-STAGE" as *u8, mode, green) 268 sys_exit(6); return 6 269 } 270 let rrc: i64 = sys_renameat(CC_STAGE, CC_BASELINE) // same device (drvfs) -> atomic 271 if rrc != 0 { 272 cp_copy(CC_BAK, CC_BASELINE) 273 cp_w(1, "CC-PROMOTE verdict=ROLLED-BACK reason=rename-failed rc=" as *u8); cp_wn(1, rrc); cp_w(1, " (baseline restored)\n" as *u8) 274 cp_log("ROLLED-BACK-RENAME" as *u8, mode, green) 275 sys_exit(7); return 7 276 } 277 if cp_files_equal(CC_BASELINE, CC_CHAL) == 0 { 278 cp_copy(CC_BAK, CC_BASELINE) 279 cp_w(1, "CC-PROMOTE verdict=ROLLED-BACK reason=swap-bytes-mismatch (baseline restored)\n" as *u8) 280 cp_log("ROLLED-BACK-LAND" as *u8, mode, green) 281 sys_exit(8); return 8 282 } 283 if cp_post_probe() == 1 { 284 cp_w(1, "CC-PROMOTE verdict=DEPLOYED equiv=GREEN swap_landed=1 post_probe=PASS backup=" as *u8); cp_w(1, CC_BAK); cp_w(1, "\n" as *u8) 285 cp_log("DEPLOYED" as *u8, mode, green) 286 sys_exit(0); return 0 287 } 288 cp_copy(CC_BAK, CC_BASELINE) 289 cp_w(1, "CC-PROMOTE verdict=ROLLED-BACK reason=post-probe-failed (baseline restored)\n" as *u8) 290 cp_log("ROLLED-BACK" as *u8, mode, green) 291 sys_exit(5); return 5 292}