feat(ios): pair plain and honor-modal AX reads in the alert surface corpus
Merged
TH
4h ago+3605+3605+0123456789012345678901234567890123456789-640-640-012345678901234567890123456789
Summary
PR 1 of 3 for modal-scoped alert. No behavior change for alert or snapshot.
- The runner reads one app three times: honor-modal (
snapshotKeyHonorModalViews), plain, honor-modal again. Each node carries its AX identity and its parent's. Pairing is linear on (element, parent). Typed outcomes cover unsupported, failed, and unstable (truncated, unmatched, reparented, moved, screen changed). - Corpus format 2 records both trees, the read order, timings, and the toolchain. All fixtures are re-recorded. New: Settings page sheet, Settings without a modal. The sheet's
expectpins today'sdismissPopupoutput. - Finding:
XCUIApplication.snapshot()equals the honor-modal tree on all 7 recordings, soexpectreplays onhonorModal. - The capture runner has its own bundle id, so it runs beside a session runner.
20 files.
Validation
check:affected --run --head f7d69df7b9 passed. Host swift test 36/36. iOS and macOS runner builds pass.
iPhone Duo, iOS 27.1. Nodes plain/honor; ms honor/plain/confirm:
| Scenario | Nodes | ms |
|---|---|---|
| Settings, no modal | 237/237 | 144¹/40/40 |
| Settings sheet | 426/86 | 21/104/14 |
| SpringBoard alert, folded | 248/41 | 14/44/12 |
| SpringBoard alert, unfolded | 243/40 | 13/38/10 |
| SpringBoard, no alert | 337/337 | 57/48/45 |
| Safari prompt, folded | 85/67 | 29/15/22 |
| Safari prompt, unfolded | 93/75 | 16/15/12 |
¹ Warm-up read.
Positive control: each prompt removes nodes; each screen without one removes none (asserted per recording).
Not verified: physical devices, iOS before 27.1, iPad popovers, React Native Modal, SFSafariViewController.
🤖 Generated with Claude Code