Skip to content

LANE-SPEED: device lanes run the devices of a target at once; parity:devices runs both targets; lanes.json written under a lock - #42

Merged
thejackshelton merged 43 commits into
masterfrom
lane-speed
Oct 1, 2026
Merged

thejackshelton merged 43 commits into
masterfrom
lane-speed

Conversation

@thejackshelton

@thejackshelton thejackshelton commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

What changed

  • parity:lanes --run-device runs the devices of a target at once, each in its own process (--device-jobs N, capped by the matrix and by free memory). Outcomes are merged in matrix order, and each device boots while its cases are computed.
  • parity:lanes --target runs write out/lanes.json under a lock and re-read it at write time, so both targets can run at once.
  • pnpm run parity:devices runs both targets at once, each device step under its platform lease, and judges the merged file.
  • Each Android device still does its own launcher settle wait (Android runner: wait for the launcher focus to settle before am start, with a pure tested step and a timeout that names the state (T112) #36): every device has its own process, its own wait state, and adb -s <serial> on every call. In both runs all three emulators booted together and each settled on its own in 4.4 to 5.5 s.
  • out/lanes.json is regenerated: only the 10 laneCode evidence stamps change, because the lane code changed.

Measured on the Mac (/tmp/device-lease.sh pnpm run parity:lanes -- --run-host --run-device, master aafd848 against this branch)

  • Wall time: 813.0 s → 572.9 s.
  • Device phases: iOS 156.6 → 124.9 s, Android 345.1 → 225.4 s; together 501.7 → 350.3 s (−30%).
  • Final run on master b41500f (388 cases, both apps freshly built): iOS devices 108.7 s, Android devices 237.1 s.

Same results

  • device-failures-{ios,android}.json are byte-identical to master's.
  • lanes.json differs only in laneCode. States, counts, dump digests, capture trust and dump-fault rows are identical, and device-pixels still fails with 135 (iOS) and 182 (Android) failures, as on master.
  • The lanes.json that parity:devices writes is byte-identical to the one parity:lanes writes.

Passed: pnpm typecheck; pnpm test, 124 files and 2502 tests. No test, tolerance or fixture changed.

Known limit: /tmp/device-lease.sh on the owner's machine is one lock for the whole machine, so the two platform steps of parity:devices still run one after the other there.

🤖 Generated with Claude Code

Rounds 3-4 (aecc8b1, e3d5132, 0ffe344): device runs are machine-exclusive under the device lease, so the cross-process slot files and file lock are removed. One parity:lanes process runs both targets at once within an in-memory memory budget, and even the first device waits for the budget. A failed stop blocks the device outcome. Also includes T124 (from closed #44: north-star:check and wpt:interop-labels gain --conditions=dragon-internal, and script-conditions.test.ts) and T125 (lanes.ts SUITE_LINE: an undeclared suite fails the host lane instead of being dropped; master output parses to the same 13 suites). Final: device run 460 cases, only laneCode moves, device-failures byte-identical to master; pnpm typecheck; pnpm test 2929/2929.

Note

Add concurrent device lanes, parity:devices command, and device lease/memory admission

  • Device runs for a target now execute concurrently as separate child processes via a bounded pool, with app prebuilds overlapping host lane runs; failed prebuilds skip that target's devices. Default width is 1 (sequential).
  • Adds a device lease guard and a memory-budget admission system: device boot waits in polls until the process's reserved device memory fits after an 8 GiB machine reserve, and reserves are held until a started device is confirmed stopped.
  • Adds a shared child-process helper (exec/execAsync/spawnChild) that fails closed on nonzero exits, signals, and timeouts unless callers supply a nonblank allowFailure reason; all device command paths in device-run.ts and device-vectors.ts now use it.
  • Broadens parseNativeOutput suite-line parsing to any nonempty label plus its first pass/total count, so undeclared suites fail host judging instead of being ignored. Adds the parity:devices script and --prebuild mode in cli/lanes.ts.
  • Behavioral Change: lanes.json entries get a new laneCode value; device runs now refuse to start without a valid lease or sufficient memory; failed device cleanup discards that device's results as blocked tooling faults instead of reporting them.

Macroscope summarized 0402e6a.

…ock, re-read at write time, so the two targets can run at once; pnpm run parity:devices runs both targets at once, each device step under its platform lease, and judges the merged file; the devices of a target run at once in their own processes (--device-jobs, capped by memory), merged in matrix order; the cases are computed while a device boots
…pnpm run parity:lanes -- --run-host --run-device (then pnpm run parity:devices, byte-identical): only the device lanes' laneCode evidence stamp changes (device-jobs.ts and the lane code); verdicts, counts, dump digests and failure lists equal master's
# Conflicts:
#	packages/parity/out/lanes.json
…tmp/device-lease.sh pnpm run parity:lanes -- --run-host --run-device (376 cases; 3 emulators at once, each settled on its own serial; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to 58604a2's)
# Conflicts:
#	packages/parity/out/lanes.json
…tmp/device-lease.sh pnpm run parity:lanes -- --run-host --run-device (388 cases; 2 simulators and 3 emulators at once, each emulator settled on its own serial; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to b41500f's)
Comment thread packages/parity/src/device-lanes.ts
@macroscopeapp

macroscopeapp Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR substantially changes parity execution by running hosts and device lanes concurrently, adding child-process coordination, memory admission, lease enforcement, and new cleanup/failure semantics across shared device infrastructure. Its effects are primarily in the test harness, but the scope and shared-resource behavior make it unsuitable for automatic approval.

You can add or adjust custom eligibility rules. Learn more.

…ine-wide memory budget (device-slots.ts: holder files under a lock in /tmp/dragon-device-slots, judged against one fresh memory reading, released with the device, on a failed boot and at exit, stale holders of dead processes dropped); deviceJobs sizes processes only; parity:devices run inside the device lease does not take it again
… run parity:devices (both targets at once under the device lease, every boot on a machine-wide memory slot; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to master's)
…while another process lists the slots holds nothing; a lock released meanwhile, or whose owner has not yet written its pid, is not dead; a dead owner's lock is taken over under a takeover lock that re-reads the owner (a stale takeover lock is an error naming it). Tests: ten slot processes at once, three processes meeting a dead lock at once
… run parity:devices (both targets at once, every boot on a machine-wide memory slot; iPad (A16) waited 131 s for one; only laneCode moves against b41500f, out/device-failures-{ios,android}.json byte-identical)
…s iOS simulator takes a machine-wide device memory slot before it boots (held to exit), like every matrix boot; a test pins that each parity source that boots a device takes a slot first. --own-exit answers for the lanes the run produced (a host run does not fail on device records it carried; ownFailures, tested)
… run parity:devices (both targets at once, every boot on a machine-wide memory slot, 448 s wall clock; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
# Conflicts:
#	packages/parity/out/lanes.json
…tmp/device-lease.sh pnpm run parity:devices (438 cases; both targets at once, every boot on a machine-wide memory slot, iPad (A16) waited 75 s for one; 334 s of steps after the lease; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
Comment thread packages/parity/src/device-slots.ts Outdated
Comment thread packages/parity/src/device-run.ts Outdated
Comment thread packages/parity/src/file-lock.ts Outdated
Comment thread packages/parity/src/cli/devices.ts Outdated
…52) and the re-audit of device-slots, device-run, file-lock and parity:devices

- 4147492181: the device slots directory must be an absolute, real (not symlinked) directory of this user that no one else can write (made 0700 when absent), else an error naming it; writeFileAtomic creates its temporary file under an unpredictable name, exclusively and without following links.
- 4147492203: release gives a device's memory slot back only once a device this runner started is confirmed stopped; a device it did not start, or one whose stop failed, keeps its slot until the process exits, and a failed stop is logged and returned (no longer swallowed).
- 4147492214: a lock is published whole (a staged directory holding the owner's pid and token, renamed into place), removed by renaming it away first, and a holder removes only its own lock, failing when another owner's is there.
- 4147492252: parity:devices judges each target's app build step too, so a failed --prebuild fails the run.
- Re-audit: a failed boot that cannot stop the device it started keeps the slot (DeviceLeftRunning); the retry of an emulator that never attached stops the process it spawned (stopSpawned); holder files must hold finite bytes and since and a non-empty device; parity:devices checks each argument in place (a stray value or a repeated --device-jobs is refused).

Tests: each fails without its fix (file-lock.test.ts observes every lock publication; device-slots.test.ts; lanes-concurrent.test.ts). Retargeted: the release test now releases a device started here with a stop that succeeds (a device not started here keeps its slot, per 4147492203).
…internal (T124)

north-star:check failed on master (SyntaxError: 'dragon' does not provide collectFontFaces) because its script ran check.ts
without the dragon-internal condition. packages/parity/test/script-conditions.test.ts walks the static import graph of every
'node <file>.ts' script in every workspace package.json and requires the condition wherever the graph reaches the dragon
package; it found wpt:interop-labels too.
…-z0-9][a-z0-9 -]*) (\d+)/(\d+), ' corpus' still stripped), so judgeHost fails a suite the manifest does not declare instead of dropping it uncounted; master's native:swift and native:kotlin output parse to the same 13 suites as before
…ixes: /tmp/device-lease.sh pnpm run parity:devices (438 cases; both targets at once, 406 s of steps after the lease; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
# Conflicts:
#	packages/parity/out/lanes.json
… parity:lanes -- --run-host --run-device): only the laneCode stamp of the ten device lanes changes (lanes.ts changed); every lane verdict, suite count, case list and failure list is identical to master (5a14a96)
…tmp/device-lease.sh pnpm run parity:devices (456 cases; both targets at once, 617 s of steps after the lease; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
Comment thread packages/parity/src/device-slots.ts Outdated
Comment thread packages/parity/src/device-run.ts Outdated
Comment thread packages/parity/src/device-run.ts Outdated
Comment thread packages/parity/src/file-lock.ts Outdated
…r the device lease (removes the cross-process slot files and the file lock)

Device runs are machine-exclusive under /tmp/device-lease.sh, so the cross-process state solved a problem the lease removes. Now:
- parity:lanes --run-device runs only under the lease (its holder must be an ancestor; else it stops at once, naming the lease), and
  so does every boot (admitDevice checks it). parity:devices is parity:lanes -- --run-host --run-device.
- One process runs both targets at once: the host lanes of both targets at once (the host run is awaited, not blocking), the app
  builds in their own processes meanwhile (a failed build fails the run and its target runs no devices), then the device lanes of
  both targets at once; it writes out/lanes.json once.
- That process boots and stops every device; each device's work runs in its own process, handed the device on stdin (checked
  against its matrix device). Every boot is admitted by one in-memory budget (device-run.ts: one fresh memory reading per
  admission, the reservation made in the same synchronous step, given back only once a device started here is confirmed stopped).
- Removed: device-slots.ts, file-lock.ts, cli/devices.ts and their tests, --own-exit, the locked lanes.json update.
- Findings 4149425883 and 4149425914: a failed boot runs every cleanup stop (stopAll), and a stop that rejects or reports a problem
  keeps the device counted as running (DeviceLeftRunning), so the spawned emulator is stopped even when adb emu kill fails.
- Re-audit: a target whose device run throws no longer abandons the other's booted devices (settled, then rethrown); a host that
  cannot start is judged as before, not a crash; parity:lanes checks each argument in place.

Tests: the budget across both targets, the first device always booting, waits and their timeout, release only after a confirmed
stop, a failed boot keeping or giving back memory, every cleanup stop running, the lease check, the handed device, a failed build,
the argument checks, and that every boot path in the parity sources is admitted first.
…: /tmp/device-lease.sh pnpm run parity:devices (now parity:lanes -- --run-host --run-device in one process; 456 cases, both targets at once, 395 s wall clock with the lease free; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
…et too, so a run never boots a device the budget does not admit; one that can never be admitted fails after the wait naming the device, the memory it needs and the free memory read. Retargeted test: the first device waits, fails with the named error when starved, and is admitted once memory frees
…/tmp/device-lease.sh pnpm run parity:devices (456 cases, both targets at once, 288 s with the lease free; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
Comment thread packages/parity/src/device-lanes.ts Outdated
Comment thread packages/parity/src/device-run.ts Outdated
…device-lanes.ts and device-run.ts

- 4149997382: a device whose stop reports a problem gives a blocked outcome naming it (afterRelease), in the sequential run and in
  the parent of a device process alike, so a device that may still be running is a tooling fault, never a pass.
- 4149997421: isAncestor counts pid 1, so a lease held by a container's entrypoint covers its descendants.
- Re-audit (results dropped): the iOS boot retry stops the simulator and checks it stopped before booting it again (a failed stop
  fails the boot, whose cleanup keeps its memory reserved); every emulator preparation step (no animations, the text scale, awake
  and unlocked, no error dialogs) must succeed, else the boot fails naming the step.

Tests: afterRelease blocks an outcome whose stop failed and keeps one whose stop succeeded (and the merge records it); a pid 1
lease holder covers its descendants (fails without the fix).
…/device-lease.sh pnpm run parity:devices (456 cases, both targets at once; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
# Conflicts:
#	packages/parity/out/lanes.json
… WM-0): /tmp/device-lease.sh pnpm run parity:devices (460 cases, both targets at once, 515 s; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
thejackshelton added a commit that referenced this pull request Sep 30, 2026
…lines, plant after #42); T125 done (ships in #42); T133 INL1a-tags
# Conflicts:
#	packages/parity/out/lanes.json
…te-line (T125) and 38e9d90: /tmp/device-lease.sh pnpm run parity:devices (460 cases, both targets at once, 405 s; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
Comment thread packages/parity/src/lanes.ts Outdated
Comment thread packages/parity/src/device-run.ts
…elper for the device code, and any suite label

- 4150454065, the class (a command's result dropped, the third round): every child process of packages/parity/src/device-*.ts and
  cli/lanes.ts (adb, simctl, xcrun, the emulator, ps, vm_stat, the build tools, the device and build processes) now runs through
  device-exec.ts, which throws on a non-zero exit, a signal, a start error or a timeout unless the call names why its failure is an
  answer (allowFailure: '<reason>'; an empty reason is refused). serialsRunning reads adb devices through it, so a failed read is
  not an empty list and a stop whose read failed reports the device as possibly running. vm_stat output that cannot be read throws
  instead of falling back.
- Every allowFailure reason re-audited: each is an answer the caller judges (bootstatus retried, then failing on its output; the
  done-file poll now fails on any error but "No such file"; a failed pull recorded as the run's error; a suite crash its cause; a
  failed build failing the run). device-exec.ts joins the device lane evidence code.
- 4150454066: SUITE_LINE takes any nonempty label before the first count on the line (^(.+?) ...; lazy, so a count in a trailing note
  is not the suite's). master's native:swift and native:kotlin output, committed as test/native-output, parses to the same 13 suites
  as before, and no other line holds a count.

Tests: a scan that fails on any raw child process in those sources or a non-throwing run() import; the helper's failures reaching the
caller (exit, signal, ENOENT, timeout, async, streamed, bytes) and allowFailure returning the result; a stop whose adb devices read
fails (fails without the fix); any-label suite lines; the committed native output's 13 suites.
…/device-lease.sh pnpm run parity:devices (460 cases, both targets at once, 609 s; only laneCode moves, out/device-failures-{ios,android}.json byte-identical to origin/master's)
@thejackshelton
thejackshelton merged commit 9b60a9e into master Oct 1, 2026
4 checks passed
thejackshelton added a commit that referenced this pull request Oct 1, 2026
… errors, #49 north star and the Android profile rule, board) into anim-a2-translate: lanes.ts takes master's SUITE_LINE and SUITE_CAUSE; native-swift and native-kotlin pins take master's cause check with rt 54588/54588 appended; generated files (engines, harnesses, corpus.json, lanes.json) taken from master and regenerated in the next commit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant