code wiki / _hdl_build / nx_wd_core.nx
nx_wd_core.nx source
↩ module page · 440 lines · 16519 B
1// nx_wd_core.nx -- DISPATCH-ACT CORE (mainless): the Warden's attempt loop
2// over the assignment queue (X-Q-003 rung 1; spec 2026-06-11-sclass-exceed-
3// backlog.md ADDENDUM Q3). RACI: Warden verb = ATTEMPT/route. This core
4// owns NO priority (PM's w column), NO sequencing policy (an_pick is the
5// Conductor's, imported), NO done-judging (nx_reconcile owns DONE flips on
6// MARK evidence -- this core RUNS reconcile and READS the queue back).
7//
8// THE BEAT (wd_beat), bounded to <=1 raise + <=1 attempt per call:
9// pick = an_pick (highest-w runnable TODO)
10// pick has ||EXEC=<organ> recipe -> ATTEMPT: lane-build+run the organ
11// (deadline-guarded, hang-proof), run nx_reconcile, re-read the queue;
12// row DONE -> ASSIGNRESULT verdict=GREEN
13// row !DONE -> flip row RED (fail-loud, no livelock) + verdict=RED
14// pick has no recipe -> RAISE: flip row TODO->NOVEL (tutor batch picks it
15// up -- "a RAISED-HAND row instead of a silent stall") AND still
16// attempt the best EXEC-covered runnable row so the beat does work.
17// Rung 2 (filed as its own queue row): covered-SHAPE routing via the warden
18// pattern classifier -> emitter lanes; this rung covers the EXEC-recipe class
19// (the X-EX-001 convention) only, and ASSIGNRESULT route= says which.
20//
21// Trust boundary (defensive at boundary law): EXEC basenames come from a
22// hand-curated TSV but are validated anyway -- [a-z0-9_] only, 1..63 chars,
23// so a recipe can never path-escape the lane's source resolution.
24// Queue rewrites change line length (TODO->NOVEL grows, TODO->RED shrinks);
25// sys_openat_wr has no O_TRUNC, so shrink pads trailing '\n' bytes -- an_load
26// and the line-walkers all skip empty lines (verified against their parsers).
27// license_tier: ORIGINAL
28
29import "nx_syscalls.nx"
30import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
31import "nx_assign_core.nx"
32import "nx_guarded_run.nx"
33const WD_MAGIC_1048576: i64 = 1048576
34const WD_MAGIC_1048575: i64 = 1048575
35const WD_MAGIC_4096: i64 = 4096
36const WD_MAGIC_4095: i64 = 4095
37
38const WD_DEADLINE_DEFAULT: i64 = 600000 // ms; overridden by work_dispatcher.conf exec_deadline_ms=
39const WD_EXECCAP: i64 = 64
40
41// wcx block: wcx[0]=queue path wcx[1]=results log path wcx[2]=lane elf path
42// wcx[3]=reconcile elf path wcx[4]=deadline ms wcx[5]=open results fd
43// wcx[6]=auto-builder elf path (the SPEC route: X-Q-006 rung "rows that BUILD")
44func wd_newwcx(qp: *u8, respath: *u8, lane: *u8, rec: *u8, ab: *u8, dl: i64) -> *i64 {
45 let w: *i64 = sys_mmap(64) as *i64
46 w[0] = qp as i64
47 w[1] = respath as i64
48 w[2] = lane as i64
49 w[3] = rec as i64
50 w[4] = dl
51 w[5] = 0 - 1
52 w[6] = ab as i64
53 return w
54}
55
56func wd_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 }
57// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
58// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
59// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
60// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
61func wd_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
62
63func wd_both(wcx: *i64, s: *u8) -> i64 {
64 wd_w(1, s)
65 let fd: i64 = wcx[5]
66 if fd >= 0 { wd_w(fd, s) }
67 return 0
68}
69
70func wd_bothn(wcx: *i64, v: i64) -> i64 {
71 wd_wn(1, v)
72 let fd: i64 = wcx[5]
73 if fd >= 0 { wd_wn(fd, v) }
74 return 0
75}
76
77// pattern-at: does buf[i..] start with pat (pl bytes)? (rc_at shape)
78func wd_at(buf: *u8, n: i64, i: i64, pat: *u8, pl: i64) -> i64 {
79 if i + pl > n { return 0 }
80 var k: i64 = 0
81 while k < pl { if buf[i + k] != pat[k] { return 0 } k = k + 1 }
82 return 1
83}
84
85// extract ||EXEC=<basename> from a gate cell into out (cap WD_EXECCAP).
86// returns 1 valid recipe, 0 none-or-invalid. Charset law: [a-z0-9_] only.
87func wd_exec_name(gate: *u8, out: *u8) -> i64 {
88 var gl: i64 = 0
89 while gate[gl] != (0 as u8) { gl = gl + 1 }
90 var i: i64 = 0
91 var at: i64 = 0 - 1
92 while i < gl {
93 if wd_at(gate, gl, i, "||EXEC=" as *u8, 7) == 1 { at = i + 7; i = gl } else { i = i + 1 }
94 }
95 if at < 0 { return 0 }
96 var k: i64 = 0
97 var go: i64 = 1
98 while go == 1 {
99 let ch: i64 = gate[at + k] as i64
100 var stop: i64 = 0
101 if ch == 0 { stop = 1 }
102 if ch == 9 { stop = 1 }
103 if ch == 124 { stop = 1 }
104 if stop == 1 { go = 0 } else {
105 var okc: i64 = 0
106 if ch >= 97 { if ch <= 122 { okc = 1 } }
107 if ch >= 48 { if ch <= 57 { okc = 1 } }
108 if ch == 95 { okc = 1 }
109 if okc == 0 { return 0 }
110 if k >= WD_EXECCAP - 1 { return 0 }
111 out[k] = ch as u8
112 k = k + 1
113 }
114 }
115 if k == 0 { return 0 }
116 out[k] = 0 as u8
117 return 1
118}
119
120const WD_SPECCAP: i64 = 192
121
122// extract ||SPEC=<specfile> from a gate cell into out (cap WD_SPECCAP).
123// returns 1 valid, 0 none-or-invalid. Trust boundary: the path must live
124// under knowledge/specs/ (no traversal -- ".." rejected), charset
125// [a-z0-9_/.-] only, so a row can never route the builder outside the tree.
126func wd_spec_name(gate: *u8, out: *u8) -> i64 {
127 var gl: i64 = 0
128 while gate[gl] != (0 as u8) { gl = gl + 1 }
129 var i: i64 = 0
130 var at: i64 = 0 - 1
131 while i < gl {
132 if wd_at(gate, gl, i, "||SPEC=" as *u8, 7) == 1 { at = i + 7; i = gl } else { i = i + 1 }
133 }
134 if at < 0 { return 0 }
135 var k: i64 = 0
136 var go: i64 = 1
137 while go == 1 {
138 let ch: i64 = gate[at + k] as i64
139 var stop: i64 = 0
140 if ch == 0 { stop = 1 }
141 if ch == 9 { stop = 1 }
142 if ch == 124 { stop = 1 }
143 if stop == 1 { go = 0 } else {
144 var okc: i64 = 0
145 if ch >= 97 { if ch <= 122 { okc = 1 } }
146 if ch >= 48 { if ch <= 57 { okc = 1 } }
147 if ch == 95 { okc = 1 }
148 if ch == 47 { okc = 1 }
149 if ch == 46 { okc = 1 }
150 if ch == 45 { okc = 1 }
151 if okc == 0 { return 0 }
152 if k >= WD_SPECCAP - 1 { return 0 }
153 out[k] = ch as u8
154 k = k + 1
155 }
156 }
157 if k == 0 { return 0 }
158 out[k] = 0 as u8
159 if wd_at(out, k, 0, "knowledge/specs/" as *u8, 16) != 1 { return 0 }
160 var j: i64 = 0
161 while j + 1 < k {
162 if out[j] == (46 as u8) { if out[j + 1] == (46 as u8) { return 0 } }
163 j = j + 1
164 }
165 return 1
166}
167
168// is row r covered (carries a valid EXEC recipe or SPEC build)?
169func wd_covered(cx: *i64, r: i64) -> i64 {
170 let tmp: *u8 = sys_mmap(WD_SPECCAP)
171 if wd_exec_name(an_gate_at(cx, r), tmp) == 1 { return 1 }
172 if wd_spec_name(an_gate_at(cx, r), tmp) == 1 { return 1 }
173 return 0
174}
175
176// rewrite the queue file flipping row <id>'s status field (field index 5) to
177// newst. Whole-file read -> patch into a second buffer -> write back; if the
178// patched file is SHORTER, pad with '\n' (no O_TRUNC in the syscall wrapper;
179// empty lines are invisible to every queue parser). Returns 1 flipped, 0 not.
180func wd_flip_status(qp: *u8, id: *u8, newst: *u8) -> i64 {
181 let buf: *u8 = sys_mmap(WD_MAGIC_1048576)
182 let fd: i64 = sys_openat_rd(qp)
183 if fd < 0 { return 0 }
184 var n: i64 = 0
185 var r: i64 = sys_read(fd, buf, WD_MAGIC_1048575)
186 while r > 0 { n = n + r; r = sys_read(fd, buf + n, WD_MAGIC_1048575 - n) }
187 sys_close(fd)
188 var idl: i64 = 0
189 while id[idl] != (0 as u8) { idl = idl + 1 }
190 var nsl: i64 = 0
191 while newst[nsl] != (0 as u8) { nsl = nsl + 1 }
192 let out: *u8 = sys_mmap(WD_MAGIC_1048576 + 64)
193 var o: i64 = 0
194 var hit: i64 = 0
195 var ls: i64 = 0
196 var i: i64 = 0
197 while i <= n {
198 var eol: i64 = 0
199 if i == n { eol = 1 } else { if buf[i] == (10 as u8) { eol = 1 } }
200 if eol == 1 {
201 var mrow: i64 = 0
202 if wd_at(buf, n, ls, id, idl) == 1 {
203 if ls + idl < n { if buf[ls + idl] == (9 as u8) { mrow = 1 } }
204 }
205 if mrow == 1 {
206 if hit == 0 {
207 hit = 1
208 // copy fields 0..4 verbatim (5 tabs), swap field 5, copy rest
209 var tabs: i64 = 0
210 var j: i64 = ls
211 while tabs < 5 {
212 if j >= i { tabs = 5 } else {
213 out[o] = buf[j]
214 if buf[j] == (9 as u8) { tabs = tabs + 1 }
215 o = o + 1
216 j = j + 1
217 }
218 }
219 var k: i64 = 0
220 while k < nsl { out[o] = newst[k]; o = o + 1; k = k + 1 }
221 // skip the old status field content
222 var sk: i64 = 1
223 while sk == 1 {
224 if j >= i { sk = 0 } else {
225 if buf[j] == (9 as u8) { sk = 0 } else { j = j + 1 }
226 }
227 }
228 while j < i { out[o] = buf[j]; o = o + 1; j = j + 1 }
229 } else {
230 var j2: i64 = ls
231 while j2 < i { out[o] = buf[j2]; o = o + 1; j2 = j2 + 1 }
232 }
233 } else {
234 var j3: i64 = ls
235 while j3 < i { out[o] = buf[j3]; o = o + 1; j3 = j3 + 1 }
236 }
237 if i < n { out[o] = 10 as u8; o = o + 1 }
238 ls = i + 1
239 }
240 i = i + 1
241 }
242 if hit == 0 { return 0 }
243 while o < n { out[o] = 10 as u8; o = o + 1 }
244 let wf: i64 = sys_openat_wr(qp, 0x1a4)
245 if wf < 0 { return 0 }
246 var off: i64 = 0
247 while off < o { let w2: i64 = sys_write(wf, out + off, o - off); if w2 <= 0 { off = o } else { off = off + w2 } }
248 sys_close(wf)
249 return 1
250}
251
252// the best runnable TODO row that is COVERED (EXEC recipe or SPEC build):
253// an_pick variant -- highest w, deps DONE. -1 if none.
254func wd_pick_covered(cx: *i64) -> i64 {
255 let wv: *i64 = cx[3] as *i64
256 let st: *i64 = cx[4] as *i64
257 let sz: *i64 = cx[5] as *i64
258 var best: i64 = 0 - 1
259 var r: i64 = 0
260 while r < cx[0] {
261 if st[r] == 84 {
262 if wd_covered(cx, r) == 1 {
263 if an_deps_done(cx, r) == 1 {
264 var take: i64 = 0
265 if best < 0 { take = 1 } else {
266 if wv[r] > wv[best] { take = 1 }
267 if wv[r] == wv[best] { if sz[r] < sz[best] { take = 1 } }
268 }
269 if take == 1 { best = r }
270 }
271 }
272 }
273 r = r + 1
274 }
275 return best
276}
277
278// spawn elf with ONE argument under the wcx deadline (hang-proof; composes
279// nx_guarded_run -- SIGKILL reap, rc=124 timeout convention).
280func wd_spawn2(wcx: *i64, elf: *u8, a1: *u8) -> i64 {
281 let argv: *i64 = sys_mmap(32) as *i64
282 argv[0] = elf as i64
283 argv[1] = a1 as i64
284 argv[2] = 0
285 let envp: *i64 = sys_mmap(32) as *i64
286 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
287 envp[1] = 0
288 let neg: i64 = 0 - 1
289 return nx_guarded_run(elf, argv, envp, wcx[4], neg, neg)
290}
291
292// one ASSIGNRESULT row (stdout + results log): the attempt audit trail.
293func wd_result(wcx: *i64, id: *u8, route: *u8, verdict: *u8, rc: i64) -> i64 {
294 wd_both(wcx, "ASSIGNRESULT id=" as *u8)
295 wd_both(wcx, id)
296 wd_both(wcx, " route=" as *u8)
297 wd_both(wcx, route)
298 wd_both(wcx, " verdict=" as *u8)
299 wd_both(wcx, verdict)
300 wd_both(wcx, " rc=" as *u8)
301 wd_bothn(wcx, rc)
302 wd_both(wcx, " epoch=" as *u8)
303 wd_bothn(wcx, sys_now_realtime_sec())
304 wd_both(wcx, "\n" as *u8)
305 return 0
306}
307
308// ATTEMPT one covered row end to end: run <elf a1> (the route's act), then
309// the status-keeper (nx_reconcile), then re-read the queue. Returns 0 GREEN,
310// 1 RED. Judgment is EVIDENCE ONLY -- the row is GREEN iff reconcile flipped
311// it DONE on its own MARK evaluation. An act that exits 0 but never turns
312// its marker green is a LIAR and goes RED.
313func wd_attempt(wcx: *i64, id: *u8, elf: *u8, a1: *u8, route: *u8) -> i64 {
314 let qp: *u8 = wcx[0] as *u8
315 let echk: i64 = sys_openat_rd(elf)
316 if echk < 0 {
317 wd_result(wcx, id, route, "RED-act-elf-absent" as *u8, 127)
318 return 1
319 }
320 sys_close(echk)
321 let rc_run: i64 = wd_spawn2(wcx, elf, a1)
322 let chk: i64 = sys_openat_rd(wcx[3] as *u8)
323 if chk < 0 {
324 wd_result(wcx, id, route, "RED-reconcile-elf-absent" as *u8, rc_run)
325 return 1
326 }
327 sys_close(chk)
328 wd_spawn2(wcx, wcx[3] as *u8, qp)
329 let cx2: *i64 = an_newcx()
330 let rows: i64 = an_load(qp, cx2)
331 if rows <= 0 {
332 wd_result(wcx, id, route, "RED-queue-reload-failed" as *u8, rc_run)
333 return 1
334 }
335 let r2: i64 = an_find(cx2, id)
336 if r2 >= 0 {
337 let st: *i64 = cx2[4] as *i64
338 if st[r2] == 68 {
339 wd_result(wcx, id, route, "GREEN" as *u8, rc_run)
340 return 0
341 }
342 }
343 wd_flip_status(qp, id, "RED" as *u8)
344 wd_result(wcx, id, route, "RED" as *u8, rc_run)
345 return 1
346}
347
348// route one covered row by its kind: EXEC -> the durable lane rebuilds+runs
349// the recipe organ; SPEC -> the durable auto-builder authors+gates the
350// module from the spec file (classifier-routed emitter lanes: rows that
351// BUILD). Returns wd_attempt's verdict; 1 if the row turned out uncovered
352// (caller bug -- fail loud).
353func wd_route_attempt(wcx: *i64, cx: *i64, r: i64) -> i64 {
354 let idbuf: *u8 = sys_mmap(64)
355 let pid: *u8 = an_id_at(cx, r)
356 var ci: i64 = 0
357 while pid[ci] != (0 as u8) { idbuf[ci] = pid[ci]; ci = ci + 1 }
358 idbuf[ci] = 0 as u8
359 let arg: *u8 = sys_mmap(WD_SPECCAP)
360 if wd_exec_name(an_gate_at(cx, r), arg) == 1 {
361 return wd_attempt(wcx, idbuf, wcx[2] as *u8, arg, "EXEC" as *u8)
362 }
363 if wd_spec_name(an_gate_at(cx, r), arg) == 1 {
364 return wd_attempt(wcx, idbuf, wcx[6] as *u8, arg, "SPEC" as *u8)
365 }
366 wd_result(wcx, idbuf, "NONE" as *u8, "RED-uncovered-routed" as *u8, 0)
367 return 1
368}
369
370// THE BEAT. Returns 0 healthy (work done, raised, or idle), 1 attempt RED,
371// 101 queue missing. Bounded: <=1 raise + <=1 attempt per call.
372func wd_beat(wcx: *i64) -> i64 {
373 let qp: *u8 = wcx[0] as *u8
374 let cx: *i64 = an_newcx()
375 let rows: i64 = an_load(qp, cx)
376 if rows <= 0 {
377 wd_w(1, "ASSIGNRESULT id=NONE route=NONE verdict=RED-queue-missing\n" as *u8)
378 return 101
379 }
380 let lfd: i64 = sys_openat_append(wcx[1] as *u8, 0x1a4)
381 wcx[5] = lfd
382 var bad: i64 = 0
383 let p: i64 = an_pick(cx)
384 if p < 0 {
385 wd_result(wcx, "NONE-RUNNABLE" as *u8, "NONE" as *u8, "IDLE" as *u8, 0)
386 } else {
387 if wd_covered(cx, p) == 1 {
388 bad = wd_route_attempt(wcx, cx, p)
389 } else {
390 let idbuf: *u8 = sys_mmap(64)
391 let pid: *u8 = an_id_at(cx, p)
392 var ci: i64 = 0
393 while pid[ci] != (0 as u8) { idbuf[ci] = pid[ci]; ci = ci + 1 }
394 idbuf[ci] = 0 as u8
395 wd_flip_status(qp, idbuf, "NOVEL" as *u8)
396 wd_result(wcx, idbuf, "NOVEL" as *u8, "RAISED" as *u8, 0)
397 let pe: i64 = wd_pick_covered(cx)
398 if pe >= 0 { bad = wd_route_attempt(wcx, cx, pe) }
399 }
400 }
401 if lfd >= 0 { sys_close(lfd) }
402 return bad
403}
404
405// deadline from knowledge/registry/work_dispatcher.conf (exec_deadline_ms=N);
406// bootstrap default when the conf or key is absent (config-hierarchy law).
407func wd_conf_deadline(path: *u8) -> i64 {
408 let buf: *u8 = sys_mmap(WD_MAGIC_4096)
409 let fd: i64 = sys_openat_rd(path)
410 if fd < 0 { return WD_DEADLINE_DEFAULT }
411 var n: i64 = 0
412 var r: i64 = sys_read(fd, buf, WD_MAGIC_4095)
413 while r > 0 { n = n + r; r = sys_read(fd, buf + n, WD_MAGIC_4095 - n) }
414 sys_close(fd)
415 let key: *u8 = "exec_deadline_ms=" as *u8
416 var i: i64 = 0
417 var at: i64 = 0 - 1
418 while i < n {
419 if wd_at(buf, n, i, key, 17) == 1 { at = i + 17; i = n } else { i = i + 1 }
420 }
421 if at < 0 { return WD_DEADLINE_DEFAULT }
422 var v: i64 = 0
423 var any: i64 = 0
424 var go: i64 = 1
425 while go == 1 {
426 if at >= n { go = 0 } else {
427 let ch: i64 = buf[at] as i64
428 if ch >= 48 {
429 if ch <= 57 {
430 v = v * 10 + (ch - 48)
431 any = 1
432 at = at + 1
433 } else { go = 0 }
434 } else { go = 0 }
435 }
436 }
437 if any == 0 { return WD_DEADLINE_DEFAULT }
438 if v < 1000 { return WD_DEADLINE_DEFAULT }
439 return v
440}