code wiki / _hdl_build / nx_train_triage.nx
nx_train_triage.nx source
↩ module page · 181 lines · 7831 B
1// nx_train_triage.nx -- TRAINING-DYNAMICS TRIAGE library (the team organ that handles what
2// Claude diagnosed BY HAND during T4, 2026-06-10). Reads a machine-readable training report
3// (TRIAGE-CLASS/TRIAGE-PRED/TRIAGE-ACC/TRIAGE-LOSS rows, emitted by training lanes like
4// _t4b_closed_loop_authored) and NAMES the failure mode + the gate-proven fix, mechanically.
5// The five lessons it encodes were each MEASURED on real failed gate runs:
6// MAJORITY-COLLAPSE per-sample Adam on 88:2 imbalance -> all preds one class (88/149)
7// POST-CONVERGENCE-BLOWUP constant-lr Adam after convergence: grads->0 => v->0 => mh/eps spike
8// RARE-CLASS-LOSS unweighted loss loses exactly the smallest classes (util+mon)
9// PLATEAU uncentered all-positive features froze relu nets across 3 optimizers
10// CONVERGED-HEALTHY what T4/T4b look like when the lessons are applied
11// Pure integer math on micro-loss values -- no f32 needed to judge a curve.
12// RACI: Doctor owns the DIAGNOSIS verb here (names the heal); the Engineer owns the gate.
13// LAWS: struct-free, flat ifs, no &&/||, loud verdicts. license_tier: ORIGINAL
14import "nx_syscalls.nx"
15const TT_MAGIC_8192: i64 = 8192
16
17const TT_HEALTHY: i64 = 0
18const TT_COLLAPSE: i64 = 1
19const TT_BLOWUP: i64 = 2
20const TT_RARE: i64 = 3
21const TT_PLATEAU: i64 = 4
22const TT_UNDIAG: i64 = 5
23const TT_BROKEN: i64 = 6
24const TT_MAXCLS: i64 = 16
25const TT_MAXLOSS: i64 = 512
26
27func tt_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 }
28func tt_wn(fd: i64, v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(fd,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(fd,bb,k); return 0 }
29
30func tt_match(b: *u8, i: i64, n: i64, lit: *u8) -> i64 {
31 var k: i64 = 0
32 while lit[k] != (0 as u8) {
33 if i + k >= n { return 0 }
34 if b[i+k] != lit[k] { return 0 }
35 k = k + 1
36 }
37 return 1
38}
39func tt_keynum(b: *u8, ls: i64, le: i64, key: *u8) -> i64 {
40 var i: i64 = ls
41 while i < le {
42 if tt_match(b, i, le, key) == 1 {
43 var k: i64 = 0
44 while key[k] != (0 as u8) { k = k + 1 }
45 var j: i64 = i + k
46 var v: i64 = 0
47 var got: i64 = 0
48 while j < le {
49 let c: i64 = b[j] as i64
50 if c < 48 { j = le } else { if c > 57 { j = le } else { v = v*10 + (c - 48); got = 1; j = j + 1 } }
51 }
52 if got == 1 { return v }
53 }
54 i = i + 1
55 }
56 return 0 - 1
57}
58
59// parse a report. out layout: classes[0..15], preds[16..31], [32]=right [33]=total [34]=got_acc
60// [35]=ncls [36]=nloss, losses at [40..] (micro values only, epoch order)
61func tt_read(path: *u8, out: *i64) -> i64 {
62 var z: i64 = 0
63 while z < 40 + TT_MAXLOSS { out[z] = 0; z = z + 1 }
64 out[32] = 0 - 1
65 out[33] = 0 - 1
66 let lenp: *i64 = sys_mmap(16) as *i64
67 let b: *u8 = sys_read_file(path, lenp)
68 let n: i64 = lenp[0]
69 if n <= 0 { return 0 }
70 var ls: i64 = 0
71 while ls < n {
72 var le: i64 = ls
73 var stop: i64 = 0
74 while stop == 0 {
75 if le >= n { stop = 1 } else { if b[le] == (10 as u8) { stop = 1 } else { le = le + 1 } }
76 }
77 if tt_match(b, ls, le, "TRIAGE-CLASS " as *u8) == 1 {
78 let id: i64 = tt_keynum(b, ls, le, "id=" as *u8)
79 let c: i64 = tt_keynum(b, ls, le, "count=" as *u8)
80 if id >= 0 { if id < TT_MAXCLS { if c >= 0 {
81 out[id] = c
82 if id + 1 > out[35] { out[35] = id + 1 }
83 } } }
84 }
85 if tt_match(b, ls, le, "TRIAGE-PRED " as *u8) == 1 {
86 let id2: i64 = tt_keynum(b, ls, le, "id=" as *u8)
87 let c2: i64 = tt_keynum(b, ls, le, "count=" as *u8)
88 if id2 >= 0 { if id2 < TT_MAXCLS { if c2 >= 0 { out[16 + id2] = c2 } } }
89 }
90 if tt_match(b, ls, le, "TRIAGE-ACC " as *u8) == 1 {
91 out[32] = tt_keynum(b, ls, le, "right=" as *u8)
92 out[33] = tt_keynum(b, ls, le, "total=" as *u8)
93 out[34] = 1
94 }
95 if tt_match(b, ls, le, "TRIAGE-LOSS " as *u8) == 1 {
96 let mv: i64 = tt_keynum(b, ls, le, "micro=" as *u8)
97 if mv >= 0 { if out[36] < TT_MAXLOSS {
98 out[40 + out[36]] = mv
99 out[36] = out[36] + 1
100 } }
101 }
102 ls = le + 1
103 }
104 return 1
105}
106
107// the diagnosis: priority order matters (collapse > blowup > rare-class > plateau > healthy)
108func tt_diagnose(path: *u8) -> i64 {
109 let out: *i64 = sys_mmap(TT_MAGIC_8192) as *i64
110 let ok: i64 = tt_read(path, out)
111 if ok == 0 { return TT_BROKEN }
112 let ncls: i64 = out[35]
113 let nloss: i64 = out[36]
114 let right: i64 = out[32]
115 let total: i64 = out[33]
116 if out[34] == 0 { return TT_BROKEN }
117 if ncls == 0 { return TT_BROKEN }
118 if nloss == 0 { return TT_BROKEN }
119 if total <= 0 { return TT_BROKEN }
120 let first: i64 = out[40]
121 let final: i64 = out[40 + nloss - 1]
122 var minv: i64 = first
123 var li: i64 = 1
124 while li < nloss { if out[40 + li] < minv { minv = out[40 + li] } li = li + 1 }
125 // 1: MAJORITY-COLLAPSE -- every prediction is one class and acc == that class's true count
126 var c: i64 = 0
127 while c < ncls {
128 if out[16 + c] == total { if right == out[c] { if ncls > 1 { return TT_COLLAPSE } } }
129 c = c + 1
130 }
131 // 2: POST-CONVERGENCE-BLOWUP -- loss converged (min << first) then rose >= 10x above the min
132 if final > minv * 10 { if minv < first { if final >= 50 { return TT_BLOWUP } } }
133 // 3: RARE-CLASS-LOSS -- misses exist and EVERY under-predicted class is a small class
134 if right < total {
135 var maxcls: i64 = 0
136 c = 0
137 while c < ncls { if out[c] > maxcls { maxcls = out[c] } c = c + 1 }
138 var under: i64 = 0
139 var allsmall: i64 = 1
140 c = 0
141 while c < ncls {
142 if out[16 + c] < out[c] {
143 under = under + 1
144 if out[c] * 4 > maxcls { allsmall = 0 }
145 }
146 c = c + 1
147 }
148 if under > 0 { if allsmall == 1 { return TT_RARE } }
149 }
150 // 4: PLATEAU -- misses exist and the last quarter of the curve improved < 5%
151 if right < total {
152 if nloss >= 4 {
153 let a: i64 = out[40 + (nloss * 3) / 4]
154 if a > 0 {
155 if (a - final) * 20 < a { return TT_PLATEAU }
156 }
157 }
158 }
159 // 0: CONVERGED-HEALTHY -- 100% and the final loss sits at the curve minimum
160 if right == total { if final <= minv * 2 { return TT_HEALTHY } }
161 return TT_UNDIAG
162}
163
164func tt_name(id: i64) -> *u8 {
165 if id == 0 { return "CONVERGED-HEALTHY" as *u8 }
166 if id == 1 { return "MAJORITY-COLLAPSE" as *u8 }
167 if id == 2 { return "POST-CONVERGENCE-BLOWUP" as *u8 }
168 if id == 3 { return "RARE-CLASS-LOSS" as *u8 }
169 if id == 4 { return "PLATEAU" as *u8 }
170 if id == 6 { return "BROKEN-REPORT" as *u8 }
171 return "UNDIAGNOSED" as *u8
172}
173func tt_fix(id: i64) -> *u8 {
174 if id == 0 { return "NONE" as *u8 }
175 if id == 1 { return "FULL-BATCH-GRAD-ACCUMULATION (per-sample updates collapse to the majority on imbalanced streams)" as *u8 }
176 if id == 2 { return "EARLY-STOP-AT-CONVERGENCE (grads->0 => v->0 => mh/eps spike jumps basins)" as *u8 }
177 if id == 3 { return "INVERSE-FREQ-CLASS-WEIGHTS (rare-class gradients vanish in unweighted sums)" as *u8 }
178 if id == 4 { return "CENTER-FEATURES then capacity then optimizer (all-positive features kill relu nets)" as *u8 }
179 if id == 6 { return "report is missing CLASS/ACC/LOSS rows -- fix the emitting lane" as *u8 }
180 return "new failure shape: name it, add its KAT to the triage gate" as *u8
181}