code wiki / _hdl_build / nx_assign_core.nx
nx_assign_core.nx source
↩ module page · 336 lines · 12237 B
1// nx_assign_core.nx -- DISPATCH CORE (mainless): parse assignment_queue.tsv
2// and answer the three questions the team loop needs every beat:
3// 1. an_pick(...) -- the next assignment: highest w, status TODO, all
4// deps DONE; ties -> smaller size, then file order.
5// (w is ADVISORY -- Claude's viewpoint column.)
6// 2. an_top_blocker(...)-- the STRUCTURAL view: the open row with the most
7// direct dependents (the dep graph's own opinion of
8// "largest blocker"). Where structure and w
9// disagree, the caller prints VIEWPOINT-DISCREPANCY
10// -- the operator reviews the diff, nobody hides it.
11// 3. an_novel(...) -- raised hands: NOVEL rows are the tutor queue (the
12// only work that costs Claude tokens; batching them
13// is the token-efficiency lever).
14// Queue grammar: knowledge/registry/assignment_queue.tsv -- 9 tab fields:
15// id rung w size owner status deps gate title ('#' lines skipped)
16// status by FIRST BYTE: T=TODO G=GEN N=NOVEL W=WIP D=DONE R=RED.
17// license_tier: ORIGINAL
18
19import "nx_syscalls.nx"
20const AN_MAGIC_1048576: i64 = 1048576
21const AN_MAGIC_1048575: i64 = 1048575
22
23const AN_MAX_ROWS: i64 = 1024
24const AN_IDCAP: i64 = 32
25const AN_DEPCAP: i64 = 128
26
27// context block layout (caller mmaps 1 block, passes everywhere):
28// cx[0]=nrows cx[1]=ids arena ptr cx[2]=deps arena ptr
29// cx[3]=w[] ptr cx[4]=status[] ptr cx[5]=sizerank[] ptr cx[6]=depcount[] ptr
30func an_newcx() -> *i64 {
31 let cx: *i64 = sys_mmap(128) as *i64
32 cx[0] = 0
33 cx[1] = sys_mmap(AN_MAX_ROWS * AN_IDCAP) as i64
34 cx[2] = sys_mmap(AN_MAX_ROWS * AN_DEPCAP) as i64
35 cx[3] = sys_mmap(AN_MAX_ROWS * 8) as i64
36 cx[4] = sys_mmap(AN_MAX_ROWS * 8) as i64
37 cx[5] = sys_mmap(AN_MAX_ROWS * 8) as i64
38 cx[6] = sys_mmap(AN_MAX_ROWS * 8) as i64
39 cx[7] = sys_mmap(AN_MAX_ROWS * 192) as i64 // gate cells (incl ||MARK ||EXEC suffixes)
40 cx[8] = sys_mmap(AN_MAX_ROWS * 128) as i64 // titles
41 return cx
42}
43
44func an_gate_at(cx: *i64, r: i64) -> *u8 {
45 return ((cx[7]) + r * 192) as *u8
46}
47
48func an_title_at(cx: *i64, r: i64) -> *u8 {
49 return ((cx[8]) + r * 128) as *u8
50}
51
52func an_id_at(cx: *i64, r: i64) -> *u8 {
53 return ((cx[1]) + r * AN_IDCAP) as *u8
54}
55
56func an_deps_at(cx: *i64, r: i64) -> *u8 {
57 return ((cx[2]) + r * AN_DEPCAP) as *u8
58}
59
60func an_streq(a: *u8, b: *u8) -> i64 {
61 var i: i64 = 0
62 var go: i64 = 1
63 while go == 1 {
64 if a[i] != b[i] { return 0 }
65 if a[i] == (0 as u8) { return 1 }
66 i = i + 1
67 if i >= 64 { go = 0 }
68 }
69 return 1
70}
71
72// parse the queue file into the context; returns row count (0 = fail-loud)
73func an_load(path: *u8, cx: *i64) -> i64 {
74 let buf: *u8 = sys_mmap(AN_MAGIC_1048576)
75 let fd: i64 = sys_openat_rd(path)
76 if fd < 0 { return 0 }
77 var n: i64 = 0
78 var r: i64 = sys_read(fd, buf, AN_MAGIC_1048575)
79 while r > 0 { n = n + r; r = sys_read(fd, buf + n, AN_MAGIC_1048575 - n) }
80 sys_close(fd)
81 let wv: *i64 = cx[3] as *i64
82 let st: *i64 = cx[4] as *i64
83 let sz: *i64 = cx[5] as *i64
84 var rows: i64 = 0
85 var i: i64 = 0
86 while i < n {
87 if buf[i] == (35 as u8) {
88 // comment: skip past the newline
89 var sk: i64 = 1
90 while sk == 1 {
91 if i >= n { sk = 0 } else {
92 if buf[i] == (10 as u8) { sk = 0 }
93 i = i + 1
94 }
95 }
96 } else {
97 // parse 9 tab fields; capture 0=id 2=w 3=size 5=status 6=deps
98 var f: i64 = 0
99 var fs: i64 = i
100 var idok: i64 = 0
101 while f < 9 {
102 var fe: i64 = fs
103 var scan: i64 = 1
104 while scan == 1 {
105 if fe >= n { scan = 0 } else {
106 if buf[fe] == (9 as u8) { scan = 0 } else {
107 if buf[fe] == (10 as u8) { scan = 0 } else { fe = fe + 1 }
108 }
109 }
110 }
111 let flen: i64 = fe - fs
112 if f == 0 {
113 if flen > 0 {
114 if flen < AN_IDCAP {
115 let dst: *u8 = an_id_at(cx, rows)
116 var k: i64 = 0
117 while k < flen { dst[k] = buf[fs + k]; k = k + 1 }
118 dst[flen] = 0 as u8
119 idok = 1
120 }
121 }
122 }
123 if f == 2 {
124 var v: i64 = 0
125 var q: i64 = fs
126 while q < fe {
127 if buf[q] >= (48 as u8) { if buf[q] <= (57 as u8) { v = v * 10 + ((buf[q] as i64) - 48) } }
128 q = q + 1
129 }
130 wv[rows] = v
131 }
132 if f == 3 {
133 var srk: i64 = 3
134 if flen > 0 {
135 if buf[fs] == (83 as u8) { srk = 0 } // S
136 if buf[fs] == (77 as u8) { srk = 1 } // M
137 if buf[fs] == (76 as u8) { srk = 2 } // L
138 }
139 sz[rows] = srk
140 }
141 if f == 5 {
142 var sc: i64 = 0
143 if flen > 0 { sc = buf[fs] as i64 }
144 // GRAMMAR FIX (2026-06-13): status was keyed by FIRST BYTE, so a CUSTOM
145 // status starting 'D' (e.g. "DAEMON-SIL") was misread as DONE by every
146 // consumer (an_pick, an_deps_done, capability ladder) -- a floating
147 // foundation under the whole queue. Only the exact word "DONE" is DONE;
148 // a D-prefixed non-DONE status -> '?' (63) sentinel (not DONE, not any
149 // active status). Surgical: changes ONLY D-prefixed non-DONE rows; every
150 // standard status (T/G/N/W/D/R/V) keeps its first-byte code unchanged.
151 if sc == 68 {
152 var isdone: i64 = 1
153 if flen != 4 { isdone = 0 } else {
154 if buf[fs+1] != (79 as u8) { isdone = 0 } // O
155 if buf[fs+2] != (78 as u8) { isdone = 0 } // N
156 if buf[fs+3] != (69 as u8) { isdone = 0 } // E
157 }
158 if isdone == 0 { sc = 63 }
159 }
160 st[rows] = sc // canonical status byte ('?'=63 for D-non-DONE)
161 }
162 if f == 6 {
163 let dd: *u8 = an_deps_at(cx, rows)
164 var k2: i64 = 0
165 while k2 < flen { if k2 < AN_DEPCAP - 1 { dd[k2] = buf[fs + k2] } k2 = k2 + 1 }
166 var dl: i64 = flen
167 if dl > AN_DEPCAP - 1 { dl = AN_DEPCAP - 1 }
168 dd[dl] = 0 as u8
169 }
170 if f == 7 {
171 let gg: *u8 = an_gate_at(cx, rows)
172 var k3: i64 = 0
173 while k3 < flen { if k3 < 191 { gg[k3] = buf[fs + k3] } k3 = k3 + 1 }
174 var gl: i64 = flen
175 if gl > 191 { gl = 191 }
176 gg[gl] = 0 as u8
177 }
178 if f == 8 {
179 let tt: *u8 = an_title_at(cx, rows)
180 var k4: i64 = 0
181 while k4 < flen { if k4 < 127 { tt[k4] = buf[fs + k4] } k4 = k4 + 1 }
182 var tl: i64 = flen
183 if tl > 127 { tl = 127 }
184 tt[tl] = 0 as u8
185 }
186 fs = fe + 1
187 if fe < n { if buf[fe] == (10 as u8) { f = 9; fs = fe + 1 } }
188 f = f + 1
189 if fs > n { f = 9 }
190 }
191 // advance i to fs (start of next line)
192 i = fs
193 if idok == 1 {
194 if rows < AN_MAX_ROWS - 1 { rows = rows + 1 }
195 }
196 }
197 }
198 cx[0] = rows
199 return rows
200}
201
202// find row index by id; -1 if absent
203func an_find(cx: *i64, id: *u8) -> i64 {
204 var r: i64 = 0
205 while r < cx[0] {
206 if an_streq(an_id_at(cx, r), id) == 1 { return r }
207 r = r + 1
208 }
209 return 0 - 1
210}
211
212// are all deps of row r DONE? deps "-" = yes; unknown dep id = NO (blocked loud)
213func an_deps_done(cx: *i64, r: i64) -> i64 {
214 let dd: *u8 = an_deps_at(cx, r)
215 if dd[0] == (45 as u8) { if dd[1] == (0 as u8) { return 1 } }
216 let st: *i64 = cx[4] as *i64
217 let tok: *u8 = sys_mmap(AN_IDCAP)
218 var i: i64 = 0
219 var k: i64 = 0
220 var go: i64 = 1
221 while go == 1 {
222 let ch: i64 = dd[i] as i64
223 var fin: i64 = 0
224 if ch == 0 { fin = 1 }
225 if ch == 44 { fin = 1 }
226 if fin == 1 {
227 if k > 0 {
228 tok[k] = 0 as u8
229 let dr: i64 = an_find(cx, tok)
230 if dr < 0 { return 0 }
231 if st[dr] != 68 { return 0 } // 'D'
232 k = 0
233 }
234 if ch == 0 { go = 0 }
235 } else {
236 if k < AN_IDCAP - 1 { tok[k] = ch as u8; k = k + 1 }
237 }
238 i = i + 1
239 }
240 return 1
241}
242
243// the pick: highest w TODO with deps done; ties smaller size then file order.
244// returns row idx or -1.
245func an_pick(cx: *i64) -> i64 {
246 let wv: *i64 = cx[3] as *i64
247 let st: *i64 = cx[4] as *i64
248 let sz: *i64 = cx[5] as *i64
249 var best: i64 = 0 - 1
250 var r: i64 = 0
251 while r < cx[0] {
252 if st[r] == 84 { // 'T'
253 if an_deps_done(cx, r) == 1 {
254 var take: i64 = 0
255 if best < 0 { take = 1 } else {
256 if wv[r] > wv[best] { take = 1 }
257 if wv[r] == wv[best] { if sz[r] < sz[best] { take = 1 } }
258 }
259 if take == 1 { best = r }
260 }
261 }
262 r = r + 1
263 }
264 return best
265}
266
267// structural view: count direct dependents of every OPEN row; returns the
268// open row idx with the most dependents (-1 if none have any).
269func an_top_blocker(cx: *i64) -> i64 {
270 let st: *i64 = cx[4] as *i64
271 let dc: *i64 = cx[6] as *i64
272 var r: i64 = 0
273 while r < cx[0] { dc[r] = 0; r = r + 1 }
274 let tok: *u8 = sys_mmap(AN_IDCAP)
275 r = 0
276 while r < cx[0] {
277 let dd: *u8 = an_deps_at(cx, r)
278 var i: i64 = 0
279 var k: i64 = 0
280 var go: i64 = 1
281 while go == 1 {
282 let ch: i64 = dd[i] as i64
283 var fin: i64 = 0
284 if ch == 0 { fin = 1 }
285 if ch == 44 { fin = 1 }
286 if fin == 1 {
287 if k > 0 {
288 tok[k] = 0 as u8
289 if tok[0] != (45 as u8) {
290 let dr: i64 = an_find(cx, tok)
291 if dr >= 0 { dc[dr] = dc[dr] + 1 }
292 }
293 k = 0
294 }
295 if ch == 0 { go = 0 }
296 } else {
297 if k < AN_IDCAP - 1 { tok[k] = ch as u8; k = k + 1 }
298 }
299 i = i + 1
300 }
301 r = r + 1
302 }
303 var best: i64 = 0 - 1
304 r = 0
305 while r < cx[0] {
306 var open: i64 = 0
307 if st[r] == 84 { open = 1 } // TODO
308 if st[r] == 78 { open = 1 } // NOVEL
309 if st[r] == 87 { open = 1 } // WIP
310 if open == 1 {
311 if dc[r] > 0 {
312 var take: i64 = 0
313 if best < 0 { take = 1 } else { if dc[r] > dc[best] { take = 1 } }
314 if take == 1 { best = r }
315 }
316 }
317 r = r + 1
318 }
319 return best
320}
321
322// raised hands: count NOVEL rows; first NOVEL idx into out[0] (-1 if none)
323func an_novel(cx: *i64, out: *i64) -> i64 {
324 let st: *i64 = cx[4] as *i64
325 out[0] = 0 - 1
326 var cnt: i64 = 0
327 var r: i64 = 0
328 while r < cx[0] {
329 if st[r] == 78 {
330 if out[0] < 0 { out[0] = r }
331 cnt = cnt + 1
332 }
333 r = r + 1
334 }
335 return cnt
336}