code wiki / _hdl_build / nx_treediverge_beat.nx

nx_treediverge_beat.nx source

↩ module page · 168 lines · 8026 B

1// nx_treediverge_beat.nx -- no-arg BEAT for the ONE clock: the standing cross-tree divergence gauge 2// (debt 1785622769: divergence must be caught the day it appears, not by archaeology). Composes the 3// three PROVEN instruments through tr_run_capture -- zero detector logic duplicated: nx_treediff 4// regenerates the NAS manifest, nx_treediverge compares it against the authoring manifest (pushed 5// fresh at every session start by the treecanon SessionStart hook), nx_treecanon_gate re-runs the 6// canon verdict. APPENDS one evidence line per run to the trend log -- including BEAT-FAIL lines 7// (a beat with no evidence line is a claim; failure lines are evidence too). The BURN-DOWN is the 8// point: divergent= over time IS the 8,258-file fork shrinking, minable from the trend file. 9// Scheduler contract: the tickless clock (clock_jobs.tsv / the clockjobs- store plane) fork+execs 10// this elf with NO args from cwd nishihost. argv[1]=authoring-manifest argv[2]=trendfile override 11// the defaults for testing (argv[1] pointed at a missing file is the negative control: 12// nx_treediverge REFUSES by its own non-vacuity law and this beat must append BEAT-FAIL + exit 1, 13// never a comforting line). 14// trend line: <epoch>\tDIVERGE <envelope, newlines as ' | '>\tGATE rc=<rc> <verdict line>\n 15// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 16import "nx_sovjson_lib.nx" 17import "nx_syscalls.nx" 18import "nx_tool_run.nx" 19 20const TDB_DIVCAP: i64 = 4194304 // treediverge prints a row per divergent file (8,258 today, ~40B 21 // each = ~350KB); 4MB holds ~100k rows. AT the cap = truncated = 22 // the envelope (printed LAST) may be gone -> BEAT-FAIL, because a 23 // capped result is a FLOOR, never a count. 24const TDB_SMALLCAP: i64 = 16384 25const TDB_LINE: i64 = 4096 26const TDB_MODE: i64 = 420 27const TDB_TAB: i64 = 9 28const TDB_NL: i64 = 10 29const TDB_SPACE: i64 = 32 30const TDB_PIPE: i64 = 124 31 32func tdb_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 33func tdb_werr(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 } 34func tdb_append(path: *u8, buf: *u8, n: i64) -> i64 { 35 let fd: i64 = sys_openat_append(path, TDB_MODE) 36 if fd < 0 { return 0 - 1 } 37 sys_write(fd, buf, n) 38 sys_fsync(fd) 39 sys_close(fd) 40 return 0 41} 42// end index after stripping trailing newlines 43func tdb_tail_end(buf: *u8, n: i64) -> i64 { 44 var e: i64 = n 45 var go: i64 = 1 46 while go == 1 { if e > 0 { if buf[e-1] == (TDB_NL as u8) { e = e - 1 } else { go = 0 } } else { go = 0 } } 47 return e 48} 49// start index of the `want`-th line counting back from the (newline-stripped) end; 0-bounded 50func tdb_tail_start(buf: *u8, n: i64, want: i64) -> i64 { 51 let e: i64 = tdb_tail_end(buf, n) 52 var i: i64 = e 53 var cnt: i64 = 0 54 var go: i64 = 1 55 while go == 1 { 56 if i <= 0 { go = 0 } else { 57 if buf[i-1] == (TDB_NL as u8) { cnt = cnt + 1; if cnt >= want { go = 0 } } 58 if go == 1 { i = i - 1 } 59 } 60 } 61 return i 62} 63func tdb_contains(buf: *u8, n: i64, pat: *u8) -> i64 { 64 var pl: i64 = 0 65 while pat[pl] != (0 as u8) { pl = pl + 1 } 66 if pl == 0 { return 0 } 67 var i: i64 = 0 68 while i + pl <= n { 69 var j: i64 = 0 70 var hit: i64 = 1 71 while j < pl { if buf[i+j] != pat[j] { hit = 0; j = pl } else { j = j + 1 } } 72 if hit == 1 { return 1 } 73 i = i + 1 74 } 75 return 0 76} 77// copy [s,e) of buf into ln at o, rendering newlines as ' | '; bounded by TDB_LINE 78func tdb_cat_span(ln: *u8, o: i64, buf: *u8, s: i64, e: i64) -> i64 { 79 var p: i64 = o 80 var i: i64 = s 81 while i < e { 82 if p >= TDB_LINE - 4 { i = e } else { 83 if buf[i] == (TDB_NL as u8) { ln[p] = TDB_SPACE as u8; ln[p+1] = TDB_PIPE as u8; ln[p+2] = TDB_SPACE as u8; p = p + 3 } 84 else { ln[p] = buf[i]; p = p + 1 } 85 i = i + 1 86 } 87 } 88 return p 89} 90 91func main(argc: i64, argv: *i64) -> i64 { 92 var lapmf: *u8 = "knowledge/status/treecanon_laptop.mf" as *u8 93 var trend: *u8 = "knowledge/status/treediverge_trend.log" as *u8 94 if argc > 1 { lapmf = argv[1] as *u8 } 95 if argc > 2 { trend = argv[2] as *u8 } 96 let nasmf: *u8 = "knowledge/status/treecanon_nas.mf" as *u8 97 let conf: *u8 = "knowledge/tree_canon.conf" as *u8 98 let e_td: *u8 = "/volume1/homes/elderwesto/nishihost/nx_treediff.elf" as *u8 99 let e_dv: *u8 = "/volume1/homes/elderwesto/nishihost/nx_treediverge.elf" as *u8 100 let e_gt: *u8 = "/volume1/homes/elderwesto/nishihost/nx_treecanon_gate.elf" as *u8 101 102 // 1) fresh NAS manifest (also a durable artifact other instruments can read) 103 let av1: *i64 = sys_mmap(8*4) as *i64 104 av1[0] = e_td as i64; av1[1] = ("buildroot/runtime" as *u8) as i64; av1[2] = nasmf as i64; av1[3] = 0 105 let out1: *u8 = sys_mmap(TDB_SMALLCAP) 106 let ol1: *i64 = sys_mmap(16) as *i64 107 let rc1: i64 = tr_run_capture(e_td, av1, out1, TDB_SMALLCAP, ol1) 108 109 // 2) the divergence report (the burn-down number lives in its envelope, printed LAST) 110 let av2: *i64 = sys_mmap(8*6) as *i64 111 av2[0] = e_dv as i64; av2[1] = nasmf as i64; av2[2] = lapmf as i64 112 av2[3] = ("NAS" as *u8) as i64; av2[4] = ("LAPTOP" as *u8) as i64; av2[5] = 0 113 let out2: *u8 = sys_mmap(TDB_DIVCAP) 114 let ol2: *i64 = sys_mmap(16) as *i64 115 let rc2: i64 = tr_run_capture(e_dv, av2, out2, TDB_DIVCAP, ol2) 116 117 // 3) the canon verdict (RED here is EVIDENCE, not a beat failure -- the build lane enforces; 118 // this beat only measures, so its own exit stays 0 as long as the evidence was captured) 119 let av3: *i64 = sys_mmap(8*5) as *i64 120 av3[0] = e_gt as i64; av3[1] = nasmf as i64; av3[2] = lapmf as i64; av3[3] = conf as i64; av3[4] = 0 121 let out3: *u8 = sys_mmap(TDB_SMALLCAP) 122 let ol3: *i64 = sys_mmap(16) as *i64 123 let rc3: i64 = tr_run_capture(e_gt, av3, out3, TDB_SMALLCAP, ol3) 124 125 var ok: i64 = 1 126 if rc1 != 0 { ok = 0 } 127 if ol2[0] <= 0 { ok = 0 } 128 if ol2[0] >= TDB_DIVCAP - 1 { ok = 0 } 129 if tdb_contains(out2, ol2[0], "verdict=" as *u8) == 0 { ok = 0 } 130 // MEASURED (first negative-control run): a REFUSED report also prints "verdict=REFUSED", so 131 // contains("verdict=") alone waved a blind gauge through as OK. treediverge's exit codes are 132 // rc=1 for a successful DIVERGENT compare and rc=3 for REFUSED, so rc can't be the ok-signal 133 // either without baking in its full code table -- anchor on the tool's own refusal vocabulary. 134 if tdb_contains(out2, ol2[0], "verdict=REFUSED" as *u8) == 1 { ok = 0 } 135 if ol3[0] <= 0 { ok = 0 } 136 137 let ln: *u8 = sys_mmap(TDB_LINE) 138 var o: i64 = sj_catn(ln, 0, sys_now_realtime_sec()) 139 ln[o] = TDB_TAB as u8; o = o + 1 140 if ok == 1 { 141 o = sj_cat(ln, o, "DIVERGE " as *u8) 142 o = tdb_cat_span(ln, o, out2, tdb_tail_start(out2, ol2[0], 3), tdb_tail_end(out2, ol2[0])) 143 ln[o] = TDB_TAB as u8; o = o + 1 144 o = sj_cat(ln, o, "GATE rc=" as *u8) 145 o = sj_catn(ln, o, rc3) 146 ln[o] = TDB_SPACE as u8; o = o + 1 147 o = tdb_cat_span(ln, o, out3, tdb_tail_start(out3, ol3[0], 1), tdb_tail_end(out3, ol3[0])) 148 } else { 149 o = sj_cat(ln, o, "BEAT-FAIL treediff_rc=" as *u8) 150 o = sj_catn(ln, o, rc1) 151 o = sj_cat(ln, o, " diverge_rc=" as *u8) 152 o = sj_catn(ln, o, rc2) 153 o = sj_cat(ln, o, " diverge_outlen=" as *u8) 154 o = sj_catn(ln, o, ol2[0]) 155 o = sj_cat(ln, o, " gate_rc=" as *u8) 156 o = sj_catn(ln, o, rc3) 157 } 158 ln[o] = TDB_NL as u8; o = o + 1 159 if tdb_append(trend, ln, o) != 0 { tdb_werr("TREEDIVERGE-BEAT FAIL cannot append trend file\n" as *u8); sys_exit(1); return 1 } 160 if ok == 1 { 161 tdb_puts("TREEDIVERGE-BEAT OK appended evidence line\n" as *u8) 162 sys_exit(0) 163 return 0 164 } 165 tdb_werr("TREEDIVERGE-BEAT FAIL (BEAT-FAIL line appended as evidence)\n" as *u8) 166 sys_exit(1) 167 return 1 168}