fix(ios): recover simulator recorder startup failures - #2447
Conversation
|
No code findings at 06e98c7. The tests cover error classification, graceful rollback and failed-start cleanup through the daemon. Before this is ready, please add a macOS/CoreSimulator run that observes real recording contention and confirms a subsequent recording starts and stops after the SIGINT rollback; the injected exit-16 process and ordinary iOS smoke do not prove the host recording slot is released. |
…grace The exit-16 classifier and graceful start rollback are owned by #2447 with the DEVICE_IN_USE contract; drop the overlapping COMMAND_FAILED classifier here to keep one start-path error contract. Keep only the non-overlapping daemon-startup fix: an orphaned simctl recorder is reaped with the same finalize grace the live stop path allows, plus a startup-wiring test. Refs #2170.
|
Sharing the CoreSimulator contention + recovery evidence you asked for (gathered on iPhone 16 with an isolated daemon state dir, host restored afterward). Relevant to the 1. Real recording contention — raw Confirms exit 2. Orphan recovery + a subsequent recording starts and stops (
3. Host restored (graceful stop, sim shut down, no leftover Context: #2457 was narrowed to the non-overlapping daemon-startup reap grace; the start-path classifier and graceful rollback stay here. This evidence covers the contention + subsequent-start/stop ask for both. |
|
The new CoreSimulator run proves real exit-16 contention and daemon-startup orphan recovery, which covers #2457. It does not exercise this PR’s unpublished-recorder startup rollback. The remaining validation is a failed start that invokes this build’s SIGINT rollback, followed by a successful recording start and stop on the same host. Code review remains clean and checks are green at 06e98c7; that path-specific evidence is still needed. |
…window (#2457) * fix(ios): hint the busy simctl recorder and detach recorders gracefully Classify `simctl recordVideo` exit 16 (POSIX EBUSY) as a typed COMMAND_FAILED with an actionable hint and the recorder's stderr instead of an unclassified UNKNOWN, so a caller learns another recording holds the one host-wide CoreSimulator slot or a prior recorder died without detaching. Give a recorder that is rolled back or reaped the same graceful SIGINT -> SIGTERM -> SIGKILL escalation the live stop path already uses, and widen the daemon-startup reaper's recorder grace to match it, so finalize-and-detach releases the host-wide recording lock instead of a mid-detach SIGKILL dangling it for every later recording. Closes #2170 * refactor(ios): inline the busy-recorder classifier and drop the rollback helper Simplify #2170. The exit-code classification lives beside `startError` that is its only caller, so drop the separate module and its test and assert through the real start path instead. Test through `normalizeError` to keep the wire-shape (hint lifted, reason and exit code retained) guarantee. Revert the start-rollback SIGKILL sites: the identity arm fires only when the recorder already exited, and the acquisition rollback discards an explicitly canceled recording, so fast-kill there is fine. The dangling-host path is the startup reaper, which keeps the wider finalize grace added earlier. * refactor(ios): scope this change to the daemon-startup recorder reap grace The exit-16 classifier and graceful start rollback are owned by #2447 with the DEVICE_IN_USE contract; drop the overlapping COMMAND_FAILED classifier here to keep one start-path error contract. Keep only the non-overlapping daemon-startup fix: an orphaned simctl recorder is reaped with the same finalize grace the live stop path allows, plus a startup-wiring test. Refs #2170.
|
@thymikee Built this PR's head ( A. Failed start with the host already busy → After releasing the holder: B. Failed start via client-cancel during acquisition → SIGINT rollback of a LIVE recorder ( The same daemon stayed healthy: a following Both runs show the failed-start rollback removing the partial output and leaving no orphaned |
|
The new CoreSimulator evidence at 06e98c7 closes the remaining validation gap: cancelling acquisition rolled back a live unpublished recorder, removed the partial output, and allowed a subsequent recording start/stop on the same daemon without restart. The real busy-host case also confirms DEVICE_IN_USE classification and cleanup. Code review remains clean, current-head checks pass, and there are no conflicts. Ready for human review and merge. |
Summary
Closes #2170.
Recover iOS Simulator recording startup without leaving CoreSimulator's host-wide recording slot stuck. Unpublished recorder rollback now sends
SIGINT, waits a bounded five seconds for graceful detach, and escalates only if the process remains alive. A nativesimctl recordVideoexit 16 is returned as non-retriableDEVICE_IN_USEwithdetails.reason: apple_simulator_recording_busyand actionable recovery guidance; other exits retain their existing classification.Startup now observes exits that settle before PID discovery or on the final process-identity poll. The provider scenario proves a failed start adopts no recorder and a later start/stop succeeds. Five files, +195/−26.
Validation
Tested
06e98c7e6a7bf0517c31339fb2a4814ae055d9ccwith Node 24.13.1 and pnpm 11.17.0.AGENT_DEVICE_VITEST_MAX_WORKERS=1 pnpm check:affected --runpasses: 301 files / 2,144 tests, 12 command-doc tests, format, lint, types, layering, Fallow, and build.