code wiki / (root) / nx_gatefresh.nx

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}