code wiki / _hdl_build / nx_ale_gap_filer.nx

nx_ale_gap_filer.nx source

↩ module page · 224 lines · 9764 B

1// nx_ale_gap_filer.nx -- ALE-R5 autonomy: turns the flywheel's gap LEDGER (knowledge/status/ 2// ale_gaps.tsv lines "ALEGAP<TAB>task=<path><TAB>exit=<rc><TAB>score=<n>") into queue RUNGS so the 3// RSI loop spins itself HANDS-OFF: flywheel detects a gap -> ledgers it -> THIS files a NOVEL 4// "ALE-GAP-<id>" assignment (id = ALE-GAP- + sanitized task basename) -> the team builds the missing 5// capability -> re-attempt closes the gap. IDEMPOTENT: an ALE-GAP-<id> already in the queue is 6// skipped, and duplicate ledger lines collapse to ONE rung (so re-running every beat adds 0 dupes). 7// LOCK-PROTECTED append (shared assignment_queue.tsv.lock) so it never races the beat. 8// argv[1] = queue-path override (a scratch queue, no lock) for dry-run; no-arg => LIVE. 9// BAKED SELF-TEST FIRST (scratch ledger w/ a DUP + scratch queue): control_pos files exactly 1, 10// control_idem re-run files 0; mismatch -> RED + nonzero exit, the real queue UNTOUCHED. 11// MAINLESS-LIB import convention (nx_registry_lock pulls nx_syscalls transitively; do NOT also 12// import nx_syscalls). license_tier: ORIGINAL 13// 14// module: nishi-core.ale.gap_filer 15// depends: nishi-core.autonomy.registry_lock 16// capability: ALE_GAP_AUTOFILE 17import "nx_registry_lock.nx" 18import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 19const GF_MAGIC_262144: i64 = 262144 20const GF_MAGIC_131072: i64 = 131072 21const GF_MAGIC_2048: i64 = 2048 22const GF_MAGIC_65536: i64 = 65536 23 24const GF_QCAP: i64 = 1048576 25 26func gf_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 } 27// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 28// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 29// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 30// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 31func gf_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } 32func gf_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 33 34func gf_read(path: *u8, buf: *u8, cap: i64) -> i64 { 35 let fd: i64 = sys_openat_rd(path) 36 if fd < 0 { return 0 } 37 var n: i64 = 0 38 var go: i64 = 1 39 while go == 1 { 40 let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n) 41 if r <= 0 { go = 0 } else { n = n + r } 42 if n >= cap - 1 { go = 0 } 43 } 44 sys_close(fd) 45 return n 46} 47 48func gf_write_file(path: *u8, s: *u8) -> i64 { 49 __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) 50 let fd: i64 = sys_openat_wr(path, 0x1a4) 51 if fd < 0 { return 0 } 52 sys_write(fd, s, gf_len(s)) 53 sys_close(fd) 54 return 1 55} 56 57// out = "ALE-GAP-" + sanitized(basename(path) minus ".txt"); returns id length. 58func gf_basename_id(path: *u8, out: *u8) -> i64 { 59 let pl: i64 = gf_len(path) 60 var start: i64 = 0 61 var i: i64 = 0 62 while i < pl { if path[i] == (47 as u8) { start = i + 1 } i = i + 1 } 63 var end: i64 = pl 64 if end - start > 4 { 65 if path[end-4] == (46 as u8) { if path[end-3] == (116 as u8) { if path[end-2] == (120 as u8) { if path[end-1] == (116 as u8) { end = end - 4 } } } } 66 } 67 var o: i64 = 0 68 let pre: *u8 = "ALE-GAP-" as *u8 69 var j: i64 = 0 70 while pre[j] != (0 as u8) { out[o] = pre[j]; o = o + 1; j = j + 1 } 71 var p: i64 = start 72 while p < end { 73 let c: i64 = path[p] as i64 74 var ok: i64 = 0 75 if c >= 48 { if c <= 57 { ok = 1 } } 76 if c >= 65 { if c <= 90 { ok = 1 } } 77 if c >= 97 { if c <= 122 { ok = 1 } } 78 if c == 95 { ok = 1 } 79 if c == 45 { ok = 1 } 80 if ok == 1 { out[o] = path[p] } else { out[o] = 45 as u8 } 81 o = o + 1 82 p = p + 1 83 } 84 out[o] = 0 as u8 85 return o 86} 87 88// does any line of buf[0,n) begin with id immediately followed by a TAB? 1/0. 89func gf_id_present(buf: *u8, n: i64, id: *u8) -> i64 { 90 let idl: i64 = gf_len(id) 91 var ls: i64 = 0 92 var i: i64 = 0 93 while i <= n { 94 var eol: i64 = 0 95 if i == n { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } } 96 if eol == 1 { 97 if ls + idl < i { 98 var mt: i64 = 1 99 var k: i64 = 0 100 while k < idl { if buf[ls+k] != id[k] { mt = 0; k = idl } else { k = k + 1 } } 101 if mt == 1 { if buf[ls+idl] == (9 as u8) { return 1 } } 102 } 103 ls = i + 1 104 } 105 i = i + 1 106 } 107 return 0 108} 109 110// extract the value of "task=" in ledger line [ls,le) into out (up to next tab/eol). 1/0 found. 111func gf_extract_task(buf: *u8, ls: i64, le: i64, out: *u8) -> i64 { 112 out[0] = 0 as u8 113 let key: *u8 = "task=" as *u8 114 var p: i64 = ls 115 var found: i64 = 0 - 1 116 while p + 5 <= le { 117 var mt: i64 = 1 118 var k: i64 = 0 119 while k < 5 { if buf[p+k] != key[k] { mt = 0; k = 5 } else { k = k + 1 } } 120 if mt == 1 { found = p + 5; p = le } else { p = p + 1 } 121 } 122 if found < 0 { return 0 } 123 var o: i64 = 0 124 var q: i64 = found 125 while q < le { if buf[q] == (9 as u8) { q = le } else { out[o] = buf[q]; o = o + 1; q = q + 1 } } 126 out[o] = 0 as u8 127 return 1 128} 129 130// read ledger, file a NOVEL ALE-GAP-<id> rung per UNIQUE not-yet-present gap task; return count filed. 131// caller holds the queue lock when qpath is the live queue. 132func gf_process(ledgerpath: *u8, qpath: *u8) -> i64 { 133 let lbuf: *u8 = sys_mmap(GF_MAGIC_262144) 134 let ln: i64 = gf_read(ledgerpath, lbuf, GF_MAGIC_262144) 135 if ln <= 0 { return 0 } 136 let qbuf: *u8 = sys_mmap(GF_QCAP) 137 let qn: i64 = gf_read(qpath, qbuf, GF_QCAP) 138 let seen: *u8 = sys_mmap(GF_MAGIC_131072) 139 var sn: i64 = 0 140 let qf: i64 = sys_openat_append(qpath, 0x1a4) 141 if qf < 0 { return 0 } 142 var filed: i64 = 0 143 let id: *u8 = sys_mmap(512) 144 let task: *u8 = sys_mmap(GF_MAGIC_2048) 145 var ls: i64 = 0 146 var i: i64 = 0 147 while i <= ln { 148 var eol: i64 = 0 149 if i == ln { eol = 1 } else { if lbuf[i] == (10 as u8) { eol = 1 } } 150 if eol == 1 { 151 if i > ls { if lbuf[ls] == (65 as u8) { 152 if gf_extract_task(lbuf, ls, i, task) == 1 { 153 if gf_len(task) > 0 { 154 gf_basename_id(task, id) 155 var inq: i64 = gf_id_present(qbuf, qn, id) 156 var insn: i64 = gf_id_present(seen, sn, id) 157 if inq == 0 { if insn == 0 { 158 gf_w(qf, id) 159 gf_w(qf, "\tALE\t7\tM\tBuilder\tNOVEL\tALE-R5\tflywheel-autofiled\tbuild the capability to solve flywheel-detected ALE gap: " as *u8) 160 gf_w(qf, task) 161 gf_w(qf, " (auto-filed by nx_ale_gap_filer from ale_gaps.tsv)\n" as *u8) 162 filed = filed + 1 163 // record id in the seen buffer as "<id>\t\n" so gf_id_present matches it 164 var t2: i64 = 0 165 while id[t2] != (0 as u8) { seen[sn] = id[t2]; sn = sn + 1; t2 = t2 + 1 } 166 seen[sn] = 9 as u8; sn = sn + 1 167 seen[sn] = 10 as u8; sn = sn + 1 168 } } 169 } 170 } 171 } } 172 ls = i + 1 173 } 174 i = i + 1 175 } 176 sys_close(qf) 177 return filed 178} 179 180func gf_selftest() -> i64 { 181 gf_write_file("/tmp/_gf_ledger.tsv" as *u8, "ALEGAP\ttask=knowledge/specs/ale_examples/task_gapctl.txt\texit=2\tscore=-1\nALEGAP\ttask=knowledge/specs/ale_examples/task_gapctl.txt\texit=2\tscore=-1\n" as *u8) 182 gf_write_file("/tmp/_gf_queue.tsv" as *u8, "# scratch queue head\n" as *u8) 183 let f1: i64 = gf_process("/tmp/_gf_ledger.tsv" as *u8, "/tmp/_gf_queue.tsv" as *u8) 184 var pos: i64 = 0 185 if f1 == 1 { pos = 1 } 186 let f2: i64 = gf_process("/tmp/_gf_ledger.tsv" as *u8, "/tmp/_gf_queue.tsv" as *u8) 187 var idem: i64 = 0 188 if f2 == 0 { idem = 1 } 189 // confirm the rung actually landed in the scratch queue 190 let qb: *u8 = sys_mmap(GF_MAGIC_65536) 191 let qn: i64 = gf_read("/tmp/_gf_queue.tsv" as *u8, qb, GF_MAGIC_65536) 192 var present: i64 = gf_id_present(qb, qn, "ALE-GAP-task_gapctl" as *u8) 193 gf_w(1, "SELFTEST control_pos=" as *u8); gf_wn(1, pos) 194 gf_w(1, " control_idem=" as *u8); gf_wn(1, idem) 195 gf_w(1, " rung_present=" as *u8); gf_wn(1, present) 196 var ok: i64 = 0 197 if pos == 1 { if idem == 1 { if present == 1 { ok = 1 } } } 198 if ok == 1 { gf_w(1, " verdict=GREEN\n" as *u8); return 1 } 199 gf_w(1, " verdict=RED\n" as *u8) 200 return 0 201} 202 203func main(argc: i64, argv: *i64) -> i64 { 204 if gf_selftest() == 0 { 205 gf_w(1, "GAPFILER verdict=RED reason=selftest-failed (real queue UNTOUCHED)\n" as *u8) 206 sys_exit(7); return 7 207 } 208 var qp: *u8 = "knowledge/registry/assignment_queue.tsv" as *u8 209 var is_live: i64 = 1 210 if argc >= 2 { qp = argv[1] as *u8; is_live = 0 } 211 var lk: i64 = 0 - 1 212 if is_live == 1 { lk = rt_lock("knowledge/registry/assignment_queue.tsv.lock" as *u8) } 213 let filed: i64 = gf_process("knowledge/status/ale_gaps.tsv" as *u8, qp) 214 if lk >= 0 { rt_unlock("knowledge/registry/assignment_queue.tsv.lock" as *u8, lk) } 215 gf_w(1, "GAPFILER filed=" as *u8); gf_wn(1, filed) 216 if is_live == 1 { gf_w(1, " mode=LIVE" as *u8) } else { gf_w(1, " mode=DRYRUN" as *u8) } 217 gf_w(1, " verdict=GREEN\n" as *u8) 218 if is_live == 1 { 219 let lf: i64 = sys_openat_append("knowledge/status/ale_gap_filer.log" as *u8, 0x1a4) 220 if lf >= 0 { gf_w(lf, "GAPFILER filed=" as *u8); gf_wn(lf, filed); gf_w(lf, " verdict=GREEN epoch=" as *u8); gf_wn(lf, sys_now_realtime_sec()); gf_w(lf, "\n" as *u8); sys_close(lf) } 221 } 222 sys_exit(0) 223 return 0 224}