Skip to content

feat: native iOS support for simulators and physical devices - #175

Open
banggaoo wants to merge 32 commits into
google:mainfrom
banggaoo:feat/ios-support
Open

banggaoo wants to merge 32 commits into
google:mainfrom
banggaoo:feat/ios-support

Conversation

@banggaoo

@banggaoo banggaoo commented Oct 7, 2026 •

Copy link
Copy Markdown

feat: add native iOS support for simulators and physical devices

What Changes

Add one opt-in iOS feature covering both simulators and paired physical
iPhones/iPads across the CLI, embedded and remote SDKs, daemon, MCP, and console.

  • Simulator observation/input uses Xcode MCP; lifecycle and capture use
    simctl.
  • Physical observation/input uses WebDriverAgent; lifecycle/discovery and
    polled recording use devicectl.
  • Platform-aware task payloads, device selection, queues, execution leases,
    stream targets, and recorded-session replay preserve the selected target.
  • The dependency-free remote SDK forwards the iOS platform and host-side
    workspace explicitly, and refuses legacy hosts before submitting iOS tasks.
  • Native accessibility hit points survive the shared observation/action path.
  • iOS-specific setup and troubleshooting document Xcode consent, pairing,
    Developer Mode, WDA provisioning, and supported/unsupported operations.

Android Compatibility

Android remains the default platform. The Android driver implementation is not
replaced. Shared routing changes are platform-gated, and Android task/lock/
observation/video regressions are included in verification.

Hardening

The submission also addresses observed integration risks: cancellation-safe
native/HTTP child ownership, leased diagnostic sessions, scope-aware queue
deduplication, actual CoreDevice JSON compatibility, unambiguous identifiers,
and physical-device selection in the console.

Verification

Verified head: 65c3412 on feat/ios-support. Results below are from that
lineage (code commit 19e04cc; later commit is docs-only).

  • Python 3.12 deterministic suite:
    pytest tests/unit tests/tools packages/artemis-client/tests -q:
    2900 passed, 11 skipped, 8 deselected, exit 0.
  • Repository-wide ruff format --check . and ruff check .: exit 0.
  • Existing quality_ratchet.py: passed with the baseline unchanged.
  • Protected-core pyright --project pyright-core.json: 0 errors, exit 0.
  • git diff --check and local Markdown file links: clean.
  • Dedicated iOS simulator acceptance (iOS 27.0 fixture): boot, resolve,
    install/list/launch/terminate, native recordVideo to valid MP4, and cleanup
    pass; with Xcode agent consent granted, the full MCP path also passes —
    real DeviceInteraction session, 248-element hierarchy, tap/swipe input,
    and clean session teardown. Without consent it refuses cleanly
    (XcodeApprovalRequiredError) and releases all resources.
  • Live physical acceptance on iPhone 15 Pro passed (see below).
  • Frontend: production ng build passes; karma suite 139/139 SUCCESS
    (Chrome Headless, Node 22.23.3).
  • Distribution: uv build produces wheels containing every iOS module and the
    frontend bundle; fresh-venv install passes; artemis/artemis-admin/mcp
    entry points run; --platform, iOS UDID, and --ios-workspace CLI surface
    verified.
  • Android regression on a live target: AndroidAdbDriver smoke on an Android 36 emulator (emulator-5554) — connect, real 1080x2400 screenshot, Settings launch, tap, swipe, clean disconnect (screenshot below). No physical Android handset is attached.
  • Real provider-driven Flash/Pro task quality: not established by a mocked
    model or a driver smoke test.

Evidence

iOS 27.0 simulator screen captured through the live Xcode MCP
DeviceInteraction pipeline (XcodeSimulatorDriver.get_screen_data, 248
parsed hierarchy elements):

iOS simulator screen observed by Artemis

Android 36 emulator screen through the unchanged AndroidAdbDriver:

Android emulator screen observed by Artemis

Live physical acceptance on iPhone 15 Pro (Xcode 27, LAN tunnel): WDA
session via driver-launched runner, 319-element hierarchy at 1178x2556,
tap/swipe/home on-device, WDA and devicectl screenshots, pid-verified app
terminate, physical recording to valid MP4, clean disconnect with zero
orphans. Two defects found and fixed by this acceptance: WDA product-family
vs personalized device names, and driver-launched runner auto-session
adoption.

The deterministic suite uses a dummy validation-only provider key for legacy
configuration checks, not a real credential. No provider calls or live hardware
acceptance are claimed by that result. The Android driver source is unchanged;
shared Android/iOS regression tests pass.

Platform Limits

Requires macOS and Xcode 27+. Xcode binds agent approval to the client
binary's code signature: unsigned/adhoc interpreters (uv/Homebrew Python)
receive ~24-hour grants requiring periodic re-approval, while signed agents
can hold persistent grants — documented in docs/ios.md. Hardware needs a
user-provisioned, reachable WDA runner and the user's device consent; WDA's
HTTP server cannot start while the device is locked. Physical recording is
lower-frame-rate screenshot polling; no iOS audio recording is added. Android
shell/logcat/app locking, unsupported keys, and cloud execution are not
advertised as iOS features.

Closes #174

banggaoo and others added 30 commits October 6, 2026 00:44
Integrate Xcode's native MCP interaction sessions and simctl with the shared device driver, standalone CLI, and embedded SDK.\n\nPreserve Android defaults, native accessibility hit points, and cancellation-safe device leases. Add read-only setup checks, usage documentation, and hermetic regression coverage.
Capture via xcrun simctl io recordVideo anchored to the first-frame
marker, seal segments on demand for live analyzer clips, roll on
rotation/duration/crash, and finalize VFR .mov segments to CFR MP4
with a version-2 manifest so iOS recordings flow through the same
DataEngine, extraction, and replay pipeline as Android scrcpy.

Video tools now auto-detect per platform (simctl+ffmpeg on iOS) and
the CLI/SDK no longer disable recording for iOS tasks.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
simctl io screenshot does not stream to stdout on Xcode 27, so the dimension probe always failed and segments finalized on the 1080x1920 fallback canvas. Parse the framebuffer IOSurface port size instead - it is the exact surface recordVideo encodes, excludes external scene displays, and swaps on rotation.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Extend the native Xcode 27 iOS support to every Artemis surface so iOS
reaches feature parity with Android where the platform permits:

- Shared simctl discovery (artemis/drivers/ios/discovery.py) and an
  IosDevicePool producing platform-tagged DeviceStatus entries, with
  explicit-UDID validation, Booted/Shutdown admission, and ambiguous
  "booted" rejection.
- iOS locks scoped under "ios" (ios__<UDID>) so a simulator UDID can
  never collide with an Android serial; queue items carry platform,
  ios_workspace, and the pinned UDID, and workers spawn with
  --platform ios --ios-workspace without leaking ADB_DEVICE_SERIAL.
- Web API accepts platform="ios", validates UDIDs, skips the Android
  screen-lock probe, rejects locked_app_package, and lists simulators
  alongside Android devices in /api/devices.
- Live stream follows the active iOS lock owner and captures frames
  via simctl io screenshot; /api/stream/device-state reports platform.
- Replay preserves mobile_platform from session device_info, rebuilds
  iOS contexts with DevicePlatform.IOS, honors UDID overrides, and
  lists simulators as replay targets.
- MCP mobile_run_task accepts a platform argument (daemon + standalone
  paths), mobile_get_device_state observes iOS through
  UnifiedMobileController + XcodeSimulatorDriver, and mobile_diagnose
  adjusts verdicts/probes for iOS targets.
- An ios_simulators readiness probe reports Xcode/simulator/MCP
  availability without becoming a blocker for Android-only hosts.
- artemis run and artemis batch forward platform, device_serial, and
  ios_workspace through the daemon.

Android remains the default platform and its behavior is unchanged.
Android-only operations (shell, logcat, UIAutomator, AVD launch,
accessibility helper) fail explicitly for iOS.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The run command still carried a pre-queue guard that rejected iOS
workers and forced every iOS invocation into standalone mode, so
daemon-submitted iOS tasks died at worker startup with "The daemon
and device queue support Android only". Drop the stale checks; iOS
now daemon-routes like Android (--standalone still forces local),
and the worker path reaches execute_task with platform/ios_workspace
intact. Verified live: /api/run platform=ios now spawns the iOS
worker and reaches agent init, stopping only at the missing
GOOGLE_API_KEY.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The console frontend was Android-only even though the backend already
listed iOS simulators and accepted platform=ios runs:

- connectedDevices merges ios_simulators probe metadata (udid/name/
  state/runtime) into the device list, tagged platform=ios
- Device chips render phone_iphone + an iOS badge; the hero card shows
  the selected simulator with its runtime
- /api/system/devices/select accepts platform; iOS validates the UDID
  via the iOS device pool instead of retargeting the ADB probe
- iOS selection is tracked client-side and satisfies the run button
  device gate; runTask sends platform=ios + device_serial when a
  simulator is selected
- Sessions/queue items carry platform for an iOS tag in the
  session list

Android selection/readiness/submission paths are unchanged.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The connected-device panel only rendered when the ADB probe passed, so
on hosts without an Android device the iOS simulators could never be
selected — and selecting one was what made the panel appear:

- Show an "iOS Simulators" chip panel inside the no-device guide state
  whenever the ios_simulators probe reports simulators, independent of
  ADB state
- Selecting a sim flips the device step to ready and shows the hero card
- Retitle step 3 "Device & Emulator Connection" (Android via ADB or iOS
  Simulator via Xcode 27+)

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Verified the live console with headless Chrome: the diagnostics tab now
lists all simulators and clicking one selects it via
POST /api/system/devices/select {serial, platform:"ios"}. But the
launcher composer gave no hint which device a task would run on:

- Add a target-device-chip to the composer action bar showing the
  selected device with its platform tag; clicking it jumps to the
  setup guide to change the target
- Make the prerequisites warning platform-neutral ("device" not
  "Android device")

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The device guide card mixed Android-only controls (Restart ADB,
connection methods, AVD management) with the iOS simulator picker,
and the merged chip list made the run target ambiguous.

- Add platform tabs at the top of the card; auto-select the tab that
  has devices (or the platform of the currently selected simulator)
- Scope Restart ADB, Change Connection, Android hero/switcher,
  connection methods, AVD list, and locked/unauthorized states to the
  Android tab
- Give iOS its own panel: selected-simulator hero, simulator chip
  picker, and an empty state explaining the Xcode 27+ requirement
- Selecting a chip also switches the active tab so the chosen target
  stays visible; ready banner text is platform-neutral

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Each click on a simulator POSTed to /devices/select, which re-ran
"xcrun simctl list devices" inline to validate the UDID — 4+ seconds
on a busy host while the readiness probe spawned back-to-back simctl
calls of its own. The chip only marked itself selected after the
response, so clicks appeared to do nothing.

- Cache successful simctl enumerations for 10s in drivers/ios/
  discovery.py (shared by the readiness probe, device pool, and HTTP
  endpoints); failures are never cached, force_refresh bypasses, and
  the driver clears the cache after booting a simulator
- Apply iOS selection optimistically in the console since it is
  client-side state; revert only when the server rejects the pick

Select endpoint: ~4.3s -> ~0.1s. Chip shows Active instantly.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
TS6 requires rootDir to be explicit whenever outDir is set; the
compiler was inferring ./src anyway, so this only silences the
diagnostic without changing emit layout.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Runtime now renders as "iOS 27.0" instead of "iOS 27 0"
- Hero card reports the real simulator state: green "Booted" for
  running sims, amber "Shutdown — boots on run" otherwise (previously
  always claimed "Connected"), with a matching status dot instead of
  the hardcoded green pulse
- Drop the redundant per-chip "iOS" badge (the tab and panel already
  scope the list) and the cramped "(Shutdown)" suffix; state is now a
  colored dot — green Booted, grey Shutdown, amber Booting — with a
  tooltip for the raw simctl state
- iPad simulators use the tablet_mac icon in chips and the hero card

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The feature section and roadmap entry predated the web/daemon/MCP
integration commits and still described iOS as standalone-only; only
physical devices remain planned.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Extend the iOS driver family to paired physical devices. Device routing keys
off the requested UDID: serials resolving to a CoreDevice physical entry get
PhysicalIosDriver, while simulators and unknown serials keep the existing
XcodeSimulatorDriver path.

- discovery.py: devicectl list devices JSON parsing (current and deprecated
  property shapes) with the same fail-closed caching contract as simctl
- PhysicalIosDriver subclasses the simulator driver to inherit the Xcode
  DeviceInteraction session (screenshots, hierarchy, taps, swipes, text),
  overriding only lifecycle: devicectl install/launch/terminate/openURL/apps
  and pairing/connectivity checks with actionable errors
- connect() is decomposed into seams (_require_ios_host, _prepare_device,
  _start_interaction_session, _validate_session_device) so the physical
  driver asserts deviceIsSimulator=false without duplicating session setup
- PhysicalIosRecorder polls devicectl screenshots into timestamped frames
  and assembles MP4 segments with the ffconcat demuxer; rotation and seal
  boundaries roll segments, matching the simulator manifest contract
- IosDevicePool enumerates physical devices for the console/queue, validates
  explicit serials (paired + connected), and never auto-selects hardware
- CLI help, ios.md, and both READMEs updated for physical requirements and
  recording limitations

Physical automation requires Xcode 27+, a paired/trusted device with
Developer Mode enabled, and a device-signed .app/.ipa for installs.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Agent.init picks the driver via ios_driver_class so embedded-SDK iOS
  configs resolve physical UDIDs to PhysicalIosDriver
- device stream service captures physical frames via devicectl screenshot
  (simctl io screenshot does not exist on hardware)
- replay device picker lists paired physical devices for iOS retargeting
- smoke-test repair hints cover pairing/trust/Developer Mode and offline
  physical devices

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Xcode's DeviceInteraction* MCP tools accept simulators only — verified live
against a paired iPhone (the eligible-device list contains simulators
exclusively), so physical observation and input move to WebDriverAgent:

- wda.py: stdlib HTTP client (no new dependency) covering /status, /session,
  /source?format=json, /screenshot, /window/size, W3C pointer actions for
  tap/long-press/swipe, /wda/keys for text, /wda/homescreen and
  /wda/pressButton for keys, /wda/activeAppInfo for the foreground bundle.
- Endpoint resolution probes ARTEMIS_IOS_WDA_URL, ARTEMIS_IOS_WDA_HOST, the
  CoreDevice tunnel address from devicectl info details, and 127.0.0.1:8100
  for iproxy/pymobiledevice3 forwards.
- connect() locates an installed *WebDriverAgent* runner, launches it via
  devicectl, and optionally hosts a build-for-testing .xctestrun through
  xcodebuild test-without-building (ARTEMIS_IOS_WDA_XCTESTRUN); disconnect
  tears both down.
- Hierarchy flattens the WDA JSON tree into the same ui_elements shape
  (text/resource_id/class/parsed_bounds/hit_point) so controllers, element
  lookup, and tap_element are unchanged.
- devicectl JSON for info subcommands now goes through a scratch file —
  '--json-output -' stdout is polluted by the human-readable table on
  hardware — and terminate resolves PIDs by matching the app's install URL
  prefix against running executables.
- docs/ios.md and READMEs describe the WDA prerequisite, signing options,
  endpoint overrides, and the UI-Automation passcode gate.

47 physical unit tests pass; the simulator driver and Android paths are
untouched.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ing hardening

- ios_device_pool: never auto-select physical hardware; validators match
  by UDID or name and fail open when either enumeration is indeterminate
- sdk/agent: pick the iOS driver class in a worker thread so simctl/
  devicectl subprocesses never block the event loop during init
- physical_driver: tunnel-IP reads every observed devicectl JSON shape;
  exclusive runner/xctestrun hosting; broad connect cleanup; --kill
  terminate with stale-PID fallback; normalize file:// executables; IPA
  bundle id read from Payload Info.plist; scale sanity guard
- wda: map http.client.HTTPException, propagate transport errors from
  press_button, IPv6-safe URL normalization, bracket only v6 candidates
- physical_recording: monotonic frame indices across segment rolls,
  shielded stop, broad poll-loop failure accounting, watchdog notices a
  dead poller, ffmpeg timeout, atomic .part cleanup
- discovery: null-safe property reads, iPadOS classification
- diagnose/probe: physical serials reachable via mobile_diagnose; pool
  auto-pick stays simulator-only; platform-aware smoke labels
- Sweep stale 'simulator' wording across MCP tools, routers, schemas,
  daemon client, setup script, and docs; document XCTESTRUN + UI
  Automation consent; tests: generic fixtures, WDA env pinning,
  deterministic recorder seams

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…cels

- Mock _require_ios_host on the driver fixture so _resolve_device tests
  never spawn real xcodebuild or fail on non-macOS CI
- Synthetic UDID/name/URL fixtures (no real-looking hardware identifiers)
- Await the cancelled poll task on recording start failure
- Base the session poll/watchdog fields as instance defaults

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- list_apps: simctl emits OpenStep plists that plistlib cannot read;
  route through plutil -convert json (plistlib fast-path kept)
- connect(): reconnect when the MCP bridge retired but a stale session
  key remained; null-safe deviceUUID; widen connect-cleanup catch
- device_lock: annotate_active_owner preserves lock_scope and resolves
  scoped lock files; cleanup_stale_locks glob matches scoped names
- readiness: SKIPPED probes no longer force a degraded verdict on
  non-macOS hosts
- recording: kill+reap display-probe on timeout/cancel; bound crash-loop
  respawns (spawn success no longer resets the counter; only a segment
  surviving a healthy interval does); ffmpeg/probe timeouts; bound
  concurrent conversions; guard stale seal through_time
- bridge: scoped child env (no API keys into mcpbridge), start() lock,
  close() never masks caller errors
- adb_server: iOS controller no longer aliases into the Android global
- cli: batch --ios-workspace path validation; run no longer
  misclassifies iOS prerequisite errors as missing API keys
- stream: reap cancelled simctl child; drop stale frames on target
  switch; annotate active_tasks with platform; queue items surface
  platform in the UI mapper
- env scrub pops ARTEMIS_DEVICE_ID for iOS workers (daemon + MCP)
- device_smoke: platform-aware error wording for iOS
- docs: fix broken SDK builder example; mobile_run_task documents
  platform/ios_workspace; platform_guidance no longer claims video
  analysis is unavailable
- diagnose validates platform values; visualization resolves 0-1000
  coords on small screenshots
- pyright-core covers the new iOS runtime/actuator modules

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Operator/schema key enum gains POWER, VOLUME_UP, VOLUME_DOWN — the iOS
  driver and platform guidance already support them; both platforms map
  them natively (regenerated action-surface fixtures)
- action_names translation now keeps power/volume keys bare so iOS
  drivers receive supported names
- Diagnoser drops the logcat-only analyze_logs tool on iOS
- mobile_run_task app_path doc covers iOS .app directories
- Regression test pins the iOS key set to the operator gate

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Combines feat/ios-physical (devicectl discovery, PhysicalIosDriver with WDA backend, physical recording) with the simulator branch so a single PR delivers full iOS support: simulators + paired physical devices.

Conflict resolution keeps both sides' fixes: plutil listapps parsing, dead-bridge reconnect, scoped lock metadata, SKIPPED-neutral readiness, MCP env scrubbing, and physical driver/recording internals.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…elpers

- Shared physical_ios_ready() predicate across pool, probe, and driver paths (paired + connected-or-absent tunnelState)
- Sync and async validate_explicit_serial now share matching/state rules (UDID or device name)
- normalize_device_platform/device_pool_for/target_for_platform in adb_endpoint; adopted at CLI, admin, MCP surfaces
- BOOTED_SIMULATOR_ID and DEFAULT_MAX_DURATION_SECONDS replace magic literals across iOS code
- Shared helpers: reap_process, devicectl_screenshot, pixel_element, device_matches_identifier
- MCP worker env now reuses IosTarget.apply_to_environment; IOS_LOCK_SCOPE replaces 'ios' literals
- WDA-aware smoke hints and hierarchy_backend='wda' for physical devices
- Removed dead code: duplicate replay /api/devices route, ReplayManager.list_devices/_init_device, fetchDevices JS, no-op driver overrides, unused imports
- for_ios_device() is the canonical SDK method; for_ios_simulator kept as alias; IosDeviceProbe with stable probe id and class alias
- Docstring/wording sweep: 'iOS device' instead of simulator-only phrasing where hardware is supported

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Lease and close one-shot iOS observations; drain cancelled native and WDA requests; reap incomplete simulator recording starts and bound recovery loops. Preserve modern CoreDevice schema, unambiguous targets, per-platform queue identity, and physical-device selection without changing Android driver code.

Add remote SDK iOS platform/workspace forwarding with capabilities preflight to reject legacy hosts before submission. Keep Android payloads unchanged. Add regressions and resolve protected-core typing diagnostics without changing quality thresholds.

Verification: 2900 deterministic tests passed, 11 skipped; protected-core pyright reports 0 errors; Ruff and quality ratchets pass. Live simulator acceptance is blocked by Xcode agent consent; physical/Android hardware and the compatible frontend Node runtime are unavailable. No production-readiness claim.
Live acceptance on iPhone 15 Pro surfaced two defects that blocked every
normally-named physical device:

- WDA device_info reports the product family name ("iPhone"), not the
  personalized devicectl name ("Jane's iPhone"). The identity guard now
  rejects only a conflicting *specific* name instead of any inequality.
- A driver-launched WDA runner auto-creates a session on startup; the
  foreign-session refusal then deadlocked connect(). open_session gains
  adopt_existing, enabled only when the driver owns the runner process;
  discovered/user-provisioned endpoints keep the refusal.

Verified live: WDA session, 319-element hierarchy, tap/swipe/home, WDA and
devicectl screenshots, pid-verified terminate, MP4 recording, clean
disconnect with zero orphaned processes.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@google-cla

google-cla Bot commented Oct 7, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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.

Feature: native iOS support for simulators and paired iPhone/iPad devices

1 participant