fix(ios): count the keyboard leaving as a post-gesture change

Merged
KD
12h ago
+447-42

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 --run at 975015d9d: passed, including packaged-runner-swift and xctest-selection.
  • Live iOS run at 12e96f99a vs base 035c2b6ca: iPhone 17 Pro simulator, iOS 26.4, M3 Max, keyboard-only dismiss by swipe (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, treeBound visible โ†’ absent. Default route: unchanged (about 3.7 s). no-effect in 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
Open in [code]smith ยท Need help on this PR? Tag @codesmith-bot with what you need.