code wiki / _hdl_build / nx_reconcile.nx

nx_reconcile.nx source

↩ module page · 257 lines · 11757 B

1// nx_reconcile.nx -- the AUTONOMOUS STATUS-KEEPER (X-Q-005): DONE flips on 2// EVIDENCE, with no session. For every TODO/WIP queue row whose gate cell 3// carries the machine suffix 4// ...||MARK=<logpath>::<anchor>::<green-substring> 5// evaluate it exactly like the examiner/rung-grade family (the LAST <anchor> 6// line in <logpath> must contain <green-substring>); if GREEN, rewrite that 7// row's status to DONE in place. Rows without a MARK suffix are untouched 8// (X-Q-003b migrates them); NOVEL rows are NEVER auto-flipped (tutor work is 9// judged by its own gates after the visit). Deps untouched -- the sequencer 10// re-derives readiness next beat. Appends RECONCILE rows to 11// knowledge/status/reconcile.log; rewrite is whole-file (read -> patch in 12// memory -> write), safe at this row count. 13// argv[1] = queue override (gates use a scratch queue). 14// license_tier: ORIGINAL 15import "nx_registry_lock.nx" // lib-only (nx_syscalls transitive); rt_lock/rt_unlock for the shared queue lock 16import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 17const K_MAGIC_524288: i64 = 524288 18const K_MAGIC_1048576: i64 = 1048576 19 20func rc_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 21// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 22// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 23// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 24// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 25func rc_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } 26 27// ARK-GATE-001: when a reconcile pass flips >=1 rung TODO->DONE, a rung was 28// ACHIEVED this beat -> stamp a FRESH ark-gate marker (open-wr truncate, one 29// deterministic line). nx_ark_push reads epoch=<sec> + verdict=ACHIEVED and 30// pushes ONCE (its own ark_push.state one-shot guard). flips==0 leaves the 31// marker UNTOUCHED so it goes stale on its own -> un-gated beats HOLD. Same 32// epoch=<sec> shape ARKPUSH/RECONCILE already emit, so existing readers are happy. 33func rc_stamp_ark_marker(flips: i64) -> i64 { 34 let fd: i64 = sys_openat_wr("knowledge/status/ark_gate.marker" as *u8, 0x1a4) 35 if fd < 0 { return 0 } 36 rc_w(fd, "ARKGATE epoch=" as *u8); rc_wn(fd, sys_now_realtime_sec()) 37 rc_w(fd, " verdict=ACHIEVED flipped=" as *u8); rc_wn(fd, flips) 38 rc_w(fd, " source=reconcile\n" as *u8) 39 sys_close(fd) 40 return 1 41} 42 43func rc_read(path: *u8, buf: *u8, cap: i64) -> i64 { 44 let fd: i64 = sys_openat_rd(path) 45 if fd < 0 { return 0 } 46 var n: i64 = 0 47 var r: i64 = sys_read(fd, buf, cap - 1) 48 while r > 0 { n = n + r; if n >= cap - 1 { r = 0 } else { r = sys_read(fd, buf + n, cap - 1 - n) } } 49 sys_close(fd) 50 return n 51} 52 53func rc_at(buf: *u8, n: i64, i: i64, pat: *u8, pl: i64) -> i64 { 54 if i + pl > n { return 0 } 55 var k: i64 = 0 56 while k < pl { if buf[i + k] != pat[k] { return 0 } k = k + 1 } 57 return 1 58} 59 60func rc_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 61 62// build "<qp>.lock" into out -- the shared queue lock (live queue => assignment_queue.tsv.lock). 63func rc_lockpath(qp: *u8, out: *u8) -> i64 { 64 var i: i64 = 0 65 while qp[i] != (0 as u8) { out[i] = qp[i]; i = i + 1 } 66 let s: *u8 = ".lock" as *u8 67 var k: i64 = 0 68 while s[k] != (0 as u8) { out[i] = s[k]; i = i + 1; k = k + 1 } 69 out[i] = 0 as u8 70 return 0 71} 72 73// last <anchor> line in logpath contains green? 1/0; -1 log-or-anchor absent 74func rc_eval(logpath: *u8, anchor: *u8, green: *u8) -> i64 { 75 let buf: *u8 = sys_mmap(K_MAGIC_524288) 76 let n: i64 = rc_read(logpath, buf, K_MAGIC_524288) 77 if n <= 0 { return 0 - 1 } 78 let al: i64 = rc_len(anchor) 79 let gl: i64 = rc_len(green) 80 var found: i64 = 0 81 var ls: i64 = 0 82 var fs2: i64 = 0 83 var fe2: i64 = 0 84 var i: i64 = 0 85 while i <= n { 86 var eol: i64 = 0 87 if i == n { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } } 88 if eol == 1 { 89 var j: i64 = ls 90 while j < i { 91 if rc_at(buf, n, j, anchor, al) == 1 { found = 1; fs2 = ls; fe2 = i; j = i } else { j = j + 1 } 92 } 93 ls = i + 1 94 } 95 i = i + 1 96 } 97 if found == 0 { return 0 - 1 } 98 var q: i64 = fs2 99 while q < fe2 { 100 if rc_at(buf, n, q, green, gl) == 1 { return 1 } 101 q = q + 1 102 } 103 return 0 104} 105 106func main(argc: i64, argv: *i64) -> i64 { 107 var qp: *u8 = "knowledge/registry/assignment_queue.tsv" as *u8 108 // is_live: reconcile is running against the REAL queue (no argv override). 109 // Only a LIVE reconcile arms the ark-gate marker; a gate that passes a 110 // scratch queue (argv[1]) must NEVER arm a real push (ARK-GATE-001). 111 var is_live: i64 = 1 112 if argc >= 2 { qp = argv[1] as *u8; is_live = 0 } 113 // ACQUIRE the SHARED queue lock around the whole read-modify-write of qp (serializes against 114 // nx_novel_close + the other writers); degrade-gracefully on giveup (-1 -> proceed unlocked, 115 // never wedge; O_EXCL stale-steal bounds a crashed holder to 30s). 116 let lockpath: *u8 = sys_mmap(512) 117 rc_lockpath(qp, lockpath) 118 let qlk: i64 = rt_lock(lockpath) 119 let buf: *u8 = sys_mmap(K_MAGIC_1048576) 120 let n: i64 = rc_read(qp, buf, K_MAGIC_1048576) 121 if n <= 0 { if qlk >= 0 { rt_unlock(lockpath, qlk) } rc_w(1, "RECONCILE verdict=RED reason=queue-missing\n" as *u8); sys_exit(101) } 122 let lf: i64 = sys_openat_append("knowledge/status/reconcile.log" as *u8, 0x1a4) 123 let lpath: *u8 = sys_mmap(512) 124 let anch: *u8 = sys_mmap(256) 125 let grn: *u8 = sys_mmap(256) 126 var flips: i64 = 0 127 var marked: i64 = 0 128 var ls: i64 = 0 129 var i: i64 = 0 130 while i <= n { 131 var eol: i64 = 0 132 if i == n { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } } 133 if eol == 1 { 134 // line [ls, i): find "\tTODO\t" or "\tWIP\t" position + "||MARK=". 135 // ||ASK=result rows are SUBJECTIVE deliverables (executive-sponsor 136 // doctrine 2026-06-11): objective evidence green -> flip to VIEW 137 // (sponsor viewpoint queue, same 4-byte in-place write), never 138 // straight to DONE -- only nx_sponsor_review's APPROVE does that. 139 var stp: i64 = 0 - 1 140 var iswip: i64 = 0 141 var mk: i64 = 0 - 1 142 var askr: i64 = 0 143 var j: i64 = ls 144 while j < i { 145 if rc_at(buf, n, j, "\tTODO\t" as *u8, 6) == 1 { if stp < 0 { stp = j } } 146 if rc_at(buf, n, j, "\tWIP\t" as *u8, 5) == 1 { if stp < 0 { stp = j; iswip = 1 } } 147 if rc_at(buf, n, j, "||MARK=" as *u8, 7) == 1 { mk = j + 7 } 148 if rc_at(buf, n, j, "||ASK=result" as *u8, 12) == 1 { askr = 1 } 149 j = j + 1 150 } 151 if stp >= 0 { 152 if mk >= 0 { 153 marked = marked + 1 154 // parse <log>::<anchor>::<green> ending at tab or eol 155 var p: i64 = mk 156 var k: i64 = 0 157 var go: i64 = 1 158 while go == 1 { 159 if p >= i { go = 0 } else { 160 if rc_at(buf, n, p, "::" as *u8, 2) == 1 { go = 0 } else { 161 if k < 511 { lpath[k] = buf[p]; k = k + 1 } 162 p = p + 1 163 } 164 } 165 } 166 lpath[k] = 0 as u8 167 if p < i { p = p + 2 } 168 k = 0 169 go = 1 170 while go == 1 { 171 if p >= i { go = 0 } else { 172 if rc_at(buf, n, p, "::" as *u8, 2) == 1 { go = 0 } else { 173 if k < 255 { anch[k] = buf[p]; k = k + 1 } 174 p = p + 1 175 } 176 } 177 } 178 anch[k] = 0 as u8 179 if p < i { p = p + 2 } 180 // green ends at TAB (field end) OR at "||" (the next suffix, 181 // e.g. ||EXEC=) -- without the || stop, an EXEC suffix is 182 // swallowed into green and never matches (caught live: 183 // X-EX-001 marked but flipped=0). 184 k = 0 185 go = 1 186 while go == 1 { 187 if p >= i { go = 0 } else { 188 if buf[p] == (9 as u8) { go = 0 } else { 189 if rc_at(buf, n, p, "||" as *u8, 2) == 1 { go = 0 } else { 190 if k < 255 { grn[k] = buf[p]; k = k + 1 } 191 p = p + 1 192 } 193 } 194 } 195 } 196 grn[k] = 0 as u8 197 if rc_eval(lpath, anch, grn) == 1 { 198 // flip in place: TODO->DONE or TODO->VIEW (same length); 199 // WIP->DONE shifts -- handle TODO only v1, WIP said 200 if iswip == 0 { 201 if askr == 0 { 202 buf[stp + 1] = 68 as u8 // D 203 buf[stp + 2] = 79 as u8 // O 204 buf[stp + 3] = 78 as u8 // N 205 buf[stp + 4] = 69 as u8 // E 206 } else { 207 buf[stp + 1] = 86 as u8 // V 208 buf[stp + 2] = 73 as u8 // I 209 buf[stp + 3] = 69 as u8 // E 210 buf[stp + 4] = 87 as u8 // W 211 } 212 flips = flips + 1 213 rc_w(1, "RECONCILE-FLIP line-at=" as *u8); rc_wn(1, ls) 214 if askr == 0 { rc_w(1, " marker-green\n" as *u8) } else { rc_w(1, " marker-green -> VIEW (sponsor viewpoint)\n" as *u8) } 215 if lf >= 0 { 216 rc_w(lf, "RECONCILE-FLIP offset=" as *u8); rc_wn(lf, ls) 217 if askr == 0 { rc_w(lf, " -> DONE\n" as *u8) } else { rc_w(lf, " -> VIEW\n" as *u8) } 218 } 219 } else { 220 rc_w(1, "RECONCILE-WIP-GREEN (flip manually or via v2; WIP len differs)\n" as *u8) 221 } 222 } 223 } 224 } 225 ls = i + 1 226 } 227 i = i + 1 228 } 229 if flips > 0 { 230 let wf: i64 = sys_openat_wr(qp, 0x1a4) 231 if wf >= 0 { 232 var off: i64 = 0 233 while off < n { let w: i64 = sys_write(wf, buf + off, n - off); if w <= 0 { off = n } else { off = off + w } } 234 sys_close(wf) 235 } 236 // a rung was ACHIEVED this beat -> let the ark push once (ARK-GATE-001). 237 // ONLY when reconciling the LIVE queue; a scratch-queue gate run never 238 // arms a real push. 239 if is_live == 1 { rc_stamp_ark_marker(flips) } 240 } 241 if qlk >= 0 { rt_unlock(lockpath, qlk) } 242 var p2: i64 = 0 243 while p2 < 2 { 244 var fd: i64 = 1 245 if p2 == 1 { fd = lf } 246 if fd >= 0 { 247 rc_w(fd, "RECONCILE marked=" as *u8); rc_wn(fd, marked) 248 rc_w(fd, " flipped=" as *u8); rc_wn(fd, flips) 249 rc_w(fd, " epoch=" as *u8); rc_wn(fd, sys_now_realtime_sec()) 250 rc_w(fd, " verdict=GREEN\n" as *u8) 251 } 252 p2 = p2 + 1 253 } 254 if lf >= 0 { sys_close(lf) } 255 sys_exit(0) 256 return 0 257}