fix(e2e): release claimed simulators with the e2e slot - #4845
Open
iscekic wants to merge 2 commits into
Open
Conversation
Agents that correctly ran `e2e-slot.sh release` still left their simulator booted, so `Kilo E2E - <worktree>` devices accumulated across sections with no slot held between them. Teardown was split between the script and the agent's memory: `release` stopped the worktree's dev stack automatically, but powering the device off was a manual, conditional runbook step (`xcrun simctl shutdown <udid> # only if you booted it`). Agents skipped it, and the ones that read it could not reliably answer "did you boot it" hours into a run — skipping is the safe guess, since shutting down a peer worktree's device is worse than leaking one. `simulator release <udid>` never powered anything off either, so even a fully compliant agent left the device running. The answer existed at claim time and was discarded: `bootSimulator` returns early for an already-booted device, so the claim knows whether it started it. - Record `bootedByClaim` on the claim; `release` powers off only what its own claim booted, so an adopted running device is still never shut down. - Add `simulator release-all` to hand back a whole worktree's claims, for callers that never see a UDID. - `e2e-slot.sh release` (and the reap and `stacks --reap` paths) now run it beside `pnpm dev:stop`: devices go back with the slot exactly like the stack. - Drop the conditional `simctl shutdown` from the runbook cleanup list. Verified on real simulators: a claim-booted device is powered off and renamed back by slot release alone, and a hand-booted device adopted by a claim stays running. `pnpm test:mobile-workflow` passes 135/136 — the one failure (`settle flow handles the exact iOS external-app prompt`) also fails on a clean main checkout.
`releaseWorktreeSimulators` bailed on the first failing claim, so one wedged simulator stranded every other device the worktree held — the exact leak the path exists to prevent, and the slot is already gone by the time it runs. Every claim now gets its attempt, and the failures are reported together afterwards.
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryReviewed the simulator-release-on-slot-release fix (mobile-simulator.ts/test.ts, e2e-slot.sh, and docs) and found no high-confidence security, correctness, or logic issues in the changed lines; boot attribution, shutdown-on-release, adopted-device handling, and release-all's per-claim failure isolation are consistent and covered by tests. Files Reviewed (6 files)
Reviewed by claude-sonnet-5 · Input: 22 · Output: 8.4K · Cached: 645.6K Review guidance: REVIEW.md from base branch |
jeanduplessis
approved these changes
Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Agents following
.kilo_workflowleave simulators booted even when they release their e2e slot. Devices accumulate across sections: at the time of writing, twoKilo E2E - <worktree>simulators were running with only one slot held between them, one stranded for ~8h. That burns CPU under every section that follows, which surfaces as flaky emulator boots and native-build timeouts rather than as over-subscription.Root cause
Teardown was split between the script and the agent's memory.
e2e-slot.sh releasestopped the worktree's dev stack automatically, but the device was a manual — and conditional — runbook step:Two routine failure modes:
And
pnpm dev:mobile:simulator release <udid>never powered anything off: it dropped the claim and restored the name. So even an agent that ran every command on the list left the device running unless it also remembered the separatesimctl shutdown.The information the decision needed existed and was thrown away —
bootSimulatorreturns early whendevice.state === 'Booted', so claim time knows exactly whether it started the device, and nothing recorded it.Fix
Devices go back with the slot, exactly like the stack already does.
dev/local/mobile-simulator.ts—bootSimulatorreturns whether it booted; the claim recordsbootedByClaim;releaseSimulatorpowers off only when its own claim booted the device. A shutdown failure preserves the claim and surfaces the error, matching the existing rename-failure policy. Newrelease-allhands back every claim owned by the current worktree, for callers that never see a UDID..kilo_workflow/e2e-slot.sh—stop_stackbecomesrelease_worktree_resources, runningpnpm dev:mobile:simulator release-allbesidepnpm dev:stopunder the same "no other slot still covers this worktree" guard. Wired into all three teardown paths:release, the dead-holderreap, andstacks --reap.simctl shutdownand the per-UDIDreleaseare gone from the runbook cleanup list; slot release is the teardown.WORKFLOW.mdstates that a slot, a stack, and a claimed device are one resource.An adopted device — already running before the claim — is still never shut down. That guarantee moved from the agent's memory into the claim record, which is the point.
Claims written before this change have no
bootedByClaimand are read asfalse: they leak a device at worst, and never steal one.Verification
CI does not run
test:mobile-workflow, so this was checked locally, including against real simulators.Claim-booted device, released via the slot alone — no manual shutdown, no manual
release <udid>:Peer worktrees' booted simulators were untouched throughout.
Hand-booted device adopted by a claim — the dangerous direction:
Suites:
pnpm typecheckclean,pnpm lintclean (0 warnings, 0 errors),bash -nclean.pnpm test:mobile-workflowpasses 135/136, with 6 new tests covering boot attribution, shutdown-on-release, the adopted-device case, shutdown-failure claim retention, andrelease-all's worktree scoping. The single failure —settle flow handles the exact iOS external-app prompt within existing waits— also fails on a cleanorigin/maincheckout (129/130 there), so it is pre-existing and unrelated.Not in scope
releaseAndroidDevicehas the same shape — it drops the claim without killing the emulator — but emulators are launched inside an agent-created tmux session and die withtmux kill-session, so the leak class differs. Worth a follow-up.release_worktree_resourcesskips it ([ -d "$wt" ]) and the device leaks. Not observed — every leak found had a live worktree — so no reaper was added for it.stacks --reapenumerates uncovered stack sessions, so a worktree holding a leaked simulator with no stack up does not appear there. New leaks are prevented structurally, and the existing ones are a one-time manual cleanup, so no device-sideuncovered_stacksequivalent was added.Kilo E2E - remote-cli-69f6) is another section's device with an old-format claim; left alone rather than reaped by hand.This fix only takes effect in worktrees that have the updated
e2e-slot.sh, since every worktree carries its own copy.