code wiki / _hdl_build / nx_schemadrift.nx

nx_schemadrift.nx source

↩ module page · 546 lines · 27669 B

1// nx_schemadrift.nx -- THE CONTROL FOR THE SAFETY-FLAG DERIVER (2026-08-25). Re-derives every 2// auto-derived row in knowledge/tool_schemas.conf from the organ's TRANSITIVE IMPORT CLOSURE and 3// partitions the disagreement BY DIRECTION, because the two directions are not the same defect: 4// over-warning costs information, under-warning costs safety. 5// 6// WHY IT EXISTS. tool_schemas.conf says in its own header that it is there to give "an honest safety 7// profile so an agent knows BEFORE CALLING whether a tool mutates state or reaches outside". Its 8// auto-derived rows are stamped "safety flags STATIC-DERIVED from the source's own syscalls" -- and that 9// stamp is exactly true and exactly the problem: the source's OWN syscalls are not the tool's reach. An 10// organ that writes through an imported helper reaches the write syscall in the LIB, so a single-file 11// scan publishes readOnly=1 for a writer. 12// A DERIVER MUST MEASURE ITS FALSE-POSITIVE RATE AGAINST CONTROLS BEFORE ITS OUTPUT IS TRUSTED, EXACTLY 13// AS A DETECTOR MUST -- and this is that control, run over the FULL population and never a sample. 14// 15// THE PARTITION SUMS. rows = agree + permissive + conservative + mixed + unproven, printed, so a leak 16// is visible rather than absorbed. UNPROVEN is its own bucket and never falls into AGREE: an organ whose 17// source cannot be resolved or whose marker table cannot be loaded produced NO EVIDENCE, and scoring 18// that as agreement is how a census flatters itself. 19// 20// exec reach is reported as a SEPARATE ADVISORY AXIS, not folded into the quad: neither shipping deriver 21// claims it, and a new bucket that overlaps an existing partition breaks the reconciliation silently. 22// nx_schemadrift [audit|worklist|row <name>] 23// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 24import "nx_toolsafety_lib.nx" 25import "nx_syscalls.nx" 26 27const SD_SCHEMA: *u8 = "knowledge/tool_schemas.conf" as *u8 28const SD_ALLOW: *u8 = "tool_allowlist.conf" as *u8 29// the stamp the CURRENT backfill path writes -- the population this control exists to judge, and the 30// exact string nx_tools_api's ta_od_is_regstub tests to EXCLUDE a row from the derived-flags overlay. 31const SD_MARK_NEW: *u8 = "safety flags STATIC-DERIVED from the source's own syscalls" as *u8 32// the stamp the backfill's OLD path wrote; those rows ARE overlay-eligible and are counted separately 33const SD_MARK_OLD: *u8 = "auto-derived from source header by nx_schema_backfill" as *u8 34const SD_MARK_REG: *u8 = "(registered via /api/tools/register)" as *u8 35const SD_NAMECAP: i64 = 128 36const SD_PATHCAP: i64 = 512 37const SD_LF: i64 = 10 38const SD_TAB: i64 = 9 39const SD_HASH: i64 = 35 40const SD_SLASH: i64 = 47 41const SD_ONE: i64 = 49 42const SD_F_NAME: i64 = 0 43const SD_F_RO: i64 = 2 44const SD_F_DESTR: i64 = 3 45const SD_F_IDEM: i64 = 4 46const SD_F_OPEN: i64 = 5 47const SD_F_DESC: i64 = 6 48const SD_VERB_AUDIT: i64 = 0 49const SD_VERB_WORKLIST: i64 = 1 50const SD_VERB_ROW: i64 = 2 51const SD_VERB_TRACE: i64 = 3 52// sized: distinct closure files blamed for a write. When it fills the organ SAYS SO instead of folding 53// the remainder into an existing cause -- a cause table that silently merges is a partition that lies. 54const SD_MAXCAUSE: i64 = 64 55const SD_VERB_APPLY: i64 = 4 56const SD_ZERO: i64 = 48 57const SD_TMP: *u8 = "knowledge/tool_schemas.conf.sdtmp" as *u8 58const SD_MODE644: i64 = 420 59const SD_SEEK_END: i64 = 2 60const SD_EXIT_CONCURRENT: i64 = 4 61// sized: printed worklist rows. The count is ALWAYS the full population; this bounds only how many are 62// NAMED, and when it binds the organ says so, because a list silently shorter than its own count 63// republishes a prefix as if it were the population. 64const SD_MAXNAME: i64 = 4000 65 66func sw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 67func sn(v: i64) -> i64 { 68 let b: *u8 = sys_mmap(32) 69 var x: i64 = v 70 var i: i64 = 31 71 if x < 0 { sw("-" as *u8); x = 0 - x } 72 if x == 0 { b[i] = 48 as u8; i = i - 1 } 73 while x > 0 { b[i] = ((48 + (x - ((x / 10) * 10))) as u8); x = x / 10; i = i - 1 } 74 sys_write(1, ((b as i64) + i + 1) as *u8, 31 - i) 75 return 0 76} 77 78// idx-th TAB field of buf[ls,le) -> out2[0]=off out2[1]=len ; 1 present, 0 fewer fields 79func sd_field(buf: *u8, ls: i64, le: i64, idx: i64, out2: *i64) -> i64 { 80 var fi: i64 = 0 81 var s: i64 = ls 82 var i: i64 = ls 83 while i <= le { 84 var sep: i64 = 0 85 if i == le { sep = 1 } else { if buf[i] == (SD_TAB as u8) { sep = 1 } } 86 if sep == 1 { 87 if fi == idx { out2[0] = s; out2[1] = i - s; return 1 } 88 fi = fi + 1 89 s = i + 1 90 } 91 i = i + 1 92 } 93 return 0 94} 95 96func sd_win_has(buf: *u8, s: i64, l: i64, needle: *u8) -> i64 { 97 var m: i64 = 0 98 while needle[m] != (0 as u8) { m = m + 1 } 99 if l < m { return 0 } 100 var i: i64 = 0 101 while i + m <= l { 102 var j: i64 = 0 103 var hit: i64 = 1 104 while j < m { if buf[s+i+j] != needle[j] { hit = 0; j = m } else { j = j + 1 } } 105 if hit == 1 { return 1 } 106 i = i + 1 107 } 108 return 0 109} 110 111func sd_flag(buf: *u8, ls: i64, le: i64, idx: i64) -> i64 { 112 let f: *i64 = sys_mmap(16) as *i64 113 if sd_field(buf, ls, le, idx, f) == 0 { return 0 - 1 } 114 if f[1] <= 0 { return 0 - 1 } 115 if (buf[f[0]] as i64) == SD_ONE { return 1 } 116 return 0 117} 118 119func sd_ends(s: *u8, sl: i64, suf: *u8, fl: i64) -> i64 { 120 if sl < fl { return 0 } 121 var i: i64 = 0 122 while i < fl { if s[sl-fl+i] != suf[i] { return 0 } i = i + 1 } 123 return 1 124} 125 126// Resolve the SOURCE PATH the backfill deriver would have used for `name`: find the tool's elf in the 127// allowlist, strip the artifact suffix to a stem, then probe _hdl_build then runtime -- the same order 128// and the same two roots, so the control judges the file the deriver actually read. 129// MATCH THE RESOLVER YOU REPORT ON: a control that resolves differently measures a different subject and 130// its disagreements are its own. 131func sd_srcpath(al: *u8, an: i64, name: *u8, nlen: i64, out: *u8) -> i64 { 132 var ls: i64 = 0 133 var i: i64 = 0 134 while i <= an { 135 var eol: i64 = 0 136 if i == an { eol = 1 } else { if al[i] == (SD_LF as u8) { eol = 1 } } 137 if eol == 1 { 138 let le: i64 = i 139 var use: i64 = 1 140 if le <= ls { use = 0 } 141 if use == 1 { if al[ls] == (SD_HASH as u8) { use = 0 } } 142 if use == 1 { 143 let f: *i64 = sys_mmap(16) as *i64 144 if sd_field(al, ls, le, 0, f) == 1 { 145 if f[1] == nlen { 146 var m: i64 = 1 147 var c: i64 = 0 148 while c < nlen { if al[f[0] + c] != name[c] { m = 0 } c = c + 1 } 149 if m == 1 { 150 if sd_field(al, ls, le, 1, f) == 1 { 151 let es: i64 = f[0] 152 let el: i64 = f[1] 153 var bs: i64 = 0 154 var k: i64 = 0 155 while k < el { if al[es + k] == (SD_SLASH as u8) { bs = k + 1 } k = k + 1 } 156 var stl: i64 = el - bs 157 let base: *u8 = ((al as i64) + es + bs) as *u8 158 if sd_ends(base, stl, ".sov.elf.new" as *u8, 12) == 1 { stl = stl - 12 } else { 159 if sd_ends(base, stl, ".elf.new" as *u8, 8) == 1 { stl = stl - 8 } else { 160 if sd_ends(base, stl, ".sov.elf" as *u8, 8) == 1 { stl = stl - 8 } else { 161 if sd_ends(base, stl, ".elf" as *u8, 4) == 1 { stl = stl - 4 } else { 162 if sd_ends(base, stl, ".new" as *u8, 4) == 1 { stl = stl - 4 } 163 } 164 } 165 } 166 } 167 if stl <= 0 { return 0 } 168 let stem: *u8 = sys_mmap(SD_PATHCAP) 169 var c2: i64 = 0 170 while c2 < stl { stem[c2] = base[c2]; c2 = c2 + 1 } 171 stem[stl] = 46 as u8 172 stem[stl+1] = 110 as u8 173 stem[stl+2] = 120 as u8 174 stem[stl+3] = 0 as u8 175 let got: i64 = ts_resolve(stem, out) 176 sys_munmap(stem, SD_PATHCAP) 177 return got 178 } 179 return 0 180 } 181 } 182 } 183 } 184 ls = i + 1 185 } 186 i = i + 1 187 } 188 return 0 189} 190 191// Tally one write-attribution path. Returns the new distinct-cause count; sets ovf[0] when the table is 192// full, which is REPORTED rather than absorbed. 193func sd_tally(cpath: *u8, ccount: *i64, n: i64, key: *u8, ovf: *i64) -> i64 { 194 var i: i64 = 0 195 while i < n { 196 let c: *u8 = ((cpath as i64) + i * SD_PATHCAP) as *u8 197 if ts_streq(c, key) == 1 { ccount[i] = ccount[i] + 1; return n } 198 i = i + 1 199 } 200 if n >= SD_MAXCAUSE { ovf[0] = 1; return n } 201 let d: *u8 = ((cpath as i64) + n * SD_PATHCAP) as *u8 202 var k: i64 = 0 203 while key[k] != (0 as u8) { d[k] = key[k]; k = k + 1 } 204 d[k] = 0 as u8 205 ccount[n] = 1 206 return n + 1 207} 208 209// Patch ONE flag character in the output copy. Flags are single characters, so a corrected file is the 210// SAME LENGTH as the original -- which is not a coincidence, it is the safety property: the writer can 211// assert byte-for-byte length equality and every other field, including the col8 argv contract, is 212// carried across untouched by construction rather than by careful re-serialisation. 213// Refuses any field that is not exactly one character rather than changing the file's shape. 214func sd_setflag(out: *u8, sc: *u8, ls: i64, le: i64, idx: i64, v: i64, f: *i64) -> i64 { 215 if sd_field(sc, ls, le, idx, f) == 0 { return 0 } 216 if f[1] != 1 { return 0 } 217 if v == 1 { out[f[0]] = SD_ONE as u8 } else { out[f[0]] = SD_ZERO as u8 } 218 return 1 219} 220 221func sd_pq(q: *i64) -> i64 { 222 sn(q[0]); sw("/" as *u8); sn(q[1]); sw("/" as *u8); sn(q[2]); sw("/" as *u8); sn(q[3]) 223 return 0 224} 225 226func main(argc: i64, argv: *i64) -> i64 { 227 var verb: i64 = SD_VERB_AUDIT 228 var want: *u8 = 0 as *u8 229 if argc > 1 { 230 let a1: *u8 = argv[1] as *u8 231 if ts_streq(a1, "worklist" as *u8) == 1 { verb = SD_VERB_WORKLIST } 232 if ts_streq(a1, "row" as *u8) == 1 { 233 verb = SD_VERB_ROW 234 if argc > 2 { want = argv[2] as *u8 } else { 235 sw("usage: nx_schemadrift row <tool-name>\n" as *u8); sys_exit(3); return 3 236 } 237 } 238 if ts_streq(a1, "apply" as *u8) == 1 { verb = SD_VERB_APPLY } 239 if ts_streq(a1, "trace" as *u8) == 1 { 240 verb = SD_VERB_TRACE 241 if argc > 2 { want = argv[2] as *u8 } else { 242 sw("usage: nx_schemadrift trace <tool-name>\n" as *u8); sys_exit(3); return 3 243 } 244 } 245 } 246 if ts_load_markers() == 0 { 247 sw("REFUSED marker table knowledge/tool_safety_markers.conf unreadable or a class is empty -- a scan with no markers matches nothing and reads exactly like a read-only organ\n" as *u8) 248 sys_exit(2) 249 return 2 250 } 251 let szp: *i64 = sys_mmap(16) as *i64 252 let sc: *u8 = sys_read_file(SD_SCHEMA, szp) 253 if (sc as i64) == 0 { sw("REFUSED knowledge/tool_schemas.conf unreadable\n" as *u8); sys_exit(2); return 2 } 254 let scn: i64 = szp[0] 255 let azp: *i64 = sys_mmap(16) as *i64 256 let al: *u8 = sys_read_file(SD_ALLOW, azp) 257 if (al as i64) == 0 { sw("REFUSED tool_allowlist.conf unreadable\n" as *u8); sys_exit(2); return 2 } 258 let an: i64 = azp[0] 259 260 if verb == SD_VERB_TRACE { 261 let tp: *u8 = sys_mmap(SD_PATHCAP) 262 var wl: i64 = 0 263 while want[wl] != (0 as u8) { wl = wl + 1 } 264 if sd_srcpath(al, an, want, wl, tp) == 0 { 265 sw("TRACE UNRESOLVED no .nx source under either root for " as *u8); sw(want); sw("\n" as *u8) 266 sys_exit(3) 267 return 3 268 } 269 sw("TRACE subject=" as *u8); sw(tp); sw("\n" as *u8) 270 ts_set_trace(1) 271 let tb: *i64 = sys_mmap(64) as *i64 272 let okt: i64 = ts_reach(tp, TS_DEPTH_ALL, tb) 273 ts_set_trace(0) 274 sw(" conclusive=" as *u8); sn(okt) 275 sw(" write=" as *u8); sn(tb[TS_CW]) 276 sw(" destructive=" as *u8); sn(tb[TS_CD]) 277 sw(" network=" as *u8); sn(tb[TS_CN]) 278 sw(" exec=" as *u8); sn(tb[TS_CX]) 279 sw(" files=" as *u8); sn(tb[4]) 280 sw(" unresolved=" as *u8); sn(tb[5]) 281 sw(" budget_hit=" as *u8); sn(tb[6]) 282 sw("\n" as *u8) 283 let twp: *u8 = sys_mmap(SD_PATHCAP) 284 if ts_write_src(twp) == 1 { sw(" write_attributed_to=" as *u8); sw(twp); sw("\n" as *u8) } else { sw(" write_attributed_to=NONE\n" as *u8) } 285 sys_exit(0) 286 return 0 287 } 288 289 var rows: i64 = 0 290 var pop: i64 = 0 291 var old_rows: i64 = 0 292 var reg_rows: i64 = 0 293 var authored: i64 = 0 294 var agree: i64 = 0 295 var perm: i64 = 0 296 var cons: i64 = 0 297 var mixed: i64 = 0 298 var unproven: i64 = 0 299 var ax_ro: i64 = 0 300 var ax_de: i64 = 0 301 var ax_op: i64 = 0 302 var d0_ro: i64 = 0 303 var d1_ro: i64 = 0 304 var dn_ro: i64 = 0 305 var deeper_than_1: i64 = 0 306 var exec_reach: i64 = 0 307 var named: i64 = 0 308 var nosrc: i64 = 0 309 310 let quad: *i64 = sys_mmap(64) as *i64 311 let deep: *i64 = sys_mmap(64) as *i64 312 let own: *i64 = sys_mmap(64) as *i64 313 let one: *i64 = sys_mmap(64) as *i64 314 let pth: *u8 = sys_mmap(SD_PATHCAP) 315 let nmz: *u8 = sys_mmap(SD_NAMECAP) 316 let f: *i64 = sys_mmap(16) as *i64 317 let cpath: *u8 = sys_mmap(SD_MAXCAUSE * SD_PATHCAP) 318 let ccount: *i64 = sys_mmap(SD_MAXCAUSE * 8) as *i64 319 let ovfp: *i64 = sys_mmap(16) as *i64 320 let wsp: *u8 = sys_mmap(SD_PATHCAP) 321 ovfp[0] = 0 322 var ncause: i64 = 0 323 var patched_rows: i64 = 0 324 var patched_fields: i64 = 0 325 var patch_refused: i64 = 0 326 var doapply: i64 = 0 327 if verb == SD_VERB_APPLY { doapply = 1 } 328 // The output copy starts byte-identical to the input; apply only ever overwrites single flag 329 // characters in place, so out is the same length as sc for the whole run. 330 var outb: *u8 = 0 as *u8 331 if doapply == 1 { 332 outb = sys_mmap(scn + 1) 333 var cpy: i64 = 0 334 while cpy < scn { outb[cpy] = sc[cpy]; cpy = cpy + 1 } 335 } 336 337 if verb == SD_VERB_AUDIT { sw("NX-SCHEMADRIFT -- declared safety quad vs import-closure derived quad, FULL POPULATION\n\n" as *u8) } 338 if verb == SD_VERB_WORKLIST { sw("# name\tdeclared_ro/de/id/op\tmeasured_ro/de/id/op\tdirection\tclosure_files\n" as *u8) } 339 340 var ls: i64 = 0 341 var i: i64 = 0 342 while i <= scn { 343 var eol: i64 = 0 344 if i == scn { eol = 1 } else { if sc[i] == (SD_LF as u8) { eol = 1 } } 345 if eol == 1 { 346 let le: i64 = i 347 var use: i64 = 1 348 if le <= ls { use = 0 } 349 if use == 1 { if sc[ls] == (SD_HASH as u8) { use = 0 } } 350 if use == 1 { 351 if sd_field(sc, ls, le, SD_F_DESC, f) == 1 { 352 rows = rows + 1 353 let ds: i64 = f[0] 354 let dl: i64 = f[1] 355 var cls: i64 = 0 356 if sd_win_has(sc, ds, dl, SD_MARK_NEW) == 1 { cls = 1 } else { 357 if sd_win_has(sc, ds, dl, SD_MARK_OLD) == 1 { cls = 2 } else { 358 if sd_win_has(sc, ds, dl, SD_MARK_REG) == 1 { cls = 3 } else { cls = 4 } 359 } 360 } 361 if cls == 2 { old_rows = old_rows + 1 } 362 if cls == 3 { reg_rows = reg_rows + 1 } 363 if cls == 4 { authored = authored + 1 } 364 if cls == 1 { 365 var judge: i64 = 1 366 var nlen: i64 = 0 367 if sd_field(sc, ls, le, SD_F_NAME, f) == 1 { 368 nlen = f[1] 369 var c: i64 = 0 370 while c < nlen { if c < SD_NAMECAP - 1 { nmz[c] = sc[f[0] + c] } c = c + 1 } 371 if nlen >= SD_NAMECAP { nlen = SD_NAMECAP - 1 } 372 nmz[nlen] = 0 as u8 373 } else { judge = 0 } 374 if verb == SD_VERB_ROW { if ts_streq(nmz, want) == 0 { judge = 0 } } 375 if judge == 1 { 376 pop = pop + 1 377 let dro: i64 = sd_flag(sc, ls, le, SD_F_RO) 378 let dde: i64 = sd_flag(sc, ls, le, SD_F_DESTR) 379 let dop: i64 = sd_flag(sc, ls, le, SD_F_OPEN) 380 var got: i64 = sd_srcpath(al, an, nmz, nlen, pth) 381 if got == 0 { unproven = unproven + 1; nosrc = nosrc + 1 } else { 382 let ok: i64 = ts_quad(pth, quad, deep, own) 383 if ok == 0 { unproven = unproven + 1 } else { 384 ts_reach(pth, 1, one) 385 // re-run the TRANSITIVE walk LAST so ts_write_src attributes the 386 // depth that actually decided readOnly, not the depth-0 probe. 387 // A LATCHED SIDE-CHANNEL REPORTS THE LAST CALLER, NOT THE ONE YOU MEAN. 388 ts_reach(pth, TS_DEPTH_ALL, deep) 389 var haswsrc: i64 = ts_write_src(wsp) 390 if own[TS_CW] == 0 { d0_ro = d0_ro + 1 } 391 if one[TS_CW] == 0 { d1_ro = d1_ro + 1 } 392 if deep[TS_CW] == 0 { dn_ro = dn_ro + 1 } 393 if one[TS_CW] == 0 { if deep[TS_CW] == 1 { deeper_than_1 = deeper_than_1 + 1 } } 394 if deep[TS_CX] == 1 { exec_reach = exec_reach + 1 } 395 var p: i64 = 0 396 var csv: i64 = 0 397 if dro == 1 { if quad[0] == 0 { 398 p = 1 399 ax_ro = ax_ro + 1 400 if haswsrc == 1 { ncause = sd_tally(cpath, ccount, ncause, wsp, ovfp) } 401 } } 402 if dro == 0 { if quad[0] == 1 { csv = 1 } } 403 if dde == 0 { if quad[1] == 1 { p = 1; ax_de = ax_de + 1 } } 404 if dde == 1 { if quad[1] == 0 { csv = 1 } } 405 if dop == 0 { if quad[3] == 1 { p = 1; ax_op = ax_op + 1 } } 406 if dop == 1 { if quad[3] == 0 { csv = 1 } } 407 var dir: *u8 = "AGREE" as *u8 408 if p == 1 { if csv == 1 { mixed = mixed + 1; dir = "MIXED-permissive-and-conservative" as *u8 } else { perm = perm + 1; dir = "PERMISSIVE-declared-less-reach-than-measured" as *u8 } } else { 409 if csv == 1 { cons = cons + 1; dir = "CONSERVATIVE-declared-more-reach-than-measured" as *u8 } else { agree = agree + 1 } 410 } 411 // CORRECT ONLY THE PERMISSIVE DIRECTION. A conservative row 412 // over-warns, which costs information but never safety, and 413 // loosening it here would be this organ deciding on its own that a 414 // published warning was unnecessary. Every patch can therefore only 415 // TIGHTEN what an agent is told, which is why apply needs no 416 // reviewer to be safe -- only to be useful. 417 if doapply == 1 { if p == 1 { 418 var did: i64 = 0 419 if dro == 1 { if quad[0] == 0 { 420 if sd_setflag(outb, sc, ls, le, SD_F_RO, 0, f) == 1 { did = did + 1 } else { patch_refused = patch_refused + 1 } 421 } } 422 if dde == 0 { if quad[1] == 1 { 423 if sd_setflag(outb, sc, ls, le, SD_F_DESTR, 1, f) == 1 { did = did + 1 } else { patch_refused = patch_refused + 1 } 424 } } 425 if dop == 0 { if quad[3] == 1 { 426 if sd_setflag(outb, sc, ls, le, SD_F_OPEN, 1, f) == 1 { did = did + 1 } else { patch_refused = patch_refused + 1 } 427 } } 428 if did > 0 { patched_rows = patched_rows + 1; patched_fields = patched_fields + did } 429 } } 430 var show: i64 = 0 431 if p == 1 { show = 1 } 432 if verb == SD_VERB_ROW { show = 1 } 433 if show == 1 { 434 if named < SD_MAXNAME { 435 named = named + 1 436 sw(nmz); sw("\t" as *u8) 437 sn(dro); sw("/" as *u8); sn(dde); sw("/0/" as *u8); sn(dop) 438 sw("\t" as *u8); sd_pq(quad) 439 sw("\t" as *u8); sw(dir) 440 sw("\tclosure_files=" as *u8); sn(deep[4]) 441 sw(" unresolved=" as *u8); sn(deep[5]) 442 sw(" write_from=" as *u8) 443 if haswsrc == 1 { sw(wsp) } else { sw("NONE" as *u8) } 444 sw("\n" as *u8) 445 } 446 } 447 } 448 } 449 } 450 } 451 } 452 } 453 ls = i + 1 454 } 455 i = i + 1 456 } 457 458 if doapply == 1 { 459 // COMPARE-AND-SWAP ON LENGTH. tool_schemas.conf has concurrent writers -- it grew 221 bytes 460 // under the lane that briefed this work. If it changed while this census was running, the copy 461 // in memory is stale and committing it would silently DELETE a sibling lane's appended row. 462 // Refuse and say so: A CORRECTION THAT CLOBBERS IS NOT A CORRECTION. Length is a sound guard 463 // HERE, unlike in general, precisely because this writer is length-preserving: any length change 464 // at all came from somebody else. 465 let vfd: i64 = sys_openat_rd(SD_SCHEMA) 466 var nowsz: i64 = 0 - 1 467 if vfd >= 0 { nowsz = sys_lseek(vfd, 0, SD_SEEK_END); sys_close(vfd) } 468 if nowsz != scn { 469 sw("APPLY REFUSED tool_schemas.conf changed under this run (read " as *u8); sn(scn) 470 sw(" bytes, now " as *u8); sn(nowsz) 471 sw(") -- NOTHING WRITTEN; re-run\n" as *u8) 472 sys_exit(SD_EXIT_CONCURRENT) 473 return SD_EXIT_CONCURRENT 474 } 475 sys_unlinkat(SD_TMP) 476 let ofd: i64 = sys_openat_wr(SD_TMP, SD_MODE644) 477 if ofd < 0 { sw("APPLY REFUSED cannot create the temp file -- original untouched\n" as *u8); sys_exit(2); return 2 } 478 var wo: i64 = 0 479 var wgo: i64 = 1 480 var wfail: i64 = 0 481 while wgo == 1 { 482 if wo >= scn { wgo = 0 } else { 483 let w: i64 = sys_write(ofd, ((outb as i64) + wo) as *u8, scn - wo) 484 if w <= 0 { wfail = 1; wgo = 0 } else { wo = wo + w } 485 } 486 } 487 sys_fsync(ofd) 488 sys_close(ofd) 489 if wfail == 1 { sw("APPLY REFUSED short write to the temp file -- original untouched\n" as *u8); sys_exit(2); return 2 } 490 if sys_renameat(SD_TMP, SD_SCHEMA) != 0 { sw("APPLY REFUSED rename failed -- original untouched\n" as *u8); sys_exit(2); return 2 } 491 sw("APPLY committed rows_patched=" as *u8); sn(patched_rows) 492 sw(" fields_patched=" as *u8); sn(patched_fields) 493 sw(" refused_odd_field=" as *u8); sn(patch_refused) 494 sw(" bytes_in=" as *u8); sn(scn) 495 sw(" bytes_out=" as *u8); sn(wo) 496 sw(" (length-preserving BY CONSTRUCTION -- only single flag characters are overwritten, so every\n" as *u8) 497 sw(" other field including the col8 argv contract is carried across untouched, and only the\n" as *u8) 498 sw(" PERMISSIVE direction is corrected, so a patch can only ever TIGHTEN what an agent is told)\n" as *u8) 499 } 500 if verb == SD_VERB_ROW { sys_exit(0); return 0 } 501 let total: i64 = agree + perm + cons + mixed + unproven 502 sw("\nCENSUS schema_rows=" as *u8); sn(rows) 503 sw(" population_backfill_static_derived=" as *u8); sn(pop) 504 sw("\n PARTITION agree=" as *u8); sn(agree) 505 sw(" permissive=" as *u8); sn(perm) 506 sw(" conservative=" as *u8); sn(cons) 507 sw(" mixed=" as *u8); sn(mixed) 508 sw(" unproven=" as *u8); sn(unproven) 509 sw(" sum=" as *u8); sn(total) 510 if total == pop { sw(" PARTITION-SUMS=1\n" as *u8) } else { sw(" PARTITION-BROKEN=1\n" as *u8) } 511 sw(" PERMISSIVE-BY-AXIS readOnly=" as *u8); sn(ax_ro) 512 sw(" destructive=" as *u8); sn(ax_de) 513 sw(" openWorld=" as *u8); sn(ax_op) 514 sw(" (axes OVERLAP by construction and do NOT sum to the permissive count)\n" as *u8) 515 sw(" DEPTH-LADDER readonly_at_depth0=" as *u8); sn(d0_ro) 516 sw(" depth1=" as *u8); sn(d1_ro) 517 sw(" transitive=" as *u8); sn(dn_ro) 518 sw(" found_only_past_depth1=" as *u8); sn(deeper_than_1) 519 sw("\n ADVISORY-AXIS exec_reach=" as *u8); sn(exec_reach) 520 sw(" (SEPARATE from the quad -- no shipping deriver claims it, so it is reported, never folded in)\n" as *u8) 521 sw(" OTHER-ROW-CLASSES overlay_eligible_old_backfill=" as *u8); sn(old_rows) 522 sw(" overlay_eligible_registration_stub=" as *u8); sn(reg_rows) 523 sw(" hand_authored=" as *u8); sn(authored) 524 sw("\n COVERAGE named_in_worklist=" as *u8); sn(named) 525 sw(" name_cap=" as *u8); sn(SD_MAXNAME) 526 if named >= SD_MAXNAME { sw(" LIST-IS-A-PREFIX-OF-ITS-OWN-COUNT=1" as *u8) } else { sw(" list_complete=1" as *u8) } 527 sw(" unresolved_source=" as *u8); sn(nosrc) 528 sw("\n WRITE-ATTRIBUTION (which closure file supplied the write for a readOnly-permissive row --\n" as *u8) 529 sw(" COLLAPSE TO CAUSES BEFORE SIZING A CAMPAIGN: N rows naming ONE shared lib are ONE decision)\n" as *u8) 530 var ci: i64 = 0 531 while ci < ncause { 532 sw(" cause rows=" as *u8); sn(ccount[ci]) 533 sw(" " as *u8); sw(((cpath as i64) + ci * SD_PATHCAP) as *u8) 534 sw("\n" as *u8) 535 ci = ci + 1 536 } 537 sw(" distinct_causes=" as *u8); sn(ncause) 538 if ovfp[0] == 1 { sw(" CAUSE-TABLE-FULL=1 (this list is a PREFIX)" as *u8) } else { sw(" cause_list_complete=1" as *u8) } 539 sw("\n" as *u8) 540 sw(" distinct_sources_scanned=" as *u8); sn(ts_stat_files()) 541 sw(" source_bytes=" as *u8); sn(ts_stat_bytes()) 542 sw(" memo_full=" as *u8); sn(ts_memo_full()) 543 sw("\n" as *u8) 544 sys_exit(0) 545 return 0 546}