code wiki / _hdl_build / nx_gate_sweep.nx
nx_gate_sweep.nx source
↩ module page · 134 lines · 7429 B
1// nx_gate_sweep.nx -- Runs each gate in the sovereign ecosystem sequentially, reporting pass/fail based on exit codes.
2import "nx_gate_gn.nx"
3import "nx_gate_base.nx"
4// nx_gate_sweep.nx -- SOVEREIGN multi-gate regression sweep. For each gate it sys_fork()s, redirects the
5// child's stdout+stderr to /dev/null (verdict rides the EXIT CODE), sys_execve()s the sovereign build runner
6// on that gate, and sys_wait4()s the exit code -- 0 = GREEN, nonzero = RED. Same fork/exec idiom as
7// nx_synced_push. NOTE: this REBUILDS every gate, so a full 24-gate run is a long (~14min) single process
8// that can be SIGTERM'd under WSL/multi-workstream contention -- prefer verifying RUNG BY RUNG (one gate via
9// nx_sov_build_run) during development; use this only for an occasional full pass. RV64 syscall numbers
10// (the nxasm_x86 backend translates them). CWD must be nxc2. license_tier: ORIGINAL
11import "nx_syscalls.nx"
12
13const SWEEP_RUNNER: *u8 = "_offc/nx_sov_build_run.elf"
14
15// sovereign file remove -- unlinkat as the proven nx_aw_sftp::ft_unlink does it: raw x86_64 number 263
16// (unlinkat is NOT in the backend's RV64->x86_64 translation set, unlike mkdirat=34; RV64 35 would fall
17// through to x86_64 nanosleep and silently no-op). So cleanup is sovereign too, never `rm`/Remove-Item.
18func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
19" as *u8); return ok }
20func gs_unlink(p: *u8) -> i64 { return __syscall(263, AT_FDCWD, p, 0, 0, 0, 0) }
21
22// build+run ONE gate through the sovereign runner in a child; return its exit code (0=GREEN).
23func run_gate(name: *u8) -> i64 {
24 let pid: i64 = sys_fork()
25 if pid == 0 {
26 let dn: i64 = sys_openat_wr("/dev/null\x00" as *u8, 420)
27 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) }
28 let argv: *i64 = sys_mmap(64) as *i64
29 argv[0] = SWEEP_RUNNER as i64
30 argv[1] = name as i64
31 argv[2] = 0
32 let envp: *i64 = sys_mmap(16) as *i64
33 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0
34 sys_execve(SWEEP_RUNNER, argv, envp)
35 sys_exit(127)
36 }
37 let st: *i64 = sys_mmap(16) as *i64
38 sys_wait4(pid, st, 0)
39 return (st[0] >> 8) & 0xff
40}
41
42func report(name: *u8, tally: *i64) -> i64 {
43 let code: i64 = run_gate(name)
44 gw(" " as *u8); gw(name); gw(": " as *u8)
45 if code == 0 { gw("GREEN\n" as *u8); tally[0] = tally[0] + 1 }
46 else { gw("RED (exit " as *u8); gn(code); gw(")\n" as *u8); tally[1] = tally[1] + 1 }
47 return code
48}
49
50// Append the house verdict anchor so nx_gatereg_derive / nx_gate_rollup can SEE this lane.
51// MERGED 2026-07-30 on top of the nx_gate_base refactor: ADDITIVE ONLY -- run_gate is left untouched
52// because bounded exec belongs to that refactor, not here. An earlier version of this emitter was lost
53// when the file was rewritten wholesale; re-applied on top of the current revision rather than reverting
54// it, so both changes survive.
55// IT GETS ITS OWN LOG, DELIBERATELY: knowledge/status/browser_gate.log already has a DIFFERENT producer
56// emitting a different row schema, and every reader takes the LAST line -- two producers in one
57// append-only log silently overwrite each other's lane verdict. One log, one producer.
58// logpath is a PARAMETER, not a constant. MEASURED THE HARD WAY 2026-07-30: with the path hardcoded
59// relative to CWD this wrote buildroot/knowledge/status/... because the sweep MUST run from buildroot to
60// reach _offc/nx_sov_build_run.elf -- while the maturity rollup reads nishihost/knowledge/status.
61// Evidence landed in a root no reader reads: the exact seq1339 defect, reproduced by my own new code.
62// A gate that emits evidence must let its CALLER say where the evidence goes.
63func gs_logline(tally: *i64, logpath: *u8) -> i64 {
64 let fd: i64 = sys_openat_append(logpath, 420)
65 if fd < 0 { return 0 - 1 }
66 let b: *u8 = sys_mmap(256)
67 var o: i64 = 0
68 o = gs_cat(b, o, "nx_gate_sweep browser-renderer green=" as *u8)
69 o = gs_catn(b, o, tally[0])
70 o = gs_cat(b, o, " red=" as *u8)
71 o = gs_catn(b, o, tally[1])
72 if tally[1] == 0 { o = gs_cat(b, o, " verdict=GREEN\n" as *u8) } else { o = gs_cat(b, o, " verdict=RED\n" as *u8) }
73 sys_write(fd, b, o)
74 sys_close(fd)
75 return 0
76}
77func gs_cat(b: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { b[o] = s[i]; o = o + 1; i = i + 1 } return o }
78func gs_catn(b: *u8, off: i64, v: i64) -> i64 {
79 var m: i64 = v
80 if m < 0 { m = 0 }
81 let t: *u8 = sys_mmap(28)
82 var k: i64 = 0
83 if m == 0 { t[0] = 48 as u8; k = 1 }
84 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
85 var o: i64 = off
86 var i: i64 = 0
87 while i < k { b[o] = t[k - 1 - i]; o = o + 1; i = i + 1 }
88 return o
89}
90
91func main(argc: i64, argv: *i64) -> i64 {
92 // argv[1] = where to append the verdict anchor. Default stays CWD-relative for back-compat, but the
93 // caller SHOULD pass the reader's root explicitly -- the sweep runs from buildroot and the rollup
94 // reads nishihost/knowledge/status, so the default is right for neither by accident.
95 var logpath: *u8 = "knowledge/status/browser_render_gate.log\x00" as *u8
96 if argc >= 2 { logpath = argv[1] as *u8 }
97 gw("=== nx_gate_sweep: SOVEREIGN renderer regression sweep (fork/exec, no shell) ===\n" as *u8)
98 // retire the superseded hand-shell sweep script, sovereignly (no rm).
99 gs_unlink("_flexgrow_regress.sh\x00" as *u8)
100 let tally: *i64 = sys_mmap(16) as *i64 // [0]=GREEN [1]=RED
101 tally[0] = 0
102 tally[1] = 0
103 report("nx_float_gate\x00" as *u8, tally)
104 report("nx_grid_gate\x00" as *u8, tally)
105 report("nx_grid_track_gate\x00" as *u8, tally)
106 report("nx_render_page_gate\x00" as *u8, tally)
107 report("nx_border_gate\x00" as *u8, tally)
108 report("nx_wrap_gate\x00" as *u8, tally)
109 report("nx_inline_gate\x00" as *u8, tally)
110 report("nx_float_inline_gate\x00" as *u8, tally)
111 report("nx_chrome_drop_gate\x00" as *u8, tally)
112 report("nx_table_cell_gate\x00" as *u8, tally)
113 report("nx_named_color_gate\x00" as *u8, tally)
114 report("nx_text_wrap_gate\x00" as *u8, tally)
115 report("nx_inline_hidden_gate\x00" as *u8, tally)
116 report("nx_rgb_color_gate\x00" as *u8, tally)
117 report("nx_real_page_gate\x00" as *u8, tally)
118 report("nx_specificity_gate\x00" as *u8, tally)
119 report("nx_reftest_gate\x00" as *u8, tally)
120 report("nx_justify_gate\x00" as *u8, tally)
121 report("nx_align_gate\x00" as *u8, tally)
122 report("nx_flexgrow_gate\x00" as *u8, tally)
123 report("nx_negmargin_gate\x00" as *u8, tally)
124 report("nx_cdata_gate\x00" as *u8, tally)
125 report("nx_bg_shorthand_gate\x00" as *u8, tally)
126 report("nx_wpt_run_gate\x00" as *u8, tally)
127 gw("---\nGREEN=" as *u8); gn(tally[0]); gw(" RED=" as *u8); gn(tally[1]); gw(" total=" as *u8); gn(tally[0]+tally[1]); gw("\n" as *u8)
128 gs_logline(tally, logpath)
129 // Emit the HOUSE anchor. The old verdict=ALL-GREEN / verdict=REGRESSION pair matched NOTHING:
130 // gv_is_pass accepts GREEN|PASS|VALID, so a fully-passing sweep still read RED to every instrument
131 // because of one adjective. Stdout keeps a human line; the anchor is what the readers parse.
132 if tally[1] == 0 { gw("nx_gate_sweep ALL-GREEN verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
133 gw("nx_gate_sweep REGRESSION verdict=RED\n" as *u8); sys_exit(1); return 1
134}