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}