fix(settle): keep iOS keyboard chrome classified through interactive capture
Summary
--settle on iOS lost keyboard-chrome classification in interactive captures: the regular projection dropped the Keyboard container as ineligible, re-parenting ~40 Key nodes to the app root where collectKeyboardChrome could not anchor them, and the interactive projection flattens QuickType/dock siblings beside app content entirely. Every keyboard-raising fill paid the 1.5s broad-transition confirmation window on a keyboard pop read as a screen replacement.
-
Add
Keyboardto both projection eligibility sets (TSREGULAR_ELIGIBLE_TYPES+ SwifteligibleInteractiveTypes, pinned by a sharedswift: truegolden case). -
Classify the flattened surfaces from one iOS keyboard-role table in
@agent-device/contracts/android-input-ownership(shared withtap-keyboard-occlusion.ts): surface roles chrome per node, subtree claims only under system-only hosts (UIKeyboardLayoutStar,TUIPredictionView), and MARKER roles (inputViewplaceholder, assistant view + page view) that own no subtree — an app can fill those hosts, so app content under them survives. -
The broad-transition check now compares a chrome-free candidate signal (
withoutSettleChrome), so the diff and the quiet-window verdict share one classifier.
Closes #3469.
13 files. Live evidence, iPhone 17 sim (iOS 27), Catalog fill id=catalog-search <kit|loaf> --settle, 10 alternating runs each:
| before (main) | after | |
|---|---|---|
median waitedMs | 1734 | 754 |
| captures | 5 | 2–3 |
[key] diff lines | 38 | 0 |
| noise survivors | 0 | 2 (floating toolbar, inert QuickType scroll wrapper) |
[keyboard] line | 0 | 1 |
Validation
Tested at faf754aa6. pnpm check:affected --run passed there (unit, layering, fallow, Swift differential with the new swift: true golden case, mutation-lane selection); the device table below was captured at e2ad8997e; a fresh live re-run at faf754aa6 is in the comments (median 1734 -> 786 ms, captures 5 -> 2, zero [key] lines). Live device run above executed the changed path end-to-end via the rebuilt CLI. No user-facing flags or help change. Known adjacency: the replacementSignal wiring touches the same call site #3470 proposes to restructure; no rule/default-window change is included here.