code wiki / _hdl_build / nx_adoptsweep.nx
nx_adoptsweep.nx source
↩ module page · 382 lines · 21765 B
1// nx_adoptsweep.nx -- WHAT FRACTION OF THE ADOPTER CLASS ACTUALLY MIGRATES? The D001 census
2// (nx_drysweep, debt 1786236504) found adopter=605 of 2312 gates. But ADOPTER only means dry_apply EMITS
3// a candidate -- it does NOT mean nx_gate_migrate verify ACCEPTS it. Proven end-to-end on exactly ONE
4// gate (nx_activities_gate: 400 D001 -> ships, debt 1786236952), so the ACCEPTANCE RATE is 1/1 and
5// publishing "605 migrate" would be an overclaim off a sample of one. This measures the rate.
6//
7// ★COMPOSES TWO RULERS AND RE-IMPLEMENTS NEITHER. The worklist is read from knowledge/status/drysweep.log
8// -- the census is the single source of truth, not a second walk that could silently disagree -- and each
9// gate is judged by forking nx_gate_dry_apply then nx_gate_migrate verify. This organ only TALLIES.
10//
11// ⚠THIS ORGAN MUTATES SOURCE. nx_gate_migrate verify COMMITS on acceptance (keeping <gate>.nx.premigrate,
12// rule 13) and RESTORES on any divergence -- that commit-or-restore is what makes a bounded batch safe,
13// but it is still a real edit to real gates. <max> is therefore MANDATORY and small by default: measure
14// the rate on a batch, read the result, THEN decide the size of the next one. Never sweep 605 blind.
15// ★NO SILENT CAPS: the DEFERRED remainder is printed; resume with <skip>.
16// ★DURABLE AS IT GOES: one row per gate to knowledge/status/adoptsweep.log BEFORE any summary, so an
17// interruption costs the remainder and not the run.
18//
19// nx_adoptsweep <max> [skip]
20// exit: 0 all ACCEPTED | 1 at least one REFUSED/ERROR (tally still printed) | 2 usage
21// 3 NO-CONCLUSION (census log or a ruler unresolvable)
22// license_tier: ORIGINAL No hw writes (Rule 26).
23import "nx_syscalls.nx"
24import "nx_artifact_root.nx"
25import "nx_tool_run.nx"
26const AS_MAGIC_20000: i64 = 20000
27const AS_MAGIC_4096: i64 = 4096
28const AS_MAGIC_4095: i64 = 4095
29
30const AS_CENSUS: *u8 = "knowledge/status/drysweep.log"
31const AS_LOG: *u8 = "knowledge/status/adoptsweep.log"
32const AS_CAND: *u8 = "/tmp/adoptsweep_cand.nx"
33const AS_SRCCAP: i64 = 4194304
34const AS_OUTCAP: i64 = 65536
35const AS_TIMEOUT: i64 = 120000
36const AS_NAMECAP: i64 = 512
37
38func as_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
39func as_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 }
40func as_n(fd: i64, v: i64) -> i64 {
41 if v == 0 { sys_write(fd, "0" as *u8, 1); return 0 }
42 var m: i64 = v
43 if m < 0 { sys_write(fd, "-" as *u8, 1); m = 0 - m }
44 let t: *u8 = sys_mmap(32); var k: i64 = 0
45 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
46 let o: *u8 = sys_mmap(32); var i: i64 = 0
47 while i < k { o[i] = t[k-1-i]; i = i + 1 }
48 sys_write(fd, o, k)
49 return 0
50}
51func as_has(buf: *u8, n: i64, pat: *u8) -> i64 {
52 var pl: i64 = 0
53 while pat[pl] != (0 as u8) { pl = pl + 1 }
54 if pl == 0 { return 0 }
55 var i: i64 = 0
56 while i + pl <= n {
57 var k: i64 = 0
58 var ok: i64 = 1
59 while k < pl { if buf[i+k] != pat[k] { ok = 0; k = pl } else { k = k + 1 } }
60 if ok == 1 { return 1 }
61 i = i + 1
62 }
63 return 0
64}
65// index of the LAST occurrence of pat, or -1.
66func as_lastidx(buf: *u8, n: i64, pat: *u8) -> i64 {
67 var pl: i64 = 0
68 while pat[pl] != (0 as u8) { pl = pl + 1 }
69 if pl == 0 { return 0 - 1 }
70 var best: i64 = 0 - 1
71 var i: i64 = 0
72 while i + pl <= n {
73 var k: i64 = 0
74 var ok: i64 = 1
75 while k < pl { if buf[i+k] != pat[k] { ok = 0; k = pl } else { k = k + 1 } }
76 if ok == 1 { best = i }
77 i = i + 1
78 }
79 return best
80}
81
82func as_atoi(s: *u8) -> i64 {
83 var n: i64 = 0
84 var i: i64 = 0
85 while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { n = n*10 + (c-48) } } i = i + 1 }
86 return n
87}
88
89func main(argc: i64, argv: *i64) -> i64 {
90 if argc < 2 { as_puts("usage: nx_adoptsweep <max> [skip] -- MUTATES SOURCE via nx_gate_migrate verify; max is mandatory\n" as *u8); sys_exit(2); return 2 }
91 let maxn: i64 = as_atoi(argv[1] as *u8)
92 var skipn: i64 = 0
93 if argc >= 3 { skipn = as_atoi(argv[2] as *u8) }
94 if maxn <= 0 { as_puts("nx_adoptsweep: max must be > 0 -- refusing to sweep an unbounded batch\n" as *u8); sys_exit(2); return 2 }
95
96 let dryelf: *u8 = sys_mmap(AS_NAMECAP)
97 let migelf: *u8 = sys_mmap(AS_NAMECAP)
98 if ar_resolve("nx_gate_dry_apply.elf" as *u8, dryelf) == 0 { as_puts("nx_adoptsweep: nx_gate_dry_apply.elf unresolvable -- NO CONCLUSION\n" as *u8); sys_exit(3); return 3 }
99 if ar_resolve("nx_gate_migrate.elf" as *u8, migelf) == 0 { as_puts("nx_adoptsweep: nx_gate_migrate.elf unresolvable -- NO CONCLUSION\n" as *u8); sys_exit(3); return 3 }
100
101 let cpath: *u8 = sys_mmap(AS_NAMECAP)
102 if ar_resolve(AS_CENSUS, cpath) == 0 { as_puts("nx_adoptsweep: drysweep.log not found -- run nx_drysweep first. NO CONCLUSION\n" as *u8); sys_exit(3); return 3 }
103 let src: *u8 = sys_mmap(AS_SRCCAP)
104 let n: i64 = ar_read(cpath, src, AS_SRCCAP - 1)
105 if n <= 0 { as_puts("nx_adoptsweep: census log unreadable -- NO CONCLUSION\n" as *u8); sys_exit(3); return 3 }
106
107 // ★PER-RUN CANDIDATE PATH -- THE FIXED /tmp PATH WAS THE PARALLELISM BLOCKER. Two concurrent
108 // sweeps both wrote /tmp/adoptsweep_cand.nx, so batch B could hand batch A's candidate to
109 // nx_gate_migrate verify and migrate the WRONG SOURCE INTO THE WRONG GATE. verify's judge-
110 // equivalence would almost certainly refuse the mismatch, but relying on a downstream oracle to
111 // catch a race you built in is not a design. Suffixing by <skip> makes concurrent batches
112 // disjoint by construction, which is what turns ~3.5h of sequential work into parallel batches.
113 let candp: *u8 = sys_mmap(AS_NAMECAP)
114 var co: i64 = 0
115 let cpre: *u8 = "/tmp/adoptsweep_cand_"
116 while cpre[co] != (0 as u8) { candp[co] = cpre[co]; co = co + 1 }
117 if skipn == 0 { candp[co] = 48 as u8; co = co + 1 }
118 if skipn > 0 {
119 let ct: *u8 = sys_mmap(32)
120 var ck: i64 = 0
121 var cm: i64 = skipn
122 while cm > 0 { ct[ck] = (48 + (cm % 10)) as u8; cm = cm / 10; ck = ck + 1 }
123 var cj: i64 = 0
124 while cj < ck { candp[co] = ct[ck - 1 - cj]; co = co + 1; cj = cj + 1 }
125 }
126 let csuf: *u8 = ".nx"
127 var cs: i64 = 0
128 while csuf[cs] != (0 as u8) { candp[co] = csuf[cs]; co = co + 1; cs = cs + 1 }
129 candp[co] = 0 as u8
130 as_puts(" candidate path: " as *u8); as_puts(candp); as_puts("\n" as *u8)
131
132 // ★ADMISSION CONTROL -- CAP GLOBAL SWEEP CONCURRENCY (added 2026-08-08 after I caused an outage).
133 // <max> bounds the GATE COUNT, which is NOT the load. The resource that saturates is CONCURRENT
134 // SOVEREIGN BUILDS: each gate costs 2, and nothing stopped me launching a 5th sweep. Measured
135 // consequence: sustained 503 `upstream backend gave no response inside the edge window` on /api/*
136 // for every seat, not just mine. ★A BOUNDED BATCH IS NOT A BOUNDED LOAD.
137 // Per-fork headroom would NOT have prevented it -- the fault was the NUMBER OF SWEEPS, so the
138 // control has to be GLOBAL. Three named leases; take the first free; REFUSE if all are held.
139 // Contract proven live before coding against it: LS-ACQUIRED on success, LS-BUSY <holder> when taken.
140 // The owner string is the per-run candidate path, which already encodes <skip> and so is unique.
141 let lelf: *u8 = sys_mmap(AS_NAMECAP)
142 var gotlease: i64 = 0
143 if ar_resolve("nx_lease.elf" as *u8, lelf) == 1 {
144 let lout: *u8 = sys_mmap(AS_OUTCAP)
145 let llen: *i64 = sys_mmap(16) as *i64
146 let lav: *i64 = sys_mmap(64) as *i64
147 let l1: *u8 = "gatesweep_1"
148 let l2: *u8 = "gatesweep_2"
149 let l3: *u8 = "gatesweep_3"
150 var slot: i64 = 1
151 while slot <= 3 {
152 var lname: *u8 = l1
153 if slot == 2 { lname = l2 }
154 if slot == 3 { lname = l3 }
155 lout[0] = 0 as u8; llen[0] = 0
156 lav[0] = lelf as i64; lav[1] = "acquire" as *u8 as i64; lav[2] = lname as i64
157 lav[3] = candp as i64; lav[4] = "7200" as *u8 as i64; lav[5] = 0
158 tr_run_capture_to(lelf, lav, lout, AS_OUTCAP - 1, llen, AS_MAGIC_20000)
159 if llen[0] > 0 { if as_has(lout, llen[0], "LS-ACQUIRED" as *u8) == 1 {
160 gotlease = 1
161 as_puts(" lease: " as *u8); as_puts(lname); as_puts("\n" as *u8)
162 slot = 4
163 } }
164 if gotlease == 0 { slot = slot + 1 }
165 }
166 if gotlease == 0 {
167 as_puts(" ★ALL 3 SWEEP LEASES HELD -- refusing to start a 4th concurrent sweep.\n" as *u8)
168 as_puts(" This is the control that was missing when 5 concurrent sweeps saturated /api/* for\n" as *u8)
169 as_puts(" every seat. Wait for a slot, or release a stale lease with nx_lease release.\n" as *u8)
170 as_puts(" verdict=NO-CONCLUSION\n" as *u8)
171 sys_exit(3); return 3
172 }
173 }
174 if ar_resolve("nx_lease.elf" as *u8, lelf) == 0 {
175 // ⚠FAIL-OPEN IS DELIBERATE AND NAMED: if the lease organ is absent the sweep still runs, because
176 // refusing all work when the GUARD is missing would be a worse outage than the one it prevents.
177 // Say so out loud rather than silently proceeding unguarded.
178 as_puts(" ⚠nx_lease.elf unresolvable -- running WITHOUT concurrency admission control.\n" as *u8)
179 }
180
181 let lf: i64 = sys_openat_append(AS_LOG, 0x1a4)
182 let out: *u8 = sys_mmap(AS_OUTCAP)
183 let olen: *i64 = sys_mmap(16) as *i64
184 let gname: *u8 = sys_mmap(AS_NAMECAP)
185 let av: *i64 = sys_mmap(64) as *i64
186
187 var seen: i64 = 0
188 var done: i64 = 0
189 var acc: i64 = 0
190 var refu: i64 = 0
191 var alrdy: i64 = 0
192 var errn: i64 = 0
193 as_puts("=== nx_adoptsweep -- does the ADOPTER class actually migrate? forking dry_apply + gate_migrate verify ===\n" as *u8)
194
195 // ★A LOG IS A GENERATIONAL RECORD, NOT A TABLE -- READ ONLY THE NEWEST GENERATION.
196 // drysweep.log accumulates one block per census run, each terminated by a DRYSWEEP-TOTAL line.
197 // v1 read the WHOLE file and therefore summed every generation: measured 2026-08-08, a batch
198 // reported adopters=1191 when the population is 586, because 605 (census v1) + 586 (census v2)
199 // = 1191 EXACTLY. The migrations were still sound -- each is proven individually -- but every
200 // POPULATION figure was doubled, and the skip= arithmetic silently indexed a doubled list whose
201 // halves are in different orders, so a resume offset did not mean what it said.
202 // Starting after the last TOTAL also makes RECLASSIFICATION take effect automatically: a gate
203 // that moved SKIP-OTHER -> MIGRATED in a later census is read at its newest class, not its oldest.
204 // ⚠OFF-BY-ONE GENERATION, CAUGHT BY RUNNING IT: the LAST DRYSWEEP-TOTAL is the file's FINAL line,
205 // so starting there leaves ZERO rows. The newest generation's rows lie between the SECOND-TO-LAST
206 // total and the last one. Find l1 (last), then search only [0,l1) for l2 (the one before it).
207 // With a single census present l2 = -1 and we correctly start at 0.
208 var i: i64 = 0
209 let l1: i64 = as_lastidx(src, n, "DRYSWEEP-TOTAL" as *u8)
210 if l1 > 0 {
211 let l2: i64 = as_lastidx(src, l1, "DRYSWEEP-TOTAL" as *u8)
212 if l2 >= 0 { i = l2 }
213 }
214 while i < n {
215 // line start at i; find end
216 var e: i64 = i
217 var eol: i64 = 0
218 while eol == 0 { if e >= n { eol = 1 } else { if src[e] == (10 as u8) { eol = 1 } else { e = e + 1 } } }
219 let ll: i64 = e - i
220 if ll > 20 {
221 let line: *u8 = (src as i64 + i) as *u8
222 if as_has(line, ll, "class=ADOPTER" as *u8) == 1 {
223 seen = seen + 1
224 if seen > skipn { if done < maxn {
225 // extract the gate name after "gate="
226 var p: i64 = 0
227 var gs: i64 = 0 - 1
228 while p + 5 <= ll {
229 if line[p] == (103 as u8) { if line[p+1] == (97 as u8) { if line[p+2] == (116 as u8) { if line[p+3] == (101 as u8) { if line[p+4] == (61 as u8) { if gs < 0 { gs = p + 5 } } } } } }
230 p = p + 1
231 }
232 if gs >= 0 {
233 // copy the gate name up to the next space. ★EXIT BY FLAG, NEVER BY CLOBBERING THE
234 // CURSOR: a loop that breaks by overshooting its index destroys the position it was
235 // searching for -- written 3x in one session by this estate before it was banked.
236 var k: i64 = 0
237 var stop: i64 = 0
238 while stop == 0 {
239 if gs + k >= ll { stop = 1 }
240 if gs + k < ll {
241 let c: i64 = line[gs+k] as i64
242 if c == 32 { stop = 1 }
243 if c != 32 {
244 if k >= AS_NAMECAP - 1 { stop = 1 }
245 if k < AS_NAMECAP - 1 { gname[k] = line[gs+k]; k = k + 1 }
246 }
247 }
248 }
249 gname[k] = 0 as u8
250 // ★zero the capture every iteration -- a reused buffer reports the previous gate's answer
251 out[0] = 0 as u8; olen[0] = 0
252 av[0] = dryelf as i64; av[1] = gname as i64; av[2] = candp as i64; av[3] = 0
253 tr_run_capture_to(dryelf, av, out, AS_OUTCAP - 1, olen, AS_TIMEOUT)
254 var cls: *u8 = "ERROR-DRY" as *u8
255 // ★ALREADY-MIGRATED IS A SUCCESS SPELLED AS A FAILURE. dry_apply answers an already-
256 // migrated gate with `already inherits the base class` and emits NO candidate; v1 filed
257 // that as ERROR-DRY, which dragged verdict=RED on an otherwise clean batch and would
258 // UNDERSTATE acceptance by exactly the number of gates previously migrated. A bucket
259 // named for how the reader failed merges a real failure with a healthy pass.
260 if olen[0] > 0 { if as_has(out, olen[0], "already inherits the base class" as *u8) == 1 { cls = "ALREADY-MIGRATED" as *u8 } }
261 if olen[0] > 0 { if as_has(out, olen[0], "DRY-APPLY CANDIDATE" as *u8) == 1 {
262 out[0] = 0 as u8; olen[0] = 0
263 av[0] = migelf as i64; av[1] = "verify" as *u8 as i64; av[2] = gname as i64; av[3] = candp as i64; av[4] = 0
264 tr_run_capture_to(migelf, av, out, AS_OUTCAP - 1, olen, AS_TIMEOUT)
265 cls = "ERROR-VERIFY" as *u8
266 if olen[0] > 0 {
267 if as_has(out, olen[0], "ACCEPTED" as *u8) == 1 { cls = "ACCEPTED" as *u8 }
268 if as_has(out, olen[0], "ACCEPTED" as *u8) == 0 { cls = "REFUSED" as *u8 }
269 }
270 } }
271 if as_has(cls, 8, "ACCEPTED" as *u8) == 1 { acc = acc + 1 }
272 if as_has(cls, 7, "REFUSED" as *u8) == 1 { refu = refu + 1 }
273 if as_has(cls, 16, "ALREADY-MIGRATED" as *u8) == 1 { alrdy = alrdy + 1 }
274 if as_has(cls, 5, "ERROR" as *u8) == 1 { errn = errn + 1 }
275 as_puts(" " as *u8); as_puts(cls); as_puts(" " as *u8); as_puts(gname); as_puts("\n" as *u8)
276 if lf >= 0 {
277 as_w(lf, "ADOPTSWEEP gate=" as *u8); as_w(lf, gname)
278 as_w(lf, " class=" as *u8); as_w(lf, cls); as_w(lf, "\n" as *u8)
279 }
280 done = done + 1
281 }
282 } }
283 }
284 }
285 i = e + 1
286 }
287
288 // ★RELEASE THE LEASE -- FOUND BY USING THE GUARD I HAD JUST SHIPPED. v1 acquired and NEVER released,
289 // so a COMPLETED sweep kept its slot for the full 7200s TTL: three finished runs would lock all three
290 // slots and the admission control would refuse every sweep for two hours.
291 // ★★A CONTROL THAT ACQUIRES BUT NEVER RELEASES IS A DENIAL OF SERVICE ON A TIMER -- and it fails in the
292 // WORST direction, because it looks like the guard working. Caught only because a later acquire
293 // reported `LS-BUSY holder=/tmp/adoptsweep_cand_200.nx` -- my own finished run, still holding.
294 // Released HERE, after the work loop: the lease guards BUILD LOAD, and that load is now done.
295 // ★Safe to try all three names: nx_lease release is OWNER-CHECKED (verified live -- a wrong owner
296 // returns LS-REFUSED not-holder), so only the slot we actually hold is freed.
297 if gotlease == 1 {
298 let rout: *u8 = sys_mmap(AS_MAGIC_4096)
299 let rlen: *i64 = sys_mmap(16) as *i64
300 let rav: *i64 = sys_mmap(64) as *i64
301 let r1: *u8 = "gatesweep_1"
302 let r2: *u8 = "gatesweep_2"
303 let r3: *u8 = "gatesweep_3"
304 var rs: i64 = 1
305 while rs <= 3 {
306 var rname: *u8 = r1
307 if rs == 2 { rname = r2 }
308 if rs == 3 { rname = r3 }
309 rout[0] = 0 as u8; rlen[0] = 0
310 rav[0] = lelf as i64; rav[1] = "release" as *u8 as i64; rav[2] = rname as i64
311 rav[3] = candp as i64; rav[4] = 0
312 tr_run_capture_to(lelf, rav, rout, AS_MAGIC_4095, rlen, AS_MAGIC_20000)
313 if rlen[0] > 0 { if as_has(rout, rlen[0], "LS-RELEASED" as *u8) == 1 {
314 as_puts(" lease released: " as *u8); as_puts(rname); as_puts("\n" as *u8)
315 } }
316 rs = rs + 1
317 }
318 }
319
320 // ★AN EMPTY WORKLIST IS NOT A CLEAN RUN. Caught by running the broken generation-scan above: it
321 // found zero adopters and happily printed `partition: 0 of 0 -- SUMS` with no errors -- a sweep
322 // that processed NOTHING reporting itself healthy. A TOOTH THAT PASSES ON THE EMPTY SET IS NOT A
323 // TOOTH; bind the aggregate to its denominator and REFUSE rather than bless a vacuous pass.
324 if seen == 0 {
325 as_puts("\n ★EMPTY WORKLIST -- no ADOPTER rows found in the newest census generation.\n" as *u8)
326 as_puts(" REFUSING to report a clean run over zero gates. Re-run nx_drysweep, or check that\n" as *u8)
327 as_puts(" knowledge/status/drysweep.log ends with a complete DRYSWEEP-TOTAL block.\n" as *u8)
328 as_puts(" verdict=NO-CONCLUSION\n" as *u8)
329 sys_exit(3); return 3
330 }
331 // ★ATTEMPTED NOTHING IS NOT A CLEAN RUN EITHER. The empty-worklist guard tests seen==0, but a SKIP
332 // BEYOND THE POPULATION slips past it: measured, skip=200 against seen=141 printed
333 // `attempted=0 ... deferred=-59 verdict=GREEN` -- zero work reported as success, AND a NEGATIVE
334 // COUNT sitting next to honest numbers, which discredits all of them. Same vacuous-pass shape,
335 // different door. Refuse before printing a summary that would be read as a result.
336 if done == 0 {
337 as_puts("\n ★ATTEMPTED NOTHING -- skip is at or beyond the population (" as *u8); as_n(1, seen)
338 as_puts(" adopters in the newest census generation).\n" as *u8)
339 as_puts(" REFUSING to report a verdict over zero attempted gates. verdict=NO-CONCLUSION\n" as *u8)
340 sys_exit(3); return 3
341 }
342 let sum: i64 = acc + refu + alrdy + errn
343 as_puts("\n adopters_in_census=" as *u8); as_n(1, seen)
344 as_puts(" attempted=" as *u8); as_n(1, done)
345 as_puts("\n ACCEPTED=" as *u8); as_n(1, acc)
346 as_puts(" REFUSED=" as *u8); as_n(1, refu)
347 as_puts(" ALREADY-MIGRATED=" as *u8); as_n(1, alrdy)
348 as_puts(" ERROR=" as *u8); as_n(1, errn)
349 // ★QUOTE THE RATE OVER GENUINE ATTEMPTS, NOT OVER THE BATCH. already-migrated gates were never
350 // candidates for this run; counting them in the denominator understates acceptance.
351 let genuine: i64 = acc + refu
352 as_puts("\n genuine_attempts=" as *u8); as_n(1, genuine)
353 if genuine > 0 { as_puts(" acceptance_permil=" as *u8); as_n(1, (acc * 1000) / genuine) }
354 as_puts(" partition: " as *u8); as_n(1, sum); as_puts(" of " as *u8); as_n(1, done)
355 if sum == done { as_puts(" -- SUMS\n" as *u8) }
356 if sum != done { as_puts(" -- ⚠DOES NOT SUM\n" as *u8) }
357 // ★CLAMP: a negative remainder is arithmetically possible (skip>seen) and publishing it discredits
358 // every honest number beside it. Clamped, not hidden -- the done==0 refusal above is what actually
359 // reports the condition.
360 var deferred: i64 = seen - skipn - done
361 if deferred < 0 { deferred = 0 }
362 if deferred > 0 {
363 as_puts(" ⚠DEFERRED " as *u8); as_n(1, deferred)
364 as_puts(" adopter(s) -- THIS RATE IS FROM A BOUNDED BATCH, NOT THE CLASS. resume with skip=" as *u8)
365 as_n(1, skipn + done); as_puts("\n" as *u8)
366 }
367 if lf >= 0 {
368 as_w(lf, "ADOPTSWEEP-TOTAL adopters=" as *u8); as_n(lf, seen)
369 as_w(lf, " attempted=" as *u8); as_n(lf, done)
370 as_w(lf, " accepted=" as *u8); as_n(lf, acc)
371 as_w(lf, " refused=" as *u8); as_n(lf, refu)
372 as_w(lf, " already_migrated=" as *u8); as_n(lf, alrdy)
373 as_w(lf, " genuine=" as *u8); as_n(lf, acc + refu)
374 as_w(lf, " error=" as *u8); as_n(lf, errn)
375 as_w(lf, " deferred=" as *u8); as_n(lf, deferred)
376 as_w(lf, " verdict=" as *u8)
377 if refu + errn == 0 { as_w(lf, "GREEN\n" as *u8) } else { as_w(lf, "RED\n" as *u8) }
378 sys_close(lf)
379 }
380 if refu + errn > 0 { sys_exit(1); return 1 }
381 sys_exit(0); return 0
382}