@vzn/vx-dte 0.0.0 → 0.0.650

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,2359 @@
1
+ // The orchestrator's half of a distributed run: the `vx run` the user
2
+ // started. It runs no task itself (only hits restore here) and hands
3
+ // every task to the workers online, critical path first (the order core asks in):
4
+ // each task to a worker it fits now, by the load that worker last reported
5
+ // and what was sent since, preferring the worker that already holds the
6
+ // task's dependencies; none waits in a worker's queue. No file travels through the relay: an
7
+ // worker restores a dependency's files from the cache by the key its order
8
+ // names and saves its own under the task's key, and this machine restores
9
+ // them by that key before the task ends, so the run saves and reports it
10
+ // as its own.
11
+
12
+ import { realpathSync } from 'node:fs'
13
+ import { realpath, rm } from 'node:fs/promises'
14
+ import path from 'node:path'
15
+ import {
16
+ hostPlatform,
17
+ ui,
18
+ UserError,
19
+ withForwardArgs,
20
+ type CacheFacts,
21
+ type CapturedChunk,
22
+ type ExecuteRequest,
23
+ type ExecuteResult,
24
+ type MachineReport,
25
+ type SandboxConfig,
26
+ type TaskArtifacts,
27
+ type TaskNode,
28
+ } from '@vzn/vx'
29
+ import {
30
+ bytesOf,
31
+ HELD_EVERY,
32
+ HOST_ENV,
33
+ PART_LISTS,
34
+ type PartList,
35
+ type WorkerInfo,
36
+ type DepOrder,
37
+ type RunOrder,
38
+ type RuntimeOrder,
39
+ type ToWorker,
40
+ type Load,
41
+ type ToOrchestrator,
42
+ type WiredResult,
43
+ } from './messages.js'
44
+ import { simulate, type PlanTask, type PlanWorker, type Size, type WarmUp } from './planner.js'
45
+ import { Usage, type UsageStats } from './usage.js'
46
+ import { assign, ordered, type Need } from './dispatch.js'
47
+ import { DEFAULT_STRATEGY, type Strategy } from './strategy.js'
48
+ import { RelayError, unsent, type Participant } from './wire.js'
49
+ import { ORCHESTRATOR, Trace } from './trace.js'
50
+ import { names } from './outputs.js'
51
+
52
+ /** What a task is expected to hold while it runs, from its history; `Infinity`: a whole worker (one that filled its worker). */
53
+
54
+ interface Job {
55
+ readonly req: ExecuteRequest
56
+ readonly need: Need
57
+ /** The cores and memory it holds on its worker: its need, or the whole worker for one that filled its worker. */
58
+ held?: Need
59
+ readonly resolve: (r: ExecuteResult) => void
60
+ readonly reject: (err: unknown) => void
61
+ worker?: string
62
+ /** When it last joined the run's queue. */
63
+ queuedAt?: number
64
+ /** The cores its worker booked for it. */
65
+ booked?: number
66
+ /** The workers that failed it after beginning it, with why: two fail it. */
67
+ faults?: string[]
68
+ /** Its worker's lease on it ends then unless the worker says it still holds it. */
69
+ leaseUntil?: number
70
+ /** When it was sent to its worker. */
71
+ sentAt?: number
72
+ /** When its worker began it. */
73
+ startedAt?: number
74
+ /** When its worker said its command ended: it saves since. */
75
+ ranAt?: number
76
+ /** When it first waited on the worker holding an upstream's files (`kept`). */
77
+ pinnedAt?: number
78
+ /** Its upstreams already ran again for it: it waits on their worker however long. */
79
+ fellBack?: true
80
+ /** An uncached task run again for a dependant (`kept`): its result is the run's alone. */
81
+ again?: { readonly only?: string; readonly avoid?: string }
82
+ /** Dependency keys it ran again for, sent again from this machine (`resend`). */
83
+ reread?: Set<string>
84
+ /** Its upstreams, once the graph named it: walked again at every pump, 8000 queued spent 5 ms a pump on it (2026-10-25). */
85
+ up?: Set<string>
86
+ }
87
+
88
+ /** The longest a task waits on the worker holding its upstream's files before they are made again (2026-10-23: 10 s on any wait). */
89
+ const PIN_WAIT_MS = 10_000
90
+
91
+ /** No task began or ended this long: the run says what it waits on, each time again. */
92
+ const STALL_MS = 60_000
93
+
94
+ interface Worker {
95
+ readonly id: string
96
+ /** What the run gives it: its machine, held to its declared size. */
97
+ readonly cores: number
98
+ readonly memory: number
99
+ /** What its machine has, as it reported on joining. */
100
+ readonly reported: { readonly cores: number; readonly memory: number }
101
+ /** Its machine's platform, as it reported on joining. */
102
+ readonly platform: string | undefined
103
+ readonly label: string
104
+ /** From its start to its join, when this run started it. */
105
+ readonly warmUpMs: number | undefined
106
+ /** The size it was started as, when this run started it. */
107
+ readonly declared: Declared | undefined
108
+ /** Tasks it finished. */
109
+ ran: number
110
+ /** Their ids, for the summary of a worker that ran few: what held it. */
111
+ readonly done: string[]
112
+ readonly usage: Usage
113
+ /** Sent to it and not finished: running there or in its queue. */
114
+ readonly busy: Map<string, Job>
115
+ /** Of `busy`, the ones it began. */
116
+ readonly started: Set<string>
117
+ usedCores: number
118
+ usedMemory: number
119
+ /** Its machine's use as its last frame said, and when this machine heard it. */
120
+ load?: { cpu: number; memory: number; at: number }
121
+ /** Tasks whose outputs it holds: it ran them, or was sent them. */
122
+ readonly has: Set<string>
123
+ /** Sends to it go out in order: a run's parts before the run. */
124
+ chain: Promise<void>
125
+ /** It was sent the run's sandbox configs. */
126
+ sandboxes: boolean
127
+ /** It failed a task it began: it gets no more. */
128
+ faulty: boolean
129
+ }
130
+
131
+ /** What a worker was started as: its size's `cpu` and `memory` in bytes. */
132
+ export interface Declared {
133
+ readonly size: string
134
+ readonly cpu: number
135
+ readonly memory: number
136
+ /** Its size's platform: a worker reporting another is refused. None: planned as this machine's. */
137
+ readonly platform?: string
138
+ }
139
+
140
+ export interface OrchestratorOptions {
141
+ readonly me: Participant
142
+ readonly root: string
143
+ /** The tree of the run's tracked files (`tree.ts`). */
144
+ readonly tree: string
145
+ readonly nodes: () => ReadonlyMap<string, TaskNode> | undefined
146
+ readonly need: (taskId: string) => Need
147
+ /** The run's cache, by key: what workers save to and this machine restores from. */
148
+ readonly artifacts: TaskArtifacts
149
+ /** What workers read the cache under (`ExecutorContext.cacheFacts`). */
150
+ readonly cacheFacts: () => Promise<CacheFacts>
151
+ /** The run id, which a task's run key is made of. */
152
+ readonly run: string
153
+ /**
154
+ * Sends of each task under `run`, kept by whoever outlives this run:
155
+ * `vx watch` runs again under one run id, and a send counted from 0
156
+ * again took the last run's run key (2026-10-24).
157
+ */
158
+ readonly sent?: Map<string, number>
159
+ /** A task's cache key, once the run computed it (a hit's too); undefined when it has none. */
160
+ readonly keyOf: (taskId: string) => string | undefined
161
+ /** Worker `id` (its label) was lost, or never started: room for another. */
162
+ readonly lost?: (id: string) => void
163
+ /** A worker joined: the run plans again. */
164
+ readonly joined?: () => void
165
+ /** Worker `id` did not join within twice its size's first join: given up, for its adapter to stop. */
166
+ readonly overdue?: (id: string) => void
167
+ /** A worker failed a task or its lease: the run plans again, now, so a replacement starts. */
168
+ readonly faulted?: () => void
169
+ /** How much of the run waits on a task (`tailsMs`): the queue places the most first. */
170
+ readonly tail?: (taskId: string) => number
171
+ /** How waiting tasks are ordered and placed (`Strategy`). */
172
+ readonly how?: Strategy
173
+ /** The platform a task naming none runs on (`homeOf`). */
174
+ readonly home?: string | undefined
175
+ /** No task began or ended for `stallMs`: what the run waits on, one line a task. */
176
+ readonly stalled?: (waiting: string) => void
177
+ /** A fault the run works around (a message the relay would not take); default stderr. */
178
+ readonly warn?: (message: string) => void
179
+ /** Default `STALL_MS`. */
180
+ readonly stallMs?: number
181
+ /** How long a task waits on the worker holding its upstream's files (`PIN_WAIT_MS`). */
182
+ readonly pinWaitMs?: number
183
+ }
184
+
185
+ /**
186
+ * Lent tokens are asked again this long before they end: inside the
187
+ * window a remote exchanges a new one (turboCache's is five minutes).
188
+ */
189
+ const RELEND_MS = 4 * 60_000
190
+ /**
191
+ * A lent token already inside that window is lent again no sooner: one
192
+ * lent as it ended relent at once, in a loop (vx-cache, 2026-10-25).
193
+ */
194
+ const RELEND_FLOOR_MS = 30_000
195
+
196
+ /**
197
+ * A batch's orders past this go in another: the relay takes a body of
198
+ * 1 MiB at most, and sealing it grows it by a third.
199
+ */
200
+ const BATCH_BYTES = 512 * 1024
201
+ /** A worker that ran this many tasks or fewer names them in the summary. */
202
+ const FEW = 2
203
+ /** The unmeasured tasks the summary names; the rest it counts. */
204
+ const NAMED = 40
205
+ /** Core-time of history the run's tasks finish before their use reshapes bookings: a few tasks' worth. */
206
+ const LEARN_MS = 60_000
207
+ /** The most a worker's bookings may add up to, in its cores, when tasks use less than their history. */
208
+ const OVERBOOK = 1.5
209
+ /** Failed starts the run replaces before it calls its setup broken. */
210
+ const RESTARTS = 3
211
+
212
+ /** `orders` in runs of at most `max` bytes of JSON; one bigger goes alone. */
213
+ function parts<T extends { order: unknown }>(orders: readonly T[], max: number): T[][] {
214
+ const out: T[][] = []
215
+ let part: T[] = []
216
+ let bytes = 0
217
+ for (const o of orders) {
218
+ const size = bytesOf(o.order)
219
+ if (part.length > 0 && bytes + size > max) {
220
+ out.push(part)
221
+ part = []
222
+ bytes = 0
223
+ }
224
+ part.push(o)
225
+ bytes += size
226
+ }
227
+ if (part.length > 0) out.push(part)
228
+ return out
229
+ }
230
+
231
+ /** `items` in runs of at most `max` bytes of JSON. */
232
+ function chunks<T>(items: readonly T[], max: number): T[][] {
233
+ const out: T[][] = []
234
+ let piece: T[] = []
235
+ let bytes = 0
236
+ for (const item of items) {
237
+ const size = bytesOf(item) + 1
238
+ if (piece.length > 0 && bytes + size > max) {
239
+ out.push(piece)
240
+ piece = []
241
+ bytes = 0
242
+ }
243
+ piece.push(item)
244
+ bytes += size
245
+ }
246
+ if (piece.length > 0) out.push(piece)
247
+ return out
248
+ }
249
+
250
+ /** A task's end when its run stopped it before it began anywhere. */
251
+ const stopped = (): ExecuteResult => result({ exitCode: 130, signal: 'SIGTERM', durationMs: 0 }, [])
252
+
253
+ export class Orchestrator {
254
+ private readonly workers = new Map<string, Worker>()
255
+ /** The run tree's files, workspace-relative, listed when a task first asks. */
256
+ private inTree?: Promise<Tree | undefined>
257
+ private readonly queue: Job[] = []
258
+ /** Where a worker saved each task it ran: its cache key or its run key. */
259
+ private readonly savedAs = new Map<string, string>()
260
+ /** Keys the run's requests named for their dependencies. */
261
+ private readonly upstreamKeys = new Map<string, string>()
262
+ /** How many times each task was sent: its run key is one per send. */
263
+ private readonly sent: Map<string, number>
264
+ /** Each hit's key, once on a remote a worker reads (`TaskArtifacts.share`). */
265
+ private readonly shared = new Map<string, Promise<boolean>>()
266
+ /** Keys a worker could not read, sent again over the remotes' copy: once each, whether one reached a remote. */
267
+ private readonly resent = new Map<string, Promise<boolean>>()
268
+ /** When each worker this run started was asked for, by `WorkerSpawn.index`. */
269
+ private readonly spawnedAt = new Map<string, { at: number; declared: Declared }>()
270
+ private ended = false
271
+ /**
272
+ * How long a worker of a size may take to join: twice the first of its
273
+ * size that did (2026-10-23: "we learn that from first we get, and
274
+ * double its time"). Unknown until one joins, so no wait before.
275
+ */
276
+ private readonly joinLimitMs = new Map<string, number>()
277
+ private endedAt = 0
278
+ /** A long result's lists, by worker and task, until its `end` comes. */
279
+ private readonly parts = new Map<string, Partial<Record<PartList, unknown[]>>>()
280
+ /** Workers that joined and left, for the footer. */
281
+ private readonly gone: Worker[] = []
282
+ /** Of `gone`, those the run let go (`release`), not lost. */
283
+ private readonly retired = new Set<Worker>()
284
+ /** Workers asked for that never joined, with why. */
285
+ private readonly unstarted = new Map<string, string>()
286
+ /** Started, then not needed before they joined (`release`), or given up as late. */
287
+ private readonly cancelled = new Set<string>()
288
+ /** Of `cancelled`, those given up past their join limit, not let go: taken if they join. */
289
+ private readonly late = new Set<string>()
290
+ /** Of `cancelled`, those that joined anyway and were told to exit. */
291
+ private readonly turnedAway = new Set<string>()
292
+ /** Let go before they joined, and left running by their adapter (`leftRunning`): no plan lets them go again. */
293
+ private readonly unstoppable = new Set<string>()
294
+ /** A failed start was given another worker (`failed`). */
295
+ private restarts = 0
296
+ /**
297
+ * Each uncached task a worker ran: its files are there alone, so its
298
+ * dependants go there (2026-10-25). An uncached task declares no
299
+ * outputs, and a dependant elsewhere did not find them (#4407).
300
+ */
301
+ private readonly kept = new Map<string, { worker: string; req: ExecuteRequest; need: Need }>()
302
+ /** Of `kept`, those running again for a dependant. */
303
+ private readonly remaking = new Set<string>()
304
+ /** Of `kept`, those that failed running again: their dependants fail, saying why. */
305
+ private readonly unmade = new Map<string, string>()
306
+ /** The tasks core's probe found in no cache (`demand`): a worker uploads theirs asking nothing first. */
307
+ missed: ReadonlySet<string> = new Set()
308
+ private pumping = false
309
+ private joined = 0
310
+ private readonly startedAt = performance.now()
311
+ /** The run in time, for `--profile` (`profile`). */
312
+ readonly trace = new Trace()
313
+ /** When a task last began, ended or faulted, or the run last said it waits. */
314
+ private movedAt = performance.now()
315
+ /** When core's cache probe answered, how many placed tasks it left to run, and how many it found in no cache. */
316
+ private probe?: { at: number; left: number; misses: number }
317
+ /** Each task a worker ran: how long it held the worker, the cores it used meanwhile, its memory, the platform. */
318
+ private readonly took = new Map<
319
+ string,
320
+ { ms: number; cores: number; memory: number; platform: string | undefined }
321
+ >()
322
+ /** Each time the run asked for workers: when, how many, for which work, ending when. */
323
+ private readonly asks: {
324
+ at: number
325
+ workers: number
326
+ tasks: number
327
+ measured: number
328
+ endsInMs: number
329
+ }[] = []
330
+ /**
331
+ * The tasks the first ask had no measure of, each booking a guess:
332
+ * named, so a run that packed badly says which (main 68b851d, 2026-10-23:
333
+ * 41 of 119, small#7 holding 3.9 cores for a task using 0.3).
334
+ */
335
+ private unmeasured: readonly string[] = []
336
+ /**
337
+ * Core-time measured tasks used here, and what their history said they
338
+ * would: tasks booked at their history used 82% of it on every Linux
339
+ * worker of cold run 3 (66% of the cores against the sim's 86%,
340
+ * 2026-10-23). A measured task books its need times what they used.
341
+ */
342
+ private usedMs = 0
343
+ private needMs = 0
344
+ private readonly stop = new AbortController()
345
+ private readonly loop: Promise<void>
346
+ private readonly realRoot: string
347
+ /** What workers read the cache under, asked once and again before its lent tokens end. */
348
+ private facts: Promise<CacheFacts> | undefined
349
+ private relend: ReturnType<typeof setTimeout> | undefined
350
+ /** The plugins any of the run's lends lent a token. */
351
+ private readonly lenders = new Set<string>()
352
+
353
+ constructor(private readonly o: OrchestratorOptions) {
354
+ this.sent = o.sent ?? new Map()
355
+ // A root that is gone fails the run's own reads first.
356
+ this.realRoot = (() => {
357
+ try {
358
+ return realpathSync(o.root)
359
+ // quiet: a root that is gone fails the run's own reads first
360
+ } catch {
361
+ return o.root
362
+ }
363
+ })()
364
+ // Leases ran on while either side was away, as a relay restarted: what
365
+ // a worker holds is still its own, not run again elsewhere
366
+ // (2026-10-25: "make sure on rejoin we don't repeat work").
367
+ o.me.resumed((peer) => {
368
+ for (const a of this.workers.values())
369
+ if (peer === undefined || a.id === peer)
370
+ for (const j of a.busy.values()) if (j.leaseUntil !== undefined) this.lease(j)
371
+ })
372
+ this.loop = this.listen()
373
+ }
374
+
375
+ async execute(req: ExecuteRequest): Promise<ExecuteResult> {
376
+ if (this.lost !== undefined) throw new Error(this.lost)
377
+ // A worker runs the run's tree: a file its key includes that the tree
378
+ // lacks (untracked) is not there, and the output it saved under that
379
+ // key was a stale hit for the next run (2026-10-24).
380
+ if (req.inputs !== undefined && req.inputs.files.length > 0) {
381
+ this.inTree ??= listTree(this.o.root, this.o.tree)
382
+ const tree = await this.inTree
383
+ // A dependency's output is restored there before the task runs: a
384
+ // key that reads an upstream's bytes (`inputs.tasks` leaving it out)
385
+ // was refused (2026-10-25).
386
+ const nodes = this.o.nodes()
387
+ const restored = [...upstream(nodes, req.taskId)].flatMap((id) => {
388
+ const n = nodes?.get(id)
389
+ const outs = n?.config.cache?.outputs
390
+ return n === undefined || outs === undefined
391
+ ? []
392
+ : [
393
+ names(path.relative(this.o.root, n.projectDir), {
394
+ files: outs.files,
395
+ workspaceFiles: outs.workspaceFiles ?? [],
396
+ }),
397
+ ]
398
+ })
399
+ const stray =
400
+ tree === undefined
401
+ ? undefined
402
+ : req.inputs.files.find(
403
+ (f) => !tree.files.has(f.path) && !restored.some((made) => made(f.path)),
404
+ )
405
+ if (stray !== undefined) {
406
+ // A committed submodule's files are not the tree's: its gitlink is.
407
+ const link = tree?.links.find((l) => stray.path.startsWith(`${l}/`))
408
+ throw new UserError(
409
+ link === undefined
410
+ ? `${req.taskId} keys ${stray.path}, which is not in the commit workers check out: commit it, or leave it out of the task's inputs`
411
+ : `${req.taskId} keys ${stray.path}, in submodule ${link}, whose files workers do not check out: leave it out of the task's inputs`,
412
+ )
413
+ }
414
+ }
415
+ for (const u of req.inputs?.upstream ?? []) this.upstreamKeys.set(u.taskId, u.hash)
416
+ await this.share(req.taskId)
417
+ return new Promise((resolve, reject) => {
418
+ const need = this.o.need(req.taskId)
419
+ const job: Job = {
420
+ req,
421
+ need: { ...need, platform: req.platform ?? this.o.home },
422
+ queuedAt: performance.now(),
423
+ resolve,
424
+ reject,
425
+ }
426
+ // A signal aborted already fires no `abort`.
427
+ if (req.signal?.aborted === true) return resolve(stopped())
428
+ req.signal?.addEventListener('abort', () => this.abort(job), { once: true })
429
+ this.queue.push(job)
430
+ this.pumpSoon()
431
+ })
432
+ }
433
+
434
+ /** The run's cache facts; once they end, every worker is sent them again before then. */
435
+ private cache(): Promise<CacheFacts> {
436
+ this.facts ??= this.lend(undefined)
437
+ return this.facts
438
+ }
439
+
440
+ /**
441
+ * The run's cache facts, asked now. A remote whose lend failed this time
442
+ * (core lends it nothing) keeps the token `held` lent it until that ends,
443
+ * and is asked again `RELEND_FLOOR_MS` on: sent without it, workers lost
444
+ * the remote while its token held, and for the rest of the run when it
445
+ * was the one remote whose token ends (no relend came again).
446
+ */
447
+ private lend(held: CacheFacts | undefined): Promise<CacheFacts> {
448
+ let short = false
449
+ const asked = this.o.cacheFacts().then((fresh) => {
450
+ for (const p of Object.keys(fresh.lent)) this.lenders.add(p)
451
+ short = [...this.lenders].some((p) => !(p in fresh.lent))
452
+ const live =
453
+ held !== undefined && (held.expiresAt === undefined || held.expiresAt > Date.now())
454
+ if (!short || !live) return fresh
455
+ const lent = { ...fresh.lent }
456
+ for (const [p, t] of Object.entries(held.lent)) if (!(p in lent)) lent[p] = t
457
+ const expiresAt = Math.min(fresh.expiresAt ?? Infinity, held.expiresAt ?? Infinity)
458
+ return { ...fresh, lent, ...(expiresAt === Infinity ? {} : { expiresAt }) }
459
+ })
460
+ void asked.then(
461
+ (f) => {
462
+ if ((f.expiresAt === undefined && !short) || this.ended) return
463
+ const left = (f.expiresAt ?? Infinity) - Date.now()
464
+ this.relend = setTimeout(
465
+ () => {
466
+ const next = this.lend(f)
467
+ this.facts = next
468
+ void next.then(
469
+ (cache) => {
470
+ if (JSON.stringify(cache) === JSON.stringify(f)) return
471
+ for (const a of this.workers.values()) this.post(a, { type: 'cache', cache })
472
+ },
473
+ // quiet: the workers keep what they hold, and the next ask asks again
474
+ () => {},
475
+ )
476
+ },
477
+ short ? RELEND_FLOOR_MS : left > RELEND_MS ? left - RELEND_MS : RELEND_FLOOR_MS,
478
+ )
479
+ this.relend.unref()
480
+ },
481
+ () => {
482
+ if (this.facts === asked) this.facts = undefined
483
+ },
484
+ )
485
+ return asked
486
+ }
487
+
488
+ async close(): Promise<void> {
489
+ clearTimeout(this.relend)
490
+ this.ended = true
491
+ this.endedAt = performance.now()
492
+ this.stop.abort()
493
+ await this.loop
494
+ await this.o.me.leave()
495
+ for (const a of this.workers.values()) this.trace.left(a.label, 'stopped', this.endedAt)
496
+ }
497
+
498
+ /** The run's trace events (`TaskExecutor.profile`), once it had workers. */
499
+ profile(): Record<string, unknown>[] {
500
+ if (this.spawnedAt.size + this.workers.size + this.gone.length === 0) return []
501
+ return this.trace.events(this.ended ? this.endedAt : performance.now())
502
+ }
503
+
504
+ private abort(job: Job): void {
505
+ const at = this.queue.indexOf(job)
506
+ if (at >= 0) {
507
+ this.queue.splice(at, 1)
508
+ job.resolve(stopped())
509
+ } else if (job.worker !== undefined) {
510
+ const a = this.workers.get(job.worker)
511
+ if (a !== undefined) this.post(a, { type: 'cancel', task: job.req.taskId })
512
+ }
513
+ }
514
+
515
+ /** Tasks core starts in one tick go out as one batch. */
516
+ private pumpSoon(): void {
517
+ if (this.pumping) return
518
+ this.pumping = true
519
+ setImmediate(() => {
520
+ this.pumping = false
521
+ this.pump()
522
+ })
523
+ }
524
+
525
+ private pump(): void {
526
+ const batches = new Map<Worker, Job[]>()
527
+ const left: Job[] = []
528
+ // Core asks in its critical-path order, but only as tasks get ready: a
529
+ // long chain ready a moment late queued behind 46 shards ready at once,
530
+ // and its 41 s build began 47 s in, the run's end (main, 2026-10-23).
531
+ const tail = this.o.tail
532
+ const how = this.o.how ?? DEFAULT_STRATEGY
533
+ const waiting = this.queue.splice(0)
534
+ const workers = [...this.workers.values()].filter((a) => !a.faulty)
535
+ const byId = new Map(waiting.map((j) => [j.req.taskId, j]))
536
+ const queue =
537
+ tail === undefined
538
+ ? waiting
539
+ : ordered(
540
+ waiting.map((j) => j.req.taskId),
541
+ how.order,
542
+ tail,
543
+ (id) => byId.get(id)!.need,
544
+ Math.max(1, ...workers.map((a) => a.cores)),
545
+ ).map((id) => byId.get(id)!)
546
+ const bins = workers.map((a) => {
547
+ let running = 0
548
+ for (const j of a.busy.values()) if (j.held !== undefined) running++
549
+ return {
550
+ id: a.id,
551
+ cores: a.cores,
552
+ memory: a.memory,
553
+ used: this.used(a),
554
+ running,
555
+ has: a.has,
556
+ platform: a.platform,
557
+ a,
558
+ }
559
+ })
560
+ const nodes = this.o.nodes()
561
+ const asks = []
562
+ const placed: Job[] = []
563
+ // The least any ask needs: once no worker takes it, the pass ends. Each
564
+ // pump read every queued task, 8000 of them a pump (2026-10-25).
565
+ const floor = { cores: Infinity, memory: Infinity }
566
+ for (const job of queue) {
567
+ const up =
568
+ job.up ??
569
+ (nodes?.has(job.req.taskId) === true
570
+ ? (job.up = upstream(nodes, job.req.taskId))
571
+ : upstream(nodes, job.req.taskId))
572
+ const pin = this.pin(job, up)
573
+ if (pin === 'done') continue
574
+ if (pin === 'wait') {
575
+ left.push(job)
576
+ continue
577
+ }
578
+ placed.push(job)
579
+ const need = this.booking(job.need)
580
+ floor.cores = Math.min(floor.cores, need.cores)
581
+ floor.memory = Math.min(floor.memory, need.memory)
582
+ asks.push({ id: job.req.taskId, need, up, ...pin })
583
+ }
584
+ const to = assign(asks, bins, (above, platform) => this.bigger(above, platform), how.place, {
585
+ floor,
586
+ kinds: Infinity,
587
+ })
588
+ for (const job of placed) {
589
+ const a = to.get(job.req.taskId)?.a
590
+ if (a === undefined) {
591
+ left.push(job)
592
+ continue
593
+ }
594
+ job.worker = a.id
595
+ job.sentAt = performance.now()
596
+ a.busy.set(job.req.taskId, job)
597
+ job.held = {
598
+ cores: Math.min(this.booking(job.need).cores, a.cores),
599
+ memory: Math.min(job.need.memory, a.memory),
600
+ }
601
+ job.booked = job.held.cores
602
+ this.trace.slice({
603
+ track: ORCHESTRATOR,
604
+ name: job.req.taskId,
605
+ cat: 'queue',
606
+ from: job.queuedAt ?? job.sentAt,
607
+ to: job.sentAt,
608
+ args: { to: a.label },
609
+ })
610
+ a.usedCores += job.held.cores
611
+ a.usedMemory += job.held.memory
612
+ const batch = batches.get(a)
613
+ if (batch === undefined) batches.set(a, [job])
614
+ else batch.push(job)
615
+ }
616
+ this.queue.push(...left)
617
+ for (const [a, jobs] of batches) this.send(a, jobs)
618
+ }
619
+
620
+ /**
621
+ * Where `job` may go, as its upstreams' files no cache holds lie (`kept`):
622
+ * on the worker holding them, waiting there up to `PIN_WAIT_MS`. Past
623
+ * that, with that worker gone, or with them on more than one, each
624
+ * upstream not on the worker holding most runs again where the job may
625
+ * go, and the job waits for it. `'wait'`: not yet; `'done'`: it failed.
626
+ */
627
+ private pin(
628
+ job: Job,
629
+ up: ReadonlySet<string>,
630
+ ): 'wait' | 'done' | { only?: string; avoid?: string } {
631
+ if (job.again !== undefined) {
632
+ const { only } = job.again
633
+ const waited = performance.now() - (job.pinnedAt ?? performance.now())
634
+ const there = only === undefined || this.workers.get(only)?.faulty === false
635
+ return there && waited < this.pinWaitMs ? job.again : {}
636
+ }
637
+ // Nothing kept or made again: no upstream can pin it.
638
+ if (this.kept.size === 0 && this.remaking.size === 0 && this.unmade.size === 0) return {}
639
+ const held = new Map<string, string[]>()
640
+ for (const id of up) {
641
+ const why = this.unmade.get(id)
642
+ if (why !== undefined) {
643
+ job.resolve(result({ exitCode: 1, durationMs: 0 }, [{ text: `${why}\n`, err: true }]))
644
+ return 'done'
645
+ }
646
+ if (this.remaking.has(id)) return 'wait'
647
+ const k = this.kept.get(id)
648
+ if (k === undefined) continue
649
+ const on = held.get(k.worker)
650
+ if (on === undefined) held.set(k.worker, [id])
651
+ else on.push(id)
652
+ }
653
+ if (held.size === 0) return {}
654
+ const now = performance.now()
655
+ if (job.pinnedAt === undefined) {
656
+ job.pinnedAt = now
657
+ setTimeout(() => this.pumpSoon(), this.pinWaitMs + 50).unref?.()
658
+ }
659
+ // A worker of another platform cannot take it: they run again on one that can.
660
+ const platform = job.need.platform
661
+ const live = [...held].filter(([w]) => {
662
+ const a = this.workers.get(w)
663
+ return a?.faulty === false && (platform === undefined || a.platform === platform)
664
+ })
665
+ live.sort((x, y) => y[1].length - x[1].length)
666
+ const target = live[0]?.[0]
667
+ if (target !== undefined && held.size === 1) {
668
+ if (job.fellBack === true || now - job.pinnedAt < this.pinWaitMs) return { only: target }
669
+ // Its worker stayed full: they run again on another, and it follows.
670
+ this.remake(held.get(target)!, { avoid: target })
671
+ } else
672
+ for (const [w, ids] of held)
673
+ if (w !== target) this.remake(ids, target === undefined ? { platform } : { only: target })
674
+ job.fellBack = true
675
+ delete job.pinnedAt
676
+ return 'wait'
677
+ }
678
+
679
+ private get pinWaitMs(): number {
680
+ return this.o.pinWaitMs ?? PIN_WAIT_MS
681
+ }
682
+
683
+ /** `ids`, uncached tasks that ran, run again where `where` says, for a dependant. */
684
+ private remake(
685
+ ids: readonly string[],
686
+ where: { only?: string; avoid?: string; platform?: string | undefined },
687
+ ): void {
688
+ for (const id of ids) {
689
+ const k = this.kept.get(id)
690
+ if (k === undefined || this.remaking.has(id)) continue
691
+ this.remaking.add(id)
692
+ this.kept.delete(id)
693
+ const done = (why: string | undefined): void => {
694
+ this.remaking.delete(id)
695
+ if (why !== undefined) this.unmade.set(id, why)
696
+ this.pumpSoon()
697
+ }
698
+ this.pumpSoon()
699
+ this.queue.unshift({
700
+ req: k.req,
701
+ need: where.platform === undefined ? k.need : { ...k.need, platform: where.platform },
702
+ queuedAt: performance.now(),
703
+ pinnedAt: performance.now(),
704
+ again: {
705
+ ...(where.only === undefined ? {} : { only: where.only }),
706
+ ...(where.avoid === undefined ? {} : { avoid: where.avoid }),
707
+ },
708
+ resolve: (r) =>
709
+ done(
710
+ r.exitCode === 0 ? undefined : `${id} ran again for it and failed (exit ${r.exitCode})`,
711
+ ),
712
+ reject: (err) =>
713
+ done(
714
+ `${id} ran again for it and failed: ${err instanceof Error ? err.message : String(err)}`,
715
+ ),
716
+ })
717
+ if (where.only !== undefined || where.avoid !== undefined)
718
+ setTimeout(() => this.pumpSoon(), this.pinWaitMs + 50).unref?.()
719
+ }
720
+ }
721
+
722
+ /**
723
+ * What worker `a` has in use: what the run booked there, or more when
724
+ * its last load and what was sent since say more. Never less: load lags
725
+ * a task's start, so placing below the booking sent small#3 38 test
726
+ * shards on 4 cores at once, 28 failed (main bf77a58, 2026-10-23).
727
+ */
728
+ private used(a: Worker): { cores: number; memory: number } {
729
+ const booked = { cores: a.usedCores, memory: a.usedMemory }
730
+ const l = a.load
731
+ if (l === undefined || a.cores !== a.reported.cores) return booked
732
+ let cores = l.cpu
733
+ let memory = l.memory
734
+ for (const j of a.busy.values())
735
+ if (j.held !== undefined && (j.sentAt ?? 0) > l.at) {
736
+ cores += j.held.cores
737
+ memory += j.held.memory
738
+ }
739
+ return { cores: Math.max(booked.cores, cores), memory: Math.max(booked.memory, memory) }
740
+ }
741
+
742
+ /** Whether a task of `a`'s holds its cores: its command still runs, or has yet to. */
743
+ private holding(a: Worker): boolean {
744
+ for (const j of a.busy.values()) if (j.held !== undefined) return true
745
+ return false
746
+ }
747
+
748
+ /**
749
+ * What a task books: a measured one its need times the share of their
750
+ * history the run's tasks used, once they used `LEARN_MS` of it, and
751
+ * never under `1 / OVERBOOK` of its need. An unmeasured one (`ask`)
752
+ * books what it books.
753
+ */
754
+ booking(need: Need): Need {
755
+ if (need.cores === Infinity || need.ask !== undefined || this.needMs < LEARN_MS) return need
756
+ const used = Math.min(1, Math.max(1 / OVERBOOK, this.usedMs / this.needMs))
757
+ return { ...need, cores: need.cores * used }
758
+ }
759
+
760
+ /**
761
+ * `job`'s cores and memory go back to its worker, once: its command
762
+ * ended, or it left. Its worker's last load counted it busy, and its
763
+ * save: kept, a Mac read 2.9 cores busy with one 1.1-core task left and
764
+ * the next task waited for the next frame (main 2975e38, 2026-10-24).
765
+ */
766
+ private unhold(a: Worker, job: Job): void {
767
+ delete a.load
768
+ if (job.held === undefined) return
769
+ a.usedCores -= job.held.cores
770
+ a.usedMemory -= job.held.memory
771
+ if (a.started.has(job.req.taskId)) a.usage.book(-job.held.cores)
772
+ delete job.held
773
+ }
774
+
775
+ /** What a frame of `a`'s says its machine uses now. */
776
+ private heard(a: Worker, load: Load | undefined): void {
777
+ if (load === undefined) return
778
+ a.load = { cpu: Number(load.cpu) || 0, memory: Number(load.memory) || 0, at: performance.now() }
779
+ const avg = Number(load.avg)
780
+ if (Number.isFinite(avg) && avg >= 0) a.usage.machineAvg = avg
781
+ const tasks = Number(load.tasks)
782
+ this.trace.load(a.label, a.load.cpu, a.load.at, Number.isFinite(tasks) ? tasks : undefined)
783
+ }
784
+
785
+ /** Whether a worker of `platform` bigger than `above` is starting: a task that filled that size waits for it. */
786
+ private bigger(
787
+ above: { readonly cores: number; readonly memory: number },
788
+ platform: string | undefined,
789
+ ): boolean {
790
+ const joined = new Set([...this.workers.values(), ...this.gone].map((x) => x.label))
791
+ for (const [id, { declared }] of this.spawnedAt)
792
+ if (
793
+ !joined.has(id) &&
794
+ !this.unstarted.has(id) &&
795
+ !this.cancelled.has(id) &&
796
+ (platform === undefined || (declared.platform ?? hostPlatform()) === platform) &&
797
+ (declared.cpu > above.cores || declared.memory > above.memory)
798
+ )
799
+ return true
800
+ return false
801
+ }
802
+
803
+ /**
804
+ * This send's number for `task`. A remote keeps the first bytes put
805
+ * under a key, as this machine's store keeps the ones it restored: a
806
+ * retry saved under its first attempt's run key restored that failure's
807
+ * files and output under a green result (2026-10-24).
808
+ */
809
+ private sends(task: string): number {
810
+ const n = this.sent.get(task) ?? 0
811
+ this.sent.set(task, n + 1)
812
+ return n
813
+ }
814
+
815
+ /**
816
+ * Each dependency of `id` this machine restored, a hit no worker ran, put
817
+ * on a remote the workers read: a worker reads the remotes alone, and a
818
+ * hit only this cache held (the remote evicted it, or this machine does
819
+ * not write it) failed the dependant there (2026-10-24).
820
+ */
821
+ private async share(id: string): Promise<void> {
822
+ const nodes = this.o.nodes()
823
+ const keys: string[] = []
824
+ for (const d of upstream(nodes, id)) {
825
+ if (this.savedAs.has(d) || nodes?.get(d)?.config.cache?.outputs === undefined) continue
826
+ const key = this.upstreamKeys.get(d) ?? this.o.keyOf(d)
827
+ if (key !== undefined) keys.push(key)
828
+ }
829
+ await Promise.all(
830
+ keys.map((k) => {
831
+ let got = this.shared.get(k)
832
+ if (got === undefined) {
833
+ // quiet: a key no remote took fails on the worker, by name
834
+ got = this.o.artifacts.share(k).catch(() => false)
835
+ this.shared.set(k, got)
836
+ }
837
+ return got
838
+ }),
839
+ )
840
+ }
841
+
842
+ /**
843
+ * True when one of `keys` `job` did not run again for already reached a
844
+ * remote. Each key is sent once and each job runs again once for it:
845
+ * two dependants failing on one key together, the second found it sent
846
+ * and failed, though the copy it could not read was replaced.
847
+ */
848
+ private async resend(job: Job, keys: readonly string[]): Promise<boolean> {
849
+ const fresh = keys.filter((k) => typeof k === 'string' && job.reread?.has(k) !== true)
850
+ if (fresh.length === 0) return false
851
+ job.reread = new Set([...(job.reread ?? []), ...fresh])
852
+ const got = await Promise.all(
853
+ fresh.map((k) => {
854
+ let sent = this.resent.get(k)
855
+ if (sent === undefined) {
856
+ // quiet: a key no remote took fails the task, by name
857
+ sent = this.o.artifacts.resend(k).catch(() => false)
858
+ this.resent.set(k, sent)
859
+ }
860
+ return sent
861
+ }),
862
+ )
863
+ return got.includes(true)
864
+ }
865
+
866
+ private send(a: Worker, jobs: readonly Job[]): void {
867
+ const nodes = this.o.nodes()
868
+ const orders = jobs.map((job) => {
869
+ const { req } = job
870
+ const deps: DepOrder[] = []
871
+ // Every one, held there or not: the worker restores each key once, and
872
+ // one it was sent but gave back it never ran or restored.
873
+ for (const id of upstream(nodes, req.taskId)) {
874
+ const n = nodes?.get(id)
875
+ const outs = n?.config.cache?.outputs
876
+ const key = this.savedAs.get(id) ?? this.upstreamKeys.get(id) ?? this.o.keyOf(id)
877
+ if (n === undefined || outs === undefined || key === undefined) continue
878
+ a.has.add(id)
879
+ deps.push({
880
+ task: id,
881
+ key,
882
+ project: path.relative(this.o.root, n.projectDir),
883
+ outputs: { files: [...outs.files], workspaceFiles: [...(outs.workspaceFiles ?? [])] },
884
+ })
885
+ }
886
+ // A task sent here leaves its outputs here for its dependants.
887
+ a.has.add(req.taskId)
888
+ const node = nodes?.get(req.taskId)
889
+ const env = defined(req.env, req.envDefine)
890
+ return {
891
+ job,
892
+ order: {
893
+ task: req.taskId,
894
+ // Unsaved: under the run's own key, as an uncached task's.
895
+ ...(req.cacheKey === undefined || req.unsaved === true ? {} : { key: req.cacheKey }),
896
+ runKey: runKey(this.o.run, req.taskId, this.sends(req.taskId)),
897
+ // A held key keeps its entry, and restored here it replaced the
898
+ // forced run's fresh bytes with the ones it bypassed (2026-10-25).
899
+ ...(req.refresh === true ? { fresh: true as const } : {}),
900
+ // A HEAD per upload was the workers' only remote probe: the run
901
+ // already knows the key is in no cache (2026-10-25).
902
+ ...(req.refresh !== true && this.missed.has(req.taskId) ? { absent: true as const } : {}),
903
+ deps,
904
+ nested: node === undefined ? [] : nestedIn(nodes!, node.projectDir, this.o.root),
905
+ command: withForwardArgs(req.command, req.forwardArgs),
906
+ cwd: path.relative(this.o.root, req.cwd),
907
+ project: node === undefined ? '' : path.relative(this.o.root, node.projectDir),
908
+ env,
909
+ ...(node?.config.exec?.env?.secret === undefined
910
+ ? {}
911
+ : { secret: [...node.config.exec.env.secret] }),
912
+ ...(req.timeoutMs === undefined ? {} : { timeoutMs: req.timeoutMs }),
913
+ outputs: {
914
+ files: [...req.outputs.files],
915
+ workspaceFiles: [...req.outputs.workspaceFiles],
916
+ },
917
+ cores: job.held?.cores ?? 1,
918
+ memory: job.held?.memory ?? 0,
919
+ ...(req.sandbox === undefined || node?.config.exec?.sandbox === undefined
920
+ ? {}
921
+ : {
922
+ sandbox: {
923
+ config: node.config.exec.sandbox,
924
+ ...(req.sandbox.keyed === undefined
925
+ ? {}
926
+ : { keyed: req.sandbox.keyed.map((d) => path.relative(this.o.root, d)) }),
927
+ },
928
+ }),
929
+ ...runtimeOrders(req),
930
+ ...(req.servers === undefined || req.servers.length === 0
931
+ ? {}
932
+ : {
933
+ servers: req.servers.map((s) => ({
934
+ task: s.taskId,
935
+ command: s.command,
936
+ cwd: path.relative(this.o.root, s.cwd),
937
+ env: defined(s.env, {}),
938
+ ...(s.readyWhen === undefined ? {} : { readyWhen: s.readyWhen }),
939
+ ...(s.timeoutMs === undefined ? {} : { timeoutMs: s.timeoutMs }),
940
+ ...(s.sandbox === undefined
941
+ ? {}
942
+ : {
943
+ sandbox: {
944
+ config: s.sandbox.config,
945
+ nested: s.sandbox.nested.map((d) => path.relative(this.o.root, d)),
946
+ },
947
+ }),
948
+ })),
949
+ }),
950
+ } as RunOrder,
951
+ }
952
+ })
953
+ const sandboxed = orders.some(
954
+ (o) => o.order.sandbox !== undefined || o.order.servers?.some((s) => s.sandbox !== undefined),
955
+ )
956
+ const sandboxes = sandboxed && !a.sandboxes ? sandboxConfigs(nodes) : undefined
957
+ if (sandboxed) a.sandboxes = true
958
+ const pending = new Set(jobs)
959
+ a.chain = a.chain
960
+ .then(async () => {
961
+ for (const [i, part] of parts(orders, BATCH_BYTES).entries()) {
962
+ // An order too long for a frame (a task of thousands of
963
+ // dependencies was refused, 413) sends its deps ahead.
964
+ const tasks: RunOrder[] = []
965
+ for (const { order } of part) {
966
+ if (bytesOf(order) <= BATCH_BYTES) {
967
+ tasks.push(order)
968
+ continue
969
+ }
970
+ for (const deps of chunks(order.deps, BATCH_BYTES)) {
971
+ const ahead = { type: 'deps', task: order.task, deps } satisfies ToWorker
972
+ await this.o.me.send(a.id, ahead)
973
+ a.usage.messagesIn++
974
+ a.usage.bytesIn += bytesOf(ahead)
975
+ }
976
+ tasks.push({ ...order, deps: [] })
977
+ }
978
+ const batch = {
979
+ type: 'batch',
980
+ root: this.o.root,
981
+ realRoot: this.realRoot,
982
+ tasks,
983
+ ...(sandboxes === undefined || i > 0 ? {} : { sandboxes }),
984
+ } satisfies ToWorker
985
+ await this.o.me.send(a.id, batch)
986
+ a.usage.messagesIn++
987
+ a.usage.bytesIn += bytesOf(batch)
988
+ for (const { job } of part) {
989
+ this.lease(job)
990
+ pending.delete(job)
991
+ }
992
+ }
993
+ })
994
+ .catch((err: unknown) => {
995
+ // The relay refused the send (the worker is gone): the loss notice
996
+ // puts the tasks back.
997
+ if (err instanceof RelayError && err.status === 404) return
998
+ // One its worker never got leaves its booking there: held with no
999
+ // lease to end it, the worker's cores were gone for the run. One
1000
+ // sent before runs there, and ends as any; one its worker's loss
1001
+ // put back is the queue's.
1002
+ for (const job of pending)
1003
+ if (a.busy.get(job.req.taskId) === job && this.retry(a, job, 'not sent') !== undefined)
1004
+ job.reject(err)
1005
+ this.pumpSoon()
1006
+ })
1007
+ }
1008
+
1009
+ /** Three of the worker's `held` from now. */
1010
+ private lease(job: Job): void {
1011
+ job.leaseUntil = performance.now() + 3 * HELD_EVERY * this.o.me.heartbeatMs
1012
+ }
1013
+
1014
+ /**
1015
+ * Worker `a` let `job` down: lost, its lease ended, or it could not run
1016
+ * it. One it had begun counts, and two workers fail it; one still in its
1017
+ * queue goes back free.
1018
+ */
1019
+ private retry(a: Worker, job: Job, why: string): Job | undefined {
1020
+ const now = performance.now()
1021
+ this.trace.slice({
1022
+ track: a.label,
1023
+ name: job.req.taskId,
1024
+ cat: 'task',
1025
+ from: job.sentAt ?? now,
1026
+ to: now,
1027
+ args: { failed: why },
1028
+ })
1029
+ job.queuedAt = now
1030
+ a.busy.delete(job.req.taskId)
1031
+ // Its outputs are not there to place a dependant by.
1032
+ a.has.delete(job.req.taskId)
1033
+ this.unhold(a, job)
1034
+ delete job.worker
1035
+ delete job.leaseUntil
1036
+ delete job.sentAt
1037
+ delete job.ranAt
1038
+ const begun = a.started.delete(job.req.taskId)
1039
+ // Stopped, it runs nowhere again; the cancel sent its worker has no one to answer it.
1040
+ if (job.req.signal?.aborted === true) {
1041
+ job.resolve(stopped())
1042
+ return undefined
1043
+ }
1044
+ if (begun) {
1045
+ a.faulty = true
1046
+ job.faults = [...(job.faults ?? []), `${a.label} ${why}`]
1047
+ if (job.faults.length >= 2) {
1048
+ job.reject(new Error(`${job.req.taskId}: failed on 2 workers: ${job.faults.join(', ')}`))
1049
+ return undefined
1050
+ }
1051
+ }
1052
+ return job
1053
+ }
1054
+
1055
+ /**
1056
+ * Nothing began or ended for `STALL_MS`: each task a worker holds, and
1057
+ * whether it runs or saves. Main 32f73ed sat 13 min saying nothing
1058
+ * (2026-10-23).
1059
+ */
1060
+ private stall(): void {
1061
+ const now = performance.now()
1062
+ if (now - this.movedAt < (this.o.stallMs ?? STALL_MS)) return
1063
+ const lines: string[] = []
1064
+ for (const a of this.workers.values())
1065
+ for (const j of a.busy.values()) {
1066
+ const s = (t: number | undefined) => `${Math.round((now - (t ?? now)) / 1000)} s`
1067
+ lines.push(
1068
+ j.ranAt !== undefined
1069
+ ? `${j.req.taskId} on ${a.label}: saving its outputs for ${s(j.ranAt)}`
1070
+ : a.started.has(j.req.taskId)
1071
+ ? `${j.req.taskId} on ${a.label}: running for ${s(j.startedAt)}`
1072
+ : `${j.req.taskId} sent to ${a.label} ${s(j.sentAt)} ago, not begun`,
1073
+ )
1074
+ }
1075
+ if (lines.length === 0) return
1076
+ this.movedAt = now
1077
+ this.trace.mark(ORCHESTRATOR, 'stalled', { waiting: lines })
1078
+ this.o.stalled?.(lines.join('\n'))
1079
+ }
1080
+
1081
+ /** Each task whose worker's lease on it ended goes back. */
1082
+ private expire(): void {
1083
+ // With no socket no `held` arrives: the leases wait for it (`resumed`).
1084
+ if (!this.o.me.online) return
1085
+ const now = performance.now()
1086
+ const back: Job[] = []
1087
+ for (const a of this.workers.values())
1088
+ for (const j of [...a.busy.values()])
1089
+ if (j.leaseUntil !== undefined && j.leaseUntil < now) {
1090
+ const again = this.retry(a, j, 'lease expired')
1091
+ if (again !== undefined) back.push(again)
1092
+ }
1093
+ if (back.length === 0) return
1094
+ this.queue.unshift(...back)
1095
+ this.o.faulted?.()
1096
+ if (this.spawnedAt.size > 0) this.stranded(`a task's lease ended and no worker is left`)
1097
+ this.pumpSoon()
1098
+ }
1099
+
1100
+ private post(a: Worker, m: ToWorker): void {
1101
+ a.chain = a.chain.then(() => this.o.me.send(a.id, m)).catch(this.unsent(m.type, a.label))
1102
+ }
1103
+
1104
+ private unsent(what: string, to: string): (err: unknown) => void {
1105
+ return unsent(this.warn, what, to)
1106
+ }
1107
+
1108
+ private readonly warn = (m: string): void =>
1109
+ (this.o.warn ?? ((l) => ui.say('warn', l)))(`[vx] ${m}`)
1110
+
1111
+ /** Worker `a` began `task`: from now its cores are booked. */
1112
+ private begun(a: Worker, task: string): void {
1113
+ const job = a.busy.get(task)
1114
+ if (job === undefined || a.started.has(task)) return
1115
+ a.started.add(task)
1116
+ job.startedAt = performance.now()
1117
+ a.usage.book(job.held?.cores ?? 0)
1118
+ }
1119
+
1120
+ /** Worker `a` ran `task` and saved it under `key`: it is done there, and done here once restored. */
1121
+ private async finish(a: Worker, task: string, key: string, head: WiredResult): Promise<void> {
1122
+ const job = a.busy.get(task)
1123
+ if (job === undefined) return
1124
+ // A dependency's remote copy did not read back there (a truncated
1125
+ // upload): sent again from this machine, the task runs again. Taken as
1126
+ // the task's failure, it failed every run, the remote saying it held the
1127
+ // key (2026-10-25).
1128
+ // Ended there, it is in no `held` again: a lease running on through a
1129
+ // slow resend called its worker faulty and gave the task to another.
1130
+ delete job.leaseUntil
1131
+ if (head.unread !== undefined) {
1132
+ const sent = await this.resend(job, head.unread)
1133
+ // Its worker was lost meanwhile: the task went back to the queue.
1134
+ if (a.busy.get(task) !== job) return
1135
+ if (sent) {
1136
+ // Its cores back while it still counts as begun, which `unhold` reads;
1137
+ // then not begun, so `retry` holds no fault against its worker.
1138
+ this.unhold(a, job)
1139
+ a.started.delete(task)
1140
+ const again = this.retry(a, job, `could not read ${head.unread.join(', ')}`)
1141
+ if (again !== undefined) {
1142
+ this.queue.unshift(again)
1143
+ this.pumpSoon()
1144
+ }
1145
+ return
1146
+ }
1147
+ }
1148
+ const endAt = performance.now()
1149
+ this.traceTask(a, job, head, endAt)
1150
+ a.busy.delete(task)
1151
+ this.unhold(a, job)
1152
+ a.started.delete(task)
1153
+ a.ran++
1154
+ a.done.push(task)
1155
+ a.usage.record(head, job.sentAt)
1156
+ const share = shareOf(head.cpuShare)
1157
+ const held = Math.max(1, job.startedAt === undefined ? head.durationMs : endAt - job.startedAt)
1158
+ this.took.set(task, {
1159
+ ms: held,
1160
+ cores:
1161
+ head.cpuMs === undefined
1162
+ ? (job.held?.cores ?? 1)
1163
+ : // The cores it used over the time it held: its free cores over
1164
+ // its crowded length counted the crowd twice (6a7224f: ideal
1165
+ // 127.8 s, real 111.0 s).
1166
+ Math.min(a.cores, head.cpuMs / held),
1167
+ memory: head.peakRssBytes ?? job.held?.memory ?? 0,
1168
+ platform: a.platform,
1169
+ })
1170
+ if (
1171
+ job.need.cores !== Infinity &&
1172
+ job.need.ask === undefined &&
1173
+ head.cpuMs !== undefined &&
1174
+ head.exitCode === 0
1175
+ ) {
1176
+ const ms = Math.max(1, head.durationMs)
1177
+ // What it would have used with a core free: on a crowded worker its
1178
+ // share only, and booking the next tasks by that packed them tighter.
1179
+ this.usedMs += Math.min(head.cpuMs / share, a.cores * ms)
1180
+ this.needMs += Math.min(job.need.cores, a.cores) * ms
1181
+ }
1182
+ this.savedAs.set(task, key)
1183
+ const node = this.o.nodes()?.get(task)
1184
+ // No remote took its upload (too big, a 429): its files are there
1185
+ // alone, as an uncached task's are, and its output came with its `end`.
1186
+ const unshared = head.unshared === true
1187
+ if (head.exitCode === 0 && (unshared || (node !== undefined && leaves(node))))
1188
+ this.kept.set(task, { worker: a.id, req: job.req, need: job.need })
1189
+ this.pumpSoon()
1190
+ if (job.again !== undefined) return job.resolve(result(head, []))
1191
+ const restore = () =>
1192
+ this.o.artifacts.restore(key, {
1193
+ id: task,
1194
+ projectDir: node?.projectDir ?? job.req.cwd,
1195
+ command: job.req.command,
1196
+ outputs: job.req.outputs,
1197
+ })
1198
+ // A remote-only task's files stay off this disk: an install's
1199
+ // node_modules laid over this machine's own (schema: `'only'`). A
1200
+ // deferred one's land only if a task here needs them: under
1201
+ // `--cache-download=none` every file of the run came home (2026-10-24).
1202
+ const deferred = job.req.remoteOnly !== true && job.req.download === 'deferred'
1203
+ const read = () =>
1204
+ job.req.remoteOnly === true || deferred ? this.o.artifacts.output(key) : restore()
1205
+ const log = await (
1206
+ unshared
1207
+ ? Promise.resolve(wiredLog(head.output))
1208
+ : // A failed download reads as a miss: asked once more, since one
1209
+ // stalled download failed a CI run whose upload had landed
1210
+ // (main, 2026-10-28).
1211
+ read().then((got) => got ?? read())
1212
+ ).catch((err: unknown) => {
1213
+ job.reject(err)
1214
+ return undefined
1215
+ })
1216
+ this.trace.slice({
1217
+ track: ORCHESTRATOR,
1218
+ name: task,
1219
+ cat: 'restore',
1220
+ from: endAt,
1221
+ to: performance.now(),
1222
+ })
1223
+ if (log === undefined) return
1224
+ if (log === null) {
1225
+ job.reject(
1226
+ new Error(
1227
+ `${task}: ${a.label} ran it, and no cache this machine reads gave its entry, asked twice: a download failed (warned above), ${a.label}'s upload failed (its log says why), or the workers and this machine share no remote cache the workers write`,
1228
+ ),
1229
+ )
1230
+ return
1231
+ }
1232
+ // An upstream's file the task removed there is gone here too: a restore
1233
+ // lays files, never removes one, and this disk is what core saves from.
1234
+ if (head.removed !== undefined && job.req.remoteOnly !== true && !deferred && !unshared) {
1235
+ const ours = names(
1236
+ path.relative(this.o.root, node?.projectDir ?? job.req.cwd),
1237
+ job.req.outputs,
1238
+ )
1239
+ // A link on the path (`dist/in -> ../../other`) would lead the rm
1240
+ // into another project or off the workspace: the path is removed only
1241
+ // where it is, as a restore lays a file only there (archive.ts).
1242
+ const real = new Map<string, Promise<string | undefined>>()
1243
+ for (const f of head.removed) {
1244
+ if (typeof f !== 'string' || f.split('/').includes('..') || !ours(f)) continue
1245
+ const dir = path.dirname(f)
1246
+ let at = real.get(dir)
1247
+ if (at === undefined)
1248
+ real.set(
1249
+ dir,
1250
+ (at = realpath(path.join(this.o.root, dir)).catch(
1251
+ // quiet: a dir that is gone holds nothing to remove
1252
+ () => undefined,
1253
+ )),
1254
+ )
1255
+ const where = await at
1256
+ if (where === undefined) continue
1257
+ if (where === path.join(this.realRoot, dir))
1258
+ await rm(path.join(this.o.root, f), { force: true })
1259
+ else
1260
+ this.warn(
1261
+ `dte: ${task}: kept ${f}, which the worker removed: a link on its path leads elsewhere`,
1262
+ )
1263
+ }
1264
+ }
1265
+ // In the order the task wrote it: replayed a stream at a time, an error
1266
+ // line came after the stdout that followed it (2026-10-25).
1267
+ for (const c of log) (c.err ? job.req.onStderr : job.req.onStdout)(c.text)
1268
+ job.resolve({
1269
+ ...result(head, log),
1270
+ ...(unshared
1271
+ ? {
1272
+ outputs: {
1273
+ kind: 'deferred' as const,
1274
+ materialize: () =>
1275
+ Promise.reject(
1276
+ new UserError(
1277
+ `${task}: its files are on ${a.label} alone: no remote cache took its upload (its log says why)`,
1278
+ ),
1279
+ ),
1280
+ },
1281
+ }
1282
+ : deferred
1283
+ ? {
1284
+ outputs: {
1285
+ kind: 'deferred' as const,
1286
+ materialize: async () => {
1287
+ if ((await restore()) === null)
1288
+ throw new Error(`${task}'s entry ${key} is in no cache this machine reads`)
1289
+ },
1290
+ },
1291
+ }
1292
+ : {}),
1293
+ where: a.label,
1294
+ machine: {
1295
+ cores: a.reported.cores,
1296
+ memoryBytes: a.reported.memory,
1297
+ ...(share < 1 ? { cpuShare: share } : {}),
1298
+ },
1299
+ })
1300
+ }
1301
+
1302
+ /**
1303
+ * Task `job` ended on `a` at `endAt`: one slice from its send, in its
1304
+ * parts (sent, restoring its dependencies, its command, saving), and
1305
+ * the cores it booked there and used.
1306
+ */
1307
+ private traceTask(a: Worker, job: Job, head: WiredResult, endAt: number): void {
1308
+ const sent = job.sentAt ?? endAt
1309
+ const began = Math.max(sent, job.startedAt ?? sent)
1310
+ const ran = Math.min(endAt, Math.max(began, job.ranAt ?? endAt))
1311
+ const command = Math.max(began, ran - Math.max(0, head.durationMs))
1312
+ this.trace.slice({
1313
+ track: a.label,
1314
+ name: job.req.taskId,
1315
+ cat: 'task',
1316
+ from: sent,
1317
+ to: endAt,
1318
+ args: {
1319
+ exitCode: head.exitCode,
1320
+ durationMs: Math.round(head.durationMs),
1321
+ ...(head.cpuMs === undefined ? {} : { cpuMs: Math.round(head.cpuMs) }),
1322
+ ...(head.peakRssBytes === undefined ? {} : { peakRssBytes: head.peakRssBytes }),
1323
+ bookedCores: job.booked ?? 0,
1324
+ ...(head.cpuShare === undefined ? {} : { cpuShare: head.cpuShare }),
1325
+ },
1326
+ parts: [
1327
+ { name: 'sent', from: sent, to: began },
1328
+ { name: 'restore deps', from: began, to: command },
1329
+ { name: 'command', from: command, to: ran },
1330
+ { name: 'save', from: ran, to: endAt },
1331
+ ],
1332
+ })
1333
+ this.trace.use({ track: a.label, from: began, to: ran, booked: job.booked ?? 0, used: 0 })
1334
+ if (head.cpuMs !== undefined && ran > command)
1335
+ this.trace.use({
1336
+ track: a.label,
1337
+ from: command,
1338
+ to: ran,
1339
+ booked: 0,
1340
+ used: head.cpuMs / Math.max(1, head.durationMs),
1341
+ })
1342
+ }
1343
+
1344
+ private onRelay(body: { type?: string; id?: string; info?: unknown }): void {
1345
+ if (body.type === 'joined') {
1346
+ const info = body.info as Partial<WorkerInfo> | null
1347
+ const id = body.id!
1348
+ const refuse = (reason: string): void => {
1349
+ void this.o.me
1350
+ .send(id, { type: 'refused', reason } satisfies ToWorker)
1351
+ .catch(this.unsent('refused', id))
1352
+ }
1353
+ if (
1354
+ info?.v !== 1 ||
1355
+ !(info.id === undefined || typeof info.id === 'string') ||
1356
+ !(Number(info.cores) >= 1) ||
1357
+ !(Number(info.memory) >= 0)
1358
+ ) {
1359
+ return refuse('speaks another vx-dte protocol')
1360
+ }
1361
+ if (info.tree !== this.o.tree) {
1362
+ return refuse(`checked out tree ${info.tree}, the run is on ${this.o.tree}`)
1363
+ }
1364
+ const platform = typeof info.platform === 'string' ? info.platform : undefined
1365
+ const sized = info.id === undefined ? undefined : this.spawnedAt.get(info.id)?.declared
1366
+ if (sized?.platform !== undefined && platform !== sized.platform) {
1367
+ return refuse(
1368
+ `runs on ${platform ?? 'no platform it names'}, and its size ${sized.size} says ${sized.platform}`,
1369
+ )
1370
+ }
1371
+ // One given up only for joining late is work the run can use: main
1372
+ // db1702f told three smalls GitHub queued 60 s to exit and ended at
1373
+ // 232 s against an ideal of 141 s. The plan lets it go if not.
1374
+ if (info.id !== undefined && this.late.delete(info.id)) {
1375
+ this.cancelled.delete(info.id)
1376
+ this.unstarted.delete(info.id)
1377
+ }
1378
+ if (info.id !== undefined && this.cancelled.has(info.id)) {
1379
+ this.turnedAway.add(info.id)
1380
+ void this.o.me
1381
+ .send(id, { type: 'exit' } satisfies ToWorker)
1382
+ .catch(this.unsent('exit', info.id))
1383
+ return
1384
+ }
1385
+ // A worker this run started keeps the name it was started under;
1386
+ // one that joined on its own is numbered in the order it joined.
1387
+ const spawn = info.id === undefined ? undefined : this.spawnedAt.get(info.id)
1388
+ const asked = spawn?.at
1389
+ const taken = new Set([...this.workers.values(), ...this.gone].map((a) => a.label))
1390
+ let label = asked === undefined ? undefined : info.id
1391
+ while (label === undefined || taken.has(label)) label = `worker#${++this.joined}`
1392
+ // Workers of one host (local()) each report the host: the size it
1393
+ // was started as is its share (Growth, 2026-10-21: 16 wide on 4 cores).
1394
+ const cores = Math.min(Number(info.cores), spawn?.declared.cpu ?? Infinity)
1395
+ const memory = Math.min(Number(info.memory), spawn?.declared.memory ?? Infinity)
1396
+ const warmUpMs = asked === undefined ? undefined : performance.now() - asked
1397
+ this.trace.joined(
1398
+ label,
1399
+ performance.now(),
1400
+ asked,
1401
+ typeof info.booted === 'number' ? info.booted : undefined,
1402
+ )
1403
+ const size = spawn?.declared.size
1404
+ if (warmUpMs !== undefined && size !== undefined && !this.joinLimitMs.has(size)) {
1405
+ this.joinLimitMs.set(size, 2 * warmUpMs)
1406
+ for (const [s, { at, declared }] of this.spawnedAt)
1407
+ if (s !== info.id && declared.size === size) this.deadline(s, at, size)
1408
+ }
1409
+ this.workers.set(id, {
1410
+ id,
1411
+ warmUpMs,
1412
+ declared: spawn?.declared,
1413
+ ran: 0,
1414
+ done: [],
1415
+ usage: new Usage(),
1416
+ cores,
1417
+ memory,
1418
+ reported: { cores: Number(info.cores), memory: Number(info.memory) },
1419
+ platform,
1420
+ usedCores: 0,
1421
+ usedMemory: 0,
1422
+ label,
1423
+ busy: new Map(),
1424
+ started: new Set(),
1425
+ has: new Set(),
1426
+ // Its name first, before any batch.
1427
+ chain: this.cache()
1428
+ .then((cache) =>
1429
+ this.o.me.send(id, {
1430
+ type: 'welcome',
1431
+ name: label,
1432
+ cache,
1433
+ } satisfies ToWorker),
1434
+ )
1435
+ .catch(this.unsent('welcome', label)),
1436
+ sandboxes: false,
1437
+ faulty: false,
1438
+ })
1439
+ this.o.joined?.()
1440
+ } else if (body.type === 'lost') {
1441
+ const a = this.workers.get(body.id!)
1442
+ if (a === undefined) return
1443
+ this.lose(a)
1444
+ } else {
1445
+ return
1446
+ }
1447
+ this.pumpSoon()
1448
+ }
1449
+
1450
+ /** Worker `a` is gone without a word: its tasks run again elsewhere. */
1451
+ private lose(a: Worker): void {
1452
+ this.workers.delete(a.id)
1453
+ a.usage.to = performance.now()
1454
+ this.trace.left(a.label, 'lost', a.usage.to)
1455
+ this.gone.push(a)
1456
+ // Its tasks run again: first, as they were first to go.
1457
+ const back: Job[] = []
1458
+ for (const j of [...a.busy.values()]) {
1459
+ const again = this.retry(a, j, 'lost')
1460
+ if (again !== undefined) back.push(again)
1461
+ }
1462
+ this.queue.unshift(...back)
1463
+ this.o.lost?.(a.label)
1464
+ // Workers that join on their own may yet join; ones this run starts
1465
+ // are all it gets.
1466
+ if (this.spawnedAt.size > 0) this.stranded(`${a.label} was lost and no worker is left`)
1467
+ }
1468
+
1469
+ private async listen(): Promise<void> {
1470
+ while (!this.stop.signal.aborted) {
1471
+ let messages
1472
+ try {
1473
+ // The client asks again through faults shorter than 10 s.
1474
+ messages = await this.o.me.poll(this.stop.signal)
1475
+ } catch (err) {
1476
+ if (this.stop.signal.aborted) return
1477
+ // A relay unreachable longer has lost this run. Its tasks fail, not
1478
+ // run here: this machine may be far smaller than the workers. A rerun
1479
+ // resumes from what finished and saved.
1480
+ this.fail(`the relay is unreachable (${(err as Error).message})`)
1481
+ return
1482
+ }
1483
+ for (const { from, body } of messages) {
1484
+ if (from === 'relay') {
1485
+ this.onRelay(body as { type?: string })
1486
+ continue
1487
+ }
1488
+ const a = this.workers.get(from)
1489
+ if (a === undefined) continue
1490
+ const m = body as ToOrchestrator
1491
+ if ('load' in m) this.heard(a, m.load)
1492
+ if (m.type !== 'held') this.movedAt = performance.now()
1493
+ if (m.type === 'start') {
1494
+ this.begun(a, m.task)
1495
+ this.pumpSoon()
1496
+ } else if (m.type === 'held') {
1497
+ for (const t of m.running) {
1498
+ const j = a.busy.get(t)
1499
+ if (j?.leaseUntil !== undefined) this.lease(j)
1500
+ this.begun(a, t)
1501
+ }
1502
+ this.pumpSoon()
1503
+ } else if (m.type === 'ran') {
1504
+ // Its command ended: its cores are the next task's while it saves.
1505
+ const j = a.busy.get(m.task)
1506
+ if (j === undefined) continue
1507
+ this.begun(a, m.task)
1508
+ this.unhold(a, j)
1509
+ // Its worker still says it holds it while it saves.
1510
+ j.ranAt = performance.now()
1511
+ this.pumpSoon()
1512
+ } else if (m.type === 'fault') {
1513
+ const j = a.busy.get(m.task)
1514
+ if (j === undefined) continue
1515
+ // It began it whether or not its `start` came first.
1516
+ this.begun(a, m.task)
1517
+ if (this.retry(a, j, m.reason) !== undefined) this.queue.unshift(j)
1518
+ if (a.faulty) this.o.faulted?.()
1519
+ if (a.faulty && this.spawnedAt.size > 0)
1520
+ this.stranded(`${a.label} could not run ${m.task} (${m.reason}) and no worker is left`)
1521
+ this.pumpSoon()
1522
+ } else if (m.type === 'part') {
1523
+ a.usage.messagesOut++
1524
+ a.usage.bytesOut += bytesOf(m)
1525
+ const k = `${from}\0${m.task}`
1526
+ const held = this.parts.get(k) ?? {}
1527
+ for (const list of PART_LISTS) {
1528
+ const items = m[list] as readonly unknown[] | undefined
1529
+ if (items !== undefined) held[list] = [...(held[list] ?? []), ...items]
1530
+ }
1531
+ this.parts.set(k, held)
1532
+ } else if (m.type === 'end') {
1533
+ a.usage.messagesOut++
1534
+ a.usage.bytesOut += bytesOf(m)
1535
+ const k = `${from}\0${m.task}`
1536
+ const held = this.parts.get(k)
1537
+ this.parts.delete(k)
1538
+ const result: Record<string, unknown> = { ...m.result }
1539
+ for (const list of PART_LISTS) {
1540
+ const items = m.result[list] as readonly unknown[] | undefined
1541
+ if (held?.[list] !== undefined) result[list] = [...held[list], ...(items ?? [])]
1542
+ }
1543
+ // Not awaited: one task's restore holds no other message.
1544
+ void this.finish(a, m.task, m.key, result as WiredResult)
1545
+ }
1546
+ }
1547
+ // After the batch: a `held` in it renews its lease first. Expired
1548
+ // before, a stalled loop's waiting `held` came too late and the task
1549
+ // ran again on another worker (2026-10-27).
1550
+ this.expire()
1551
+ this.stall()
1552
+ }
1553
+ }
1554
+
1555
+ /** The worker running a task; `null` while it waits, here or in a worker's queue, for one to begin it. */
1556
+ where(taskId: string): string | null | undefined {
1557
+ for (const a of this.workers.values())
1558
+ if (a.busy.has(taskId)) return a.started.has(taskId) ? a.label : null
1559
+ return this.queue.some((j) => j.req.taskId === taskId) ? null : undefined
1560
+ }
1561
+
1562
+ /**
1563
+ * Core's cache probe answered: `left` placed tasks no cache holds or none
1564
+ * answered for, `misses` of them found in none. Said in the footer: main
1565
+ * d0cdc2c asked 15 workers for 119 tasks, 91 restored, and no line said
1566
+ * what the probe had answered (2026-10-23).
1567
+ */
1568
+ probed(left: number, misses: number): void {
1569
+ if (this.probe !== undefined) return
1570
+ this.probe = { at: performance.now() - this.startedAt, left, misses }
1571
+ this.trace.mark(ORCHESTRATOR, 'cache probe', { remaining: left, misses })
1572
+ }
1573
+
1574
+ /** The run asks for `workers` more for `tasks` it knows miss, its plan ending in `endsInMs`. */
1575
+ asked(
1576
+ workers: number,
1577
+ tasks: number,
1578
+ measured: number,
1579
+ endsInMs: number,
1580
+ unmeasured: readonly string[] = [],
1581
+ ): void {
1582
+ if (this.asks.length === 0) this.unmeasured = unmeasured
1583
+ this.asks.push({ at: performance.now() - this.startedAt, workers, tasks, measured, endsInMs })
1584
+ this.trace.mark(ORCHESTRATOR, `ask ${workers} worker${workers === 1 ? '' : 's'}`, {
1585
+ tasks,
1586
+ measured,
1587
+ planEndsInMs: Math.round(endsInMs),
1588
+ })
1589
+ }
1590
+
1591
+ /**
1592
+ * A plan that started or let go of workers, in the profile: what it saw
1593
+ * of each worker (cores busy / ms left per task, or when it joins) and
1594
+ * of the work, so a worker asked and cancelled a second later can be
1595
+ * read back (PR #4214's run: 7, 2026-10-23).
1596
+ */
1597
+ planned(p: {
1598
+ start: readonly string[]
1599
+ release: readonly string[]
1600
+ tasks: number
1601
+ endsInMs: number
1602
+ boundMs: number
1603
+ workers: readonly PlanWorker[]
1604
+ /**
1605
+ * A plan that starts workers: the work it played (seconds, cores,
1606
+ * platform) and each size's warm-up, so it can be played back. Main
1607
+ * d8a795c planned 38 s with one Mac for a Mac lane of 88 s, and
1608
+ * nothing in the profile said what it thought each task took.
1609
+ */
1610
+ work?: readonly PlanTask[]
1611
+ warmUps?: ReadonlyMap<string, number>
1612
+ }): void {
1613
+ const work = p.work?.map(
1614
+ (t) =>
1615
+ `${t.id} ${t.ms === undefined ? '?' : Math.round(t.ms / 100) / 10}s ${Math.round(t.cores * 100) / 100}c${t.platform === undefined ? '' : ` ${t.platform}`}`,
1616
+ )
1617
+ const workers = p.workers.map((w) =>
1618
+ w.starting === true
1619
+ ? `${w.id} joins ${Math.round(w.at)}`
1620
+ : `${w.id} ${w.running.map((r) => `${Math.round(r.cores * 10) / 10}c/${Math.round(r.endsIn)}`).join(' ') || 'idle'}`,
1621
+ )
1622
+ this.trace.mark(ORCHESTRATOR, 'plan', {
1623
+ start: p.start.join(' '),
1624
+ release: p.release.join(' '),
1625
+ tasks: p.tasks,
1626
+ endsInMs: Math.round(p.endsInMs),
1627
+ boundMs: Math.round(p.boundMs),
1628
+ workers: workers.join(' · '),
1629
+ ...(work === undefined ? {} : { work: work.join(' · ') }),
1630
+ ...(p.warmUps === undefined
1631
+ ? {}
1632
+ : {
1633
+ warmUps: [...p.warmUps]
1634
+ .map(([s, ms]) => `${s} ${Math.round(ms / 100) / 10}s`)
1635
+ .join(' · '),
1636
+ }),
1637
+ })
1638
+ }
1639
+
1640
+ /** `demand` asked the adapter for worker `id` (`WorkerSpawn.id`). */
1641
+ spawned(id: string, declared: Declared): void {
1642
+ // A worker whose start broke where no adapter sees it counted as
1643
+ // starting for good: main's run sat 18 min (ee3fc6e, 2026-10-23). Its
1644
+ // job ending says so (`failed`); one that never ends is given up past
1645
+ // its size's join limit.
1646
+ const at = performance.now()
1647
+ this.spawnedAt.set(id, { at, declared })
1648
+ this.deadline(id, at, declared.size)
1649
+ }
1650
+
1651
+ /** Worker `id`, started at `at`, is given up once its size's join limit passes unjoined. */
1652
+ private deadline(id: string, at: number, size: string): void {
1653
+ const ms = this.joinLimitMs.get(size)
1654
+ if (ms === undefined) return
1655
+ setTimeout(
1656
+ () => {
1657
+ if (this.ended || this.cancelled.has(id) || this.unstarted.has(id)) return
1658
+ if ([...this.workers.values(), ...this.gone].some((a) => a.label === id)) return
1659
+ this.cancelled.add(id)
1660
+ this.late.add(id)
1661
+ this.o.overdue?.(id)
1662
+ this.failed(id, `did not join within ${Math.round(ms / 1000)} s`, true)
1663
+ },
1664
+ Math.max(0, at + ms - performance.now()),
1665
+ ).unref()
1666
+ }
1667
+
1668
+ /**
1669
+ * The shortest time a worker this run asked for took to join, once one
1670
+ * has: what a worker asked for now takes, at the least.
1671
+ */
1672
+ warmUpMs(): number | undefined {
1673
+ let least: number | undefined
1674
+ for (const a of [...this.workers.values(), ...this.gone])
1675
+ if (a.warmUpMs !== undefined && !(least! <= a.warmUpMs)) least = a.warmUpMs
1676
+ return least
1677
+ }
1678
+
1679
+ /** Each task a worker ran, how long it held the worker, by `forecast`'s ms (`slowdown`). */
1680
+ finished(
1681
+ forecast: (id: string) => number | undefined,
1682
+ ): { took: number; forecast: number | undefined }[] {
1683
+ return [...this.took].map(([id, t]) => ({ took: t.ms, forecast: forecast(id) }))
1684
+ }
1685
+
1686
+ /** `warmUpMs` of each size a worker this run started joined as. */
1687
+ warmUpsMs(): Map<string, number> {
1688
+ const out = new Map<string, number>()
1689
+ for (const a of [...this.workers.values(), ...this.gone]) {
1690
+ const size = a.declared?.size
1691
+ if (a.warmUpMs === undefined || size === undefined) continue
1692
+ if (!(out.get(size)! <= a.warmUpMs)) out.set(size, a.warmUpMs)
1693
+ }
1694
+ return out
1695
+ }
1696
+
1697
+ /**
1698
+ * The workers the plan places on (`planner.ts`): each online, what it
1699
+ * runs and how long that has left by `ms`, and each still starting,
1700
+ * joining after its size's `warmUpMs` from its ask. With them, the tasks they run.
1701
+ */
1702
+ planState(
1703
+ ms: (taskId: string) => number | undefined,
1704
+ warmUpMs: WarmUp,
1705
+ ): { workers: PlanWorker[]; running: Set<string> } {
1706
+ const now = performance.now()
1707
+ const running = new Set<string>()
1708
+ const workers: PlanWorker[] = []
1709
+ for (const a of this.workers.values()) {
1710
+ if (a.faulty) continue
1711
+ const jobs = [...a.busy.values()].filter((j) => j.startedAt !== undefined)
1712
+ for (const j of jobs) running.add(j.req.taskId)
1713
+ // Its tasks use more than their history said (a suite given more
1714
+ // workers): the plan holds them at what the load says, this run.
1715
+ const over = a.usedCores > 0 ? Math.max(1, this.used(a).cores / a.usedCores) : 1
1716
+ workers.push({
1717
+ id: a.id,
1718
+ cores: a.cores,
1719
+ memory: a.memory,
1720
+ platform: a.platform,
1721
+ ...(a.declared === undefined ? {} : { size: a.declared.size }),
1722
+ at: 0,
1723
+ idle: a.busy.size === 0,
1724
+ // An unmeasured one counts as it did waiting (`ask`), not as the
1725
+ // one core it holds: started, it freed a core the plan then found
1726
+ // spare, and the worker asked for it was cancelled (cold main
1727
+ // bcc84f1: 11 of 25, 2026-10-23).
1728
+ running: jobs.map((j) => ({
1729
+ id: j.req.taskId,
1730
+ cores: Math.min(a.cores, Math.max((j.held?.cores ?? 0) * over, j.need.ask?.cores ?? 0)),
1731
+ memory: Math.max(j.held?.memory ?? 0, Math.min(a.memory, j.need.ask?.memory ?? 0)),
1732
+ endsIn: Math.max(0, (ms(j.req.taskId) ?? 0) - (now - j.startedAt!)),
1733
+ })),
1734
+ })
1735
+ }
1736
+ const joined = new Set([...this.workers.values(), ...this.gone].map((a) => a.label))
1737
+ for (const [id, { at, declared }] of this.spawnedAt)
1738
+ if (!joined.has(id) && !this.unstarted.has(id) && !this.cancelled.has(id))
1739
+ workers.push({
1740
+ id,
1741
+ cores: declared.cpu,
1742
+ memory: declared.memory,
1743
+ // A size naming none is planned as this machine's, as `dte()`
1744
+ // planned it: on no platform, no task naming one fit it, and the
1745
+ // next plan cancelled it.
1746
+ platform: declared.platform ?? hostPlatform(),
1747
+ size: declared.size,
1748
+ at: Math.max(
1749
+ 0,
1750
+ (typeof warmUpMs === 'number' ? warmUpMs : warmUpMs(declared.size)) - (now - at),
1751
+ ),
1752
+ idle: false,
1753
+ running: [],
1754
+ // One its adapter could not stop joins whatever the plan says.
1755
+ ...(this.unstoppable.has(id) ? {} : { starting: true as const }),
1756
+ })
1757
+ return { workers, running }
1758
+ }
1759
+
1760
+ /**
1761
+ * Sizes the CI has not started a worker of past its warm-up, `warmUpMs`
1762
+ * from its ask: one given up as late counts while one of its size is
1763
+ * online. A worker asked now queues behind it: PR 2f0e2a4's run asked 7
1764
+ * more while GitHub queued its first 11 for 30-85 s; none got a runner
1765
+ * before its join limit cancelled it (2026-10-24).
1766
+ */
1767
+ queued(warmUpMs: (size: string) => number): Set<string> {
1768
+ const now = performance.now()
1769
+ const online = new Set([...this.workers.values()].map((a) => a.declared?.size))
1770
+ const joined = new Set([...this.workers.values(), ...this.gone].map((a) => a.label))
1771
+ const out = new Set<string>()
1772
+ for (const [id, { at, declared }] of this.spawnedAt) {
1773
+ if (joined.has(id)) continue
1774
+ if (
1775
+ this.late.has(id)
1776
+ ? online.has(declared.size)
1777
+ : !this.cancelled.has(id) && !this.unstarted.has(id) && now - at > warmUpMs(declared.size)
1778
+ )
1779
+ out.add(declared.size)
1780
+ }
1781
+ return out
1782
+ }
1783
+
1784
+ /**
1785
+ * Workers `ids` leave (`exit`) if still idle, and each faulty idle one:
1786
+ * none comes back. Of `ids`, each started and not joined is cancelled:
1787
+ * the ones returned, for the adapter to stop; one that joins anyway is
1788
+ * told to exit at once.
1789
+ */
1790
+ release(ids: Iterable<string>): string[] {
1791
+ for (const a of this.workers.values()) if (a.faulty && a.busy.size === 0) this.letGo(a)
1792
+ const joined = new Set([...this.workers.values(), ...this.gone].map((a) => a.label))
1793
+ const cancelled: string[] = []
1794
+ for (const id of ids) {
1795
+ const a = this.workers.get(id)
1796
+ if (a !== undefined) {
1797
+ if (a.busy.size === 0) this.letGo(a)
1798
+ } else if (
1799
+ this.spawnedAt.has(id) &&
1800
+ !joined.has(id) &&
1801
+ !this.unstarted.has(id) &&
1802
+ !this.unstoppable.has(id)
1803
+ ) {
1804
+ this.cancelled.add(id)
1805
+ cancelled.push(id)
1806
+ this.trace.unstarted(id, this.spawnedAt.get(id)!.at, 'cancelled: not needed')
1807
+ }
1808
+ }
1809
+ return cancelled
1810
+ }
1811
+
1812
+ /**
1813
+ * Of `ids`, let go before they joined, those its adapter could not stop
1814
+ * (`DteAdapter.cancel`): each counts as starting again and is taken when
1815
+ * it joins. The ones returned; one told to exit already is gone.
1816
+ */
1817
+ leftRunning(ids: Iterable<string>): string[] {
1818
+ const out: string[] = []
1819
+ for (const id of ids)
1820
+ if (this.cancelled.has(id) && !this.late.has(id) && !this.turnedAway.has(id)) {
1821
+ this.cancelled.delete(id)
1822
+ this.unstoppable.add(id)
1823
+ out.push(id)
1824
+ }
1825
+ return out
1826
+ }
1827
+
1828
+ private letGo(a: Worker): void {
1829
+ this.workers.delete(a.id)
1830
+ a.usage.to = performance.now()
1831
+ this.trace.left(a.label, 'left', a.usage.to)
1832
+ this.gone.push(a)
1833
+ this.retired.add(a)
1834
+ this.post(a, { type: 'exit' })
1835
+ }
1836
+
1837
+ /**
1838
+ * Worker `id` will not join: its start failed or it exited first. One that
1839
+ * joined is lost now, not when the relay stops hearing it (20 s, its tasks
1840
+ * waiting).
1841
+ */
1842
+ failed(id: string, reason: string, overdue = false): void {
1843
+ // Its job ended: one given up as late no longer joins after all.
1844
+ if (!overdue) this.late.delete(id)
1845
+ if (this.ended || (this.cancelled.has(id) && !overdue)) return
1846
+ const live = [...this.workers.values()].find((a) => a.label === id)
1847
+ if (live !== undefined) {
1848
+ this.lose(live)
1849
+ this.pumpSoon()
1850
+ return
1851
+ }
1852
+ // Gone already: its job ending says nothing new.
1853
+ if (this.gone.some((a) => a.label === id) || this.unstarted.has(id)) return
1854
+ this.unstarted.set(id, reason)
1855
+ const asked = this.spawnedAt.get(id)?.at
1856
+ if (asked !== undefined) this.trace.unstarted(id, asked, reason)
1857
+ // A checkout, an install or a join can fail on the network; a few more
1858
+ // workers get a go. Failing that often, the setup is broken: none more.
1859
+ if (this.restarts < RESTARTS) {
1860
+ this.restarts++
1861
+ this.o.lost?.(id)
1862
+ }
1863
+ this.stranded(`no worker could start: ${id} ${reason}`)
1864
+ }
1865
+
1866
+ /** Nothing online that takes work, none still starting: no task can run. */
1867
+ private stranded(reason: string): void {
1868
+ const joined = new Set([...this.workers.values(), ...this.gone].map((a) => a.label))
1869
+ const starting = [...this.spawnedAt.keys()].some(
1870
+ (s) => !this.unstarted.has(s) && !this.cancelled.has(s) && !joined.has(s),
1871
+ )
1872
+ const working = [...this.workers.values()].some((a) => !a.faulty)
1873
+ if (!working && !starting) this.fail(reason)
1874
+ }
1875
+
1876
+ /** Every task sent here and not done fails with `reason`; later ones too. */
1877
+ private fail(reason: string): void {
1878
+ for (const a of this.workers.values())
1879
+ for (const j of a.busy.values()) j.reject(new Error(reason))
1880
+ for (const j of this.queue.splice(0)) j.reject(new Error(reason))
1881
+ this.workers.clear()
1882
+ this.lost = reason
1883
+ }
1884
+
1885
+ /**
1886
+ * `left` when the run let it go as not needed, `lost` when it crashed or
1887
+ * went silent, `stopped` when the run's end stopped it, else `online`.
1888
+ */
1889
+ private state(a: Worker, gone: ReadonlySet<Worker>): string {
1890
+ if (this.retired.has(a)) return 'left'
1891
+ if (gone.has(a)) return 'lost'
1892
+ return this.ended ? 'stopped' : 'online'
1893
+ }
1894
+
1895
+ /**
1896
+ * The run's end had it placed its tasks perfectly (2026-10-23: "how
1897
+ * close the run was"): each task a worker ran, as long as it held its
1898
+ * worker and with the cores it used meanwhile, played on as
1899
+ * many workers as the sizes allow, each joining after its platform's
1900
+ * fastest real warm-up, from when the run first knew its work. A task
1901
+ * waits on its deps through those no worker ran (groups, hits).
1902
+ */
1903
+ idealMs(depsOf: (id: string) => readonly string[], sizes: readonly Size[]): number | undefined {
1904
+ if (this.took.size === 0) return undefined
1905
+ const all = [...this.workers.values(), ...this.gone]
1906
+ const warm = new Map<string | undefined, number>()
1907
+ for (const a of all)
1908
+ if (a.warmUpMs !== undefined && !(warm.get(a.platform)! <= a.warmUpMs))
1909
+ warm.set(a.platform, a.warmUpMs)
1910
+ const least = this.warmUpMs() ?? 0
1911
+ const pool: PlanWorker[] = (
1912
+ sizes.length > 0
1913
+ ? sizes.flatMap((s) =>
1914
+ Array.from({ length: Math.min(s.max, this.took.size) }, () => ({
1915
+ cores: s.cpu,
1916
+ memory: s.memory,
1917
+ platform: s.platform,
1918
+ })),
1919
+ )
1920
+ : all.map((a) => ({ cores: a.cores, memory: a.memory, platform: a.platform }))
1921
+ ).map((w, i) => ({
1922
+ ...w,
1923
+ id: `ideal#${i}`,
1924
+ at: warm.get(w.platform) ?? least,
1925
+ running: [],
1926
+ idle: false,
1927
+ }))
1928
+ const seen = new Map<string, string[]>()
1929
+ const ran = (id: string): string[] => {
1930
+ const known = seen.get(id)
1931
+ if (known !== undefined) return known
1932
+ seen.set(id, [])
1933
+ const out = depsOf(id).flatMap((d) => (this.took.has(d) ? [d] : ran(d)))
1934
+ seen.set(id, out)
1935
+ return out
1936
+ }
1937
+ const tasks: PlanTask[] = [...this.took].map(([id, t]) => ({ id, ...t, deps: ran(id) }))
1938
+ const end = simulate(tasks, pool)
1939
+ if (!Number.isFinite(end)) return undefined
1940
+ return (this.probe?.at ?? this.asks[0]?.at ?? 0) + end
1941
+ }
1942
+
1943
+ /**
1944
+ * Each machine that joined, and this one when it ran tasks, with what it
1945
+ * did: the run summary's `machines`. Core asks once its tasks are over,
1946
+ * before its teardown ends this run: then is its end, so one still here
1947
+ * was online until now (small#2 read online 1ms, booked 433802 of 4:
1948
+ * main, 2026-10-23). Each is `ok`, or `failed` when lost or faulty.
1949
+ */
1950
+ machines(): MachineReport[] {
1951
+ const gone = new Set(this.gone)
1952
+ const until = this.ended ? this.endedAt : performance.now()
1953
+ const report = (
1954
+ name: string,
1955
+ status: string,
1956
+ u: Usage,
1957
+ cores: number,
1958
+ memoryBytes: number,
1959
+ ): MachineReport => {
1960
+ const st = u.stats(cores, until)
1961
+ return {
1962
+ name,
1963
+ status,
1964
+ cores,
1965
+ memoryBytes,
1966
+ done: u.done,
1967
+ failed: u.failed,
1968
+ onlineMs: st.onlineMs,
1969
+ busyMs: st.busyMs,
1970
+ cpuPeak: st.cpuPeak,
1971
+ cpuAvg: st.cpuAvg,
1972
+ memoryPeakBytes: st.memoryPeak,
1973
+ memoryAvgBytes: st.memoryAvg,
1974
+ utilization: st.utilization,
1975
+ bookedAvg: st.bookedAvg,
1976
+ }
1977
+ }
1978
+ return [...this.workers.values(), ...this.gone].map((a) => ({
1979
+ // Whether it failed or ran its part, not why it left (2026-10-23).
1980
+ ...report(
1981
+ a.label,
1982
+ (gone.has(a) && !this.retired.has(a)) || a.faulty ? 'failed' : 'ok',
1983
+ a.usage,
1984
+ a.reported.cores,
1985
+ a.reported.memory,
1986
+ ),
1987
+ ...(a.warmUpMs === undefined ? {} : { warmUpMs: a.warmUpMs }),
1988
+ startMs: Math.max(0, a.usage.from - (a.warmUpMs ?? 0) - this.startedAt),
1989
+ }))
1990
+ }
1991
+
1992
+ /**
1993
+ * The `workers` rows, live and at the end: one per worker by `size#index`
1994
+ * with its status, the tasks it has done and the ones it runs now, then
1995
+ * this machine's; once over, each joined worker's warm-up and, under
1996
+ * each, the cores and memory its size declares and its machine reports;
1997
+ * and `ideal`, the run's `idealMs`, asked once it ended.
1998
+ */
1999
+ footer(ideal?: () => number | undefined): [string, ui.Inline][] {
2000
+ // Over with none, it says so: core's own `N workers` (this machine's
2001
+ // pool) read as the run's when no task needed one (2026-10-25).
2002
+ if (this.spawnedAt.size === 0 && this.workers.size === 0 && this.gone.length === 0)
2003
+ return this.ended ? [['workers', ui.dim('none started: no task needed one')]] : []
2004
+ const gone = new Set(this.gone)
2005
+ const all = [...this.workers.values(), ...this.gone]
2006
+ const joined = new Set(all.map((a) => a.label))
2007
+ const count = (u: Usage, running: number): string =>
2008
+ [
2009
+ `${u.done} done`,
2010
+ ...(u.failed > 0 ? [`${u.failed} failed`] : []),
2011
+ ...(u.aborted > 0 ? [`${u.aborted} aborted`] : []),
2012
+ ...(running > 0 ? [`${running} running`] : []),
2013
+ ].join(' · ')
2014
+ // Once over, under its row: what its size declares and what its machine
2015
+ // reports, green when they agree, red when not.
2016
+ const res = new Map<string, ui.Inline>()
2017
+ const resources = (label: string, cores: number, memory: number, d?: Declared): void => {
2018
+ const actual = `${cores}c/${compact(memory)}`
2019
+ res.set(
2020
+ label,
2021
+ d === undefined
2022
+ ? ui.dim(`${indent}actual ${actual}`)
2023
+ : [
2024
+ ui.dim(`${indent}declared ${d.cpu}c/${compact(d.memory)} · actual `),
2025
+ ui.tone(matches(cores, memory, d) ? 'ok' : 'error', actual),
2026
+ ],
2027
+ )
2028
+ }
2029
+ // Live, those that left are one line: a run that replaced many pushed
2030
+ // the rows that run past the screen.
2031
+ const left = this.ended ? [] : all.filter((a) => this.retired.has(a))
2032
+ const lines: [string, string, string][] = all
2033
+ .filter((a) => !left.includes(a))
2034
+ .map((a) => [
2035
+ a.label,
2036
+ this.state(a, gone),
2037
+ count(a.usage, gone.has(a) ? 0 : a.started.size) +
2038
+ (this.ended && a.warmUpMs !== undefined ? ` · warm-up ${seconds(a.warmUpMs)}` : '') +
2039
+ (this.ended && a.done.length > 0 && a.done.length <= FEW
2040
+ ? ` · ${a.done.join(', ')}`
2041
+ : ''),
2042
+ ])
2043
+ for (const id of this.spawnedAt.keys())
2044
+ if (!joined.has(id) && (!this.cancelled.has(id) || this.late.has(id))) {
2045
+ const why = this.unstarted.get(id)
2046
+ // Over, the run waits on none: one still starting then never joined.
2047
+ lines.push(
2048
+ why !== undefined
2049
+ ? [id, 'failed', why]
2050
+ : this.ended
2051
+ ? [id, 'not joined', 'the run ended first']
2052
+ : [id, 'starting', ''],
2053
+ )
2054
+ }
2055
+ lines.sort((x, y) => x[0].localeCompare(y[0], 'en', { numeric: true }))
2056
+ const cancelled = [...this.cancelled].filter(
2057
+ (id) => !joined.has(id) && !this.late.has(id),
2058
+ ).length
2059
+ if (cancelled > 0)
2060
+ lines.push([`${cancelled} worker${cancelled === 1 ? '' : 's'}`, 'cancelled', 'not needed'])
2061
+ if (left.length > 0)
2062
+ lines.push([
2063
+ `${left.length} worker${left.length === 1 ? '' : 's'}`,
2064
+ 'left',
2065
+ `${left.reduce((n, a) => n + a.usage.done, 0)} done`,
2066
+ ])
2067
+ const width = Math.max(...lines.map(([l]) => l.length))
2068
+ const indent = ' '.repeat(width + 2)
2069
+ const status = Math.max(...lines.map(([, st]) => st.length))
2070
+ // Once over, what each did, under its line.
2071
+ const usage = new Map<string, readonly string[]>()
2072
+ let cpuMs = 0
2073
+ let coreMs = 0
2074
+ if (this.ended)
2075
+ for (const a of all) {
2076
+ resources(a.label, a.reported.cores, a.reported.memory, a.declared)
2077
+ const st = a.usage.stats(a.cores, this.endedAt)
2078
+ cpuMs += st.cpuMs
2079
+ coreMs += a.cores * st.onlineMs
2080
+ usage.set(a.label, describe(st, a.usage))
2081
+ }
2082
+ return [
2083
+ ...lines.flatMap(([l, st, t], i): [string, ui.Inline][] => [
2084
+ [
2085
+ i === 0 ? 'workers' : '',
2086
+ st === 'failed'
2087
+ ? [ui.place(l), `${' '.repeat(width - l.length)} `, ui.tone('error', st), ` ${t}`]
2088
+ : t === ''
2089
+ ? [ui.place(l), `${' '.repeat(width - l.length)} ${st}`]
2090
+ : [ui.place(l), `${' '.repeat(width - l.length)} ${st.padEnd(status)} ${t}`],
2091
+ ],
2092
+ ...(res.has(l) ? [['', res.get(l)!] as [string, ui.Inline]] : []),
2093
+ ...(usage.get(l) ?? []).map((d): [string, ui.Inline] => ['', ui.dim(`${indent}${d}`)]),
2094
+ ]),
2095
+ ...(this.ended && this.probe !== undefined
2096
+ ? [
2097
+ [
2098
+ '',
2099
+ ui.dim(
2100
+ `${'probe'.padEnd(width)} at ${seconds(this.probe.at)}: ${plural(this.probe.left, 'task')} left to run, ${plural(this.probe.misses, 'known miss')}`,
2101
+ ),
2102
+ ] as [string, ui.Inline],
2103
+ ]
2104
+ : []),
2105
+ ...(this.ended && this.asks.length > 0
2106
+ ? [
2107
+ [
2108
+ '',
2109
+ ui.dim(
2110
+ `${'asked'.padEnd(width)} ${this.asks
2111
+ .map(
2112
+ (x) =>
2113
+ `${x.workers} at ${seconds(x.at)} for ${plural(x.tasks, 'task')} (${x.measured} measured), plan ${seconds(x.endsInMs)}`,
2114
+ )
2115
+ .join(' · ')}`,
2116
+ ),
2117
+ ] as [string, ui.Inline],
2118
+ // Beside core's time row, how close the run came (2026-10-23).
2119
+ ...((ms) =>
2120
+ ms === undefined
2121
+ ? []
2122
+ : [
2123
+ [
2124
+ '',
2125
+ ui.dim(
2126
+ `${'ideal'.padEnd(width)} ${seconds(ms)}: each task as long as it took, placed perfectly`,
2127
+ ),
2128
+ ] as [string, ui.Inline],
2129
+ ])(ideal?.()),
2130
+ ...(this.unmeasured.length === 0
2131
+ ? []
2132
+ : [
2133
+ [
2134
+ '',
2135
+ ui.dim(
2136
+ `${'unmeasured'.padEnd(width)} ${this.unmeasured.slice(0, NAMED).join(', ')}${this.unmeasured.length > NAMED ? ` and ${this.unmeasured.length - NAMED} more` : ''}`,
2137
+ ),
2138
+ ] as [string, ui.Inline],
2139
+ ]),
2140
+ ]
2141
+ : []),
2142
+ ...(coreMs > 0
2143
+ ? [
2144
+ [
2145
+ '',
2146
+ `${'total'.padEnd(width)} cpu utilization ${percent(cpuMs / coreMs)}${this.needMs < LEARN_MS ? '' : ` · tasks used ${percent(this.usedMs / this.needMs)} of their history`}`,
2147
+ ] as [string, ui.Inline],
2148
+ ]
2149
+ : []),
2150
+ ]
2151
+ }
2152
+
2153
+ /** Why the relay was given up on; then every task runs here. */
2154
+ lost: string | undefined
2155
+ }
2156
+
2157
+ function result(r: WiredResult, log: readonly CapturedChunk[]): ExecuteResult {
2158
+ const { cpuShare: _, removed: __, unread: ___, unshared: ____, output: _____, ...rest } = r
2159
+ const violations = (r.violations ?? []).map((v) => ({ ...v, timestamp: new Date(v.timestamp) }))
2160
+ const of = (err: boolean) =>
2161
+ log
2162
+ .filter((c) => c.err === err)
2163
+ .map((c) => c.text)
2164
+ .join('')
2165
+ return { ...rest, stdout: of(false), stderr: of(true), violations, outputs: { kind: 'disk' } }
2166
+ }
2167
+
2168
+ /** A worker's `output`, read at the boundary: its chunks of text. */
2169
+ function wiredLog(output: unknown): CapturedChunk[] {
2170
+ return Array.isArray(output)
2171
+ ? output
2172
+ .filter(
2173
+ (c): c is CapturedChunk =>
2174
+ typeof c === 'object' && c !== null && typeof c.text === 'string',
2175
+ )
2176
+ .map((c) => ({ text: c.text, err: c.err === true }))
2177
+ : []
2178
+ }
2179
+
2180
+ /**
2181
+ * A worker's `cpuShare`, read at the boundary: in (0, 1], else 1. A twentieth
2182
+ * at least: a share that small is a misread, and dividing by it would book
2183
+ * a task a whole machine.
2184
+ */
2185
+ function shareOf(share: unknown): number {
2186
+ return typeof share === 'number' && share > 0 && share <= 1 ? Math.max(0.05, share) : 1
2187
+ }
2188
+
2189
+ /** What a task that is not a cache hit to be is saved under: of this run, task and send, so no lookup ever finds it. */
2190
+ export function runKey(run: string, task: string, send = 0): string {
2191
+ const id = send === 0 ? `vx-dte\0${run}\0${task}` : `vx-dte\0${run}\0${task}\0${send}`
2192
+ return Bun.hash.xxHash3(id).toString(16).padStart(16, '0')
2193
+ }
2194
+
2195
+ /**
2196
+ * The projects inside `dir`, root-relative, once per graph and project:
2197
+ * read for each order, a run of 9000 tasks walked every task 9000 times
2198
+ * (2026-10-25).
2199
+ */
2200
+ export function nestedIn(
2201
+ nodes: ReadonlyMap<string, TaskNode>,
2202
+ dir: string,
2203
+ root: string,
2204
+ ): string[] {
2205
+ let of = nested.get(nodes)
2206
+ if (of === undefined) {
2207
+ of = { dirs: [...new Set([...nodes.values()].map((n) => n.projectDir))], by: new Map() }
2208
+ nested.set(nodes, of)
2209
+ }
2210
+ let out = of.by.get(dir)
2211
+ if (out === undefined) {
2212
+ out = of.dirs
2213
+ .filter((d) => d.startsWith(dir + path.sep))
2214
+ .map((d) => path.relative(root, d))
2215
+ .sort()
2216
+ of.by.set(dir, out)
2217
+ }
2218
+ return [...out]
2219
+ }
2220
+ const nested = new WeakMap<
2221
+ ReadonlyMap<string, TaskNode>,
2222
+ { dirs: string[]; by: Map<string, readonly string[]> }
2223
+ >()
2224
+
2225
+ /** A tree's file paths, and its submodules' (gitlinks). */
2226
+ type Tree = { files: Set<string>; links: string[] }
2227
+
2228
+ /** `tree`'s files and submodules under `root`, relative to it; undefined when git cannot list it. */
2229
+ async function listTree(root: string, tree: string): Promise<Tree | undefined> {
2230
+ const p = Bun.spawn(['git', 'ls-tree', '-r', '-z', tree], {
2231
+ cwd: root,
2232
+ stdin: 'ignore',
2233
+ stdout: 'pipe',
2234
+ stderr: 'ignore',
2235
+ })
2236
+ const [out, code] = await Promise.all([new Response(p.stdout).text(), p.exited])
2237
+ if (code !== 0) return undefined
2238
+ const files = new Set<string>()
2239
+ const links: string[] = []
2240
+ for (const e of out.split('\0')) {
2241
+ const tab = e.indexOf('\t')
2242
+ if (tab === -1) continue
2243
+ const name = e.slice(tab + 1)
2244
+ if (e.startsWith('160000 ')) links.push(name)
2245
+ else files.add(name)
2246
+ }
2247
+ return { files, links }
2248
+ }
2249
+
2250
+ /** Each distinct `exec.sandbox` of the run, for a worker's sandbox runtime. */
2251
+ function sandboxConfigs(nodes: ReadonlyMap<string, TaskNode> | undefined): SandboxConfig[] {
2252
+ const seen = new Map<string, SandboxConfig>()
2253
+ for (const n of nodes?.values() ?? []) {
2254
+ const s = n.config.exec?.sandbox
2255
+ if (s !== undefined) seen.set(JSON.stringify(s), s)
2256
+ }
2257
+ return [...seen.values()]
2258
+ }
2259
+
2260
+ /**
2261
+ * An uncached task that may leave files its dependants read: no cache
2262
+ * carries them. A sandboxed one writes only where its grants say; with
2263
+ * none (a gate like a lint or a version check) it leaves nothing, and
2264
+ * pinning every dependant to its worker held a run's shards on one.
2265
+ */
2266
+ function leaves(node: TaskNode): boolean {
2267
+ if (node.config.cache !== undefined) return false
2268
+ const sandbox = node.config.exec?.sandbox
2269
+ return sandbox === undefined || (sandbox.allow?.write?.length ?? 0) > 0
2270
+ }
2271
+
2272
+ /**
2273
+ * Every task `id` depends on, transitively, each after its own
2274
+ * dependencies: the order a worker restores them in. Dependant first, an
2275
+ * additive task's rewrite of its upstream's file was undone by the
2276
+ * upstream's restore, and its dependants read the old bytes (2026-10-25).
2277
+ */
2278
+ function upstream(nodes: ReadonlyMap<string, TaskNode> | undefined, id: string): Set<string> {
2279
+ const seen = new Set<string>()
2280
+ const out = new Set<string>()
2281
+ const walk = (t: string): void => {
2282
+ for (const d of nodes?.get(t)?.deps ?? []) {
2283
+ if (seen.has(d)) continue
2284
+ seen.add(d)
2285
+ walk(d)
2286
+ out.add(d)
2287
+ }
2288
+ }
2289
+ walk(id)
2290
+ return out
2291
+ }
2292
+
2293
+ /** A worker's run, spelled out: time, CPU, memory and the relay. */
2294
+ function describe(st: UsageStats, u: Usage): string[] {
2295
+ return [
2296
+ `online ${seconds(st.onlineMs)} · busy ${seconds(st.busyMs)} · idle ${seconds(Math.max(0, st.onlineMs - st.busyMs))}`,
2297
+ `cpu peak ${st.cpuPeak.toFixed(1)} · used avg ${st.cpuAvg.toFixed(1)} cores${st.machineAvg === undefined ? '' : ` · machine avg ${st.machineAvg.toFixed(1)}`} · booked avg ${st.bookedAvg.toFixed(1)} · cpu utilization ${percent(st.utilization)}`,
2298
+ // No task said what it held: `memory peak 0 B` would be a claim.
2299
+ ...(st.memoryPeak > 0
2300
+ ? [`memory peak ${bytes(st.memoryPeak)} · avg ${bytes(st.memoryAvg)}`]
2301
+ : []),
2302
+ `sent ${plural(u.messagesIn, 'message')} · ${bytes(u.bytesIn)}, received ${plural(u.messagesOut, 'message')} · ${bytes(u.bytesOut)}`,
2303
+ ]
2304
+ }
2305
+
2306
+ const plural = (n: number, w: string): string =>
2307
+ `${n} ${w}${n === 1 ? '' : w.endsWith('s') ? 'es' : 's'}`
2308
+ const percent = (n: number): string => `${Math.round(n * 100)}%`
2309
+ const bytes = (n: number): string =>
2310
+ n >= 1e9
2311
+ ? `${(n / 1e9).toFixed(1)} GB`
2312
+ : n >= 1e6
2313
+ ? `${(n / 1e6).toFixed(1)} MB`
2314
+ : n >= 1e3
2315
+ ? `${Math.round(n / 1e3)} KB`
2316
+ : `${n} B`
2317
+
2318
+ /** A machine is what its size says: the same cores, memory within a tenth. */
2319
+ const matches = (cores: number, memory: number, d: Declared): boolean =>
2320
+ cores === d.cpu && Math.abs(memory - d.memory) <= d.memory * 0.1
2321
+
2322
+ /** Bytes as whole gigabytes, one decimal under 10: `16G`, `0.5G`. */
2323
+ const compact = (n: number): string => {
2324
+ const g = n / 1e9
2325
+ return `${g >= 10 ? Math.round(g) : Number(g.toFixed(1))}G`
2326
+ }
2327
+
2328
+ const seconds = (ms: number): string => `${(ms / 1000).toFixed(1)}s`
2329
+
2330
+ /** The probes a task's key folds: a worker that answers otherwise fails it (C-2). */
2331
+ function runtimeOrders(req: ExecuteRequest): { runtime?: RuntimeOrder[] } {
2332
+ const inputs = req.inputs
2333
+ if (inputs === undefined) return {}
2334
+ const runtime: RuntimeOrder[] = [
2335
+ ...inputs.runtime.map((r) => ({ command: r.command, output: r.output })),
2336
+ ...inputs.workspaceRuntime.map((r) => ({
2337
+ command: r.command,
2338
+ output: r.output,
2339
+ workspace: true as const,
2340
+ })),
2341
+ ]
2342
+ return runtime.length === 0 ? {} : { runtime }
2343
+ }
2344
+
2345
+ /**
2346
+ * `env` as it travels: no unset name, and no name whose value is the
2347
+ * worker's (`HOST_ENV`) unless `define` sets it. A defined `HOME` ran
2348
+ * with the worker's own, saved under a key that folded the defined one
2349
+ * (2026-10-24).
2350
+ */
2351
+ function defined(
2352
+ env: NodeJS.ProcessEnv,
2353
+ define: Readonly<Record<string, string>>,
2354
+ ): Record<string, string> {
2355
+ const out: Record<string, string> = {}
2356
+ for (const [k, v] of Object.entries(env))
2357
+ if (v !== undefined && (!HOST_ENV.includes(k) || k in define)) out[k] = v
2358
+ return out
2359
+ }