code wiki / (root) / nx_buildpath_lib.nx

nx_buildpath_lib.nx source

↩ module page · 127 lines · 7285 B

1// nx_buildpath_lib.nx -- THE ONE ANSWER TO "WHERE DID THE BUILDER PUT THE ARTIFACT?" 2// 3// WHY IT EXISTS (measured 2026-08-16, coverage_complete=1 / corpus_complete=1 over 23,232 sources): 4// nx_sov_build_run anchors its CWD to buildroot/ and writes _build/<name>.sov.elf. It has not 5// written /tmp since 2026-07-30, when the flock change moved artifacts to a per-target 6// _build/<n>.lock+.s+.sov.elf. Every consumer that wants the freshly-built artifact must know that, 7// and the knowledge had NO CALLABLE HOME -- so each one re-derived it, and they disagree: 8// - nx_stale_check probes /tmp THEN _build. Fixed under seq1554, whose own comment records that 9// probing ONE location "made this guard answer BUILDFAIL for EVERY target, so 10// the instrument that gates a risky promote was DEAD". 11// - nx_drift_watch INLINED nx_stale_check's logic ("the SAME logic as nx_stale_check, INLINED 12// to stay single-nest") and NEVER RECEIVED THAT FIX. It logged BUILDFAIL 13// against all four of its subjects for ~32h while reporting verdict=GREEN. 14// - 13 further organs probe /tmp ONLY, with no fallback at all. 15// *AN INLINED COPY DOES NOT RECEIVE ITS ORIGINAL'S FIXES -- the duplicate-ruler defect with a 16// delay fuse, because the copy looks correct on the day it is written. 17// *A BUG YOU FIX BY REWRITING THE LINE, RATHER THAN BY EXTRACTING THE FIX, IS A BUG YOU WILL 18// WRITE AGAIN. A correction with no callable home fixes exactly one site. 19// 20// WHY THE PROBE ORDER IS WHAT IT IS -- every root is here for a NAMED reason, never for luck: 21// BP_AT_BUILD "_build/" the caller already runs from buildroot/ (nx_drift_watch 22// chdirs there before checking; so does the builder itself). 23// BP_AT_BUILDROOT "buildroot/_build/" the caller runs from the SERVING ROOT -- every gate invoked 24// by /api/gate_run, and the 48 gates repointed on 2026-08-16. 25// BP_AT_TMP "/tmp/" LEGACY, pre-2026-07-30. Kept so an older host still 26// resolves, and reported SEPARATELY so that resolving only 27// via the legacy root is VISIBLE AS DRIFT instead of silent. 28// 29// THE RETURN IS THE ROOT THAT MATCHED, NOT A BOOLEAN. A caller that can say WHICH root answered can 30// distinguish "found where the builder writes" from "found only in the legacy location" -- and one of 31// those is a warning. A two-state answer collapses them, which is how this class stayed invisible. 32// *AN AXIS THAT CANNOT SAY *WHICH* CANNOT REPORT DRIFT, ONLY PRESENCE. 33// 34// `out` ALWAYS holds a path, including on failure, where it holds the PRIMARY probe -- so a caller 35// reports what it LOOKED FOR rather than printing an empty string. A guard whose failure message 36// names no path is the reason this defect took three lanes to find. 37// 38// NO GUESSED CAP: bp_needed(name) derives the exact bytes required from the longest prefix, the 39// caller's name and the suffix, and bp_artifact REFUSES with BP_OUTCAP_TOO_SMALL rather than 40// truncating. A ceiling that has to be guessed is a defect generator in both directions. 41// 42// PROVEN BY nx_buildpath_gate (7/7, bite-proven with 3 mutants each killing exactly one tooth: 43// drop-primary-probe-restore -> T4, outcap-guard-never-fires -> T2, collapse-legacy-into-success -> 44// T6). Its binding tooth resolves the GATE'S OWN artifact, which the builder wrote in order to run it, 45// so lib-vs-builder disagreement fails loudly instead of becoming a sixteenth stale copy. 46// 47// LAYERING: lives in runtime/ so BOTH runtime/ and _hdl_build/ can import it -- the same rule 48// nx_itoa_lib.nx states for itself. NO main: this is a library and has no binary BY DESIGN. 49// license_tier: ORIGINAL No hw writes (Rule 26). 50import "nx_syscalls.nx" 51 52const BP_OUTCAP_TOO_SMALL: i64 = 0 - 1 53const BP_NOTFOUND: i64 = 0 54const BP_AT_BUILD: i64 = 1 55const BP_AT_BUILDROOT: i64 = 2 56const BP_AT_TMP: i64 = 3 57 58func bp_slen(s: *u8) -> i64 { 59 var n: i64 = 0 60 while s[n] != (0 as u8) { n = n + 1 } 61 return n 62} 63func bp_cat(dst: *u8, off: i64, src: *u8) -> i64 { 64 var o: i64 = off 65 var j: i64 = 0 66 while src[j] != (0 as u8) { dst[o] = src[j]; o = o + 1; j = j + 1 } 67 return o 68} 69// The three roots and the suffix, as named accessors so no caller ever retypes a literal. 70func bp_root_build() -> *u8 { return "_build/" as *u8 } 71func bp_root_buildroot() -> *u8 { return "buildroot/_build/" as *u8 } 72func bp_root_tmp() -> *u8 { return "/tmp/" as *u8 } 73func bp_suffix() -> *u8 { return ".sov.elf" as *u8 } 74 75// Exact bytes bp_artifact needs, DERIVED: longest root + name + suffix + NUL. No guessing, and a 76// caller that sizes from this can never be truncated. 77func bp_needed(name: *u8) -> i64 { 78 var longest: i64 = bp_slen(bp_root_build()) 79 let b: i64 = bp_slen(bp_root_buildroot()) 80 if b > longest { longest = b } 81 let t: i64 = bp_slen(bp_root_tmp()) 82 if t > longest { longest = t } 83 return longest + bp_slen(name) + bp_slen(bp_suffix()) + 1 84} 85 86func bp_exists(path: *u8) -> i64 { 87 let fd: i64 = sys_openat_rd(path) 88 if fd < 0 { return 0 } 89 sys_close(fd) 90 return 1 91} 92 93// Compose <root><name><suffix> into out. Returns the length written (NUL-terminated). 94func bp_compose(root: *u8, name: *u8, out: *u8) -> i64 { 95 var o: i64 = bp_cat(out, 0, root) 96 o = bp_cat(out, o, name) 97 o = bp_cat(out, o, bp_suffix()) 98 out[o] = 0 as u8 99 return o 100} 101 102// THE ONE RESOLVER. Returns BP_AT_* for the root that matched, BP_NOTFOUND if none does, or 103// BP_OUTCAP_TOO_SMALL if the caller's buffer cannot hold the longest probe. 104// On BP_NOTFOUND, out holds the PRIMARY probe (_build/) so the caller can name what it looked for. 105func bp_artifact(name: *u8, out: *u8, outcap: i64) -> i64 { 106 if outcap < bp_needed(name) { return BP_OUTCAP_TOO_SMALL } 107 bp_compose(bp_root_build(), name, out) 108 if bp_exists(out) == 1 { return BP_AT_BUILD } 109 bp_compose(bp_root_buildroot(), name, out) 110 if bp_exists(out) == 1 { return BP_AT_BUILDROOT } 111 bp_compose(bp_root_tmp(), name, out) 112 if bp_exists(out) == 1 { return BP_AT_TMP } 113 // Restore the PRIMARY probe: the failure message should name where the builder WRITES, not the 114 // legacy root that happened to be probed last. 115 bp_compose(bp_root_build(), name, out) 116 return BP_NOTFOUND 117} 118 119// Human-readable reason, so every caller prints the SAME words for the same state and a reader can 120// grep one vocabulary across the fleet instead of N spellings of the same outcome. 121func bp_reason(code: i64) -> *u8 { 122 if code == BP_AT_BUILD { return "found in _build/ (where the builder writes)" as *u8 } 123 if code == BP_AT_BUILDROOT { return "found in buildroot/_build/ (caller ran from the serving root)" as *u8 } 124 if code == BP_AT_TMP { return "found ONLY in legacy /tmp/ -- the builder has not written there since 2026-07-30; this is DRIFT, not a healthy resolve" as *u8 } 125 if code == BP_OUTCAP_TOO_SMALL { return "REFUSED: caller buffer smaller than bp_needed(name) -- refusing to truncate a path" as *u8 } 126 return "NOT FOUND in any known build root" as *u8 127}