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}