@linzumi/cli 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,738 @@
1
+ #!/usr/bin/env node
2
+ // golden-scale-test.mjs
3
+ //
4
+ // SCALE TEST: find the empirical "agents-per-host" number for the forkable-golden
5
+ // model on THIS Mac (Apple Silicon, HVF). Cold-boot ONE forkable golden, provision
6
+ // a warm base (toolchain + small project + npm install), then ramp concurrent forks
7
+ // N = 1,2,4,8,16,24,32,48,64 off that single golden and measure at each N:
8
+ // - fork wall time + per-fork latency distribution (p50/p95)
9
+ // - concurrent light-job completion time
10
+ // - RAM: sum RSS of `smol-vmm _boot-vm` procs (ps) + host free/used (vm_stat)
11
+ // BEFORE and AFTER the fork wave -> COW marginal-RSS-per-fork
12
+ // - EAGAIN (os error 35) retries triggered (instrumented helper)
13
+ // - native crash / connection-closed / instability (caught + recorded)
14
+ // Tear down the N forks before the next stage (delete()); keep the golden.
15
+ //
16
+ // Fork pattern reused from /tmp/smol-rt-132/fork-proof.mjs:
17
+ // Machine.create({ forkable:true, network:true }) golden -> golden.fork(name)
18
+ // The golden is exec-DEAD after forking, so we ONLY fork it (never exec post-fork);
19
+ // all exec jobs run IN the forks. EAGAIN/os-error-35 retry+backoff on every
20
+ // exec AND fork.
21
+ //
22
+ // HARD HYGIENE: exclusive HVF. Synchronous. ALWAYS delete() in finally. No lingering
23
+ // background processes. NEVER pkill golden-*. No git. Confirm 0 leaked smol-vmm at exit.
24
+
25
+ import { createRequire } from 'node:module';
26
+ import { join } from 'node:path';
27
+ import { execSync } from 'node:child_process';
28
+
29
+ const rtDir = '/tmp/smol-rt-132';
30
+ const require = createRequire(join(rtDir, 'package.json'));
31
+ const { Machine } = require('smolmachines');
32
+
33
+ // ----- config ---------------------------------------------------------------
34
+ const STAGES = [1, 2, 4, 8, 16, 24, 32, 48, 64];
35
+ const PROJECT = '/root/project';
36
+ const FORK_CPUS = 2;
37
+ const FORK_MEM_MB = 2048;
38
+ const MIN_FREE_HEADROOM_GB = 8; // stop ramping if host free RAM < this after a wave
39
+ const FORK_STAGGER_MS = 60; // pace fork launches to respect control-plane EAGAIN
40
+ const PAGE_BYTES = 16384; // Apple Silicon vm_stat page size (verified below)
41
+ // CPU-saturating per-fork job: a fixed-size busy loop. Each fork runs the SAME
42
+ // amount of CPU work, so as N exceeds available cores the per-job wall time grows
43
+ // (jobs queue behind cores). The point where per-job time departs from the N<=cores
44
+ // baseline is the empirical CPU ceiling -- the real "active agents per host".
45
+ const CPU_JOB = `node -e "const t=Date.now();let x=0;for(let i=0;i<8e8;i++){x+=Math.sqrt(i)%7;}console.log(Date.now()-t,x|0)"`;
46
+
47
+ const t = () => Date.now();
48
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
49
+ const log = (...a) => console.log('[scale]', ...a);
50
+ const GB = (bytes) => (bytes / 1024 / 1024 / 1024).toFixed(2);
51
+ const MB = (bytes) => (bytes / 1024 / 1024).toFixed(0);
52
+
53
+ // ----- EAGAIN-aware retry helpers (instrumented) ----------------------------
54
+ let EAGAIN_RETRIES = 0; // global; snapshotted per stage
55
+ const isEagain = (e) =>
56
+ /os error 35|temporarily unavailable|EAGAIN|resource temporarily/i.test(
57
+ String(e?.stack || e)
58
+ );
59
+ const isInstability = (e) =>
60
+ /connection closed|connection reset|ECONNRESET|EPIPE|broken pipe|hangup|panic|signal|abort/i.test(
61
+ String(e?.stack || e)
62
+ );
63
+
64
+ async function withRetry(label, fn, { tries = 8, base = 150 } = {}) {
65
+ let lastErr;
66
+ for (let attempt = 0; attempt < tries; attempt++) {
67
+ try {
68
+ return await fn();
69
+ } catch (e) {
70
+ lastErr = e;
71
+ if (isEagain(e)) {
72
+ EAGAIN_RETRIES++;
73
+ await sleep(base * (attempt + 1));
74
+ continue;
75
+ }
76
+ throw e; // non-EAGAIN (incl. instability) bubbles up to be classified
77
+ }
78
+ }
79
+ throw lastErr;
80
+ }
81
+
82
+ async function sh(m, label, cmd, timeout = 240) {
83
+ return withRetry(label, async () => {
84
+ const r = await m.exec(['/bin/sh', '-c', cmd], { timeout });
85
+ return {
86
+ code: r.exitCode,
87
+ out: (r.stdout || '').trim(),
88
+ err: (r.stderr || '').trim(),
89
+ };
90
+ });
91
+ }
92
+
93
+ // ----- host measurement -----------------------------------------------------
94
+ function sysctlStr(key) {
95
+ try {
96
+ return execSync(`sysctl -n ${key}`, { encoding: 'utf8' }).trim();
97
+ } catch {
98
+ return '?';
99
+ }
100
+ }
101
+
102
+ // Free RAM (bytes) that's actually reclaimable: free + inactive + speculative.
103
+ // Apple's vm_stat counts inactive/speculative as available under pressure.
104
+ function hostMem() {
105
+ let pageBytes = PAGE_BYTES;
106
+ let free = 0,
107
+ inactive = 0,
108
+ speculative = 0,
109
+ wired = 0,
110
+ active = 0,
111
+ compressed = 0;
112
+ try {
113
+ const out = execSync('vm_stat', { encoding: 'utf8' });
114
+ const m = out.match(/page size of (\d+) bytes/);
115
+ if (m) pageBytes = Number(m[1]);
116
+ const grab = (re) => {
117
+ const mm = out.match(re);
118
+ return mm ? Number(mm[1]) : 0;
119
+ };
120
+ free = grab(/Pages free:\s+(\d+)/);
121
+ active = grab(/Pages active:\s+(\d+)/);
122
+ inactive = grab(/Pages inactive:\s+(\d+)/);
123
+ speculative = grab(/Pages speculative:\s+(\d+)/);
124
+ wired = grab(/Pages wired down:\s+(\d+)/);
125
+ compressed = grab(/Pages occupied by compressor:\s+(\d+)/);
126
+ } catch {}
127
+ const avail = (free + inactive + speculative) * pageBytes;
128
+ const used = (active + wired + compressed) * pageBytes;
129
+ return {
130
+ availBytes: avail,
131
+ usedBytes: used,
132
+ freePagesBytes: free * pageBytes,
133
+ };
134
+ }
135
+
136
+ // Sum RSS (bytes) of all `smol-vmm _boot-vm` processes, and count them.
137
+ function vmmRss() {
138
+ try {
139
+ // ps rss is in KB. Match the boot-vm worker that backs each live VM.
140
+ const out = execSync(
141
+ "ps -axo rss,command | grep 'smol-vmm' | grep '_boot-vm' | grep -v grep",
142
+ { encoding: 'utf8' }
143
+ );
144
+ const lines = out.split('\n').filter((l) => l.trim());
145
+ let sum = 0;
146
+ for (const l of lines) {
147
+ const kb = Number(l.trim().split(/\s+/)[0]);
148
+ if (Number.isFinite(kb)) sum += kb * 1024;
149
+ }
150
+ return { count: lines.length, rssBytes: sum };
151
+ } catch {
152
+ return { count: 0, rssBytes: 0 };
153
+ }
154
+ }
155
+
156
+ function countLeakedVmm() {
157
+ try {
158
+ const out = execSync(
159
+ "ps -axo pid,command | grep 'smol-vmm' | grep -v grep",
160
+ {
161
+ encoding: 'utf8',
162
+ }
163
+ );
164
+ return out.split('\n').filter((l) => l.trim()).length;
165
+ } catch {
166
+ return 0;
167
+ }
168
+ }
169
+
170
+ function percentile(arr, p) {
171
+ if (!arr.length) return 0;
172
+ const s = [...arr].sort((a, b) => a - b);
173
+ const idx = Math.min(s.length - 1, Math.floor((p / 100) * s.length));
174
+ return s[idx];
175
+ }
176
+
177
+ // ----- one-time wedged-runtime recovery -------------------------------------
178
+ async function maybeUnwedge() {
179
+ // If the runtime is wedged, a first exec returns connection-closed. Probe once
180
+ // with a throwaway non-forkable micro-VM; if it fails, kill stray boot-vms and
181
+ // clear the vms cache ONCE, then let it re-extract.
182
+ log('probe: booting a throwaway micro-VM to check runtime health ...');
183
+ let probe;
184
+ try {
185
+ probe = await Machine.create({
186
+ name: `probe-${process.pid}`,
187
+ resources: { cpus: 1, memoryMb: 512 },
188
+ });
189
+ const r = await probe.exec(['/bin/sh', '-c', 'echo healthy'], {
190
+ timeout: 60,
191
+ });
192
+ log(`probe exec -> "${(r.stdout || '').trim()}" (exit ${r.exitCode})`);
193
+ await probe.delete().catch(() => {});
194
+ return true;
195
+ } catch (e) {
196
+ log(
197
+ `probe FAILED (${String(e).slice(0, 120)}). Attempting one-time unwedge ...`
198
+ );
199
+ try {
200
+ if (probe) await probe.delete().catch(() => {});
201
+ } catch {}
202
+ try {
203
+ execSync(
204
+ "ps -axo pid,command | grep smol-vmm | grep _boot-vm | grep -v grep | awk '{print $1}' | xargs -r kill -9",
205
+ {
206
+ encoding: 'utf8',
207
+ }
208
+ );
209
+ } catch {}
210
+ try {
211
+ execSync('rm -rf ~/Library/Caches/smolvm/vms/*', {
212
+ encoding: 'utf8',
213
+ shell: '/bin/zsh',
214
+ });
215
+ } catch {}
216
+ log(
217
+ 'unwedge done (killed stray boot-vms + cleared vms cache). Re-probing ...'
218
+ );
219
+ await sleep(2000);
220
+ try {
221
+ probe = await Machine.create({
222
+ name: `probe2-${process.pid}`,
223
+ resources: { cpus: 1, memoryMb: 512 },
224
+ });
225
+ const r = await probe.exec(['/bin/sh', '-c', 'echo healthy'], {
226
+ timeout: 90,
227
+ });
228
+ log(`re-probe exec -> "${(r.stdout || '').trim()}" (exit ${r.exitCode})`);
229
+ await probe.delete().catch(() => {});
230
+ return true;
231
+ } catch (e2) {
232
+ log(`re-probe STILL failing: ${String(e2).slice(0, 160)}`);
233
+ try {
234
+ if (probe) await probe.delete().catch(() => {});
235
+ } catch {}
236
+ return false;
237
+ }
238
+ }
239
+ }
240
+
241
+ // ----------------------------------------------------------------------------
242
+ const created = new Set(); // track everything for finally cleanup
243
+ let golden;
244
+ const rows = [];
245
+
246
+ const HOST = {
247
+ ncpu: sysctlStr('hw.ncpu'),
248
+ physicalcpu: sysctlStr('hw.physicalcpu'),
249
+ memsize: Number(sysctlStr('hw.memsize')) || 0,
250
+ hv_support: sysctlStr('kern.hv_support'),
251
+ brand: sysctlStr('machdep.cpu.brand_string'),
252
+ };
253
+
254
+ async function main() {
255
+ console.log('\n==================== HOST FACTS ====================');
256
+ console.log(`cpu.brand_string : ${HOST.brand}`);
257
+ console.log(
258
+ `hw.ncpu : ${HOST.ncpu} (physical ${HOST.physicalcpu})`
259
+ );
260
+ console.log(
261
+ `hw.memsize : ${HOST.memsize} bytes (${GB(HOST.memsize)} GB)`
262
+ );
263
+ console.log(`kern.hv_support : ${HOST.hv_support}`);
264
+ console.log(`smolmachines : 1.3.2 (runtime ${rtDir})`);
265
+ console.log(
266
+ `fork resources : cpus=${FORK_CPUS} memoryMb=${FORK_MEM_MB} per fork`
267
+ );
268
+ console.log('====================================================\n');
269
+
270
+ const healthy = await maybeUnwedge();
271
+ if (!healthy) {
272
+ console.error(
273
+ '[scale] runtime did not become healthy after one unwedge; aborting.'
274
+ );
275
+ process.exitCode = 1;
276
+ return;
277
+ }
278
+
279
+ // 1) Cold-boot ONE forkable golden + provision warm base ------------------
280
+ log('cold-booting ONE forkable golden (network:true) ...');
281
+ const tBoot = t();
282
+ golden = await withRetry('golden create', () =>
283
+ Machine.create({
284
+ name: `golden-${process.pid}`,
285
+ resources: { cpus: FORK_CPUS, memoryMb: FORK_MEM_MB, network: true },
286
+ forkable: true,
287
+ })
288
+ );
289
+ created.add(golden);
290
+ const goldenBootMs = t() - tBoot;
291
+ log(`golden booted forkable in ${goldenBootMs}ms`);
292
+
293
+ log('provisioning warm base (toolchain + small project + npm install) ...');
294
+ const tProv = t();
295
+ await sh(
296
+ golden,
297
+ 'apk',
298
+ 'apk add --no-cache ca-certificates nodejs npm git >/dev/null 2>&1 && update-ca-certificates >/dev/null 2>&1 || true',
299
+ 600
300
+ );
301
+ await sh(
302
+ golden,
303
+ 'project',
304
+ [
305
+ `mkdir -p ${PROJECT} && cd ${PROJECT}`,
306
+ 'git init -q && git config user.email a@b.c && git config user.name base',
307
+ 'printf "# scale base\\n" > README.md',
308
+ 'npm init -y >/dev/null 2>&1',
309
+ 'npm install --no-fund --no-audit is-odd >/dev/null 2>&1 || true',
310
+ 'git add -A && git commit -q -m "pristine base"',
311
+ ].join(' && '),
312
+ 600
313
+ );
314
+ const baseCommit = (
315
+ await sh(
316
+ golden,
317
+ 'base commit',
318
+ `cd ${PROJECT} && git rev-parse --short HEAD`
319
+ )
320
+ ).out;
321
+ const baseNode = (await sh(golden, 'base node', 'node --version')).out;
322
+ const provMs = t() - tProv;
323
+ log(
324
+ `golden provisioned in ${provMs}ms (commit=${baseCommit}, node=${baseNode})`
325
+ );
326
+
327
+ // Baseline RAM with golden up, no forks.
328
+ const goldenVmm = vmmRss();
329
+ const goldenMem = hostMem();
330
+ log(
331
+ `baseline: vmm procs=${goldenVmm.count} sumRSS=${MB(goldenVmm.rssBytes)}MB host avail=${GB(
332
+ goldenMem.availBytes
333
+ )}GB`
334
+ );
335
+
336
+ // NOTE: golden is exec-DEAD after the first fork. We never exec it again.
337
+ let goldenForked = false;
338
+
339
+ // 2) Ramp the stages -----------------------------------------------------
340
+ for (const N of STAGES) {
341
+ log(`\n---------- STAGE N=${N} ----------`);
342
+ const eagainBefore = EAGAIN_RETRIES;
343
+ const memBefore = hostMem();
344
+ const vmmBefore = vmmRss();
345
+ let status = 'ok';
346
+ let note = '';
347
+ const forks = [];
348
+ const forkLatencies = [];
349
+ let forkWall = 0;
350
+ let workMs = 0;
351
+
352
+ try {
353
+ // ---- fork wave (paced, EAGAIN-retried, concurrent-ish) ----
354
+ const tFork = t();
355
+ const forkPromises = [];
356
+ for (let i = 1; i <= N; i++) {
357
+ // small stagger so we don't slam the control plane (EAGAIN source)
358
+ await sleep(FORK_STAGGER_MS);
359
+ const name = `f-${N}-${i}-${process.pid}`;
360
+ forkPromises.push(
361
+ (async () => {
362
+ const tf = t();
363
+ const child = await withRetry(`fork ${name}`, () =>
364
+ golden.fork(name)
365
+ );
366
+ const lat = t() - tf;
367
+ goldenForked = true;
368
+ created.add(child);
369
+ forks.push(child);
370
+ forkLatencies.push(lat);
371
+ return child;
372
+ })()
373
+ );
374
+ }
375
+ await Promise.all(forkPromises);
376
+ forkWall = t() - tFork;
377
+ log(
378
+ `forked ${forks.length}/${N} in ${forkWall}ms (p50=${percentile(
379
+ forkLatencies,
380
+ 50
381
+ )}ms p95=${percentile(forkLatencies, 95)}ms)`
382
+ );
383
+
384
+ // ---- RAM after fork wave (settle a beat for COW pages to fault in) ----
385
+ await sleep(800);
386
+ const vmmAfter = vmmRss();
387
+ const memAfter = hostMem();
388
+
389
+ // ---- job in each fork, concurrently ----
390
+ // Each fork: light I/O (write+commit) + a CPU-saturating busy loop. The CPU
391
+ // loop is what exposes the core ceiling: per-job in-VM ms is captured so we
392
+ // can see queueing once N > cores.
393
+ const tWork = t();
394
+ const perJobMs = [];
395
+ const workResults = await Promise.allSettled(
396
+ forks.map((child, idx) =>
397
+ (async () => {
398
+ await sh(
399
+ child,
400
+ `io${idx}`,
401
+ [
402
+ `cd ${PROJECT}`,
403
+ `printf "fork ${idx} active\\n" > fork-${idx}.txt`,
404
+ `git add -A && git commit -q -m "fork ${idx}"`,
405
+ ].join(' && '),
406
+ 180
407
+ );
408
+ const r = await sh(child, `cpu${idx}`, CPU_JOB, 300);
409
+ const ms = Number((r.out || '').split(/\s+/)[0]);
410
+ if (Number.isFinite(ms)) perJobMs.push(ms);
411
+ })()
412
+ )
413
+ );
414
+ workMs = t() - tWork;
415
+ const jobP50 = percentile(perJobMs, 50);
416
+ const jobP95 = percentile(perJobMs, 95);
417
+ const workOk = workResults.filter((r) => r.status === 'fulfilled').length;
418
+ const workFail = workResults.length - workOk;
419
+ if (workFail > 0) {
420
+ status = 'degraded';
421
+ note += `${workFail}/${forks.length} jobs failed; `;
422
+ const firstErr = workResults.find((r) => r.status === 'rejected');
423
+ if (firstErr && isInstability(firstErr.reason)) {
424
+ status = 'crash';
425
+ note += `instability: ${String(firstErr.reason).slice(0, 80)}; `;
426
+ }
427
+ }
428
+ log(`concurrent work: ${workOk}/${forks.length} ok in ${workMs}ms`);
429
+
430
+ // ---- COW / marginal RSS accounting ----
431
+ const deltaVmmRss = vmmAfter.rssBytes - vmmBefore.rssBytes;
432
+ const newVmms = vmmAfter.count - vmmBefore.count;
433
+ const marginalPerFork = newVmms > 0 ? deltaVmmRss / newVmms : 0;
434
+ const hostUsedDelta = memBefore.availBytes - memAfter.availBytes; // host RAM consumed by the wave
435
+ const marginalHostPerFork =
436
+ forks.length > 0 ? hostUsedDelta / forks.length : 0;
437
+
438
+ const eagainThisStage = EAGAIN_RETRIES - eagainBefore;
439
+
440
+ rows.push({
441
+ N,
442
+ forked: forks.length,
443
+ forkWall,
444
+ p50: percentile(forkLatencies, 50),
445
+ p95: percentile(forkLatencies, 95),
446
+ marginalRssMB: Number(MB(marginalPerFork)),
447
+ marginalHostMB: Number(MB(marginalHostPerFork)),
448
+ totalVmmRssGB: Number(GB(vmmAfter.rssBytes)),
449
+ vmmCount: vmmAfter.count,
450
+ hostAvailGB: Number(GB(memAfter.availBytes)),
451
+ eagain: eagainThisStage,
452
+ workMs,
453
+ jobP50,
454
+ jobP95,
455
+ workOk,
456
+ status,
457
+ note: note.trim(),
458
+ });
459
+
460
+ log(
461
+ `CPU job in-VM ms: p50=${jobP50} p95=${jobP95} (baseline ~single-fork time => >cores means queueing)`
462
+ );
463
+ log(
464
+ `RAM: vmm sumRSS ${MB(vmmBefore.rssBytes)}->${MB(vmmAfter.rssBytes)}MB (+${MB(
465
+ deltaVmmRss
466
+ )}MB for ${newVmms} new vmm) -> marginal ${MB(marginalPerFork)}MB/fork (proc) / ${MB(
467
+ marginalHostPerFork
468
+ )}MB/fork (host)`
469
+ );
470
+ log(
471
+ `host avail ${GB(memBefore.availBytes)}->${GB(memAfter.availBytes)}GB EAGAIN(stage)=${eagainThisStage} status=${status}`
472
+ );
473
+
474
+ // ---- tear down THIS stage's forks (keep golden) ----
475
+ log(`tearing down ${forks.length} forks ...`);
476
+ for (const child of forks) {
477
+ try {
478
+ await withRetry('delete', () => child.delete(), {
479
+ tries: 5,
480
+ base: 120,
481
+ });
482
+ } catch {
483
+ try {
484
+ await child.stop();
485
+ } catch {}
486
+ }
487
+ created.delete(child);
488
+ }
489
+ await sleep(1500); // let teardown settle before next stage measures baseline
490
+
491
+ // ---- stop conditions ----
492
+ if (status === 'crash') {
493
+ log(`STOP: native/connection instability at N=${N}.`);
494
+ break;
495
+ }
496
+ if (memAfter.availBytes / 1024 / 1024 / 1024 < MIN_FREE_HEADROOM_GB) {
497
+ log(
498
+ `STOP: host free RAM (${GB(memAfter.availBytes)}GB) dropped below ${MIN_FREE_HEADROOM_GB}GB headroom.`
499
+ );
500
+ break;
501
+ }
502
+ } catch (e) {
503
+ // A stage-level throw = fork wave or measurement failed hard.
504
+ const crash = isInstability(e);
505
+ status = crash ? 'crash' : 'degraded';
506
+ note += String(e?.stack || e).slice(0, 140);
507
+ const memAfter = hostMem();
508
+ rows.push({
509
+ N,
510
+ forked: forks.length,
511
+ forkWall,
512
+ p50: percentile(forkLatencies, 50),
513
+ p95: percentile(forkLatencies, 95),
514
+ marginalRssMB: NaN,
515
+ marginalHostMB: NaN,
516
+ totalVmmRssGB: Number(GB(vmmRss().rssBytes)),
517
+ vmmCount: vmmRss().count,
518
+ hostAvailGB: Number(GB(memAfter.availBytes)),
519
+ eagain: EAGAIN_RETRIES - eagainBefore,
520
+ workMs,
521
+ jobP50: 0,
522
+ jobP95: 0,
523
+ workOk: 0,
524
+ status,
525
+ note: note.trim(),
526
+ });
527
+ log(`STAGE N=${N} FAILED (${status}): ${note.slice(0, 140)}`);
528
+ // best-effort teardown of whatever forked
529
+ for (const child of forks) {
530
+ try {
531
+ await child.delete();
532
+ } catch {
533
+ try {
534
+ await child.stop();
535
+ } catch {}
536
+ }
537
+ created.delete(child);
538
+ }
539
+ break; // stop ramping past the failure ceiling
540
+ }
541
+ }
542
+
543
+ printReport(goldenBootMs, provMs, baseNode);
544
+ }
545
+
546
+ function printReport(goldenBootMs, provMs, baseNode) {
547
+ const pad = (s, n) => String(s).padStart(n);
548
+ console.log('\n\n==================== SCALE TABLE ====================');
549
+ console.log(
550
+ [
551
+ pad('N', 3),
552
+ pad('forkWall', 9),
553
+ pad('p50', 6),
554
+ pad('p95', 6),
555
+ pad('mRSS/fk', 8),
556
+ pad('totVmmGB', 9),
557
+ pad('hostFreeGB', 11),
558
+ pad('EAGAIN', 7),
559
+ pad('cpuJobp50', 10),
560
+ pad('cpuJobp95', 10),
561
+ pad('allMs', 7),
562
+ pad('status', 9),
563
+ ].join(' | ')
564
+ );
565
+ console.log('-'.repeat(110));
566
+ for (const r of rows) {
567
+ console.log(
568
+ [
569
+ pad(r.N, 3),
570
+ pad(`${r.forkWall}ms`, 9),
571
+ pad(`${r.p50}`, 6),
572
+ pad(`${r.p95}`, 6),
573
+ pad(`${r.marginalRssMB}MB`, 8),
574
+ pad(`${r.totalVmmRssGB}`, 9),
575
+ pad(`${r.hostAvailGB}`, 11),
576
+ pad(`${r.eagain}`, 7),
577
+ pad(`${r.jobP50}ms`, 10),
578
+ pad(`${r.jobP95}ms`, 10),
579
+ pad(`${r.workMs}`, 7),
580
+ pad(`${r.status}`, 9),
581
+ ].join(' | ')
582
+ );
583
+ if (r.note) console.log(` note(N=${r.N}): ${r.note}`);
584
+ }
585
+ console.log('====================================================');
586
+
587
+ // ---- SUMMARY: binding constraint analysis ----
588
+ const okRows = rows.filter((r) => r.status === 'ok');
589
+ const maxStable = okRows.length ? Math.max(...okRows.map((r) => r.N)) : 0;
590
+ const crashRow = rows.find((r) => r.status === 'crash');
591
+ const degradedRow = rows.find((r) => r.status === 'degraded');
592
+
593
+ // COW marginal: average of valid marginal-RSS figures across stages.
594
+ const validMarg = rows
595
+ .map((r) => r.marginalRssMB)
596
+ .filter((x) => Number.isFinite(x) && x > 0);
597
+ const avgMarginalMB = validMarg.length
598
+ ? validMarg.reduce((a, b) => a + b, 0) / validMarg.length
599
+ : NaN;
600
+ const validHostMarg = rows
601
+ .map((r) => r.marginalHostMB)
602
+ .filter((x) => Number.isFinite(x) && x > 0);
603
+ const avgHostMarginalMB = validHostMarg.length
604
+ ? validHostMarg.reduce((a, b) => a + b, 0) / validHostMarg.length
605
+ : NaN;
606
+
607
+ const cores = Number(HOST.ncpu) || 0;
608
+ const totalRamGB = HOST.memsize / 1024 / 1024 / 1024;
609
+ // RAM-bound cap = (totalRAM - reserve(golden+host headroom)) / marginal-per-fork
610
+ const reserveGB = 16; // golden + macOS + headroom
611
+ const ramCap =
612
+ Number.isFinite(avgHostMarginalMB) && avgHostMarginalMB > 0
613
+ ? Math.floor(((totalRamGB - reserveGB) * 1024) / avgHostMarginalMB)
614
+ : NaN;
615
+ // CPU-bound cap for CPU-active forks ~= cores / FORK_CPUS (oversubscribe up to ~cores)
616
+ const cpuCapActive = Math.floor(cores / FORK_CPUS);
617
+ const cpuCapBurst = cores;
618
+
619
+ // Empirical CPU ceiling: per-job CPU time vs the N=1 baseline. When N exceeds
620
+ // available cores, jobs queue and per-job wall time inflates. Find the largest N
621
+ // where per-job p50 stayed within ~1.5x of baseline (the "no meaningful queueing"
622
+ // throughput frontier).
623
+ const baseJob = rows.length ? rows[0].jobP50 || 0 : 0;
624
+ let cpuFrontier = 0;
625
+ let cpuKneeN = 0;
626
+ for (const r of rows) {
627
+ if (!baseJob) break;
628
+ if ((r.jobP50 || 0) <= baseJob * 1.5) cpuFrontier = r.N;
629
+ else if (!cpuKneeN) cpuKneeN = r.N;
630
+ }
631
+
632
+ // Determine binding constraint.
633
+ let binding = 'undetermined';
634
+ if (crashRow)
635
+ binding = `native/control-plane stability (broke at N=${crashRow.N})`;
636
+ else if (degradedRow && degradedRow.eagain > 0)
637
+ binding = 'control-plane EAGAIN saturation';
638
+ else if (cpuKneeN && cpuKneeN <= maxStable)
639
+ binding = `CPU cores (per-job time knees up at N=${cpuKneeN}; throughput frontier N~${cpuFrontier} vs ${cores} cores)`;
640
+ else if (Number.isFinite(ramCap) && ramCap <= maxStable)
641
+ binding = 'RAM (memory headroom / marginal-RSS-per-fork)';
642
+ else
643
+ binding =
644
+ 'none reached (control plane + CPU + RAM all had headroom through max N tested)';
645
+
646
+ console.log('\n==================== SUMMARY ====================');
647
+ console.log(
648
+ `host : ${HOST.brand}, ${cores} cores, ${GB(HOST.memsize)}GB RAM, HVF=${HOST.hv_support}`
649
+ );
650
+ console.log(
651
+ `golden cold-boot : ${goldenBootMs}ms; provision warm base: ${provMs}ms; node in fork: ${baseNode}`
652
+ );
653
+ console.log(
654
+ `max STABLE concurrent N : ${maxStable}${crashRow ? ` (broke/degraded at N=${crashRow.N})` : ''}`
655
+ );
656
+ console.log(
657
+ `COW marginal RAM / fork : ~${Number.isFinite(avgMarginalMB) ? avgMarginalMB.toFixed(0) : '?'}MB (vmm proc RSS) / ~${
658
+ Number.isFinite(avgHostMarginalMB) ? avgHostMarginalMB.toFixed(0) : '?'
659
+ }MB (host) vs ${FORK_MEM_MB}MB full VM => COW sharing ${
660
+ Number.isFinite(avgHostMarginalMB) && avgHostMarginalMB < FORK_MEM_MB
661
+ ? `${(100 - (avgHostMarginalMB / FORK_MEM_MB) * 100).toFixed(0)}% saved`
662
+ : 'NOT observed'
663
+ }`
664
+ );
665
+ console.log(`EAGAIN total (all stages) : ${EAGAIN_RETRIES}`);
666
+ console.log(
667
+ `CPU per-job p50 (baseline ${baseJob}ms): ${rows.map((r) => `N${r.N}=${r.jobP50}ms`).join(' ')}`
668
+ );
669
+ console.log(
670
+ `CPU throughput frontier : N~${cpuFrontier} within 1.5x baseline (knee at N=${cpuKneeN || 'none<=maxN'})`
671
+ );
672
+ console.log(`BINDING CONSTRAINT : ${binding}`);
673
+ console.log(
674
+ ` - CPU-bound cap (active) : ~${cpuCapActive} forks (cores/${FORK_CPUS}); burst up to ~${cpuCapBurst}`
675
+ );
676
+ console.log(
677
+ ` - RAM-bound cap : ~${Number.isFinite(ramCap) ? ramCap : '?'} forks ((${totalRamGB.toFixed(
678
+ 0
679
+ )}GB - ${reserveGB}GB reserve) / ${Number.isFinite(avgHostMarginalMB) ? avgHostMarginalMB.toFixed(0) : '?'}MB)`
680
+ );
681
+ // Recommended cap for CPU-ACTIVE forks = empirical CPU throughput frontier
682
+ // (where per-job time stays ~baseline), bounded by stability + RAM.
683
+ const candidates = [
684
+ cpuFrontier || cpuCapBurst,
685
+ maxStable,
686
+ Number.isFinite(ramCap) ? ramCap : Infinity,
687
+ ].filter((x) => Number.isFinite(x) && x > 0);
688
+ const recommended = candidates.length ? Math.min(...candidates) : maxStable;
689
+ console.log(
690
+ `RECOMMENDED concurrent-ACTIVE-fork cap (THIS Mac): ~${recommended}`
691
+ );
692
+ console.log(
693
+ ` scaling rule: CPU-bound active work -> cap ~= cores (${cores}); long-lived idle forks -> cap = (RAM_headroom_GB*1024)/marginalMB (~${
694
+ Number.isFinite(ramCap) ? ramCap : '?'
695
+ }).`
696
+ );
697
+ console.log(
698
+ `smol 1.3.2 stability : ${crashRow ? `BROKE at N=${crashRow.N} (${crashRow.note})` : `STABLE through N=${maxStable}`}`
699
+ );
700
+ console.log('================================================');
701
+ }
702
+
703
+ // ----------------------------------------------------------------------------
704
+ main()
705
+ .catch((e) => {
706
+ console.error('[scale] FATAL:', e?.stack || String(e));
707
+ process.exitCode = 1;
708
+ })
709
+ .finally(async () => {
710
+ log('\nfinal cleanup: deleting all tracked machines (forks + golden) ...');
711
+ for (const m of [...created]) {
712
+ try {
713
+ await m.delete();
714
+ } catch {
715
+ try {
716
+ await m.stop();
717
+ } catch {}
718
+ }
719
+ }
720
+ // give the runtime a moment to reap boot-vm workers
721
+ await sleep(2500);
722
+ const leaked = countLeakedVmm();
723
+ console.log(
724
+ `[scale] leaked smol-vmm processes at exit: ${leaked} (expect 0)`
725
+ );
726
+ if (leaked > 0) {
727
+ console.log(
728
+ '[scale] WARNING: leaked vmm detected; listing (not killing golden-* per hygiene):'
729
+ );
730
+ try {
731
+ console.log(
732
+ execSync('ps -axo pid,command | grep smol-vmm | grep -v grep', {
733
+ encoding: 'utf8',
734
+ })
735
+ );
736
+ } catch {}
737
+ }
738
+ });