code wiki / _hdl_build / nx_classifier_sync.nx
nx_classifier_sync.nx source
↩ module page · 141 lines · 5841 B
1// nx_classifier_sync.nx -- the AUDITOR's pattern-census DRIFT GATE. The library grows from BOTH ends
2// (this arc added shapes 6-10's emitters; a concurrent arc added 11-14: MATH_KERNEL/WIRE_TLV/
3// STRUCT_WALK/IO_CONTRACT) -- and the classifier + library census silently fell behind (pl_n_patterns
4// stuck at 10 while emit6-9 ship shape 14). This gate makes that class of skew IMPOSSIBLE to miss:
5// it scans runtime/_hdl_build for nx_pattern_emit*.nx sources, parses each header's "(shape N" claim
6// (the EMITTERS are the ground truth -- they exist and gate; the census is derived), and fails unless
7// (a) pl_n_patterns() >= max shape claimed by any emitter (the library census is current)
8// (b) plc_max_covered() >= max shape claimed (every shape has a classifier route)
9// Verdict line appends to knowledge/status/classifier_sync.log -- pulse-able like every gate.
10// RACI: AUDITOR verb (detect the skew); the Builder fixes it by adding rows. license_tier: ORIGINAL
11import "nx_pattern_classify.nx"
12import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
13import "nx_syscalls.nx"
14const K_MAGIC_1200: i64 = 1200
15const K_MAGIC_65536: i64 = 65536
16
17func cs_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 }
18// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
19// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
20// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
21// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
22func cs_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
23func cs_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[off+i]=s[i]; i=i+1 } dst[off+i]=0 as u8; return off+i }
24
25// does name match nx_pattern_emit*.nx? (prefix + .nx suffix)
26func cs_is_emit(name: *u8) -> i64 {
27 let pre: *u8 = "nx_pattern_emit" as *u8
28 var i: i64 = 0
29 while pre[i] != (0 as u8) {
30 if name[i] != pre[i] { return 0 }
31 i = i + 1
32 }
33 var n: i64 = i
34 while name[n] != (0 as u8) { n = n + 1 }
35 if n < i + 3 { return 0 }
36 if name[n-3] != (46 as u8) { return 0 }
37 if name[n-2] != (110 as u8) { return 0 }
38 if name[n-1] != (120 as u8) { return 0 }
39 return 1
40}
41
42// parse the max "(shape N" claim in a file's first 1200 bytes (the header doc is the claim site)
43func cs_shape_claim(path: *u8) -> i64 {
44 let lenp: *i64 = sys_mmap(16) as *i64
45 let b: *u8 = sys_read_file(path, lenp)
46 var n: i64 = lenp[0]
47 if n <= 0 { return 0 }
48 if n > K_MAGIC_1200 { n = K_MAGIC_1200 }
49 let m: *u8 = "(shape " as *u8
50 var best: i64 = 0
51 var i: i64 = 0
52 while i < n {
53 var k: i64 = 0
54 var hit: i64 = 1
55 while m[k] != (0 as u8) {
56 if i + k >= n { hit = 0 }
57 if hit == 1 { if b[i+k] != m[k] { hit = 0 } }
58 k = k + 1
59 }
60 if hit == 1 {
61 var v: i64 = 0
62 var j: i64 = i + k
63 while j < n {
64 let c: i64 = b[j] as i64
65 if c < 48 { j = n }
66 if j < n { if c > 57 { j = n } }
67 if j < n { v = v * 10 + (c - 48); j = j + 1 }
68 }
69 if v > best { best = v }
70 }
71 i = i + 1
72 }
73 return best
74}
75
76// scan runtime/_hdl_build for emitter sources; return the MAX shape any emitter claims
77func cs_max_emitter_shape() -> i64 {
78 let dfd: i64 = sys_openat_rd("runtime/_hdl_build" as *u8)
79 if dfd < 0 { return 0 - 1 }
80 let buf: *u8 = sys_mmap(K_MAGIC_65536)
81 var best: i64 = 0
82 var more: i64 = 1
83 while more == 1 {
84 let nb: i64 = sys_getdents64(dfd, buf, K_MAGIC_65536)
85 if nb <= 0 { more = 0 }
86 var off: i64 = 0
87 while off < nb {
88 let rec: *u8 = (buf as i64 + off) as *u8
89 let nm: *u8 = dirent_name(rec)
90 if cs_is_emit(nm) == 1 {
91 let p: *u8 = sys_mmap(512)
92 var o: i64 = cs_cat(p, 0, "runtime/_hdl_build/" as *u8)
93 o = cs_cat(p, o, nm)
94 let v: i64 = cs_shape_claim(p)
95 if v > best { best = v }
96 }
97 off = off + dirent_reclen(rec)
98 }
99 }
100 sys_close(dfd)
101 return best
102}
103
104// the gate: emitters are ground truth; census + classifier must cover them. out[0]=max emitter shape,
105// out[1]=pl_n_patterns, out[2]=plc_max_covered. Returns 1 GREEN / 0 RED.
106func cs_gate(out: *i64) -> i64 {
107 out[0] = cs_max_emitter_shape()
108 out[1] = pl_n_patterns()
109 out[2] = plc_max_covered()
110 if out[0] <= 0 { return 0 }
111 var ok: i64 = 1
112 if out[1] < out[0] { ok = 0 }
113 if out[2] < out[0] { ok = 0 }
114 return ok
115}
116
117func cs_report(out: *i64, green: i64) -> i64 {
118 let fd: i64 = sys_openat_append("knowledge/status/classifier_sync.log" as *u8, 0x1a4)
119 if fd < 0 { return 0 }
120 cs_w(fd, "CLSYNC max_emitter_shape=" as *u8); cs_wn(fd, out[0])
121 cs_w(fd, " library_census=" as *u8); cs_wn(fd, out[1])
122 cs_w(fd, " classifier_covered=" as *u8); cs_wn(fd, out[2])
123 cs_w(fd, " epoch=" as *u8); cs_wn(fd, sys_now_realtime_sec())
124 if green == 1 { cs_w(fd, " verdict=GREEN\n" as *u8) }
125 if green != 1 { cs_w(fd, " verdict=RED(census-behind-emitters)\n" as *u8) }
126 sys_close(fd)
127 return 1
128}
129
130func main() -> i64 {
131 let out: *i64 = sys_mmap(32) as *i64
132 let green: i64 = cs_gate(out)
133 cs_report(out, green)
134 cs_w(1, "CLSYNC max_emitter_shape=" as *u8); cs_wn(1, out[0])
135 cs_w(1, " library_census=" as *u8); cs_wn(1, out[1])
136 cs_w(1, " classifier_covered=" as *u8); cs_wn(1, out[2])
137 if green == 1 { cs_w(1, " verdict=GREEN\n" as *u8); sys_exit(0) }
138 cs_w(1, " verdict=RED(census-behind-emitters)\n" as *u8)
139 sys_exit(1)
140 return 1
141}