nx_gatefresh.nx source
↩ module page · 205 lines · 11416 B
1// nx_gatefresh.nx -- "I just ran that gate and it said GREEN. Can I believe it?" ONE target, cheap, now.
2//
3// THE GAP IT FILLS, and why it is not a fourth copy of anything:
4// nx_stale_check <t> REBUILDS and byte-compares _offc/<t>.elf -- authoritative, but O(one build) and a
5// DIFFERENT subject (the _offc build tree, not the deploy root that gate_run executes).
6// nx_rebuild_plan walks the transitive closure for the WHOLE fleet and names the trigger file -- the
7// right instrument for a census, far too heavy to ask about one gate.
8// nx_gatebuilt_gate censuses every authored gate and reports stale_built, but its list is CAPPED at 25,
9// so the gate you actually care about is usually NOT in the output.
10// None of them answer the per-target question in one call, which is the question an agent has RIGHT AFTER
11// reading a verdict. All the arithmetic is nx_srcfresh (live, gated 10/10) -- this organ is only resolution
12// plus an exit code, so it cannot drift from the predicate the gate proved.
13//
14// WHY IT MATTERS (measured 2026-08-07): /api/gate_run served exit 0 verdict=GREEN pass=23/23 for
15// nx_fsops_gate from a 2026-07-29 binary whose source declared 28 checks, and I reported "no regression" on
16// that GREEN. A gate ELF statically links its libs, so a verdict is a claim about the tree AS OF ITS BUILD.
17// Fleet-wide: 715 gates have a binary and only 176 are current with their own source.
18// A GATE VERDICT IS EVIDENCE ABOUT THE SOURCE ONLY IF THE GATE WAS BUILT FROM IT.
19//
20// SCOPE IS DECLARED IN THE OUTPUT, never implied: OWN-SOURCE mtime only. A gate invalidated by a shared dep
21// reads FRESH here, and mtime proves a source MOVED, not that codegen CHANGED (a comment-only edit moves it
22// and leaves the binary byte-identical). So FRESH is a floor, not a guarantee, and the payload says so.
23// nx_gatefresh <name>
24// -> exit 0 FRESH | 1 STALE | 3 UNBUILT (no <name>.elf) | 4 NO-SOURCE | 2 usage
25// license_tier: ORIGINAL Read-only: it stats two paths and writes nothing. (Rule 26)
26import "nx_srcfresh.nx"
27
28const GF_PATHCAP: i64 = 512
29const GF_RC_STALE: i64 = 1
30const GF_RC_USAGE: i64 = 2
31const GF_RC_UNBUILT: i64 = 3
32const GF_RC_NOSRC: i64 = 4
33const GF_RC_LIB: i64 = 5
34const GF_SCAN: i64 = 262144 // bounded source scan for the entry-point probe
35const GF_DIR_RT: *u8 = "buildroot/runtime/"
36const GF_DIR_HDL: *u8 = "buildroot/runtime/_hdl_build/"
37
38func gf_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
39func gf_putn(v: i64) -> i64 {
40 let b: *u8 = sys_mmap(32)
41 let t: *u8 = sys_mmap(32)
42 var m: i64 = v
43 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
44 var k: i64 = 0
45 if m == 0 { t[0] = 48 as u8; k = 1 }
46 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
47 var i: i64 = 0
48 while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
49 sys_write(1, b, k)
50 return 0
51}
52func gf_cat(d: *u8, at: i64, s: *u8) -> i64 {
53 var a: i64 = at
54 var i: i64 = 0
55 while s[i] != (0 as u8) { d[a] = s[i]; a = a + 1; i = i + 1 }
56 d[a] = 0 as u8
57 return a
58}
59
60// needle-at-offset, so the entry-point probe needs no deep if-nesting (5 nested ifs on one line is a
61// documented NishiLang build failure with an empty diagnostic).
62func gf_at(buf: *u8, i: i64, n: i64, needle: *u8) -> i64 {
63 var k: i64 = 0
64 while needle[k] != (0 as u8) {
65 if i + k >= n { return 0 }
66 if buf[i + k] != needle[k] { return 0 }
67 k = k + 1
68 }
69 return 1
70}
71// Does the source declare an entry point? A LIB (no main) can NEVER have a binary of its own -- nxasm
72// answers "UNDEFINED label: main", rc=102 -- so reporting UNBUILT for one is a FALSE ALARM, and I proved
73// the cost on myself within minutes of shipping this organ: it told me nx_janitor_dupname was UNBUILT, I
74// ran /api/build on it, and the compiler replied "rc=102 = USUALLY a no-main LIBRARY built standalone".
75// THE BUILD SYSTEM ALREADY KNEW THE DISTINCTION AND MY INSTRUMENT DID NOT ASK -- so it sent me to do a
76// build that could not succeed. LAW: BEFORE REPORTING SOMETHING MISSING, CHECK WHETHER IT WAS EVER
77// SUPPOSED TO EXIST.
78func gf_has_main(path: *u8) -> i64 {
79 let fd: i64 = sys_openat_rd(path)
80 if fd < 0 { return 0 - 1 }
81 sys_close(fd)
82 // DI12 (2026-09-06): DERIVED, not capped. A `func main` sitting past the first GF_SCAN bytes read as "no main",
83 // i.e. a PROGRAM misread as a library and never reported UNBUILT -- the silent-prefix defect this organ's own
84 // header warns about, one paragraph above. sys_read_file sizes its buffer from the file and cannot short-read.
85 let ln: *i64 = sys_mmap(16) as *i64
86 let buf: *u8 = sys_read_file(path, ln)
87 if (buf as i64) == 0 { return 0 }
88 let n: i64 = ln[0]
89 if n <= 0 { return 0 }
90 var i: i64 = 0
91 while i < n {
92 if gf_at(buf, i, n, "func main" as *u8) == 1 { return 1 }
93 i = i + 1
94 }
95 return 0
96}
97
98func main(argc: i64, argv: *i64) -> i64 {
99 if argc < 2 {
100 gf_puts("usage: nx_gatefresh <name> (is <name>.elf built from its own <name>.nx?)\n" as *u8)
101 gf_puts(" exit 0 FRESH | 1 STALE | 3 UNBUILT | 4 NO-SOURCE. Own-source mtime only -- see the scope line.\n" as *u8)
102 return GF_RC_USAGE
103 }
104 let nm: *u8 = argv[1] as *u8
105 let elf: *u8 = sys_mmap(GF_PATHCAP)
106 var eo: i64 = gf_cat(elf, 0, nm)
107 eo = gf_cat(elf, eo, ".elf" as *u8)
108
109 // RESOLUTION ORDER IS THE COMPILER'S, NOT MINE -- AND I SHIPPED IT BACKWARDS FIRST (2026-08-07).
110 // nx_sov_build_run probes runtime/_hdl_build/ FIRST, so a basename present in BOTH dirs compiles from
111 // _hdl_build and the runtime copy is a SHADOW that never compiles. nx_janitor_dupname.nx:3 documents the
112 // class and found TWO nx_sovereignty_audit.nx shadowing each other. Probing runtime/ first reported
113 // freshness for a file the build never reads -- the exact instrument-subject mismatch this organ exists
114 // to expose, committed inside the organ. Found by reading the build path, not by a failing test.
115 // LAW: MATCH THE RESOLVER YOU ARE REPORTING ON, OR YOU ARE REPORTING ON A DIFFERENT FILE.
116 let src: *u8 = sys_mmap(GF_PATHCAP)
117 let alt: *u8 = sys_mmap(GF_PATHCAP)
118 var so: i64 = gf_cat(src, 0, GF_DIR_HDL)
119 so = gf_cat(src, so, nm)
120 so = gf_cat(src, so, ".nx" as *u8)
121 var ao: i64 = gf_cat(alt, 0, GF_DIR_RT)
122 ao = gf_cat(alt, ao, nm)
123 ao = gf_cat(alt, ao, ".nx" as *u8)
124 var shadowed: i64 = 0
125 if sf_mtime_ns(src) < 0 {
126 so = gf_cat(src, 0, GF_DIR_RT)
127 so = gf_cat(src, so, nm)
128 so = gf_cat(src, so, ".nx" as *u8)
129 }
130 else { if sf_mtime_ns(alt) >= 0 { shadowed = 1 } }
131
132 let em: i64 = sf_mtime_ns(elf)
133 let sm: i64 = sf_mtime_ns(src)
134
135 // FAIL-LOUD, non-blocking, and BEFORE the verdict: the verdict speaks about ONE file, so a caller has no
136 // other way to learn a second candidate exists. A silent pick is how you debug the wrong source for an hour.
137 if shadowed == 1 {
138 gf_puts("NX-GATEFRESH SHADOWED " as *u8); gf_puts(nm)
139 gf_puts(" exists in BOTH source dirs. The build compiles " as *u8); gf_puts(src)
140 gf_puts(" and " as *u8); gf_puts(alt)
141 gf_puts(" NEVER compiles (nx_sov_build_run probes _hdl_build FIRST). FIX: reconcile to ONE dir.\n" as *u8)
142 }
143
144 gf_puts("NX-GATEFRESH " as *u8); gf_puts(nm)
145 // NO-SOURCE is reported BEFORE unbuilt: with no source there is nothing to be stale against, and calling
146 // that STALE would be a false alarm on every organ whose source lives outside the two scanned dirs.
147 if sm < 0 {
148 gf_puts(" verdict=NO-SOURCE elf=" as *u8); gf_puts(elf)
149 gf_puts(" (no <name>.nx in buildroot/runtime or _hdl_build -- nothing to compare, NOT a staleness claim)\n" as *u8)
150 return GF_RC_NOSRC
151 }
152 if em < 0 {
153 // LIB before UNBUILT: a library is not a missing binary, it is a file that never has one.
154 if gf_has_main(src) == 0 {
155 gf_puts(" verdict=LIB src=" as *u8); gf_puts(src)
156 gf_puts(" (no `func main` -- a library has NO binary of its own BY DESIGN; building it standalone\n" as *u8)
157 gf_puts(" fails nxasm rc=102 \"UNDEFINED label: main\". A lib freshness is a property of its\n" as *u8)
158 gf_puts(" IMPORTERS: rebuild those, and use nx_rebuild_plan to enumerate which ones.)\n" as *u8)
159 return GF_RC_LIB
160 }
161 gf_puts(" verdict=UNBUILT src=" as *u8); gf_puts(src)
162 gf_puts(" (source exists and declares main, " as *u8); gf_puts(elf)
163 gf_puts(" does not -- /api/gate_run would answer exit 127 NOT-FOUND. FIX: /api/build then /api/promote.)\n" as *u8)
164 return GF_RC_UNBUILT
165 }
166 // PROVENANCE FIRST when a receipt exists. Exact content identity beats a clock, and it closes the
167 // FALSE-FRESH direction that bit this organ hours after it shipped: a concurrent promote resets the
168 // artifact mtime and thereby erases the evidence of an unadopted source edit. An ABSENT or MALFORMED
169 // sidecar returns -1 = UNKNOWN and we fall through to mtime -- UNKNOWN is never treated as fresh.
170 let prov: *u8 = sys_mmap(GF_PATHCAP)
171 var po: i64 = gf_cat(prov, 0, nm)
172 po = gf_cat(prov, po, ".provenance" as *u8)
173 let pv: i64 = sf_prov_stale(prov, src)
174 if pv == 1 {
175 gf_puts(" verdict=STALE method=provenance elf=" as *u8); gf_puts(elf)
176 gf_puts(" src=" as *u8); gf_puts(src)
177 gf_puts("\n THE DIGEST RECORDED AT BUILD TIME DOES NOT MATCH THE SOURCE ON DISK. This is EXACT, not a" as *u8)
178 gf_puts(" clock guess, so no promote by any seat can fake it. FIX: /api/build target=" as *u8)
179 gf_puts(nm); gf_puts(" then /api/promote with expect_sha256.\n" as *u8)
180 return GF_RC_STALE
181 }
182 if pv == 0 {
183 gf_puts(" verdict=FRESH method=provenance elf=" as *u8); gf_puts(elf)
184 gf_puts(" src=" as *u8); gf_puts(src)
185 gf_puts("\n EXACT: the digest recorded at build time equals the source digest now -- no clock involved." as *u8)
186 gf_puts(" Import-closure staleness is still nx_rebuild_plan job (it names the trigger file).\n" as *u8)
187 return 0
188 }
189 let lag: i64 = sf_lag_sec(elf, src)
190 if sf_src_stale(elf, src) == 1 {
191 gf_puts(" verdict=STALE lag_sec=" as *u8); gf_putn(lag)
192 gf_puts(" elf=" as *u8); gf_puts(elf)
193 gf_puts(" src=" as *u8); gf_puts(src)
194 gf_puts("\n THE BINARY PREDATES ITS OWN SOURCE: a GREEN from it is a claim about the tree as of its\n" as *u8)
195 gf_puts(" build, not the tree on disk. FIX: /api/build target=" as *u8); gf_puts(nm)
196 gf_puts(" then /api/promote with expect_sha256.\n" as *u8)
197 gf_puts(" scope: own-source mtime only; a shared-dep invalidation is nx_rebuild_plan (it names the trigger).\n" as *u8)
198 return GF_RC_STALE
199 }
200 gf_puts(" verdict=FRESH lag_sec=0 elf=" as *u8); gf_puts(elf)
201 gf_puts(" src=" as *u8); gf_puts(src)
202 gf_puts("\n scope: FRESH IS A FLOOR, NOT A GUARANTEE -- own-source mtime only. It does NOT walk the import\n" as *u8)
203 gf_puts(" closure, so a gate invalidated by a shared dep still reads FRESH here (use nx_rebuild_plan), and\n" as *u8)
204 gf_puts(" mtime proves a source MOVED, not that codegen CHANGED (use nx_stale_check to confirm by bytes).\n" as *u8)
205 return 0
206}