code wiki / _hdl_build / nx_capgraph_derive_lib.nx
nx_capgraph_derive_lib.nx source
↩ module page · 508 lines · 16730 B
1// nx_capgraph_derive_lib.nx -- pure core for nx_capgraph_derive: level thresholds, row parsing,
2// the progress-rail snapshot compare, and the history reader.
3//
4// SPLIT OUT 2026-07-31 for the SAME reason as nx_capgraph_edges_lib: these helpers sat beside a main()
5// and were therefore unreachable from any gate, which pinned the progress rail at level 2 (WORKS) on
6// this system's own ladder. Gating the edges organ but not this one would have been applying the
7// standard selectively -- exactly the kind of convenient inconsistency this arc exists to catch.
8// license_tier: ORIGINAL No hw writes (Rule 26).
9import "nx_capgraph_lib.nx"
10import "nx_tool_run.nx"
11
12const CD2_CAP: i64 = 262144
13const CD2_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_sota_status.elf" as *u8
14const CD2_EDGES: *u8 = "knowledge/status/capgraph_edges.conf" as *u8
15
16const CD2_HIST: *u8 = "knowledge/status/capgraph_history.jrnl" as *u8
17const CD2_SBMAX: i64 = 16384
18const CD2_AX_COVER: i64 = 0
19const CD2_AX_EVID: i64 = 1
20const CD2_AX_FRONTIER: i64 = 2
21
22func cd2_match(buf: *u8, at: i64, end: i64, pat: *u8) -> i64 {
23 var k: i64 = 0
24 var ok: i64 = 1
25 var done: i64 = 0
26 while done == 0 {
27 let pc: i64 = pat[k] as i64
28 if pc == 0 { done = 1 }
29 else {
30 if at + k >= end { ok = 0 }
31 else { if buf[at + k] as i64 != pc { ok = 0 } }
32 if ok == 0 { done = 1 }
33 else { k = k + 1 }
34 }
35 }
36 return ok
37}
38
39func cd2_find(buf: *u8, ls: i64, le: i64, pat: *u8) -> i64 {
40 var j: i64 = ls
41 var hit: i64 = 0 - 1
42 while j < le {
43 if cd2_match(buf, j, le, pat) == 1 {
44 hit = j
45 j = le
46 } else { j = j + 1 }
47 }
48 return hit
49}
50
51func cd2_num(buf: *u8, at: i64, end: i64) -> i64 {
52 var v: i64 = 0
53 var i: i64 = at
54 var any: i64 = 0
55 var done: i64 = 0
56 while done == 0 {
57 if i >= end { done = 1 }
58 else {
59 let c: i64 = buf[i] as i64
60 if c < 48 { done = 1 }
61 else {
62 if c > 57 { done = 1 }
63 else {
64 v = v * 10 + (c - 48)
65 any = 1
66 i = i + 1
67 }
68 }
69 }
70 }
71 if any == 0 { return CG_UNMEASURED }
72 return v
73}
74
75// The flat permil becomes an ORDINAL level. Thresholds are declared here as named boundaries so the
76// conversion is auditable; the level is then capped by evidence (L6) at cg_set, which is what actually
77// stops a big coverage claim from reading as progress.
78func cd2_cov_level(permil: i64) -> i64 {
79 if permil == CG_UNMEASURED { return CG_UNMEASURED }
80 if permil < 200 { return 0 }
81 if permil < 400 { return 1 }
82 if permil < 600 { return 2 }
83 if permil < 800 { return 3 }
84 if permil < 950 { return 4 }
85 return 5
86}
87
88func cd2_gap_level(gaps: i64) -> i64 {
89 if gaps == CG_UNMEASURED { return CG_UNMEASURED }
90 if gaps <= 0 { return 5 }
91 var d: i64 = gaps / 3
92 if d > 5 { d = 5 }
93 return 5 - d
94}
95
96// ---- THE PROGRESS RAIL ----
97// Ordinal levels make a level-up COUNTABLE, but counting requires a record. Without a time series the
98// graph can only ever answer "where are we", never "did we move" -- and the operator asked for the
99// second. This appends ONE row per ACTUAL CHANGE (content-idempotent, the nx_debt pattern): re-running
100// on an unchanged graph appends nothing, so the journal is a history of MOVEMENT, not of invocations.
101// A rail that grows on every run would drown the signal it exists to carry.
102static cd2_sb: *u8
103static cd2_sn: i64
104
105func cd2_sput(s: *u8) {
106 var i: i64 = 0
107 while s[i] != (0 as u8) {
108 if cd2_sn < CD2_SBMAX - 2 {
109 cd2_sb[cd2_sn] = s[i]
110 cd2_sn = cd2_sn + 1
111 }
112 i = i + 1
113 }
114 cd2_sb[cd2_sn] = 0 as u8
115}
116
117func cd2_sputi(x: i64) {
118 let b: *u8 = sys_mmap(64) as *u8
119 var v: i64 = x
120 var neg: i64 = 0
121 if v < 0 {
122 neg = 1
123 v = 0 - v
124 }
125 var i: i64 = 40
126 if v == 0 {
127 i = i - 1
128 b[i] = 48 as u8
129 }
130 while v > 0 {
131 let d: i64 = v - (v / 10) * 10
132 i = i - 1
133 b[i] = (d + 48) as u8
134 v = v / 10
135 }
136 if neg == 1 {
137 i = i - 1
138 b[i] = 45 as u8
139 }
140 b[40] = 0 as u8
141 cd2_sput(((b as i64) + i) as *u8)
142}
143
144func cd2_is_infra(s: *u8) -> i64 {
145 if s[0] != (105 as u8) { return 0 }
146 if s[1] != (110 as u8) { return 0 }
147 if s[2] != (102 as u8) { return 0 }
148 if s[3] != (114 as u8) { return 0 }
149 if s[4] != (97 as u8) { return 0 }
150 if s[5] != (58 as u8) { return 0 }
151 return 1
152}
153
154func cd2_name_of(buf: *u8, ls: i64, le: i64, dst: *u8) -> i64 {
155 var start: i64 = ls
156 var found: i64 = 0
157 var scan: i64 = ls
158 while scan < le {
159 if found == 0 {
160 if buf[scan] != (32 as u8) {
161 start = scan
162 found = 1
163 }
164 }
165 scan = scan + 1
166 }
167 if found == 0 { return 0 }
168 var w: i64 = 0
169 var q: i64 = start
170 var done: i64 = 0
171 while done == 0 {
172 if q >= le { done = 1 }
173 else {
174 if buf[q] == (32 as u8) { done = 1 }
175 else {
176 if w < CG_NAMEW - 1 {
177 dst[w] = buf[q]
178 w = w + 1
179 }
180 q = q + 1
181 }
182 }
183 }
184 dst[w] = 0 as u8
185 return w
186}
187
188// ---- READING the rail. Recording movement is only half a progress system; a journal nobody can read
189// is a log, not an instrument. This diffs the last two rows and names what MOVED, in both directions --
190// a level going DOWN is the more important signal, because it means something that used to hold no
191// longer does, and a report that only ever shows gains is a cheerleader, not a ruler.
192const CD2_MAXPAIR: i64 = 512
193const CD2_PW: i64 = 48
194static cd2_pn: *u8
195static cd2_pv: *i64
196static cd2_np: i64
197static cd2_cn: *u8
198static cd2_cv: *i64
199static cd2_nc: i64
200
201func cd2_parse_row(buf: *u8, s: i64, e: i64) {
202 cd2_np = 0
203 var i: i64 = s
204 while i < e {
205 let c: i64 = buf[i] as i64
206 var adv: i64 = 1
207 if c == 32 { adv = 1 }
208 else {
209 if c == 9 { adv = 1 }
210 else {
211 var j: i64 = i
212 var eqp: i64 = 0 - 1
213 var done: i64 = 0
214 while done == 0 {
215 if j >= e { done = 1 }
216 else {
217 let cj: i64 = buf[j] as i64
218 if cj == 32 { done = 1 }
219 else {
220 if cj == 9 { done = 1 }
221 else {
222 if cj == 61 {
223 if eqp < 0 { eqp = j }
224 }
225 j = j + 1
226 }
227 }
228 }
229 }
230 if eqp > 0 {
231 if cd2_np < CD2_MAXPAIR {
232 var w: i64 = 0
233 var k: i64 = i
234 while k < eqp {
235 if w < CD2_PW - 1 {
236 let dst: *u8 = ((cd2_pn as i64) + cd2_np * CD2_PW + w) as *u8
237 dst[0] = buf[k]
238 w = w + 1
239 }
240 k = k + 1
241 }
242 let term: *u8 = ((cd2_pn as i64) + cd2_np * CD2_PW + w) as *u8
243 term[0] = 0 as u8
244 var v: i64 = 0
245 var neg: i64 = 0
246 var m: i64 = eqp + 1
247 if m < j {
248 if buf[m] == (45 as u8) {
249 neg = 1
250 m = m + 1
251 }
252 }
253 while m < j {
254 let cm: i64 = buf[m] as i64
255 if cm >= 48 {
256 if cm <= 57 { v = v * 10 + (cm - 48) }
257 }
258 m = m + 1
259 }
260 if neg == 1 { v = 0 - v }
261 cd2_pv[cd2_np] = v
262 cd2_np = cd2_np + 1
263 }
264 }
265 if j > i { adv = j - i }
266 }
267 }
268 i = i + adv
269 }
270}
271
272func cd2_prev_lookup(nm: *u8) -> i64 {
273 var i: i64 = 0
274 var hit: i64 = 0 - 999
275 while i < cd2_np {
276 if cg_streq(((cd2_pn as i64) + i * CD2_PW) as *u8, nm) == 1 {
277 hit = cd2_pv[i]
278 i = cd2_np
279 } else { i = i + 1 }
280 }
281 return hit
282}
283
284func cd2_progress() -> i64 {
285 cd2_pn = sys_mmap(CD2_MAXPAIR * CD2_PW + 64) as *u8
286 cd2_pv = sys_mmap(CD2_MAXPAIR * 8 + 64) as *i64
287 cd2_cn = sys_mmap(CD2_MAXPAIR * CD2_PW + 64) as *u8
288 cd2_cv = sys_mmap(CD2_MAXPAIR * 8 + 64) as *i64
289 let hl: *i64 = sys_mmap(16) as *i64
290 let hb: *u8 = sys_read_file(CD2_HIST, hl)
291 let hn: i64 = hl[0]
292 cax_puts("=== NX-CAPGRAPH PROGRESS: what MOVED between the last two recorded states ===\n" as *u8)
293 if hn <= 0 {
294 cax_puts("no history yet -- run nx_capgraph_derive once to record a baseline.\n" as *u8)
295 return 0
296 }
297 let l2: i64 = cd2_last_row_start(hb, hn)
298 let l1: i64 = cd2_prev_row_start(hb, hn)
299 if l1 < 0 {
300 cax_puts("only ONE recorded state -- a baseline, no movement to report yet.\n" as *u8)
301 cax_puts("This is the honest answer: progress needs two points, and there is one.\n" as *u8)
302 return 0
303 }
304 var e1: i64 = l2 - 1
305 var e2: i64 = hn
306 cd2_parse_row(hb, l2, e2)
307 cd2_nc = cd2_np
308 var c: i64 = 0
309 while c < cd2_nc {
310 var w: i64 = 0
311 let src: *u8 = ((cd2_pn as i64) + c * CD2_PW) as *u8
312 while src[w] != (0 as u8) {
313 let d: *u8 = ((cd2_cn as i64) + c * CD2_PW + w) as *u8
314 d[0] = src[w]
315 w = w + 1
316 }
317 let t: *u8 = ((cd2_cn as i64) + c * CD2_PW + w) as *u8
318 t[0] = 0 as u8
319 cd2_cv[c] = cd2_pv[c]
320 c = c + 1
321 }
322 cd2_parse_row(hb, l1, e1)
323
324 var up: i64 = 0
325 var down: i64 = 0
326 var newn: i64 = 0
327 var i: i64 = 0
328 while i < cd2_nc {
329 let nm: *u8 = ((cd2_cn as i64) + i * CD2_PW) as *u8
330 let now: i64 = cd2_cv[i]
331 let was: i64 = cd2_prev_lookup(nm)
332 if was == (0 - 999) {
333 cax_puts(" NEW " as *u8)
334 cax_puts(nm)
335 cax_puts(" = " as *u8)
336 cax_puti(now)
337 cax_puts("\n" as *u8)
338 newn = newn + 1
339 } else {
340 if now > was {
341 cax_puts(" UP " as *u8)
342 cax_puts(nm)
343 cax_puts(" " as *u8)
344 cax_puti(was)
345 cax_puts(" -> " as *u8)
346 cax_puti(now)
347 cax_puts("\n" as *u8)
348 up = up + 1
349 }
350 if now < was {
351 cax_puts(" DOWN " as *u8)
352 cax_puts(nm)
353 cax_puts(" " as *u8)
354 cax_puti(was)
355 cax_puts(" -> " as *u8)
356 cax_puti(now)
357 cax_puts(" <- REGRESSION\n" as *u8)
358 down = down + 1
359 }
360 }
361 i = i + 1
362 }
363 cax_puts("\n" as *u8)
364 cax_kv("advanced" as *u8, up)
365 cax_kv("REGRESSED" as *u8, down)
366 cax_kv("new_nodes" as *u8, newn)
367 cax_puts("\n" as *u8)
368 if up == 0 {
369 if down == 0 {
370 if newn == 0 { cax_puts("no level changed between the last two states (structure moved, levels did not).\n" as *u8) }
371 }
372 }
373 if down > 0 {
374 cax_puts("⚠a DOWN is the signal that matters: something that used to hold no longer does.\n" as *u8)
375 }
376 return 0
377}
378
379// ROW-BOUNDARY DECISIONS, extracted for the same reason as cd2_last_row_matches: `progress` diffs the
380// LAST TWO rows, so if these pick the wrong pair it silently reports the wrong movement -- and an
381// off-by-one here is invisible in a live run, which only ever exercises whatever shape the journal
382// happens to have that day. Trailing-newline handling is the trap: a file ending in \n must NOT be read
383// as having an extra empty final row.
384// cd2_last_row_start: byte offset where the final non-empty row begins, or -1 if there is none.
385func cd2_last_row_start(hb: *u8, hn: i64) -> i64 {
386 if hn <= 0 { return 0 - 1 }
387 var lstart: i64 = 0
388 var k: i64 = 0
389 while k < hn {
390 if hb[k] == (10 as u8) {
391 if k + 1 < hn { lstart = k + 1 }
392 }
393 k = k + 1
394 }
395 return lstart
396}
397
398// cd2_prev_row_start: offset of the row BEFORE the last one, or -1 when fewer than two rows exist.
399func cd2_prev_row_start(hb: *u8, hn: i64) -> i64 {
400 let last: i64 = cd2_last_row_start(hb, hn)
401 if last <= 0 { return 0 - 1 }
402 var prev: i64 = 0
403 var k: i64 = 0
404 while k < last - 1 {
405 if hb[k] == (10 as u8) {
406 if k + 1 < last { prev = k + 1 }
407 }
408 k = k + 1
409 }
410 return prev
411}
412
413// THE DECISION THE WHOLE RAIL TURNS ON: does the LAST journal row already carry exactly this state?
414// If this says yes when it should say no, real movement is silently DROPPED. If it says no when it
415// should say yes, the journal fills with duplicate rows and progress reports phantom changes. It was
416// the one piece of the rail with no standing tooth, so it is now a PURE function -- extracted solely
417// to make it gate-reachable. Compares the state field (after the epoch TAB) of the final line.
418// Returns 1 = identical (do not append) · 0 = differs, or unreadable.
419func cd2_last_row_matches(hb: *u8, hn: i64, st: *u8, stn: i64) -> i64 {
420 if hn <= 0 { return 0 }
421 let lstart: i64 = cd2_last_row_start(hb, hn)
422 if lstart < 0 { return 0 }
423 var tab: i64 = lstart
424 var found: i64 = 0
425 var t: i64 = lstart
426 while t < hn {
427 if found == 0 {
428 if hb[t] == (9 as u8) {
429 tab = t + 1
430 found = 1
431 }
432 }
433 t = t + 1
434 }
435 if found == 0 { return 0 }
436 var m: i64 = 1
437 var q: i64 = 0
438 while q < stn {
439 if tab + q >= hn { m = 0 }
440 else { if hb[tab + q] != st[q] { m = 0 } }
441 q = q + 1
442 }
443 // A stored PREFIX must not read as equal: the byte after the compared span has to END the line.
444 // Without this, a shorter recorded state would swallow every longer one and the rail would go deaf.
445 if tab + stn < hn {
446 if hb[tab + stn] != (10 as u8) { m = 0 }
447 }
448 return m
449}
450
451// Append one row IFF the graph state differs from the last recorded row.
452// returns 1 = movement recorded · 0 = unchanged (nothing appended) · -1 = write failed
453func cd2_snapshot(edges: i64) -> i64 {
454 cd2_sn = 0
455 cd2_sput("nodes=" as *u8)
456 cd2_sputi(cg_nn)
457 cd2_sput(" edges=" as *u8)
458 cd2_sputi(edges)
459 // Record BOTH the effective level and the node's OWN level.
460 // Measured 2026-08-01: `instrument` earned executable evidence (own 0->3 GATED) and the rail showed
461 // NOTHING, because L4 correctly capped its EFFECTIVE level at 0 behind an unevidenced prerequisite.
462 // A rail that records only the capped figure tells a lane that did real work that nothing happened --
463 // ★★★UNDER A WEAKEST-PATH RULE, PROGRESS AND ITS VISIBILITY ARE DIFFERENT THINGS, so record both:
464 // `<name>` is what the graph can currently deliver, `<name>#own` is what that node itself earned.
465 var i: i64 = 0
466 while i < cg_nn {
467 cd2_sput(" " as *u8)
468 cd2_sput(cg_node_name(i))
469 cd2_sput("=" as *u8)
470 cd2_sputi(cg_effective(i))
471 cd2_sput(" " as *u8)
472 cd2_sput(cg_node_name(i))
473 cd2_sput("#own=" as *u8)
474 cd2_sputi(cg_own[i])
475 i = i + 1
476 }
477
478 let hl: *i64 = sys_mmap(16) as *i64
479 let hb: *u8 = sys_read_file(CD2_HIST, hl)
480 let hn: i64 = hl[0]
481 let same: i64 = cd2_last_row_matches(hb, hn, cd2_sb, cd2_sn)
482 if same == 1 { return 0 }
483
484 let ts: *i64 = sys_mmap(32) as *i64
485 sys_clock_gettime_real(ts)
486 let fd: i64 = sys_openat_append(CD2_HIST, 0x1A4)
487 if fd < 0 { return 0 - 1 }
488 let eb: *u8 = sys_mmap(64) as *u8
489 var v: i64 = ts[0]
490 var bi: i64 = 40
491 if v == 0 {
492 bi = bi - 1
493 eb[bi] = 48 as u8
494 }
495 while v > 0 {
496 let d: i64 = v - (v / 10) * 10
497 bi = bi - 1
498 eb[bi] = (d + 48) as u8
499 v = v / 10
500 }
501 sys_write(fd, ((eb as i64) + bi) as *u8, 40 - bi)
502 sys_write(fd, "\t" as *u8, 1)
503 sys_write(fd, cd2_sb, cd2_sn)
504 sys_write(fd, "\n" as *u8, 1)
505 sys_close(fd)
506 return 1
507}
508