code wiki / _hdl_build / nx_forkgrade.nx

nx_forkgrade.nx source

↩ module page · 402 lines · 20394 B

1// nx_forkgrade.nx -- GRADE THE WHOLE CROSS-TREE FORK, IN ONE PROCESS, IN NISHILANG. 2// 3// WHY THIS EXISTS. Every reading of the cross-tree fork so far was a SAMPLE -- 40 files, then 120, then 4// 250 of 2,845 -- and the reason was purely instrumental: nx_srcdiff is one process per pair, and driven 5// from the laptop each spawn crosses WSL at ~2s, so the population cost ~95 minutes and I shrank the 6// evidence to fit the tool. ★★★★★★WHEN AN INSTRUMENT IS EXPENSIVE, PAY THE COST OR FIX THE INSTRUMENT -- 7// NEVER SHRINK THE POPULATION TO FIT IT. Forking the same ruler NAS-side costs ~10ms, so the whole 8// population is seconds. 9// 10// AND IT COMPOSES, IT DOES NOT REIMPLEMENT. The ruler stays nx_srcdiff -- one ruler, one implementation. 11// This organ only iterates and tallies. A second line-differ would be the duplicate-ruler defect that 12// this lane has already paid for twice (a local symbol-set check that was weaker than nx_srcdiff, and a 13// second flow-back with a second guard). 14// 15// It also exists because the tally is JUDGEMENT, and judgement belongs in an organ: a .ps1 that 16// classifies verdicts and counts distributions is exactly the shell-side judging the doctrine forbids. 17// Shell's remaining job is moving bytes between the two trees, which is irreducibly laptop-side. 18// 19// usage: nx_forkgrade <listfile> <dirA> <dirB> [outfile] 20// listfile : one tree-relative path per line 21// dirA : NAS tree root (e.g. buildroot/runtime) 22// dirB : staged other tree (e.g. /tmp/lapfork) 23// outfile : per-file rows "<verdict> <bpaired> <bunpaired> <relpath>"; stdout carries only the 24// envelope so a payload cap can never bound coverage. 25// 26// UNKNOWN IS ITS OWN BUCKET: a pair whose ruler output carries no anchored verdict is counted 27// UNREADABLE, never folded into a known class. The bucket an unrecognised value lands in becomes the 28// number somebody plans against. 29// license_tier: ORIGINAL expect_exit: 0 30import "nx_syscalls.nx" 31import "nx_tool_run.nx" 32const FG_MAGIC_2048: i64 = 2048 33 34const FG_STDOUT: i64 = 1 35const FG_LISTCAP: i64 = 4194304 36const FG_OUTCAP: i64 = 8388608 37const FG_RUNCAP: i64 = 262144 38const FG_PATHCAP: i64 = 1024 39const FG_DEADLINE: i64 = 20000 40const FG_RULER: *u8 = "nx_srcdiff.elf" 41 42// class slots 43const FG_IDENT: i64 = 0 44const FG_ASUP: i64 = 1 45const FG_BSUP: i64 = 2 46const FG_BIDI: i64 = 3 47const FG_UNREAD: i64 = 4 48const FG_TOTAL: i64 = 5 49const FG_BIDI_PAIRED0: i64 = 6 // BIDIRECTIONAL rows whose B-side is fully paired (looks like an edit) 50// MERGE-BURDEN BUCKETS. "BIDIRECTIONAL" says both trees hold unique lines; it says NOTHING about how 51// many, and that magnitude is the only thing that decides whether a human merge is minutes or days. 52// Two different questions, so two bucket sets, deliberately not merged: 53// surplusB = RAW count of lines the other tree uniquely holds (what a person actually reconciles) 54// bunpaired = those left over AFTER one-to-one modification pairing (what is not just an older line) 55// Reporting only one of them would answer the other question badly. 56const FG_SB0: i64 = 7 // surplusB == 0 57const FG_SB1_3: i64 = 8 // 1..3 58const FG_SB4_20: i64 = 9 // 4..20 59const FG_SB21: i64 = 10 // >20 60const FG_BU0: i64 = 11 // bunpaired == 0 61const FG_BU1_3: i64 = 12 62const FG_BU4_20: i64 = 13 63const FG_BU21: i64 = 14 64// COUNT THE DENOMINATOR, NEVER INFER IT. sb/bu start at -1 and the buckets below are 0 / 1..3 / 4..20 / 65// >20, so a pair whose ruler output carried no anchored surplusB/bunpaired line lands in NO bucket and 66// SILENTLY SHRINKS the histogram's population. The sum was then checked against FG_TOTAL, which prints 67// UNSOUND on every run containing a single unreadable file. 68// MEASURED 2026-08-16 over 45 compare matrices with 2 unreadable (one tree simply lacks the file): 69// both histograms read "sum=43 UNSOUND" while being exactly correct. 70// ★★★★★A DETECTOR THAT IS PERMANENTLY RED IS ONE EVERYONE LEARNS TO IGNORE, and the quickest way to 71// build one is to check a partition against a population it was never over. 72// ★BIND EVERY AGGREGATE ASSERTION TO ITS DENOMINATOR -- and count that denominator rather than deriving 73// it as total-unreadable, because a READABLE pair can also lack the line, and deriving would hide that 74// case inside a number that looks reconciled. 75const FG_SB_SEEN: i64 = 15 // pairs that yielded a parseable surplusB 76const FG_BU_SEEN: i64 = 16 // pairs that yielded a parseable bunpaired 77const FG_SLOTS: i64 = 24 78 79func fg_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(FG_STDOUT, s, n); return 0 } 80 81func fg_num(v: i64) -> i64 { 82 let b: *u8 = sys_mmap(32) 83 var m: i64 = v 84 if m < 0 { m = 0 - m; sys_write(FG_STDOUT, "-" as *u8, 1) } 85 let t: *u8 = sys_mmap(32) 86 var k: i64 = 0 87 if m == 0 { t[0] = 48 as u8; k = 1 } 88 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 89 var i: i64 = 0 90 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 91 sys_write(FG_STDOUT, b, k) 92 return 0 93} 94 95func fg_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 96 97func fg_cat(dst: *u8, o: i64, s: *u8) -> i64 { 98 var i: i64 = 0 99 while s[i] != (0 as u8) { dst[o + i] = s[i]; i = i + 1 } 100 return o + i 101} 102 103func fg_catn(dst: *u8, o: i64, v: i64) -> i64 { 104 let t: *u8 = sys_mmap(32) 105 var m: i64 = v 106 var k: i64 = 0 107 if m == 0 { t[0] = 48 as u8; k = 1 } 108 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 109 var i: i64 = 0 110 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 111 return o + k 112} 113 114// Is `pat` present at buf[i..]? 115func fg_at(buf: *u8, n: i64, i: i64, pat: *u8) -> i64 { 116 let pl: i64 = fg_len(pat) 117 if i + pl > n { return 0 } 118 var k: i64 = 0 119 while k < pl { if buf[i + k] != pat[k] { return 0 } k = k + 1 } 120 return 1 121} 122 123// ANCHORED SCAN: find `pat` only at the START OF A LINE. nx_srcdiff ECHOES surplus source lines, and 124// gate sources contain the literal text "verdict=GREEN" -- an unanchored search reads those echoes as 125// the ruler's own answer (measured: 26 GREEN + 18 RED, verdicts nx_srcdiff cannot emit). 126// ★A PARSER THAT DOES NOT ANCHOR WILL EVENTUALLY READ THE DATA AS THE ANSWER. 127// Returns the offset just past `pat`, or -1. 128func fg_find_line(buf: *u8, n: i64, pat: *u8) -> i64 { 129 let pl: i64 = fg_len(pat) 130 var i: i64 = 0 131 var atline: i64 = 1 132 while i < n { 133 if atline == 1 { if fg_at(buf, n, i, pat) == 1 { return i + pl } } 134 if buf[i] == (10 as u8) { atline = 1 } else { atline = 0 } 135 i = i + 1 136 } 137 return 0 - 1 138} 139 140// Decimal at buf[i..]; -1 when there is no digit there. 141func fg_int_at(buf: *u8, n: i64, i: i64) -> i64 { 142 if i < 0 { return 0 - 1 } 143 if i >= n { return 0 - 1 } 144 var v: i64 = 0 145 var got: i64 = 0 146 var k: i64 = i 147 while k < n { 148 let c: i64 = buf[k] as i64 149 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; k = k + 1 } } 150 if c < 48 { k = n } 151 if c > 57 { k = n } 152 } 153 if got == 0 { return 0 - 1 } 154 return v 155} 156 157func fg_join(dir: *u8, rel: *u8, out: *u8) -> i64 { 158 var o: i64 = fg_cat(out, 0, dir) 159 out[o] = 47 as u8 160 o = o + 1 161 o = fg_cat(out, o, rel) 162 out[o] = 0 as u8 163 return o 164} 165 166// ---- CE8 (codeeffectiveness fg_trend, 2026-09-05): A LEVEL CANNOT EXPRESS A TRAJECTORY ---- 167// The census below is a LEVEL: identical / superset / bidirectional counts for ONE run. The board's open rung 168// (matrix row "cross-tree fork and duplication graded over every pair"; codequality's fg_trend) names the gap: 169// GitClear is Best on AI-era duplication because it publishes the TREND, and a trend needs two censuses in time. 170// So every successful census appends ONE row to a spine, announced (fd= wrote= of=), and two rows apart in time 171// are a trajectory. adoptable_permil = (identical + a_superset) * 1000 / total -- the same figure the verdict line 172// prints as adoptable_by_proof, so the spine and the verdict cannot disagree. 173// THE SPINE PATH IS DERIVED FROM THE ARGUMENTS, NEVER A CONSTANT (the GRR_STAMP law): the production census 174// (dirA == FG_PROD_DIRA) writes the production spine; ANY other dirA -- a gate's /tmp fixture -- writes a sidecar 175// beside its own root, so a trial run can never forge the production trajectory and a gate never shares a 176// fixture with a beat. 177const FG_SPINE_PROD: *u8 = "knowledge/status/forkgrade.spine" 178const FG_PROD_DIRA: *u8 = "buildroot/runtime" 179const FG_SPINE_SIDECAR: *u8 = ".forkgrade.spine" 180const FG_SPINEBUF: i64 = 1024 181const FG_MODE644: i64 = 420 182const FG_PERMIL: i64 = 1000 183func fg_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 } 184func fg_trend(ctr: *i64, dirA: *u8) -> i64 { 185 let path: *u8 = sys_mmap(FG_PATHCAP) 186 var po: i64 = 0 187 if fg_streq(dirA, FG_PROD_DIRA) == 1 { po = fg_cat(path, 0, FG_SPINE_PROD) } else { po = fg_cat(path, 0, dirA); po = fg_cat(path, po, FG_SPINE_SIDECAR) } 188 path[po] = 0 as u8 189 let row: *u8 = sys_mmap(FG_SPINEBUF) 190 var o: i64 = fg_catn(row, 0, sys_now_realtime_sec()) 191 o = fg_cat(row, o, "|total=" as *u8); o = fg_catn(row, o, ctr[FG_TOTAL]) 192 o = fg_cat(row, o, "|identical=" as *u8); o = fg_catn(row, o, ctr[FG_IDENT]) 193 o = fg_cat(row, o, "|a_superset=" as *u8); o = fg_catn(row, o, ctr[FG_ASUP]) 194 o = fg_cat(row, o, "|b_superset=" as *u8); o = fg_catn(row, o, ctr[FG_BSUP]) 195 o = fg_cat(row, o, "|bidirectional=" as *u8); o = fg_catn(row, o, ctr[FG_BIDI]) 196 o = fg_cat(row, o, "|unreadable=" as *u8); o = fg_catn(row, o, ctr[FG_UNREAD]) 197 o = fg_cat(row, o, "|bidi_fully_paired=" as *u8); o = fg_catn(row, o, ctr[FG_BIDI_PAIRED0]) 198 var permil: i64 = 0 199 if ctr[FG_TOTAL] > 0 { permil = ((ctr[FG_IDENT] + ctr[FG_ASUP]) * FG_PERMIL) / ctr[FG_TOTAL] } 200 o = fg_cat(row, o, "|adoptable_permil=" as *u8); o = fg_catn(row, o, permil) 201 row[o] = 10 as u8; o = o + 1 202 let fd: i64 = sys_openat_append(path, FG_MODE644) 203 var wrote: i64 = 0 - 1 204 if fd >= 0 { wrote = sys_write(fd, row, o); sys_close(fd) } 205 fg_puts("# FORKGRADE spine=" as *u8); fg_puts(path); fg_puts(" fd=" as *u8); fg_num(fd); fg_puts(" wrote=" as *u8); fg_num(wrote); fg_puts(" of=" as *u8); fg_num(o); fg_puts("\n" as *u8) 206 return wrote 207} 208 209func main(argc: i64, argv: *i64) -> i64 { 210 if argc < 4 { 211 fg_puts("usage: nx_forkgrade <listfile> <dirA> <dirB> [outfile]\n" as *u8) 212 fg_puts(" Forks nx_srcdiff per pair (ONE ruler, composed not reimplemented) and tallies.\n" as *u8) 213 fg_puts(" Classes: IDENTICAL / A-SUPERSET / B-SUPERSET / BIDIRECTIONAL / UNREADABLE; the partition is printed and MUST sum.\n" as *u8) 214 sys_exit(2) 215 return 2 216 } 217 let listp: *u8 = argv[1] as *u8 218 let dirA: *u8 = argv[2] as *u8 219 let dirB: *u8 = argv[3] as *u8 220 221 let lsz: *i64 = sys_mmap(16) as *i64 222 let lbuf: *u8 = sys_read_file(listp, lsz) 223 if (lbuf as i64) == 0 { 224 fg_puts("NX-FORKGRADE verdict=RED reason=listfile-unreadable\n" as *u8) 225 sys_exit(3) 226 return 3 227 } 228 let ln: i64 = lsz[0] 229 230 let ctr: *i64 = sys_mmap(FG_SLOTS * 8) as *i64 231 var z: i64 = 0 232 while z < FG_SLOTS { ctr[z] = 0; z = z + 1 } 233 234 let out: *u8 = sys_mmap(FG_OUTCAP) 235 var oo: i64 = 0 236 let runbuf: *u8 = sys_mmap(FG_RUNCAP) 237 let runlen: *i64 = sys_mmap(16) as *i64 238 let pa: *u8 = sys_mmap(FG_PATHCAP) 239 let pb: *u8 = sys_mmap(FG_PATHCAP) 240 let rel: *u8 = sys_mmap(FG_PATHCAP) 241 let av: *i64 = sys_mmap(64) as *i64 242 243 var i: i64 = 0 244 while i < ln { 245 // one relpath per line 246 // ⚠SAME BUG I DOCUMENTED IN nx_srcdiff AN HOUR EARLIER AND THEN WROTE AGAIN HERE: breaking a 247 // scan with `e = ln` exits the loop AND destroys the position, so `end` became the end of the 248 // WHOLE FILE and the first line swallowed every other row -- total=1 for a 3-row list. 249 // ★A LOOP THAT BREAKS BY CLOBBERING ITS OWN CURSOR CANNOT ALSO REPORT WHERE IT STOPPED. 250 // Knowing the law is not the same as having the habit; separate cursor, explicit flag. 251 var end: i64 = i 252 var scan: i64 = 1 253 while scan == 1 { 254 if end >= ln { scan = 0 } 255 if end < ln { if lbuf[end] == (10 as u8) { scan = 0 } else { end = end + 1 } } 256 } 257 var rl: i64 = end - i 258 if rl > 0 { if lbuf[i + rl - 1] == (13 as u8) { rl = rl - 1 } } 259 if rl > 0 { 260 var c: i64 = 0 261 while c < rl { rel[c] = lbuf[i + c]; c = c + 1 } 262 rel[rl] = 0 as u8 263 fg_join(dirA, rel, pa) 264 fg_join(dirB, rel, pb) 265 266 av[0] = FG_RULER as i64 267 av[1] = pa as i64 268 av[2] = pb as i64 269 av[3] = 0 270 runlen[0] = 0 271 let rc: i64 = tr_run_capture_to(FG_RULER, av, runbuf, FG_RUNCAP - 8, runlen, FG_DEADLINE) 272 let rn: i64 = runlen[0] 273 274 var cls: i64 = FG_UNREAD 275 var bp: i64 = 0 - 1 276 var bu: i64 = 0 - 1 277 if rn > 0 { 278 if fg_find_line(runbuf, rn, "# verdict=IDENTICAL" as *u8) >= 0 { cls = FG_IDENT } 279 if fg_find_line(runbuf, rn, "# verdict=A-SUPERSET" as *u8) >= 0 { cls = FG_ASUP } 280 if fg_find_line(runbuf, rn, "# verdict=B-SUPERSET" as *u8) >= 0 { cls = FG_BSUP } 281 if fg_find_line(runbuf, rn, "# verdict=BIDIRECTIONAL" as *u8) >= 0 { cls = FG_BIDI } 282 let pp: i64 = fg_find_line(runbuf, rn, "# SRCDIFF-PAIRING bpaired=" as *u8) 283 if pp >= 0 { 284 bp = fg_int_at(runbuf, rn, pp) 285 let up: i64 = fg_find_line(runbuf, rn, "# SRCDIFF-PAIRING bpaired=" as *u8) 286 var q: i64 = up 287 while q < rn { if fg_at(runbuf, rn, q, " bunpaired=" as *u8) == 1 { bu = fg_int_at(runbuf, rn, q + 11); q = rn } else { q = q + 1 } } 288 } 289 } 290 // surplusB comes off the ruler's own summary line, anchored like everything else. 291 var sb: i64 = 0 - 1 292 if rn > 0 { 293 let sp2: i64 = fg_find_line(runbuf, rn, "# SRCDIFF linesA=" as *u8) 294 if sp2 >= 0 { 295 var q2: i64 = sp2 296 while q2 < rn { if fg_at(runbuf, rn, q2, " surplusB=" as *u8) == 1 { sb = fg_int_at(runbuf, rn, q2 + 10); q2 = rn } else { q2 = q2 + 1 } } 297 } 298 } 299 // The seen-counters are the histograms' OWN denominator, incremented on exactly the 300 // condition that puts a row into a bucket. -1 means the ruler emitted no anchored line. 301 if sb >= 0 { ctr[FG_SB_SEEN] = ctr[FG_SB_SEEN] + 1 } 302 if sb == 0 { ctr[FG_SB0] = ctr[FG_SB0] + 1 } 303 if sb >= 1 { if sb <= 3 { ctr[FG_SB1_3] = ctr[FG_SB1_3] + 1 } } 304 if sb >= 4 { if sb <= 20 { ctr[FG_SB4_20] = ctr[FG_SB4_20] + 1 } } 305 if sb > 20 { ctr[FG_SB21] = ctr[FG_SB21] + 1 } 306 if bu >= 0 { ctr[FG_BU_SEEN] = ctr[FG_BU_SEEN] + 1 } 307 if bu == 0 { ctr[FG_BU0] = ctr[FG_BU0] + 1 } 308 if bu >= 1 { if bu <= 3 { ctr[FG_BU1_3] = ctr[FG_BU1_3] + 1 } } 309 if bu >= 4 { if bu <= 20 { ctr[FG_BU4_20] = ctr[FG_BU4_20] + 1 } } 310 if bu > 20 { ctr[FG_BU21] = ctr[FG_BU21] + 1 } 311 ctr[cls] = ctr[cls] + 1 312 ctr[FG_TOTAL] = ctr[FG_TOTAL] + 1 313 if cls == FG_BIDI { if bu == 0 { ctr[FG_BIDI_PAIRED0] = ctr[FG_BIDI_PAIRED0] + 1 } } 314 315 if oo < FG_OUTCAP - FG_MAGIC_2048 { 316 if cls == FG_IDENT { oo = fg_cat(out, oo, "IDENTICAL " as *u8) } 317 if cls == FG_ASUP { oo = fg_cat(out, oo, "A-SUPERSET " as *u8) } 318 if cls == FG_BSUP { oo = fg_cat(out, oo, "B-SUPERSET " as *u8) } 319 if cls == FG_BIDI { oo = fg_cat(out, oo, "BIDIRECTIONAL " as *u8) } 320 if cls == FG_UNREAD { oo = fg_cat(out, oo, "UNREADABLE " as *u8) } 321 oo = fg_catn(out, oo, bp) 322 out[oo] = 32 as u8; oo = oo + 1 323 oo = fg_catn(out, oo, bu) 324 out[oo] = 32 as u8; oo = oo + 1 325 oo = fg_cat(out, oo, rel) 326 // rc and captured-byte count travel with every row: an UNREADABLE with rn=0 is a fork 327 // that produced nothing, which is a different defect from one that produced text this 328 // reader could not classify. NAME WHICH REASON. 329 oo = fg_cat(out, oo, " rc=" as *u8); oo = fg_catn(out, oo, rc) 330 oo = fg_cat(out, oo, " rn=" as *u8); oo = fg_catn(out, oo, rn) 331 out[oo] = 10 as u8; oo = oo + 1 332 } 333 } 334 i = end + 1 335 } 336 337 if argc > 4 { 338 let fd: i64 = sys_openat_wr(argv[4] as *u8, 0x1a4) 339 if fd >= 0 { sys_write(fd, out, oo); sys_close(fd) } 340 fg_puts("# rows written: " as *u8); fg_puts(argv[4] as *u8) 341 fg_puts(" bytes=" as *u8); fg_num(oo); fg_puts("\n" as *u8) 342 } 343 344 // NON-VACUITY FLOOR: a census that graded nothing prints all-zero counts that read like a clean tree. 345 if ctr[FG_TOTAL] <= 0 { 346 fg_puts("NX-FORKGRADE verdict=RED reason=no-pairs-graded -- a zero census is not a clean census\n" as *u8) 347 sys_exit(1) 348 return 1 349 } 350 351 let partsum: i64 = ctr[FG_IDENT] + ctr[FG_ASUP] + ctr[FG_BSUP] + ctr[FG_BIDI] + ctr[FG_UNREAD] 352 fg_puts("# FORKGRADE total=" as *u8); fg_num(ctr[FG_TOTAL]) 353 fg_puts(" identical=" as *u8); fg_num(ctr[FG_IDENT]) 354 fg_puts(" a_superset=" as *u8); fg_num(ctr[FG_ASUP]) 355 fg_puts(" b_superset=" as *u8); fg_num(ctr[FG_BSUP]) 356 fg_puts(" bidirectional=" as *u8); fg_num(ctr[FG_BIDI]) 357 fg_puts(" unreadable=" as *u8); fg_num(ctr[FG_UNREAD]) 358 fg_puts(" bidi_fully_paired=" as *u8); fg_num(ctr[FG_BIDI_PAIRED0]) 359 fg_puts("\n" as *u8) 360 // MERGE BURDEN, and BOTH bucket sets must sum to the total or the shape is unreliable. 361 let sbsum: i64 = ctr[FG_SB0] + ctr[FG_SB1_3] + ctr[FG_SB4_20] + ctr[FG_SB21] 362 let busum: i64 = ctr[FG_BU0] + ctr[FG_BU1_3] + ctr[FG_BU4_20] + ctr[FG_BU21] 363 fg_puts("# FORKGRADE surplusB_lines 0=" as *u8); fg_num(ctr[FG_SB0]) 364 fg_puts(" 1to3=" as *u8); fg_num(ctr[FG_SB1_3]) 365 fg_puts(" 4to20=" as *u8); fg_num(ctr[FG_SB4_20]) 366 fg_puts(" over20=" as *u8); fg_num(ctr[FG_SB21]) 367 // of= IS THE DENOMINATOR, PRINTED. A sum with no stated population is an assertion the reader has 368 // to reconstruct, and reconstructing it wrongly is how "sum=43" read as a defect for every run that 369 // contained an unreadable file. unparsed= names the gap between the graded pairs and the bucketed 370 // ones, so a ruler that stops emitting its anchored line is VISIBLE instead of silently shrinking 371 // the population. UNKNOWN IS ITS OWN BUCKET. 372 fg_puts(" sum=" as *u8); fg_num(sbsum) 373 fg_puts(" of=" as *u8); fg_num(ctr[FG_SB_SEEN]) 374 fg_puts(" unparsed=" as *u8); fg_num(ctr[FG_TOTAL] - ctr[FG_SB_SEEN]) 375 if sbsum != ctr[FG_SB_SEEN] { fg_puts(" UNSOUND" as *u8) } 376 fg_puts("\n" as *u8) 377 fg_puts("# FORKGRADE unpaired_after_pairing 0=" as *u8); fg_num(ctr[FG_BU0]) 378 fg_puts(" 1to3=" as *u8); fg_num(ctr[FG_BU1_3]) 379 fg_puts(" 4to20=" as *u8); fg_num(ctr[FG_BU4_20]) 380 fg_puts(" over20=" as *u8); fg_num(ctr[FG_BU21]) 381 fg_puts(" sum=" as *u8); fg_num(busum) 382 fg_puts(" of=" as *u8); fg_num(ctr[FG_BU_SEEN]) 383 fg_puts(" unparsed=" as *u8); fg_num(ctr[FG_TOTAL] - ctr[FG_BU_SEEN]) 384 if busum != ctr[FG_BU_SEEN] { fg_puts(" UNSOUND" as *u8) } 385 fg_puts("\n" as *u8) 386 fg_puts("# FORKGRADE partition_sum=" as *u8); fg_num(partsum) 387 if partsum == ctr[FG_TOTAL] { fg_puts(" == total OK\n" as *u8) } 388 if partsum != ctr[FG_TOTAL] { fg_puts(" != total UNSOUND\n" as *u8) } 389 if partsum != ctr[FG_TOTAL] { 390 fg_puts("NX-FORKGRADE verdict=RED reason=partition-unsound\n" as *u8) 391 sys_exit(1) 392 return 1 393 } 394 // CE8: the trajectory row, written only AFTER the partition is proven sound -- a row from an unsound census 395 // would be a fabricated point on the trend. 396 fg_trend(ctr, dirA) 397 fg_puts("NX-FORKGRADE verdict=GREEN graded=" as *u8); fg_num(ctr[FG_TOTAL]) 398 fg_puts(" adoptable_by_proof=" as *u8); fg_num(ctr[FG_IDENT] + ctr[FG_ASUP]) 399 fg_puts("\n" as *u8) 400 sys_exit(0) 401 return 0 402}