Skip to content

fix(e2e): release claimed simulators with the e2e slot - #4845

Open
iscekic wants to merge 2 commits into
mainfrom
fix/simulator-leak-on-slot-release
Open

fix(e2e): release claimed simulators with the e2e slot#4845
iscekic wants to merge 2 commits into
mainfrom
fix/simulator-leak-on-slot-release

Conversation

@iscekic

@iscekic iscekic commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Problem

Agents following .kilo_workflow leave simulators booted even when they release their e2e slot. Devices accumulate across sections: at the time of writing, two Kilo 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 release stopped the worktree's dev stack automatically, but the device was a manual — and conditional — runbook step:

xcrun simctl shutdown <udid>                 # only if you booted it
pnpm dev:mobile:simulator release <udid>     # every simulator you claimed

Two routine failure modes:

  1. Agents skipped the line outright, having released the slot and considered teardown done.
  2. The ones that read it had no reliable way to answer "did you boot it" several hours into a run — and skipping is the correct safe guess, because shutting down a peer worktree's device is far worse than leaking one.

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 separate simctl shutdown.

The information the decision needed existed and was thrown away — bootSimulator returns early when device.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.tsbootSimulator returns whether it booted; the claim records bootedByClaim; releaseSimulator powers 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. New release-all hands back every claim owned by the current worktree, for callers that never see a UDID.
  • .kilo_workflow/e2e-slot.shstop_stack becomes release_worktree_resources, running pnpm dev:mobile:simulator release-all beside pnpm dev:stop under the same "no other slot still covers this worktree" guard. Wired into all three teardown paths: release, the dead-holder reap, and stacks --reap.
  • Docs — the conditional simctl shutdown and the per-UDID release are gone from the runbook cleanup list; slot release is the teardown. WORKFLOW.md states 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 bootedByClaim and are read as false: 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>:

$ pnpm dev:mobile:simulator claim
{"device":{"id":"6D55A32C-…","name":"Kilo E2E - sim-leak-fix",…}}
  claim record: {…,"bootedByClaim":true}
  simctl list devices booted: Kilo E2E - sim-leak-fix (6D55A32C-…) (Booted)

$ .kilo_workflow/e2e-slot.sh release sim-leak-verify
released slot-1
releasing simulators claimed by /Users/igor/Projects/.worktrees/sim-leak-fix
Released 6D55A32C-…
  simctl list devices: iPhone 17 Pro E2E 4773 (6D55A32C-…) (Shutdown)   ← off, name restored
  claim file: gone

Peer worktrees' booted simulators were untouched throughout.

Hand-booted device adopted by a claim — the dangerous direction:

$ xcrun simctl boot 6D55A32C-… && pnpm dev:mobile:simulator claim 6D55A32C-…
  claim record: {…,"bootedByClaim":false}

$ .kilo_workflow/e2e-slot.sh release sim-leak-verify
Released 6D55A32C-…
  simctl list devices: iPhone 17 Pro E2E 4773 (6D55A32C-…) (Booted)   ← left running

Suites: pnpm typecheck clean, pnpm lint clean (0 warnings, 0 errors), bash -n clean. pnpm test:mobile-workflow passes 135/136, with 6 new tests covering boot attribution, shutdown-on-release, the adopted-device case, shutdown-failure claim retention, and release-all's worktree scoping. The single failure — settle flow handles the exact iOS external-app prompt within existing waits — also fails on a clean origin/main checkout (129/130 there), so it is pre-existing and unrelated.

Not in scope

  • Android emulators. releaseAndroidDevice has the same shape — it drops the claim without killing the emulator — but emulators are launched inside an agent-created tmux session and die with tmux kill-session, so the leak class differs. Worth a follow-up.
  • Deleted-worktree leaks. If a worktree is removed while its device is booted, release_worktree_resources skips it ([ -d "$wt" ]) and the device leaks. Not observed — every leak found had a live worktree — so no reaper was added for it.
  • A sweeper for devices already leaked. stacks --reap enumerates 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-side uncovered_stacks equivalent was added.
  • The pre-existing leak on this machine (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.

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.
@iscekic iscekic self-assigned this Jul 28, 2026
`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.
@kilo-code-bot

kilo-code-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Reviewed 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)
  • .kilo_workflow/WORKFLOW.md
  • .kilo_workflow/e2e-slot.sh
  • .kilo_workflow/learnings/simulators-left-booted-after-slot-release.md
  • apps/mobile/e2e/AGENTS.md
  • dev/local/mobile-simulator.test.ts
  • dev/local/mobile-simulator.ts

Reviewed by claude-sonnet-5 · Input: 22 · Output: 8.4K · Cached: 645.6K

Review guidance: REVIEW.md from base branch main

@iscekic iscekic added the human-ready The PR is ready for human review. label Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants