Reliability pass driven by a week of failure telemetry. Five tools stop reporting a failure for a state that was never one, and start naming the cause when it is.
dismiss_keyboard — nothing focused with the keyboard already down is a success, not an error. The caller asked for the keyboard down and it is down; reporting it as a failure sent agents hunting for a field to blur that does not exist. Nothing focused with the keyboard up stays a failure and now says which one: a native field owns it (system dialog, WebView, non-RN screen), so blurring React cannot close it, and the error points at the platform dismiss.
ios_terminate_app — an app that had already quit is reported as stopped. simctl exits 3 with "found nothing to terminate", which accounted for every terminate failure in the week. It gives the same error for a bundle id that is not installed, so the result never claims a termination happened: it says the app was not running, and to check the bundle id.
input_text — a testID on a wrapper around the input now resolves. A custom <Input testID="x"> that keeps the id on its container and passes only value/onChangeText down is the ordinary RN shape, and it was invisible. Accepted only when exactly one mounted input sits under the wrapper; one wrapping several is refused rather than guessed between. A screen where no input carries a testID at all is told so, instead of having the invented id echoed back. Separately, pure ASCII sent and another script back ("testing" landing as "Еуіештп") is now named as the simulator's active keyboard layout reinterpreting US keycodes, rather than a bare "landed differently" that sends the reader into the app's validation.
navigate — an app written as a plain <NavigationContainer> with no ref prop is reachable again. The fiber walk looks for resetRoot, which only a container ref has, so the default shape matched nothing. A mounted screen's own navigation object, climbed to the root navigator, is used as a last resort; it has no getRootState, so the route table comes back empty and destination validation is skipped rather than run against nothing, and the note says so. A real container ref still wins. A missing destination is tagged as a bad-arguments refusal at the refusal site.
execute_in_app — accepts code and script as aliases for expression. An undeclared key is stripped before the handler sees it, so JavaScript sent under either name arrived as a call with no arguments at all.
Network — the JS interceptor is suppressed as soon as the SDK global exists, rather than after the first mirror.
Docs: the shipped usage guide, CLAUDE.md, docs/tools.md and the device-interact skill all describe the changed behaviour, and a remap message that named a write:"native" parameter the tool does not have is corrected.