code wiki / (root) / nx_drift_watch.nx

nx_drift_watch.nx source

↩ module page · 229 lines · 16689 B

1// nx_drift_watch.nx -- STANDING deploy-drift guardrail (zero-Claude, cron-run; arch-board 06:20 precedent). 2// The F-004 stale serve AND the compare-generator drifts both accumulated SILENTLY -- nothing watched for 3// "deployed binary lags its source" (esp. after a compiler/toolchain change, which staleness-flags EVERY 4// pre-fix binary). This daily check reads a data-driven watch-list and, per target, rebuilds it --build-only 5// and byte-compares to the deployed buildroot/_offc/<t>.elf (the SAME logic as nx_stale_check, INLINED to 6// stay single-nest -- a double-nested fork of nx_stale_check-of-nx_sov_build_run fails builds). Stub-trap 7// guarded (fresh<<deployed => AMBIGUOUS, not STALE). Logs verdicts + a tally to knowledge/status/drift_watch.log. 8// nx_drift_watch [listpath] [broot] list default knowledge/registry/drift_watch.list ; broot default buildroot 9// Exit 0 always (a guardrail never fails the fleet); the LOG carries the verdict. license_tier: ORIGINAL 10import "nx_tool_run.nx" 11// The build-artifact location is NOT this organ's knowledge to hold. It held an INLINED copy of 12// nx_stale_check's probe, missed that copy's seq1554 fix, and reported BUILDFAIL against all four of 13// its subjects for ~32h while printing GREEN. The resolver now has ONE callable home, proven against 14// the real builder on every run by nx_buildpath_gate (7/7, 3 mutants, each killing one tooth). 15import "nx_buildpath_lib.nx" 16const K_MAGIC_4096: i64 = 4096 17const K_MAGIC_65536: i64 = 65536 18const K_MAGIC_65535: i64 = 65535 19const K_MAGIC_8388608: i64 = 8388608 20 21func 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 } 22func wn(fd: i64, v: i64) -> i64 { var m: i64=v; if m<0{w(fd,"-" as *u8);m=0-m} let t: *u8=sys_mmap(28); 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} let o: *u8=sys_mmap(28); var i: i64=0; while i<k{o[i]=t[k-1-i];i=i+1} sys_write(fd,o,k); return 0 } 23func d_cat(dst: *u8, off: i64, src: *u8) -> i64 { var o: i64=off; var j: i64=0; while src[j]!=(0 as u8){dst[o]=src[j];o=o+1;j=j+1} return o } 24func d_read(path: *u8, buf: *u8, cap: i64) -> i64 { 25 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 - 1 } 26 var n: i64 = 0; var go: i64 = 1 27 while go == 1 { let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } } 28 sys_close(fd); return n 29} 30// returns 1 CURRENT, 2 STALE, 3 AMBIGUOUS, 4 BUILDFAIL, 5 NOELF, 6 NOSOURCE. writes fresh/dep sizes to sz[0]/sz[1]. 31func d_check(tgt: *u8, out: *u8, ocap: i64, ol: *i64, fb: *u8, db: *u8, fcap: i64, sz: *i64) -> i64 { 32 // source present? 33 let s1: *u8 = sys_mmap(256); var o1: i64 = d_cat(s1, 0, "runtime/" as *u8); o1 = d_cat(s1, o1, tgt); o1 = d_cat(s1, o1, ".nx" as *u8); s1[o1] = 0 as u8 34 let s2: *u8 = sys_mmap(256); var o2: i64 = d_cat(s2, 0, "runtime/_hdl_build/" as *u8); o2 = d_cat(s2, o2, tgt); o2 = d_cat(s2, o2, ".nx" as *u8); s2[o2] = 0 as u8 35 let f1: i64 = sys_openat_rd(s1); var have: i64 = 0; if f1 >= 0 { sys_close(f1); have = 1 } 36 if have == 0 { let f2: i64 = sys_openat_rd(s2); if f2 >= 0 { sys_close(f2); have = 1 } } 37 if have == 0 { return 6 } 38 // build --build-only (single nested fork -- the reliable pattern) 39 let av: *i64 = sys_mmap(64) as *i64 40 av[0] = "_offc/nx_sov_build_run.elf" as i64; av[1] = tgt as i64; av[2] = "--build-only" as i64; av[3] = 0 41 let rc: i64 = tr_run_capture("_offc/nx_sov_build_run.elf" as *u8, av, out, ocap, ol) 42 if rc != 0 { return 4 } 43 // ---- 2026-08-16: THE FRESH ARTIFACT IS AT _build/, NOT /tmp/ ---- 44 // nx_sov_build_run anchors its CWD to buildroot/ and writes _build/<t>.sov.elf. It has not written 45 // /tmp/ since 2026-07-30, when the flock change moved artifacts to a per-target 46 // _build/<n>.lock+.s+.sov.elf. We already chdir'd to broot, so the correct relative form is _build/ 47 // -- exactly like the `dep` line below already uses _offc/. 48 // CONSEQUENCE, MEASURED: the build SUCCEEDED (rc==0) and this read returned -1, which falls straight 49 // into `return 4` = BUILDFAIL. So the organ blamed EVERY SUBJECT for its own wrong path: all four 50 // watch-list targets logged BUILDFAIL for ~32h while each builds clean and byte-reproduces its 51 // deployed size (nx_swcompare_sota 56210==56210, nx_maturity_board 48080==48080, verified). 52 // * A MONITOR THAT CANNOT FIND THE ARTIFACT REPORTS ITS OWN INCAPACITY AS THE SUBJECT'S DEFECT. 53 // * A FIX THAT LIVES IN ONE ORGAN AND NOT ITS SIBLING IS HALF A FIX: 48 gates were repointed off 54 // /tmp/ to the build root earlier THIS SAME DAY and this organ was missed. 55 // ONE CALL, ONE ANSWER. Was a local `_build/` concatenation -- correct, but a SECOND copy of a 56 // fact this organ already got wrong once by copying. bp_artifact probes the roots the builder 57 // actually uses, in order, sized by bp_needed so nothing is guessed, and leaves the PRIMARY probe 58 // in `fresh` when nothing resolves -- so the BUILDFAIL below now names where the builder WRITES 59 // rather than wherever the last probe happened to land. 60 let fcapn: i64 = bp_needed(tgt) 61 let fresh: *u8 = sys_mmap(fcapn) 62 sz[2] = bp_artifact(tgt, fresh, fcapn) 63 let dep: *u8 = sys_mmap(256); var od: i64 = d_cat(dep, 0, "_offc/" as *u8); od = d_cat(dep, od, tgt); od = d_cat(dep, od, ".elf" as *u8); dep[od] = 0 as u8 64 let fn: i64 = d_read(fresh, fb, fcap) 65 let dn: i64 = d_read(dep, db, fcap) 66 sz[0] = fn; sz[1] = dn 67 if fn < 0 { return 4 } 68 if dn < 0 { return 5 } 69 if fn == dn { var i: i64 = 0; var same: i64 = 1; while i < fn { if fb[i] != db[i] { same = 0; i = fn } else { i = i + 1 } } if same == 1 { return 1 } } 70 if fn * 2 < dn { if fn < K_MAGIC_4096 { return 3 } } 71 return 2 72} 73 74func main(argc: i64, argv: *i64) -> i64 { 75 var listp: *u8 = "knowledge/registry/drift_watch.list" as *u8 76 if argc >= 2 { listp = argv[1] as *u8 } 77 var broot: *u8 = "buildroot" as *u8 78 if argc >= 3 { broot = argv[2] as *u8 } 79 80 // ---- 2026-09-03: A CONSTANT LOG PATH LETS ANY TRIAL RUN FORGE THE PRODUCTION HEARTBEAT ---- 81 // The journal path was a hardcoded literal, so a fixture run with a custom watch-list appended to 82 // the SAME file the cron beat writes. The estate has measured this exact class before (GRR_STAMP): 83 // A LIVENESS SIGNAL ANY TRIAL RUN CAN WRITE PROVES THE ORGAN EXECUTED SOMEWHERE, NEVER THAT THE 84 // WATCHED WORK RAN -- and it fails in the flattering direction, so nobody investigates. It also made 85 // a NON-FORGING GATE FIXTURE IMPOSSIBLE: any bite of this organ had to write the production record. 86 // argv[3] is OPTIONAL and defaults to the production path, so every existing caller is unchanged. 87 var logp: *u8 = "knowledge/status/drift_watch.log" as *u8 88 if argc >= 4 { logp = argv[3] as *u8 } 89 90 let lst: *u8 = sys_mmap(K_MAGIC_65536) 91 let ln: i64 = d_read(listp, lst, K_MAGIC_65535) 92 // resolve the log path BEFORE chdir (it is relative to the run cwd), then chdir into the build tree. 93 let lfd: i64 = sys_openat_append(logp, 0x1a4) 94 // ---- 2026-09-03: A MISSING WATCH-LIST USED TO LEAVE NO RECORD AT ALL ---- 95 // This branch printed one line to stdout and exited 0 WITHOUT writing the journal, so a watch whose 96 // list vanished was byte-indistinguishable, IN THE ARTIFACT THE CRON PLANE READS, from a watch that 97 // never ran. AN AXIS THAT CANNOT SEE MUST ABSTAIN OUT LOUD; abstaining in silence is how a detector 98 // goes dark with no alarm anywhere. Same for a failed chdir below. 99 if ln <= 0 { 100 w(1, "nx_drift_watch: watch-list missing\n" as *u8) 101 if lfd >= 0 { 102 w(lfd, "DRIFT-WATCH epoch=" as *u8); wn(lfd, sys_now_realtime_sec()) 103 w(lfd, " checked=0 current=0 STALE=0 ambiguous=0 other=0 measured=0/0 verdict=UNMEASURED-NOLIST\n" as *u8) 104 w(lfd, "DRIFT-WATCH measured=0/0 verdict=RED\n" as *u8) 105 sys_close(lfd) 106 } 107 sys_exit(0); return 0 108 } 109 if sys_chdir(broot) != 0 { 110 w(1, "nx_drift_watch: cannot chdir build tree\n" as *u8) 111 if lfd >= 0 { 112 w(lfd, "DRIFT-WATCH epoch=" as *u8); wn(lfd, sys_now_realtime_sec()) 113 w(lfd, " checked=0 current=0 STALE=0 ambiguous=0 other=0 measured=0/0 verdict=UNMEASURED-NOCHDIR\n" as *u8) 114 w(lfd, "DRIFT-WATCH measured=0/0 verdict=RED\n" as *u8) 115 sys_close(lfd) 116 } 117 sys_exit(0); return 0 118 } 119 120 let ocap: i64 = K_MAGIC_65536 121 let out: *u8 = sys_mmap(ocap) 122 let ol: *i64 = sys_mmap(16) as *i64 123 let fcap: i64 = K_MAGIC_8388608 124 let fb: *u8 = sys_mmap(fcap) 125 let db: *u8 = sys_mmap(fcap) 126 // 3 slots: [0]=fresh bytes [1]=deployed bytes [2]=WHICH build root answered (BP_AT_*), so a 127 // target that resolves only via the legacy /tmp root is REPORTED as drift instead of passing 128 // silently. The resolver returns which root on purpose; discarding it would throw away the one 129 // signal that distinguishes a healthy resolve from a fossil one. 130 let sz: *i64 = sys_mmap(24) as *i64 131 let tgt: *u8 = sys_mmap(128) 132 133 var nstale: i64=0; var namb: i64=0; var ncur: i64=0; var noth: i64=0; var ntot: i64=0 134 var p: i64 = 0 135 while p < ln { 136 var q: i64 = p 137 while q < ln { if lst[q] == (10 as u8) { break } q = q + 1 } 138 var ti: i64 = 0; var s: i64 = p 139 while s < q { let c: i64 = lst[s]; if c == 13 { s = q } else { if ti < 120 { tgt[ti] = lst[s] as u8; ti = ti + 1 } s = s + 1 } } 140 tgt[ti] = 0 as u8 141 p = q + 1 142 if ti == 0 { } else { if tgt[0] == (35 as u8) { } else { 143 ntot = ntot + 1 144 let v: i64 = d_check(tgt, out, ocap, ol, fb, db, fcap, sz) 145 // REPORTED FOR ANY TARGET, not only failing ones: a target can be perfectly CURRENT and 146 // still have been resolved from the LEGACY root, and that is drift the healthy verdict 147 // would otherwise hide entirely. bp_reason gives every caller in the fleet the same words 148 // for the same state, so a reader greps one vocabulary instead of N spellings. 149 if sz[2] == BP_AT_TMP { 150 if lfd >= 0 { 151 w(lfd, " LEGACY-ROOT " as *u8); w(lfd, tgt) 152 w(lfd, " -- " as *u8); w(lfd, bp_reason(sz[2])); w(lfd, "\n" as *u8) 153 } 154 } 155 if v == 1 { ncur = ncur + 1 } else { 156 if v == 2 { nstale = nstale + 1 } else { if v == 3 { namb = namb + 1 } else { noth = noth + 1 } } 157 if lfd >= 0 { 158 w(lfd, " " as *u8) 159 if v == 2 { w(lfd, "STALE " as *u8) } else { if v == 3 { w(lfd, "AMBIGUOUS " as *u8) } else { if v == 4 { w(lfd, "BUILDFAIL " as *u8) } else { if v == 5 { w(lfd, "NOELF " as *u8) } else { w(lfd, "NOSOURCE " as *u8) } } } } 160 w(lfd, tgt); w(lfd, " (fresh=" as *u8); wn(lfd, sz[0]); w(lfd, " deployed=" as *u8); wn(lfd, sz[1]); w(lfd, ")\n" as *u8) 161 } 162 } 163 } } 164 } 165 // ---- 2026-09-03: THE VERDICT MUST BE CONDITIONAL ON HAVING MEASURED SOMETHING ---- 166 // MEASURED = the comparisons that ACTUALLY COMPLETED. `noth` (BUILDFAIL/NOELF/NOSOURCE) counts the 167 // subjects where the comparison NEVER RAN, so it is evidence of nothing and is not part of the 168 // denominator of evidence. The 2026-08-16 fix already made `other` refuse to acquit -- and left the 169 // EMPTY SET wide open: with a watch-list that is absent, empty, or all-comment, the loop body never 170 // executes, EVERY counter stays 0, and the ladder fell straight through to CLEAN/GREEN. 171 // * A TOOTH THAT PASSES ON THE EMPTY SET IS NOT A TOOTH -- put the count in the CONDITION, not 172 // merely in the printout beside it. 173 // * THE DENOMINATOR TRAVELS WITH THE VERDICT: `measured=0/0 verdict=GREEN` is self-refuting on 174 // sight, while a bare `verdict=GREEN` is indistinguishable from a healthy 4/4 run. 175 // nabst is a SEPARATE conjunct rather than folded into noth, so the printed partition still 176 // reconciles (current + STALE + ambiguous + other = checked) and no existing counter changes meaning. 177 var nmeas: i64 = ncur + nstale + namb 178 var nabst: i64 = 0 179 if nmeas <= 0 { nabst = 1 } 180 if lfd >= 0 { 181 w(lfd, "DRIFT-WATCH epoch=" as *u8); wn(lfd, sys_now_realtime_sec()) 182 w(lfd, " checked=" as *u8); wn(lfd, ntot); w(lfd, " current=" as *u8); wn(lfd, ncur) 183 w(lfd, " STALE=" as *u8); wn(lfd, nstale); w(lfd, " ambiguous=" as *u8); wn(lfd, namb); w(lfd, " other=" as *u8); wn(lfd, noth) 184 // THE DENOMINATOR RIDES ON THE VERDICT, NEVER BESIDE IT. `measured=<completed>/<subjects>` makes 185 // an empty or blind run self-refuting to a human AND greppable to a machine: measured=0/0 and 186 // measured=0/4 are now distinguishable from measured=4/4, which a bare verdict word never was. 187 w(lfd, " measured=" as *u8); wn(lfd, nmeas); w(lfd, "/" as *u8); wn(lfd, ntot) 188 // A run that could not EXAMINE a subject holds no evidence about it, and NO EVIDENCE IS NOT 189 // CLEAN. noth counts BUILDFAIL/NOELF/NOSOURCE -- every state where the comparison never 190 // happened. It was printed per-target and then DROPPED from both verdicts below. 191 if nstale > 0 { w(lfd, " verdict=DRIFT-DETECTED\n" as *u8) } else { 192 // UNMEASURED is a NEW word for a case that previously printed CLEAN: nothing was even 193 // ATTEMPTED (list absent, empty, or all-comment). UNEXAMINED is left EXACTLY as it was -- 194 // attempts were made and every one failed. Two different blindnesses with two different 195 // remedies (fix the LIST vs fix the BUILD); collapsing them would re-create the 196 // bucket-named-for-how-the-reader-failed defect this organ was already bitten by. 197 if noth > 0 { w(lfd, " verdict=UNEXAMINED\n" as *u8) } else { if nmeas <= 0 { w(lfd, " verdict=UNMEASURED\n" as *u8) } else { w(lfd, " verdict=CLEAN\n" as *u8) } } 198 } 199 // ---- CANONICAL ANCHOR 2026-08-07 --------------------------------------------------------- 200 // CLEAN and DRIFT-DETECTED are DOMAIN words; nx_gate_rollup classifies neither, so this gate 201 // sat in `unreadable_dialect` -- a bucket named for HOW THE READER FAILED rather than for what 202 // the subject IS, which put this healthy gate in the same bucket as a LIVE regression nobody 203 // was triaging. The rollup prescribes the fix and is right: migrate the EMITTER, never widen 204 // the reader vocabulary -- widening is how the estate got two different spellings of success. 205 // The domain row above is kept verbatim; gv_last_line anchors BY POSITION so this goes LAST. 206 // ---- 2026-08-16: THE VERDICT MUST NOT ACQUIT WHAT IT COULD NOT EXAMINE ---- 207 // This keyed ONLY on nstale, so a run in which every subject was unexaminable printed 208 // `current=0 STALE=0 other=4 verdict=CLEAN` and then `verdict=GREEN`. MEASURED: it did exactly 209 // that across every epoch for ~32h while all four watch-list targets logged BUILDFAIL from the 210 // /tmp/ path defect fixed above -- the monitor was GREEN *because* it was blind. 211 // * A VERDICT THAT KEYS ONLY ON THE FAILURE IT CAN NAME ACQUITS EVERY FAILURE IT CANNOT. 212 // * AN AXIS THAT CANNOT SEE MUST ABSTAIN, NOT ACQUIT. 213 // The canonical word stays GREEN|RED deliberately: nx_gate_rollup reads only those two, and 214 // inventing a third spelling HERE is exactly what put this gate in `unreadable_dialect` before. 215 // The richer UNEXAMINED word lives in the DOMAIN row above, which the rollup does not parse. 216 // NOT PERMANENTLY RED BY CONSTRUCTION: with the path fixed, each watch-list target rebuilds and 217 // byte-matches its deployed elf (nx_swcompare_sota 56210==56210, nx_maturity_board 48080==48080, 218 // both verified), so noth==0 in the healthy case and this conjunct fires only on real blindness. 219 if nstale + noth + nabst > 0 { w(lfd, "DRIFT-WATCH measured=" as *u8); wn(lfd, nmeas); w(lfd, "/" as *u8); wn(lfd, ntot); w(lfd, " verdict=RED\ 220" as *u8) } 221 if nstale + noth + nabst <= 0 { w(lfd, "DRIFT-WATCH measured=" as *u8); wn(lfd, nmeas); w(lfd, "/" as *u8); wn(lfd, ntot); w(lfd, " verdict=GREEN\ 222" as *u8) } 223 sys_close(lfd) 224 } 225 w(1, "nx_drift_watch: checked=" as *u8) 226; wn(1, ntot); w(1, " current=" as *u8); wn(1, ncur); w(1, " STALE=" as *u8); wn(1, nstale); w(1, " ambiguous=" as *u8); wn(1, namb); w(1, " other=" as *u8); wn(1, noth); w(1, "\n" as *u8) 227 sys_exit(0) 228 return 0 229}