nx_board_idref_gate.nx source
↩ module page · 361 lines · 18388 B
1// nx_board_idref_gate.nx -- THE COMPARE BOARD'S REFERENTIAL INTEGRITY REFEREE (2026-09-04).
2//
3// WHY THIS EXISTS, AND WHY IT IS A MECHANISM RATHER THAN ANOTHER LAW. On 2026-09-03 a seat appended a rung
4// as LN37 and four log rows as LN36 and BOTH ids were already taken by another lane the same day. It
5// journaled the finding, renumbered to LN41/LN42 after grepping for free ids, and wrote the durable remedy
6// in its own words: "the durable fix is a uniqueness tooth in the compare referee rather than a habit."
7// By 2026-09-04 LN41 AND LN42 had each collided AGAIN -- the renumbering itself landed on ids a third lane
8// had taken while it worked. That is the whole argument for this file: GREP-BEFORE-APPEND IS A HABIT, AND
9// A HABIT LOSES TO CONCURRENCY EVERY TIME. Only something in the path can hold this invariant.
10//
11// WHAT IT JUDGES. A .plan is a board, and a board's ids are its addresses. Three ways an address goes bad:
12// DUPID two rung rows share an id, so the ranker, a watch flip and a done-rule all attach to whichever
13// the reader matches first. Measured live: lang carried one LANDED and one OPEN rung under LN42,
14// which is the dangerous shape -- the board can report the open work as finished.
15// DANGLOG a log row is journaled against a rung id no rung row declares, so the worked plan a crashed
16// session is supposed to inherit points at nothing.
17// DANGMS a milestone lists a member rung that does not exist, so its cumulative estimate is over a set
18// the board cannot enumerate.
19//
20// IT RATCHETS RATHER THAN DEMANDING ZERO, AND THAT IS DELIBERATE. Real offenders already exist that belong
21// to other lanes, and a detector that is permanently RED is one everyone learns to ignore. The bar is
22// knowledge/status/board_idref.baseline; the gate goes RED only when the count RISES, and it TIGHTENS the
23// baseline when the count falls so a fix cannot be undone silently. EVERY OFFENDER IS NAMED IN THE OUTPUT
24// whatever the verdict, because a count without a worklist is not actionable.
25//
26// COMPOSES nx_rowparse_lib for pipe-delimited column extraction rather than hand-rolling a fourth copy of
27// that arithmetic, and gk_* for directory and file access.
28// license_tier: ORIGINAL. Reads the compare trees, writes only its own baseline. No hw writes (Rule 26).
29import "nx_syscalls.nx"
30import "nx_gate_verdict.nx"
31import "nx_gatekit_lib.nx"
32import "nx_rowparse_lib.nx"
33
34// A .plan is prose-heavy: the largest measured is ~131 KB, so 4 MB is thirty times the largest real board.
35// A file over this is REFUSED BY NAME and forces coverage_complete to 0 rather than being read in part --
36// a partial board would under-report both duplicates and dangling references.
37const BI_FILECAP: i64 = 4194304
38const BI_NAMES: i64 = 262144
39const BI_NAMESTRIDE: i64 = 256
40const BI_MAXFILES: i64 = 512
41const BI_IDCAP: i64 = 32
42const BI_MAXIDS: i64 = 1024
43const BI_PATH: i64 = 1024
44const BI_OUT: i64 = 262144
45const BI_SPAN: i64 = 8
46const BI_BASEMAX: i64 = 64
47const BI_DIR_PRIMARY: *u8 = "buildroot/knowledge/compare/"
48const BI_DIR_SECONDARY: *u8 = "knowledge/compare/"
49const BI_BASELINE: *u8 = "knowledge/status/board_idref.baseline"
50const BI_FLD_KIND: i64 = 0
51const BI_FLD_RUNGID: i64 = 1
52const BI_FLD_LOGID: i64 = 2
53const BI_FLD_MSMEMBERS: i64 = 4
54const BI_COMMA: i64 = 44
55const BI_SPACE: i64 = 32
56const BI_CR: i64 = 13
57
58// store id `s` (len l) into slot table at index n; returns new n (silently refuses to overflow, and the
59// caller checks the cap so a full table becomes a NAMED refusal rather than a quiet undercount)
60func bi_put_id(tab: *u8, n: i64, b: *u8, s: i64, l: i64) -> i64 {
61 if n >= BI_MAXIDS { return n }
62 var len: i64 = l
63 if len > BI_IDCAP - 1 { len = BI_IDCAP - 1 }
64 var i: i64 = 0
65 while i < len { tab[n * BI_IDCAP + i] = b[s + i]; i = i + 1 }
66 tab[n * BI_IDCAP + len] = 0 as u8
67 return n + 1
68}
69func bi_id_at(tab: *u8, i: i64) -> *u8 { return (tab as i64 + i * BI_IDCAP) as *u8 }
70// TRIM A TRAILING CR OR SPACE FROM AN EXTRACTED ID. The compare tree has MIXED LINE ENDINGS -- measured
71// 2026-09-04, failmodes.plan is CRLF while performance.plan and enginelab.plan are LF -- and rp_le stops at
72// the newline, so on a CRLF board the carriage return rides along on whatever field ends the line. That made
73// the LAST member of every milestone on a CRLF board unresolvable and reported three declared rungs as
74// missing. The tell was that the offenders were FM5, FM8 and FM9: the last member of M1, M2 and M3 exactly.
75func bi_trim(b: *u8) -> i64 {
76 var n: i64 = 0
77 while b[n] != (0 as u8) { n = n + 1 }
78 while n > 0 {
79 let ch: i64 = b[n - 1] as i64
80 if ch == BI_CR { n = n - 1 } else {
81 if ch == BI_SPACE { n = n - 1 } else { b[n] = 0 as u8; return n }
82 }
83 }
84 b[0] = 0 as u8
85 return 0
86}
87// how many times does the id at slot `k` appear in the first `n` slots?
88func bi_count(tab: *u8, n: i64, k: i64) -> i64 {
89 var c: i64 = 0
90 var i: i64 = 0
91 while i < n {
92 if gk_streq(bi_id_at(tab, i), bi_id_at(tab, k)) == 1 { c = c + 1 }
93 i = i + 1
94 }
95 return c
96}
97func bi_has_id(tab: *u8, n: i64, want: *u8) -> i64 {
98 var i: i64 = 0
99 while i < n {
100 if gk_streq(bi_id_at(tab, i), want) == 1 { return 1 }
101 i = i + 1
102 }
103 return 0
104}
105// is this the FIRST slot holding this id? Used so a duplicate is reported ONCE per id, not once per copy.
106func bi_first(tab: *u8, k: i64) -> i64 {
107 var i: i64 = 0
108 while i < k {
109 if gk_streq(bi_id_at(tab, i), bi_id_at(tab, k)) == 1 { return 0 }
110 i = i + 1
111 }
112 return 1
113}
114
115func main(argc: i64, argv: **u8) -> i64 {
116 let ctr: *i64 = gv_ctr()
117 gv_head("NX-BOARD-IDREF-GATE" as *u8)
118
119 let names: *u8 = sys_mmap(BI_NAMES)
120 let fbuf: *u8 = sys_mmap(BI_FILECAP)
121 let rungs: *u8 = sys_mmap(BI_IDCAP * BI_MAXIDS)
122 let path: *u8 = sys_mmap(BI_PATH)
123 let idbuf: *u8 = sys_mmap(BI_IDCAP)
124 let out: *u8 = sys_mmap(BI_OUT)
125 let c: *i64 = sys_mmap(BI_SPAN * 2) as *i64
126 var o: i64 = 0
127
128 // BOTH compare trees are scanned, as SEPARATE roots rather than as an overlay. gk_dirscan's shadowdir
129 // argument means "skip a basename that also exists there", which is the right rule for resolving WHICH
130 // SOURCE COMPILES and exactly the wrong one here: a board present in both trees is two boards, and each
131 // one's ids must be judged on their own. Measured 2026-08-31: .plan is buildroot-owned and .gates is
132 // knowledge-owned, so neither root may be assumed to be the whole subject.
133 var nf: i64 = gk_dirscan_ext(BI_DIR_PRIMARY, 0 as *u8, names, BI_NAMESTRIDE, BI_MAXFILES, 0, ".plan" as *u8)
134 if nf < 0 { nf = 0 }
135 let nf2: i64 = gk_dirscan_ext(BI_DIR_SECONDARY, 0 as *u8, names, BI_NAMESTRIDE, BI_MAXFILES, nf, ".plan" as *u8)
136 if nf2 > nf { nf = nf2 }
137 var domains: i64 = 0
138 var rungrows: i64 = 0
139 var logrows: i64 = 0
140 var msrows: i64 = 0
141 var dupid: i64 = 0
142 var danglog: i64 = 0
143 var dangms: i64 = 0
144 var unread: i64 = 0
145 var oversize: i64 = 0
146 // HIGH-WATER MARK OF THE PER-FILE TABLE. The saturation control must compare the table against the
147 // largest SINGLE board, never against the fleet total: the table is refilled per board, so testing the
148 // sum makes the control fire as soon as the fleet is bigger than one board's capacity -- which it always
149 // is. Measured 2026-09-04: 1443 fleet rung rows against a 1024-slot per-board table produced exactly that
150 // false RED on the first live run.
151 var maxids: i64 = 0
152
153 var fi: i64 = 0
154 while fi < nf {
155 let nm: *u8 = (names as i64 + fi * BI_NAMESTRIDE) as *u8
156 // .plan only. gk_dirscan yields every entry, so the extension test is what scopes the subject.
157 if gk_ext_is(nm, ".plan" as *u8) == 1 {
158 // gk_dirscan_ext yields the FULL path already; joining a root onto it again would produce a
159 // doubled path that opens nothing and reports every board as unreadable.
160 rp_cstr(path, nm, 0, rp_slen(nm))
161 let sz: i64 = gk_size(path)
162 if sz > BI_FILECAP {
163 oversize = oversize + 1
164 o = rp_put(out, o, "OVERSIZE ")
165 o = rp_put(out, o, nm)
166 o = rp_put(out, o, " <== NOT READ, so this board is UNJUDGED rather than clean\n")
167 } else {
168 let n: i64 = gk_read(path, fbuf, BI_FILECAP)
169 if n <= 0 {
170 unread = unread + 1
171 } else {
172 domains = domains + 1
173 // ---- PASS 1: every rung id declared by this board.
174 var nr: i64 = 0
175 var ls: i64 = 0
176 while ls < n {
177 let le: i64 = rp_le(fbuf, ls, n)
178 if le > ls {
179 if rp_col_pipe(fbuf, ls, le, BI_FLD_KIND, c) == 1 {
180 if rp_lit_eq(fbuf, c[0], c[1], "rung" as *u8) == 1 {
181 if rp_col_pipe(fbuf, ls, le, BI_FLD_RUNGID, c) == 1 {
182 nr = bi_put_id(rungs, nr, fbuf, c[0], c[1])
183 rungrows = rungrows + 1
184 }
185 }
186 }
187 }
188 ls = le + 1
189 }
190 if nr > maxids { maxids = nr }
191 // ---- DUPID, reported once per id rather than once per copy.
192 var k: i64 = 0
193 while k < nr {
194 if bi_first(rungs, k) == 1 {
195 let cnt: i64 = bi_count(rungs, nr, k)
196 if cnt > 1 {
197 dupid = dupid + 1
198 o = rp_put(out, o, "DUPID ")
199 o = rp_put(out, o, nm)
200 o = rp_put(out, o, " id=")
201 o = rp_put(out, o, bi_id_at(rungs, k))
202 o = rp_put(out, o, " copies=")
203 o = rp_putn(out, o, cnt)
204 o = rp_put(out, o, " <== the ranker, a watch flip and a done-rule attach to whichever copy is matched first\n")
205 }
206 }
207 k = k + 1
208 }
209 // ---- PASS 2: every reference must resolve to a declared rung id.
210 ls = 0
211 while ls < n {
212 let le: i64 = rp_le(fbuf, ls, n)
213 if le > ls {
214 if rp_col_pipe(fbuf, ls, le, BI_FLD_KIND, c) == 1 {
215 if rp_lit_eq(fbuf, c[0], c[1], "log" as *u8) == 1 {
216 logrows = logrows + 1
217 if rp_col_pipe(fbuf, ls, le, BI_FLD_LOGID, c) == 1 {
218 rp_cstr(idbuf, fbuf, c[0], c[1])
219 bi_trim(idbuf)
220 if bi_has_id(rungs, nr, idbuf) == 0 {
221 danglog = danglog + 1
222 o = rp_put(out, o, "DANGLOG ")
223 o = rp_put(out, o, nm)
224 o = rp_put(out, o, " -> ")
225 o = rp_put(out, o, idbuf)
226 o = rp_put(out, o, " <== a journalled measurement addressed to a rung this board does not declare\n")
227 }
228 }
229 }
230 if rp_lit_eq(fbuf, c[0], c[1], "ms" as *u8) == 1 {
231 msrows = msrows + 1
232 if rp_col_pipe(fbuf, ls, le, BI_FLD_MSMEMBERS, c) == 1 {
233 // members are a comma list inside one field
234 var ms: i64 = c[0]
235 let me: i64 = c[0] + c[1]
236 while ms < me {
237 // MEMBERS ARE SEPARATED BY A COMMA *OR* A SPACE, AND BOTH ARE IN
238 // LIVE USE. Measured 2026-09-04: lang writes LN4,LN5,LN6 while
239 // performance writes PF2 PF11 and enginelab writes EL1 EL2. A
240 // comma-only split read every space-separated list as ONE token,
241 // which matched no rung and reported 17 dangling members on five
242 // boards whose rungs were all declared. That was a false-positive
243 // class in this detector, not a defect in those boards.
244 var mt: i64 = ms
245 while mt < me {
246 if (fbuf[mt] as i64) == BI_COMMA { break }
247 if (fbuf[mt] as i64) == BI_SPACE { break }
248 mt = mt + 1
249 }
250 if mt > ms {
251 rp_cstr(idbuf, fbuf, ms, mt - ms)
252 bi_trim(idbuf)
253 if bi_has_id(rungs, nr, idbuf) == 0 {
254 dangms = dangms + 1
255 o = rp_put(out, o, "DANGMS ")
256 o = rp_put(out, o, nm)
257 o = rp_put(out, o, " -> ")
258 o = rp_put(out, o, idbuf)
259 o = rp_put(out, o, " <== a milestone counts a member rung the board does not declare\n")
260 }
261 }
262 ms = mt + 1
263 }
264 }
265 }
266 }
267 }
268 ls = le + 1
269 }
270 }
271 }
272 }
273 fi = fi + 1
274 }
275
276 let offenders: i64 = dupid + danglog + dangms
277 o = rp_put(out, o, "ENVELOPE domains=")
278 o = rp_putn(out, o, domains)
279 o = rp_put(out, o, " rung_rows=")
280 o = rp_putn(out, o, rungrows)
281 o = rp_put(out, o, " log_rows=")
282 o = rp_putn(out, o, logrows)
283 o = rp_put(out, o, " ms_rows=")
284 o = rp_putn(out, o, msrows)
285 o = rp_put(out, o, " dupid=")
286 o = rp_putn(out, o, dupid)
287 o = rp_put(out, o, " danglog=")
288 o = rp_putn(out, o, danglog)
289 o = rp_put(out, o, " dangms=")
290 o = rp_putn(out, o, dangms)
291 o = rp_put(out, o, " offenders=")
292 o = rp_putn(out, o, offenders)
293 o = rp_put(out, o, " unreadable=")
294 o = rp_putn(out, o, unread)
295 o = rp_put(out, o, " oversize=")
296 o = rp_putn(out, o, oversize)
297 var complete: i64 = 1
298 if unread > 0 { complete = 0 }
299 if oversize > 0 { complete = 0 }
300 o = rp_put(out, o, " coverage_complete=")
301 o = rp_putn(out, o, complete)
302 o = rp_put(out, o, "\n")
303 sys_write(1, out, o)
304
305 // ---- THE RATCHET. Baseline absent means FIRST SIGHT: adopt the current count so the gate is
306 // non-breaking by construction, and say so, rather than going RED on every board at once.
307 let bb: *u8 = sys_mmap(BI_BASEMAX)
308 let bn: i64 = gk_read(BI_BASELINE, bb, BI_BASEMAX - 1)
309 var baseline: i64 = 0 - 1
310 if bn > 0 {
311 bb[bn] = 0 as u8
312 baseline = rp_num(bb, 0, bn)
313 }
314 var seeded: i64 = 0
315 if baseline < 0 { baseline = offenders; seeded = 1 }
316
317 gv_check("subject-population-non-empty" as *u8, (domains > 0) as i64, ctr)
318 gv_check("coverage-complete-no-unreadable-or-oversize-board" as *u8, complete, ctr)
319 gv_check_eq("offenders-did-not-rise-above-the-ratchet" as *u8,
320 (offenders > baseline) as i64, 0, ctr)
321 // NEG-CONTROL: the id table must not have silently filled, or a board's later rungs would be invisible
322 // and every reference to them would read as dangling -- a false positive that looks like a finding.
323 gv_check("neg-control-rung-id-table-did-not-saturate-on-the-largest-board" as *u8,
324 (maxids < BI_MAXIDS) as i64, ctr)
325
326 gv_values_head()
327 gv_kv("domains" as *u8, domains)
328 gv_kv("rung_rows" as *u8, rungrows)
329 gv_kv("log_rows" as *u8, logrows)
330 gv_kv("ms_rows" as *u8, msrows)
331 gv_kv("dupid" as *u8, dupid)
332 gv_kv("danglog" as *u8, danglog)
333 gv_kv("dangms" as *u8, dangms)
334 gv_kv("offenders" as *u8, offenders)
335 gv_kv("max_rungs_on_one_board" as *u8, maxids)
336 gv_kv("id_table_slots" as *u8, BI_MAXIDS)
337 gv_kv("baseline" as *u8, baseline)
338 gv_kv("baseline_seeded_this_run" as *u8, seeded)
339
340 // TIGHTEN ON A FALL, and only on a fall. A ratchet that rewrites its baseline on a RISE launders itself
341 // green, which is the defect this estate has bitten three times.
342 if offenders < baseline {
343 var t: i64 = 0
344 let tb: *u8 = sys_mmap(BI_BASEMAX)
345 t = rp_putn(tb, 0, offenders)
346 tb[t] = 10 as u8
347 tb[t + 1] = 0 as u8
348 gk_write(BI_BASELINE, tb)
349 }
350 if seeded == 1 {
351 var t2: i64 = 0
352 let tb2: *u8 = sys_mmap(BI_BASEMAX)
353 t2 = rp_putn(tb2, 0, offenders)
354 tb2[t2] = 10 as u8
355 tb2[t2 + 1] = 0 as u8
356 gk_write(BI_BASELINE, tb2)
357 }
358
359 return gv_verdict("BOARD-IDREF-GATE" as *u8, ctr,
360 "every rung id on every compare board is unique within its board, and every log row and milestone member resolves to a rung that board declares; the offender count is ratcheted so a rise is RED and a fall tightens the bar, and every offender is named above whatever the verdict")
361}