code wiki / _hdl_build / nx_capgraph_lib.nx

nx_capgraph_lib.nx source

↩ module page · 440 lines · 15876 B

1// nx_capgraph_lib.nx -- MANY-AXIS CAPABILITY GRAPH, pure core. No main. 2// 3// WHY (operator 2026-07-31): "a many axis capabilities graph system ... sota as of july 2026 ... 4// instead of our broken flat percent we are using to track growth towards sota so we can start 5// properly mapping forward progress". 6// 7// MEASURED DISEASE (this session, not asserted). nx_capaxes_lib made the SHAPE measurable and its six 8// laws are correct, but the wiring is DEGENERATE: nx_capaxes_derive sets evidence=0 for EVERY domain by 9// construction, so all 28 domains headline MIN=0 today and would still headline 0 after real work lands. 10// A ruler whose every reading is 0 has NO DYNAMIC RANGE and cannot map forward progress -- so the rank 11// silently falls back to leverage, and leverage IS the coverage claim = the flat percent again, wearing 12// a vector's clothes. Second defect: there is no GRAPH. A flat 6-vector per domain has no nodes, no 13// prerequisite edges, no rollup, so a capability standing on a broken prerequisite still reads healthy. 14// 15// SOTA GROUNDING (July 2026, external -- the comparator nx_cap_census correctly refuses to fake): 16// ADeLe / general scales (Nature 2026; arXiv 2503.06378; Microsoft Research + CFI) 17// -- rate DEMAND on 18 dimensions at ORDINAL levels 0..5, and define a subject's ABILITY on a 18// dimension as the demand level at which its success probability crosses 50%. Ability is DERIVED 19// from pass/fail evidence, never claimed; demand-vs-ability then PREDICTS new-task success (~88%). 20// Item Response Theory / adaptive testing for LLM eval (arXiv 2511.04689, 2505.15055, 2510.00844) 21// -- raw accuracy treats all items as equally informative; that IS the flat-percent disease. 22// HELM (Stanford CRFM, arXiv 2211.09110) -- scenario x metric MATRIX, deliberately NO single aggregate. 23// Prerequisite-DAG evaluation with weakest-path propagation (SaaSBench arXiv 2605.17526) 24// -- downstream criteria are SKIPPED when prerequisites are unmet, and the overall strength of a 25// claim mapping is determined by the WEAKEST PATH through the dependency graph. 26// 27// WHAT THIS ADDS TO nx_capaxes_lib (which stays the axis-vector authority; L1/L2/L3 are preserved): 28// 29// LAW L4 -- WEAKEST PATH. A node can NEVER exceed the minimum effective level of its prerequisites. 30// This is L1 (headline=MIN) generalised from a flat vector to a DAG. A capability standing on a TOY 31// prerequisite is a TOY capability no matter what its own axes say, because the prerequisite is what a 32// user hits first. UNMEASURED prerequisites are SKIPPED, never propagated (L3 holds across edges too). 33// 34// LAW L5 -- ORDINAL LEVELS, NOT PERCENTAGES. Each level is defined by WHAT IT TAKES TO PASS, so it 35// cannot be gamed by breadth the way a percentage can, and a level-up is a countable event -- which is 36// exactly what mapping forward progress requires. The ladder is derived from laws this ecosystem 37// already banked by measurement, not from taste: 38// 0 ABSENT nothing exists 39// 1 TOY exists, demo-only 40// 2 WORKS real inputs, happy path 41// 3 GATED a gate proves it AND the gate is proven able to fail (non-vacuity law) 42// 4 ADOPTED wired at the live chokepoint, has a production caller 43// (A GATED FN WITH NO PRODUCTION CALLER IS STILL THE BASELINE) 44// 5 SOTA beats a declared EXTERNAL reference on a declared metric 45// 46// LAW L6 -- EVIDENCE CAPS THE CLAIM. A node's level is capped by its evidence class, so a claim can 47// never outrun its proof. This is the structural cure for gamed-on-coverage-at-below-toy-quality: 48// claiming 5 with demo-only evidence yields 1, not 5. No reviewer memory required. 49// 50// LAW L7 -- PREDICTION IS THE PRODUCT. Ability >= demand, per axis, is a FORWARD question a percentage 51// can never answer. cg_predict names the BINDING axis of a task the system would fail. 52// 53// license_tier: ORIGINAL No hw writes (Rule 26). 54import "nx_capaxes_lib.nx" 55 56const CG_UNMEASURED: i64 = 0 - 1 57const CG_LMAX: i64 = 5 58const CG_MAXN: i64 = 256 59const CG_MAXAX: i64 = 16 60const CG_MAXE: i64 = 2048 61const CG_NAMEW: i64 = 40 62 63const CG_EV_NONE: i64 = 0 64const CG_EV_DEMO: i64 = 1 65const CG_EV_REAL: i64 = 2 66const CG_EV_GATE: i64 = 3 67const CG_EV_LIVE: i64 = 4 68const CG_EV_EXT: i64 = 5 69 70static cg_naxes: i64 71static cg_nn: i64 72static cg_ne: i64 73static cg_lv: *i64 74static cg_ev: *i64 75static cg_own: *i64 76static cg_eff: *i64 77static cg_effax: *i64 78static cg_ef: *i64 79static cg_et: *i64 80static cg_mark: *i64 81static cg_names: *u8 82static cg_axnames: *u8 83 84func cg_streq(a: *u8, b: *u8) -> i64 { 85 var i: i64 = 0 86 var same: i64 = 1 87 var done: i64 = 0 88 while done == 0 { 89 let ca: i64 = a[i] as i64 90 let cb: i64 = b[i] as i64 91 if ca != cb { 92 same = 0 93 done = 1 94 } else { 95 if ca == 0 { done = 1 } 96 else { i = i + 1 } 97 } 98 } 99 return same 100} 101 102func cg_level_name(l: i64) -> *u8 { 103 if l == 0 { return "ABSENT" as *u8 } 104 if l == 1 { return "TOY" as *u8 } 105 if l == 2 { return "WORKS" as *u8 } 106 if l == 3 { return "GATED" as *u8 } 107 if l == 4 { return "ADOPTED" as *u8 } 108 if l == 5 { return "SOTA" as *u8 } 109 return "UNMEASURED" as *u8 110} 111 112func cg_ev_name(e: i64) -> *u8 { 113 if e == CG_EV_NONE { return "none" as *u8 } 114 if e == CG_EV_DEMO { return "demo" as *u8 } 115 if e == CG_EV_REAL { return "real-inputs" as *u8 } 116 if e == CG_EV_GATE { return "gate-nonvacuous" as *u8 } 117 if e == CG_EV_LIVE { return "live-caller" as *u8 } 118 if e == CG_EV_EXT { return "external-battery" as *u8 } 119 return "unknown" as *u8 120} 121 122func cg_init(naxes: i64) { 123 cg_naxes = naxes 124 if cg_naxes > CG_MAXAX { cg_naxes = CG_MAXAX } 125 cg_nn = 0 126 cg_ne = 0 127 cg_lv = sys_mmap(CG_MAXN * CG_MAXAX * 8 + 64) as *i64 128 cg_ev = sys_mmap(CG_MAXN * 8 + 64) as *i64 129 cg_own = sys_mmap(CG_MAXN * 8 + 64) as *i64 130 cg_eff = sys_mmap(CG_MAXN * 8 + 64) as *i64 131 cg_effax = sys_mmap(CG_MAXN * CG_MAXAX * 8 + 64) as *i64 132 cg_ef = sys_mmap(CG_MAXE * 8 + 64) as *i64 133 cg_et = sys_mmap(CG_MAXE * 8 + 64) as *i64 134 cg_mark = sys_mmap(CG_MAXN * 8 + 64) as *i64 135 cg_names = sys_mmap(CG_MAXN * CG_NAMEW + 64) as *u8 136 cg_axnames = sys_mmap(CG_MAXAX * CG_NAMEW + 64) as *u8 137} 138 139func cg_name_put(slot: i64, src: *u8) { 140 var w: i64 = 0 141 while src[w] != (0 as u8) { 142 if w < CG_NAMEW - 1 { 143 let dst: *u8 = (slot + w) as *u8 144 dst[0] = src[w] 145 } 146 w = w + 1 147 } 148 if w > CG_NAMEW - 1 { w = CG_NAMEW - 1 } 149 let term: *u8 = (slot + w) as *u8 150 term[0] = 0 as u8 151} 152 153func cg_axis_set(a: i64, nm: *u8) { 154 if a < 0 { return } 155 if a >= cg_naxes { return } 156 cg_name_put((cg_axnames as i64) + a * CG_NAMEW, nm) 157} 158 159func cg_axis_name(a: i64) -> *u8 { 160 if a < 0 { return "unknown" as *u8 } 161 if a >= cg_naxes { return "unknown" as *u8 } 162 return ((cg_axnames as i64) + a * CG_NAMEW) as *u8 163} 164 165func cg_node_name(i: i64) -> *u8 { 166 if i < 0 { return "unknown" as *u8 } 167 if i >= cg_nn { return "unknown" as *u8 } 168 return ((cg_names as i64) + i * CG_NAMEW) as *u8 169} 170 171func cg_addnode(nm: *u8, evclass: i64) -> i64 { 172 if cg_nn >= CG_MAXN { return 0 - 1 } 173 let id: i64 = cg_nn 174 cg_name_put((cg_names as i64) + id * CG_NAMEW, nm) 175 cg_ev[id] = evclass 176 var a: i64 = 0 177 while a < cg_naxes { 178 cg_lv[id * CG_MAXAX + a] = CG_UNMEASURED 179 a = a + 1 180 } 181 cg_nn = cg_nn + 1 182 return id 183} 184 185func cg_addedge(prereq: i64, dependent: i64) -> i64 { 186 if cg_ne >= CG_MAXE { return 0 - 1 } 187 if prereq < 0 { return 0 - 1 } 188 if dependent < 0 { return 0 - 1 } 189 if prereq >= cg_nn { return 0 - 1 } 190 if dependent >= cg_nn { return 0 - 1 } 191 cg_ef[cg_ne] = prereq 192 cg_et[cg_ne] = dependent 193 cg_ne = cg_ne + 1 194 return 0 195} 196 197func cg_cap(claim: i64, evclass: i64) -> i64 { 198 if claim == CG_UNMEASURED { return CG_UNMEASURED } 199 var c: i64 = claim 200 if c > CG_LMAX { c = CG_LMAX } 201 if c < 0 { c = 0 } 202 if c > evclass { c = evclass } 203 return c 204} 205 206func cg_set(node: i64, axis: i64, claim: i64) { 207 if node < 0 { return } 208 if node >= cg_nn { return } 209 if axis < 0 { return } 210 if axis >= cg_naxes { return } 211 cg_lv[node * CG_MAXAX + axis] = cg_cap(claim, cg_ev[node]) 212} 213 214func cg_get(node: i64, axis: i64) -> i64 { 215 if node < 0 { return CG_UNMEASURED } 216 if node >= cg_nn { return CG_UNMEASURED } 217 if axis < 0 { return CG_UNMEASURED } 218 if axis >= cg_naxes { return CG_UNMEASURED } 219 return cg_lv[node * CG_MAXAX + axis] 220} 221 222func cg_own_headline(node: i64) -> i64 { 223 var best: i64 = CG_UNMEASURED 224 var a: i64 = 0 225 while a < cg_naxes { 226 let v: i64 = cg_lv[node * CG_MAXAX + a] 227 if v != CG_UNMEASURED { 228 if best == CG_UNMEASURED { best = v } 229 else { if v < best { best = v } } 230 } 231 a = a + 1 232 } 233 return best 234} 235 236func cg_starved_axis(node: i64) -> i64 { 237 var bi: i64 = 0 - 1 238 var a: i64 = 0 239 while a < cg_naxes { 240 let v: i64 = cg_lv[node * CG_MAXAX + a] 241 if v != CG_UNMEASURED { 242 if bi < 0 { bi = a } 243 else { if v < cg_lv[node * CG_MAXAX + bi] { bi = a } } 244 } 245 a = a + 1 246 } 247 return bi 248} 249 250// L4 -- WEAKEST PATH, propagated PER AXIS. Collapsing the graph to one scalar before propagating would 251// flatten every axis to the node's cross-axis minimum and destroy the multi-axis profile -- measured as a 252// real defect in the first build of this gate (T8b RED). Keeping the MATRIX and propagating each axis 253// independently is the HELM discipline (scenario x metric, never a premature aggregate) combined with the 254// prerequisite-DAG weakest-path rule. The scalar headline is derived LAST, from the per-axis result. 255func cg_relax() { 256 var i: i64 = 0 257 while i < cg_nn { 258 cg_own[i] = cg_own_headline(i) 259 var a: i64 = 0 260 while a < cg_naxes { 261 cg_effax[i * CG_MAXAX + a] = cg_lv[i * CG_MAXAX + a] 262 a = a + 1 263 } 264 i = i + 1 265 } 266 var pass: i64 = 0 267 while pass < cg_nn { 268 var e: i64 = 0 269 while e < cg_ne { 270 let p: i64 = cg_ef[e] 271 let d: i64 = cg_et[e] 272 var a2: i64 = 0 273 while a2 < cg_naxes { 274 let pv: i64 = cg_effax[p * CG_MAXAX + a2] 275 if pv != CG_UNMEASURED { 276 let dv: i64 = cg_effax[d * CG_MAXAX + a2] 277 if dv == CG_UNMEASURED { cg_effax[d * CG_MAXAX + a2] = pv } 278 else { if pv < dv { cg_effax[d * CG_MAXAX + a2] = pv } } 279 } 280 a2 = a2 + 1 281 } 282 e = e + 1 283 } 284 pass = pass + 1 285 } 286 i = 0 287 while i < cg_nn { 288 var best: i64 = CG_UNMEASURED 289 var a3: i64 = 0 290 while a3 < cg_naxes { 291 let v: i64 = cg_effax[i * CG_MAXAX + a3] 292 if v != CG_UNMEASURED { 293 if best == CG_UNMEASURED { best = v } 294 else { if v < best { best = v } } 295 } 296 a3 = a3 + 1 297 } 298 cg_eff[i] = best 299 i = i + 1 300 } 301} 302 303func cg_effective_axis(node: i64, axis: i64) -> i64 { 304 if node < 0 { return CG_UNMEASURED } 305 if node >= cg_nn { return CG_UNMEASURED } 306 if axis < 0 { return CG_UNMEASURED } 307 if axis >= cg_naxes { return CG_UNMEASURED } 308 return cg_effax[node * CG_MAXAX + axis] 309} 310 311func cg_effective(node: i64) -> i64 { 312 if node < 0 { return CG_UNMEASURED } 313 if node >= cg_nn { return CG_UNMEASURED } 314 return cg_eff[node] 315} 316 317func cg_descendants(root: i64) -> i64 { 318 if root < 0 { return 0 } 319 if root >= cg_nn { return 0 } 320 var i: i64 = 0 321 while i < cg_nn { 322 cg_mark[i] = 0 323 i = i + 1 324 } 325 cg_mark[root] = 1 326 var pass: i64 = 0 327 while pass < cg_nn { 328 var e: i64 = 0 329 while e < cg_ne { 330 let p: i64 = cg_ef[e] 331 let d: i64 = cg_et[e] 332 if cg_mark[p] == 1 { cg_mark[d] = 1 } 333 e = e + 1 334 } 335 pass = pass + 1 336 } 337 var c: i64 = 0 338 i = 0 339 while i < cg_nn { 340 if cg_mark[i] == 1 { c = c + 1 } 341 i = i + 1 342 } 343 return c - 1 344} 345 346func cg_rock(node: i64) -> i64 { 347 let e: i64 = cg_effective(node) 348 if e == CG_UNMEASURED { return 0 } 349 let deficit: i64 = CG_LMAX - e 350 if deficit <= 0 { return 0 } 351 return deficit * (1 + cg_descendants(node)) 352} 353 354// Rock with EXPLICIT leverage. When the graph has no declared edges yet, downstream mass is 0 for every 355// node and the plain rock TIES EVERYTHING -- measured on the first live run, where all 28 domains scored 356// exactly 5 and the ranking carried no information. Leverage lets the caller supply the STAKE: how large 357// a claim is riding on the missing evidence. This preserves the original insight -- the domain asserting 358// the MOST with the LEAST proof is the biggest liability -- while never letting the claim touch the LEVEL 359// itself, which T10c guards. 360func cg_rock_lev(node: i64, leverage: i64) -> i64 { 361 let e: i64 = cg_effective(node) 362 if e == CG_UNMEASURED { return 0 } 363 let deficit: i64 = CG_LMAX - e 364 if deficit <= 0 { return 0 } 365 var lev: i64 = leverage 366 if lev < 1 { lev = 1 } 367 return deficit * (1 + cg_descendants(node)) * lev 368} 369 370func cg_predict(node: i64, demand: *i64) -> i64 { 371 var worst: i64 = 0 - 1 372 var gap: i64 = 0 373 var a: i64 = 0 374 while a < cg_naxes { 375 let d: i64 = demand[a] 376 if d != CG_UNMEASURED { 377 var abil: i64 = cg_effective_axis(node, a) 378 if abil == CG_UNMEASURED { abil = 0 } 379 if abil < d { 380 let this_gap: i64 = d - abil 381 if this_gap > gap { 382 gap = this_gap 383 worst = a 384 } 385 } 386 } 387 a = a + 1 388 } 389 return worst 390} 391 392func cg_grounded(node: i64) -> i64 { 393 if node < 0 { return 0 } 394 if node >= cg_nn { return 0 } 395 if cg_ev[node] <= CG_EV_NONE { return 0 } 396 return 1 397} 398 399// A gate result that lives only in stdout is INVISIBLE to this ecosystem's evidence machinery -- the 400// rollup, the honesty gate and the evidence audit all read knowledge/status/<name>.log. A GREEN gate 401// nothing can read is, to every ruler here, an UNMEASURED capability: the exact PROVEN-BUT-UNWIRED 402// class this graph was built to expose. It would have been absurd for the capability-measuring organs 403// to be invisible to the measurement system. Append-only, so history accumulates (rule 13). 404// DELEGATES to the base layer. The implementation moved DOWN to nx_capaxes_lib once nx_capaxes_gate 405// turned out to have the same invisibility defect: keeping a second copy here would have been a 406// divergent-dup waiting to happen (two emitters, one format, drifting apart silently). This wrapper 407// stays so the three capgraph gates keep their call site unchanged. 408func cg_gate_log(path: *u8, tag: *u8, passed: i64, total: i64) { 409 cax_gate_log(path, tag, passed, total) 410} 411 412func cg_row(node: i64) { 413 cax_puts(cg_node_name(node)) 414 cax_puts(" " as *u8) 415 if cg_grounded(node) == 0 { 416 cax_puts("UNGROUNDED evidence=none -- refusing to emit a level (L2)\n" as *u8) 417 return 418 } 419 let own: i64 = cg_own[node] 420 let eff: i64 = cg_eff[node] 421 cax_kv("own" as *u8, own) 422 cax_puts("(" as *u8) 423 cax_puts(cg_level_name(own)) 424 cax_puts(") " as *u8) 425 cax_kv("effective" as *u8, eff) 426 cax_puts("(" as *u8) 427 cax_puts(cg_level_name(eff)) 428 cax_puts(") " as *u8) 429 if eff < own { 430 cax_puts("CAPPED-BY-PREREQ " as *u8) 431 } 432 cax_puts("starved=" as *u8) 433 cax_puts(cg_axis_name(cg_starved_axis(node))) 434 cax_puts(" " as *u8) 435 cax_kv("gates" as *u8, cg_descendants(node)) 436 cax_kv("rock" as *u8, cg_rock(node)) 437 cax_puts("ev=" as *u8) 438 cax_puts(cg_ev_name(cg_ev[node])) 439 cax_puts("\n" as *u8) 440}