* feat(ios): add simulator management package
* feat(testrun): add iOS platform path (simctl launch + direct TCP)
* feat(cli): add --ios-device flag and IosDevice option
* feat(sdk-ios): add Kotlin Native iOS SDK (TCP agent + POSIX socket + dispatch pauser)
* feat(folio-ios): wire SanderlingIos.start() in MainViewController
* feat(folio-ios): add test-ios justfile recipe
* fix(sdk-ios): remove unavailable C macros; manual byte swap + no-cast warnings
* fix(testrun): simctl-first launch order for iOS; Maestro init after SDK connects
* feat(proto): add env map to LaunchRequest
* feat(driver): add env param to Launch interface + all implementations
* feat(testrun): launch iOS app via XCTest with env vars instead of simctl
* feat(sidecar): add IosDriverBackend using Maestro IOSDriver + env pass-through
* feat(sidecar): wire env map in DriverService + IosDriverBackend in Main
* fix(sidecar): use LocalIOSDevice (WDA+simctl) + stop before relaunch
* fix(sidecar): include exception type in gRPC error description
* fix(sidecar): pick free WDA port instead of hardcoded 9100
Use SocketUtils.nextFreePort to pick a free port in the 22000-23000 range
rather than hardcoding 9100, which only worked if a previous WDA session
left a listener there.
* fix(sdk-ios): check semaphore wait result and throw on snapshot timeout
dispatch_semaphore_wait returns nonzero on timeout; ignoring the return
value caused pauseAndSnapshot to silently return an empty map, sending a
garbage empty STATE frame to the host. Now throws so the agent loop
reconnects instead.
* fix(folio-ios): register snapshot extractors before starting agent
SanderlingIos.start() was called before the snapshot objects were
initialized, so a PAUSE message arriving early produced an empty snapshot.
Move start() to after all extractors are registered.
* chore(ios): remove dead LaunchApp function
LaunchApp had no callers since bff3a49 switched iOS launch to go through
the sidecar driver. Remove the dead code and unused os import.
* test(ios): add unit tests for pickSimulator and iOS flag parsing
Tests for all pickSimulator branches (by name, by UDID, unknown, empty
list, iPhone preference, fallback to first). Also tests BootedUDID on a
canceled context and verifies --platform ios and --ios-device flags are
accepted by parseTestArgs.
* fix(ios): propagate error from BootedUDID instead of silently swallowing
* fix(sidecar): IosDriverBackend.healthy() returns true; WDA liveness checked in open()
* fix(sidecar): warm up WDA after health check to absorb startup race
* fix(runner): surface clear message on WDA connection drop
* docs(testrun): note WDA warmup location above WaitForHealth
* fix(sidecar): extract warmup + add one-shot WDA reconnect on IOException
* fix(runner): fatal on permanent WDA drop during hierarchy fetch
* fix(sidecar): walk cause chain in withReconnect to catch Maestro-wrapped IOException
* fix(sidecar): explicit Unit return in pressKey and waitForIdle withReconnect lambdas
* fix(sidecar): serialize WDA reconnect with ReentrantLock to prevent concurrent xcodebuild races
* fix web examples package config
* Revert "fix web examples package config"
This reverts commit 70c10ade27.
* chore: gitignore built sanderling binary
* fix(hierarchy): parse iOS [x1,y1][x2,y2] bounds + match iOS merged desc labels
* refactor(folio-spec): replace bloated spec with two focused properties
Login is opportunistic. Two concrete properties:
1. every new account starts with balance 0
2. every new txn changes ledger balance by exactly its signed amount
Actions: directed login -> addAccount -> addTxn -> back weighted flow.
* proto(driver): drop launcher_activity from LaunchRequest
* refactor(driver): rename Driver to DeviceDriver, drop launcherActivity from Launch
* refactor(driver): rename package maestro to sidecar
* feat(driver): add ChromeDriver backed by chromedp
* feat(hierarchy): replace XML parser with TreeNode JSON parser
* refactor(driver): update mock and runner to DeviceDriver, drop launcherActivity
* feat(runner): add platform routing for web vs sidecar
* test(verifier): update hierarchy fixtures from XML to TreeNode JSON
* feat(sidecar): extract readLogcat/readProcMetrics; add MaestroDriverBackend
- Extract readLogcat() and readProcMetrics() as internal package-level
helpers parameterised by serial
- Add MaestroDriverBackend wiring maestro-client AndroidDriver
- Drop launcherActivity from DriverBackend interface and StubDriverBackend
- Add maestro-utils and micrometer-core as explicit compile deps
* refactor(sidecar): inject DriverBackend into DriverService; drop launcherActivity
- Remove serial and default backend from DriverService constructor
- Make backend a required parameter
- Drop launcherActivity from launch RPC handler
- Create MaestroDriverBackend in Main.kt when platform is android
* test(sidecar): update tests for dropped launcherActivity and required backend
* chore(sidecar): remove unnecessary micrometer-core direct dependency