fix(settle): keep iOS keyboard chrome classified through interactive capture

Merged
TH
3d ago
+880-22

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 Keyboard to both projection eligibility sets (TS REGULAR_ELIGIBLE_TYPES + Swift eligibleInteractiveTypes, pinned by a shared swift: true golden case).

  • Classify the flattened surfaces from one iOS keyboard-role table in @agent-device/contracts/android-input-ownership (shared with tap-keyboard-occlusion.ts): surface roles chrome per node, subtree claims only under system-only hosts (UIKeyboardLayoutStar, TUIPredictionView), and MARKER roles (inputView placeholder, 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 waitedMs1734754
captures52–3
[key] diff lines380
noise survivors02 (floating toolbar, inert QuickType scroll wrapper)
[keyboard] line01

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.