code wiki / (root) / nx_swarm_coord.nx

nx_swarm_coord.nx source

↩ module page · 245 lines · 12605 B

1// nx_swarm_coord.nx -- SWARM FABRIC class-aware COORDINATOR (SF-R-COORD): the missing UNIFIER that routes 2// any job to the right COMPUTE CLASS across the heterogeneous pool. Fixes the "ignored the GPU" gap in the 3// fabric AND coordinates the fabric with the EXISTING mesh -- it does NOT duplicate them: 4// * GPU classes (image/video/render/train) -> the GPU worker roster from nx_worker_dispatch (wd_route/ 5// wd_worker_where: gpu-image=laptop 5080, gpu-video=west 3090). Roles/endpoints are that organ's SSOT; 6// inlined here (like nx_dispatch_lease inlined rt_lock) to keep ONE nx_syscalls surface + dodge the 7// double-main trap of importing an executable organ. 8// * CPU classes (cpu/tile/transcode/hash) -> the fabric's nx_swarm_place over live beacon telemetry. 9// * CLOUD/LLM classes -> the cloud/no-float lane. 10// * ALLOCATION under contention (VRAM/GPU-us/CPU-us fairness+preempt) = nx_resource_arbiter (already models 11// these as first-class; its budgets are "fed by nothing" today -> the beacon telemetry is the FEED to wire). 12// 13// ★★THE LIAR-KILLER = THE OPERATOR'S CORRECTION, MECHANIZED: a GPU-class job is NEVER placed on a GPU-less 14// node. If no GPU is available it QUEUES (or falls back to ANOTHER GPU) -- it must NOT silently run gen on 15// CPU. The gate proves this can't happen. That is "don't ignore the GPU", turned into a test. 16// 17// route <class> <gpu_avail_csv> -- decide subsystem+target for a job class given which GPU roles are up 18// [gate] -- self-gate: routing discriminates + the never-ignore-GPU liar-killers 19// gpu_avail_csv = comma list of live GPU roles, e.g. "gpu-image,gpu-video" or "" (none up). In production this 20// comes from the mesh probe / the beat store once GPU nodes beacon. license_tier: ORIGINAL expect_exit:0 21import "nx_swarm_lib.nx" 22import "nx_swarm_endpoint_lib.nx" // role->addr SSOT (seq1485): a LIB has no main, so the 23 // double-main trap that forced the inlined literals is gone. 24 25// compute-class kinds 26const CO_GPU: i64 = 0 27const CO_CPU: i64 = 1 28const CO_CLOUD: i64 = 2 29const CO_UNKNOWN: i64 = 3 30 31func co_puts(s: *u8) -> i64 { sys_write(1, s, fa_len(s)); return 0 } 32func co_eq(a: *u8, b: *u8) -> i64 { 33 var i: i64 = 0 34 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 35 if b[i] != (0 as u8) { return 0 } 36 return 1 37} 38 39// class -> kind. GPU = anything that needs a real accelerator (the gen north-star's core). 40func co_kind(cls: *u8) -> i64 { 41 if co_eq(cls, "image" as *u8) == 1 { return CO_GPU } 42 if co_eq(cls, "img" as *u8) == 1 { return CO_GPU } 43 if co_eq(cls, "video" as *u8) == 1 { return CO_GPU } 44 if co_eq(cls, "vid" as *u8) == 1 { return CO_GPU } 45 if co_eq(cls, "render" as *u8) == 1 { return CO_GPU } 46 if co_eq(cls, "gen" as *u8) == 1 { return CO_GPU } 47 if co_eq(cls, "train" as *u8) == 1 { return CO_GPU } 48 if co_eq(cls, "cpu" as *u8) == 1 { return CO_CPU } 49 if co_eq(cls, "tile" as *u8) == 1 { return CO_CPU } 50 if co_eq(cls, "transcode" as *u8) == 1 { return CO_CPU } 51 if co_eq(cls, "hash" as *u8) == 1 { return CO_CPU } 52 if co_eq(cls, "index" as *u8) == 1 { return CO_CPU } 53 if co_eq(cls, "llm" as *u8) == 1 { return CO_CLOUD } 54 if co_eq(cls, "embed" as *u8) == 1 { return CO_CLOUD } 55 if co_eq(cls, "text" as *u8) == 1 { return CO_CLOUD } 56 return CO_UNKNOWN 57} 58 59// preferred GPU role for a GPU class (worker_dispatch SSOT: image->gpu-image, video->gpu-video). 60func co_pref_role(cls: *u8) -> *u8 { 61 if co_eq(cls, "video" as *u8) == 1 { return "gpu-video" as *u8 } 62 if co_eq(cls, "vid" as *u8) == 1 { return "gpu-video" as *u8 } 63 return "gpu-image" as *u8 // image/render/gen/train default to the image-class GPU 64} 65 66// endpoint for a GPU role (worker_dispatch SSOT wd_worker_where). 67func co_endpoint(role: *u8) -> *u8 { 68 if co_eq(role, "gpu-image" as *u8) == 1 { let a: *u8 = se_addr_desc("gpu-image" as *u8); if (a as i64) != 0 { return a } return "unknown" as *u8 } 69 if co_eq(role, "gpu-video" as *u8) == 1 { let a: *u8 = se_addr_desc("gpu-video" as *u8); if (a as i64) != 0 { return a } return "unknown" as *u8 } 70 return "unknown" as *u8 71} 72 73// is <role> a comma-field of <csv>? (bounded field walk, boundary = ',' or NUL) 74func co_csv_has(csv: *u8, role: *u8) -> i64 { 75 let cl: i64 = fa_len(csv) 76 let rl: i64 = fa_len(role) 77 if cl == 0 { return 0 } 78 var p: i64 = 0 79 while p < cl { 80 // field is [p, fend) 81 var q: i64 = p 82 var fend: i64 = cl 83 var scan: i64 = 1 84 while scan == 1 { 85 if q >= cl { scan = 0 } else { 86 if (csv[q] as i64) == 44 { fend = q; scan = 0 } else { q = q + 1 } 87 } 88 } 89 if fend - p == rl { 90 var k: i64 = 0 91 var same: i64 = 1 92 while k < rl { if csv[p + k] != role[k] { same = 0; k = rl } else { k = k + 1 } } 93 if same == 1 { return 1 } 94 } 95 p = fend + 1 96 } 97 return 0 98} 99 100// ★★★A ROLE IS ONLY USABLE IF IT IS BOTH OFFERED AND REACHABLE (2026-08-04). 101// MEASURED DEFECT: with gpu-video's address disabled (the west box is unreachable), this 102// coordinator happily printed `target=gpu-video @ unknown ... via=worker_dispatch` and RETURNED 0 = 103// ROUTED. The gate stayed 9/9 GREEN because it asserts the return CODE, not the reachability -- the 104// very failure mode nx_swarm_endpoint_lib's own header names: "a gate that measures the PLAN does 105// not measure the REACHABILITY". Dispatching to "unknown" is the silent black-hole this whole SSOT 106// exists to end, so availability now requires an address that RESOLVES, not merely a role someone 107// listed as up. An offered-but-unresolvable role falls through to the other GPU, then QUEUES. 108func co_usable(role: *u8, gpu_avail: *u8) -> i64 { 109 if co_csv_has(gpu_avail, role) == 0 { return 0 } 110 let a: *u8 = se_addr(role) 111 if (a as i64) == 0 { return 0 } 112 if a[0] == (0 as u8) { return 0 } 113 return 1 114} 115 116// the routing decision. Prints one COORD line. Returns: 117// 0 = routed (GPU target / CPU pool / CLOUD) 3 = QUEUE (GPU class, no GPU available -- NEVER cpu) 118// 2 = REFUSED (unknown class) 119func co_route(cls: *u8, gpu_avail: *u8) -> i64 { 120 let kind: i64 = co_kind(cls) 121 let t: *u8 = sys_mmap(256) 122 var o: i64 = 0 123 if kind == CO_UNKNOWN { 124 o = fa_cat(t, o, "COORD class=" as *u8); o = fa_cat(t, o, cls) 125 o = fa_cat(t, o, " REFUSED (unknown class)\n" as *u8) 126 sys_write(1, t, o); return 2 127 } 128 if kind == CO_CPU { 129 o = fa_cat(t, o, "COORD class=" as *u8); o = fa_cat(t, o, cls) 130 o = fa_cat(t, o, " kind=CPU route=swarm_place (fabric CPU pool, load/energy-aware)\n" as *u8) 131 sys_write(1, t, o); return 0 132 } 133 if kind == CO_CLOUD { 134 o = fa_cat(t, o, "COORD class=" as *u8); o = fa_cat(t, o, cls) 135 o = fa_cat(t, o, " kind=CLOUD route=cloud|no-float (LLM/embed lane)\n" as *u8) 136 sys_write(1, t, o); return 0 137 } 138 // kind == CO_GPU: MUST land on a GPU. Never a CPU fallback. 139 let pref: *u8 = co_pref_role(cls) 140 if co_usable(pref, gpu_avail) == 1 { 141 o = fa_cat(t, o, "COORD class=" as *u8); o = fa_cat(t, o, cls) 142 o = fa_cat(t, o, " kind=GPU target=" as *u8); o = fa_cat(t, o, pref) 143 o = fa_cat(t, o, " @ " as *u8); o = fa_cat(t, o, co_endpoint(pref)) 144 o = fa_cat(t, o, " via=worker_dispatch\n" as *u8) 145 sys_write(1, t, o); return 0 146 } 147 // preferred GPU down -> try the OTHER GPU role (still a GPU, never CPU) 148 var alt: *u8 = "gpu-video" as *u8 149 if co_eq(pref, "gpu-video" as *u8) == 1 { alt = "gpu-image" as *u8 } 150 if co_usable(alt, gpu_avail) == 1 { 151 o = fa_cat(t, o, "COORD class=" as *u8); o = fa_cat(t, o, cls) 152 o = fa_cat(t, o, " kind=GPU target=" as *u8); o = fa_cat(t, o, alt) 153 o = fa_cat(t, o, " (fallback GPU) @ " as *u8); o = fa_cat(t, o, co_endpoint(alt)) 154 o = fa_cat(t, o, " via=worker_dispatch\n" as *u8) 155 sys_write(1, t, o); return 0 156 } 157 // NO GPU usable -> QUEUE. A GPU job MUST NOT run on CPU, and MUST NOT be sent to an 158 // unresolvable address either -- a black hole is not a placement. 159 o = fa_cat(t, o, "COORD class=" as *u8); o = fa_cat(t, o, cls) 160 o = fa_cat(t, o, " kind=GPU QUEUE (no REACHABLE GPU: offered roles have no resolvable address; gen NEVER falls back to CPU)\n" as *u8) 161 sys_write(1, t, o); return 3 162} 163 164func co_gate() -> i64 { 165 var pass: i64 = 0 166 var total: i64 = 0 167 168 // T1 image -> GPU gpu-image (NOT cpu) when the 5080 is up 169 total = total + 1 170 if co_route("image" as *u8, "gpu-image,gpu-video" as *u8) == 0 { pass = pass + 1; co_puts("T1 image->GPU OK\n" as *u8) } 171 172 // T2 video -> GPU gpu-video 173 total = total + 1 174 if co_route("video" as *u8, "gpu-image,gpu-video" as *u8) == 0 { pass = pass + 1; co_puts("T2 video->GPU OK\n" as *u8) } 175 176 // T3 cpu -> CPU pool (swarm_place) 177 total = total + 1 178 if co_route("cpu" as *u8, "gpu-image" as *u8) == 0 { pass = pass + 1; co_puts("T3 cpu->CPU OK\n" as *u8) } 179 180 // T4 ★★THE CORRECTION: image with NO GPU up -> QUEUE (rc 3), NEVER a CPU node 181 total = total + 1 182 if co_route("image" as *u8, "" as *u8) == 3 { pass = pass + 1; co_puts("T4 gpu-job-no-gpu->QUEUE (never CPU) OK\n" as *u8) } 183 184 // T5 image with only gpu-video offered -> fall back to gpu-video, but ONLY if it is REACHABLE. 185 // ★Expectation is DERIVED from the table, not hardcoded, so this tooth stays honest whether the 186 // west box is up or down: a resolvable gpu-video must route (0); an unresolvable one must QUEUE 187 // (3) rather than be "placed" at an unknown address. Hardcoding either number would make the 188 // gate lie the moment the estate's hardware changed -- which is exactly how this defect hid. 189 total = total + 1 190 var t5exp: i64 = 3 191 if co_usable("gpu-video" as *u8, "gpu-video" as *u8) == 1 { t5exp = 0 } 192 if co_route("image" as *u8, "gpu-video" as *u8) == t5exp { pass = pass + 1; co_puts("T5 gpu-fallback-only-if-REACHABLE OK\n" as *u8) } 193 194 // T10 ★THE INVARIANT THE OLD GATE MISSED: offered-and-reachable is usable; offered-but- 195 // unresolvable is NOT. Positive control (gpu-image resolves) proves co_usable can return 1, so 196 // it is not a constant refuser; the negative side proves it can return 0 for a listed role. 197 total = total + 1 198 if co_usable("gpu-image" as *u8, "gpu-image" as *u8) == 1 { pass = pass + 1; co_puts("T10a reachable role IS usable (positive control) OK\n" as *u8) } 199 total = total + 1 200 if co_usable("gpu-image" as *u8, "" as *u8) == 0 { pass = pass + 1; co_puts("T10b role not offered -> unusable OK\n" as *u8) } 201 total = total + 1 202 if co_usable("gpu-nosuchrole" as *u8, "gpu-nosuchrole" as *u8) == 0 { pass = pass + 1; co_puts("T10c offered but UNRESOLVABLE -> unusable (never dispatch to unknown) OK\n" as *u8) } 203 204 // T6 video with only gpu-image up -> fallback to gpu-image (still GPU) 205 total = total + 1 206 if co_route("video" as *u8, "gpu-image" as *u8) == 0 { pass = pass + 1; co_puts("T6 video-fallback-GPU OK\n" as *u8) } 207 208 // T7 unknown class -> REFUSE 209 total = total + 1 210 if co_route("frobnicate" as *u8, "gpu-image" as *u8) == 2 { pass = pass + 1; co_puts("T7 unknown->REFUSE OK\n" as *u8) } 211 212 // T8 llm -> CLOUD 213 total = total + 1 214 if co_route("llm" as *u8, "gpu-image" as *u8) == 0 { pass = pass + 1; co_puts("T8 llm->CLOUD OK\n" as *u8) } 215 216 // T9 DETERMINISM: same inputs twice -> same verdict 217 total = total + 1 218 let a: i64 = co_route("image" as *u8, "gpu-image" as *u8) 219 let b: i64 = co_route("image" as *u8, "gpu-image" as *u8) 220 if a == b { if a == 0 { pass = pass + 1; co_puts("T9 deterministic OK\n" as *u8) } } 221 222 let t: *u8 = sys_mmap(128) 223 var to: i64 = 0 224 to = fa_cat(t, to, "SWARMCOORDGATE " as *u8) 225 to = fa_catn(t, to, pass) 226 to = fa_cat(t, to, "/" as *u8) 227 to = fa_catn(t, to, total) 228 if pass == total { to = fa_cat(t, to, " verdict=GREEN\n" as *u8) } else { to = fa_cat(t, to, " verdict=RED\n" as *u8) } 229 sys_write(1, t, to) 230 if pass == total { return 0 } 231 return 1 232} 233 234func main(argc: i64, argv: *i64) -> i64 { 235 if argc >= 2 { 236 let verb: *u8 = argv[1] as *u8 237 if co_eq(verb, "route" as *u8) == 1 { 238 if argc < 3 { co_puts("usage: nx_swarm_coord route <class> [gpu_avail_csv]\n" as *u8); return 2 } 239 var avail: *u8 = "" as *u8 240 if argc >= 4 { avail = argv[3] as *u8 } 241 return co_route(argv[2] as *u8, avail) 242 } 243 } 244 return co_gate() 245}