code wiki / (root) / nx_wpt_spine.nx

nx_wpt_spine.nx source

↩ module page · 243 lines · 9710 B

1// nx_wpt_spine.nx -- LIB: the shared TEST-TELEMETRY SPINE (2026-08-19). Extracted from 2// nx_wpt_runner v3 the day nx_wpt_reftest became its second consumer (the DRY law: the second 3// copy is where drift starts). One implementation of: 4// - engine identity (sha256 of the calling binary itself, via /proc/self/exe), 5// - deterministic corpus enumeration (sorted names) + content hash (name+NUL+bytes, sorted order), 6// - the TSV history scan (newest row matching dir[/engine][/corpus]) + column reads, 7// - the 16-hex truncated digest form (64 bits, DECLARED truncation), 8// - history append. 9// Row schema (documented once, here, OTel CI/CD mapped): epoch_s(cicd.pipeline.run start) 10// col1=suite(test.suite name) col2=engine_sha16(service.version) col3=corpus_sha16(vcs.ref analog) 11// then consumer-declared counter columns, dur_ms, verdict LAST. 12// license_tier: ORIGINAL 13 14import "nx_syscalls.nx" 15import "nx_sha256.nx" 16 17// ---- THE FIELD SEPARATOR, ONE OWNER (2026-08-27) ------------------------------------------------ 18// Operator standing rule: no TSV in the estate's data plane. Every other row file here -- .matrix, 19// .refs, .registry, .gates, .plan, .sow -- is PIPE delimited, and this spine was the one place still 20// emitting tabs, so its history was the odd artifact out for no reason anyone had chosen. 21// WSP_FS is what new rows are WRITTEN with. wsp_is_fs accepts EITHER separator on READ, deliberately: 22// the history already holds rows in the old shape and the regression detector reads them to find a 23// prior baseline. A reader that only accepted the new separator would silently lose that baseline and 24// report every run as a first run -- a format change that destroys the time spine it was meant to 25// tidy. Accepting both keeps every existing row readable while nothing new is ever written as TSV. 26// It lives HERE because this spine is the single reader shared by nx_wpt_runner and nx_wpt_reftest; 27// a separator defined in each writer would be two owners of one wire format, which is where drift 28// starts -- the same reason this file was extracted in the first place. 29// ALSO DEFINED ABOVE BOTH READERS ON PURPOSE: nx is single-pass, and wsp_hist_find splits fields too. 30const WSP_FS: i64 = 124 31const WSP_FS_LEGACY: i64 = 9 32func wsp_is_fs(c: i64) -> i64 { 33 if c == WSP_FS { return 1 } 34 if c == WSP_FS_LEGACY { return 1 } 35 return 0 36} 37 38func wsp_scat(dst: *u8, off: i64, src: *u8) -> i64 { var i: i64 = 0; while src[i] != (0 as u8) { dst[off+i] = src[i]; i = i + 1 } return off + i } 39func wsp_scatn(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { dst[off+i] = src[i]; i = i + 1 } return off + n } 40func wsp_ends_with(s: *u8, n: i64, suf: *u8, sn: i64) -> i64 { 41 if n < sn { return 0 } 42 var i: i64 = 0 43 while i < sn { if s[n-sn+i] != suf[i] { return 0 } i = i + 1 } 44 return 1 45} 46func wsp_hex16(dig: *u8, out: *u8) -> i64 { 47 let hx: *u8 = "0123456789abcdef" as *u8 48 var i: i64 = 0 49 while i < 8 { 50 let b: i64 = dig[i] as i64 51 out[i*2] = hx[b / 16] 52 out[i*2+1] = hx[b % 16] 53 i = i + 1 54 } 55 out[16] = 0 as u8 56 return 16 57} 58// sha256(this very binary) as 16 hex chars. 0 ok / 1 cannot read self (caller must REFUSE an unkeyed run). 59func wsp_engine_sha(out: *u8) -> i64 { 60 let lp: *i64 = sys_mmap(16) as *i64 61 let b: *u8 = sys_read_file("/proc/self/exe" as *u8, lp) 62 if (b as i64) == 0 { return 1 } 63 let c: *Sha256 = sys_mmap(512) as *Sha256 64 sha256_init(c) 65 sha256_update(c, b, lp[0]) 66 let dig: *u8 = sys_mmap(40) 67 sha256_final(c, dig) 68 wsp_hex16(dig, out) 69 sys_free_file(b, lp[0]) 70 return 0 71} 72func wsp_name_lt(a: *u8, b: *u8) -> i64 { 73 var i: i64 = 0 74 var going: i64 = 1 75 var r: i64 = 0 76 while going == 1 { 77 let ca: i64 = a[i] as i64 78 let cb: i64 = b[i] as i64 79 if ca < cb { r = 1; going = 0 } 80 if going == 1 { if ca > cb { r = 0; going = 0 } } 81 if going == 1 { if ca == 0 { r = 0; going = 0 } } 82 i = i + 1 83 } 84 return r 85} 86// every *.<suf> name in dir into namebuf (NUL-joined) + offs[], SORTED byte-wise. -1 = unreadable dir. 87func wsp_collect_names(dir: *u8, suf: *u8, namebuf: *u8, nbcap: i64, offs: *i64, maxn: i64) -> i64 { 88 let fd: i64 = sys_openat_rd(dir) 89 if fd < 0 { return 0 - 1 } 90 var sn: i64 = 0 91 while suf[sn] != (0 as u8) { sn = sn + 1 } 92 let dbuf: *u8 = sys_mmap(1048576) 93 var cnt: i64 = 0 94 var nb: i64 = 0 95 var going: i64 = 1 96 while going == 1 { 97 let nread: i64 = sys_getdents64(fd, dbuf, 1048576) 98 if nread <= 0 { going = 0 } else { 99 var off: i64 = 0 100 while off < nread { 101 let rec: *u8 = ((dbuf as i64) + off) as *u8 102 let rl: i64 = dirent_reclen(rec) 103 let nm: *u8 = dirent_name(rec) 104 var nl: i64 = 0 105 while nm[nl] != (0 as u8) { nl = nl + 1 } 106 if wsp_ends_with(nm, nl, suf, sn) == 1 { if cnt < maxn { if (nb + nl + 1) < nbcap { 107 offs[cnt] = nb 108 nb = wsp_scatn(namebuf, nb, nm, nl) 109 namebuf[nb] = 0 as u8; nb = nb + 1 110 cnt = cnt + 1 111 } } } 112 off = off + rl 113 } 114 } 115 } 116 sys_close(fd) 117 var i: i64 = 1 118 while i < cnt { 119 let key: i64 = offs[i] 120 var j: i64 = i - 1 121 var moving: i64 = 1 122 while moving == 1 { 123 if j < 0 { moving = 0 } else { 124 if wsp_name_lt(((namebuf as i64) + key) as *u8, ((namebuf as i64) + offs[j]) as *u8) == 1 { 125 offs[j+1] = offs[j] 126 j = j - 1 127 } else { moving = 0 } 128 } 129 } 130 offs[j+1] = key 131 i = i + 1 132 } 133 return cnt 134} 135// content hash over the SORTED corpus: name+NUL then bytes per file; READFAIL sentinel keeps a 136// vanished file visible in the key. out = 16 hex chars. 137func wsp_corpus_sha(dir: *u8, namebuf: *u8, offs: *i64, n: i64, out: *u8) -> i64 { 138 let c: *Sha256 = sys_mmap(512) as *Sha256 139 sha256_init(c) 140 let path: *u8 = sys_mmap(4096) 141 var i: i64 = 0 142 while i < n { 143 let nm: *u8 = ((namebuf as i64) + offs[i]) as *u8 144 var pp: i64 = wsp_scat(path, 0, dir) 145 pp = wsp_scat(path, pp, "/" as *u8) 146 pp = wsp_scat(path, pp, nm) 147 path[pp] = 0 as u8 148 var nl: i64 = 0 149 while nm[nl] != (0 as u8) { nl = nl + 1 } 150 sha256_update(c, nm, nl + 1) 151 let lp: *i64 = sys_mmap(16) as *i64 152 let b: *u8 = sys_read_file(path, lp) 153 if (b as i64) == 0 { sha256_update(c, "READFAIL" as *u8, 8) } else { 154 sha256_update(c, b, lp[0]) 155 sys_free_file(b, lp[0]) 156 } 157 i = i + 1 158 } 159 let dig: *u8 = sys_mmap(40) 160 sha256_final(c, dig) 161 wsp_hex16(dig, out) 162 return 0 163} 164func wsp_field_eq(h: *u8, p: i64, q: i64, v: *u8) -> i64 { 165 var k: i64 = 0 166 while (p + k) < q { 167 if v[k] == (0 as u8) { return 0 } 168 if v[k] != h[p+k] { return 0 } 169 k = k + 1 170 } 171 if v[k] != (0 as u8) { return 0 } 172 return 1 173} 174// newest TSV row whose col1 == c1v and (when non-0) col2 == c2v, col3 == c3v. rowout[0] = row length. 175func wsp_hist_find(h: *u8, hn: i64, c1v: *u8, c2v: *u8, c3v: *u8, rowout: *i64) -> i64 { 176 var best: i64 = 0 - 1 177 var i: i64 = 0 178 while i < hn { 179 var e: i64 = i 180 while e < hn { if h[e] == (10 as u8) { break } e = e + 1 } 181 if e > i { 182 var col: i64 = 0 183 var p: i64 = i 184 var m1: i64 = 0 185 var m2: i64 = 1 186 var m3: i64 = 1 187 if (c2v as i64) != 0 { m2 = 0 } 188 if (c3v as i64) != 0 { m3 = 0 } 189 while p < e { 190 var q: i64 = p 191 while q < e { if wsp_is_fs(h[q] as i64) == 1 { break } q = q + 1 } 192 if col == 1 { m1 = wsp_field_eq(h, p, q, c1v) } 193 if col == 2 { if (c2v as i64) != 0 { m2 = wsp_field_eq(h, p, q, c2v) } } 194 if col == 3 { if (c3v as i64) != 0 { m3 = wsp_field_eq(h, p, q, c3v) } } 195 col = col + 1 196 p = q + 1 197 } 198 if m1 == 1 { if m2 == 1 { if m3 == 1 { best = i; rowout[0] = e - i } } } 199 } 200 i = e + 1 201 } 202 return best 203} 204func wsp_hist_col_int(h: *u8, ro: i64, rl: i64, c: i64) -> i64 { 205 var col: i64 = 0 206 var p: i64 = ro 207 let e: i64 = ro + rl 208 while p < e { 209 var q: i64 = p 210 while q < e { if wsp_is_fs(h[q] as i64) == 1 { break } q = q + 1 } 211 if col == c { 212 var v: i64 = 0 213 var k: i64 = p 214 while k < q { let ch: i64 = h[k] as i64; if ch >= 48 { if ch <= 57 { v = v * 10 + (ch - 48) } } k = k + 1 } 215 return v 216 } 217 col = col + 1 218 p = q + 1 219 } 220 return 0 - 1 221} 222func wsp_base_of(dir: *u8, out: *u8) -> i64 { 223 var n: i64 = 0 224 while dir[n] != (0 as u8) { n = n + 1 } 225 var stripping: i64 = 1 226 while stripping == 1 { if n > 0 { if dir[n-1] == (47 as u8) { n = n - 1 } else { stripping = 0 } } else { stripping = 0 } } 227 var s: i64 = n 228 var seeking: i64 = 1 229 while seeking == 1 { if s > 0 { if dir[s-1] == (47 as u8) { seeking = 0 } else { s = s - 1 } } else { seeking = 0 } } 230 var o: i64 = 0 231 var i: i64 = s 232 while i < n { out[o] = dir[i]; o = o + 1; i = i + 1 } 233 out[o] = 0 as u8 234 return o 235} 236// append row bytes to the history file; 0 ok / 1 open failed (caller must SAY the row was not durably kept) 237func wsp_append(histpath: *u8, row: *u8, ro: i64) -> i64 { 238 let fd: i64 = sys_openat_append(histpath, 0x1a4) 239 if fd < 0 { return 1 } 240 sys_write(fd, row, ro) 241 sys_close(fd) 242 return 0 243}