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}