fix(ios): count the keyboard leaving as a post-gesture change
Summary
A drag that only dismisses the soft keyboard moves no content. On iOS, the post-gesture quiet pair then matched the pre-gesture baseline on content, so the baseline distrust check polled until its 3.5 s cap and returned a stale-accepted read.
Keyboard facts (SnapshotState.keyboard) now carry treeBound when the producer read them from the capture's own tree: the iOS XCTest runner and the Android helper (visible, absent) and the docked-tree derivation. A separate visibility probe, such as WebDriver (Appium) after page source, never sets it. The verdict counts the keyboard as a change only when the baseline and the quiet capture carry disagreeing tree-bound facts; anything else falls back to content comparison.
Scope: captures through the XCTest runner. The simulator default route (simulator-ax-bridge) emits no keyboard fact yet, so it still waits for the cap; that follows in a separate PR. Android carries no baseline, so its verdict does not change.
Validation
pnpm check:affected --base upstream/main --runat975015d9d: passed, includingpackaged-runner-swiftandxctest-selection.- Live iOS run at
12e96f99avs base035c2b6ca: iPhone 17 Pro simulator, iOS 26.4, M3 Max, keyboard-only dismiss byswipe(10 runs per cell, raw data in an earlier PR comment). Runner route (snapshot --observe-only): stale-accept 3720 ms โ trust 865 ms, 12 โ 3 captures,treeBoundvisibleโabsent. Default route: unchanged (about 3.7 s).no-effectin none of 40 runs. Later commits change only tests and docs. - Not run: Swift runner unit tests locally, physical devices, iOS 26.5, Android.
AI assistance and human review
AI was used to prepare these changes. Human-reviewed by @kdy1.
- Harness: T3 Code (Claude Code)
- Model: claude-opus-5-5
- Reasoning Effort: high
๐ค Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith ยท Need help on this PR? Tag @codesmith-bot with what you need.