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}