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}