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}