You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
OpenSafari's app_tap_element and app_double_tap cannot reliably drive a Flutter app on an iPhone 17 Pro / iOS 26.4 simulator. Two failure surfaces are entangled and both must be closed before the App Review verification flow (settings, notification opt-in, IAP, account deletion, UGC controls) can run autonomously:
AX dump empty on Flutter sub-screens (e.g. the in-app Ducats Shop route). ax-bridge-native dump exits non-zero with DEVICE_CONTENT_ROOT_EMPTY; the matched device window has descendants but every candidate the walker scores reports appSemanticsCount: 0.
Coordinate space mismatch between the AX frames returned by the bridge (in macOS-screen-points relative to the device-content-root, ≈ 697×1515 on this combination) and the iOS-points the SimHID dispatcher consumes (402×874 on iPhone 17 Pro). Even when the AX tree is populated, taps issued at the AX-frame center can land outside the visible target.
Environment
App under test: Flutter app com.omofictions.omofictionsApp
Tested live in this session against the booted device above. PR #691's walker --debug events confirm both surfaces; quoted output is verbatim from the failing dump invocation.
Surface A — AX dump empty on the Ducats Shop screen
Cold-launch the app: xcrun simctl launch <UDID> com.omofictions.omofictionsApp. App restores to the in-app Ducats Shop route.
Run ax-bridge-native dump --device <UDID> --max-depth 12 --debug.
walker_app_windows_enumerated count=2 (iPhone 17 Pro device window + AXMenuBar)
walker_top_candidates totalCandidates=8, all 5 top entries have appSemanticsCount=0
[0] depth=1 role=AXGroup score=5 (matches iOSContentGroup trait)
[1..4] depth=1-2 role=AXButton/AXGroup score=0..-5
walker_overlay_roles_seen count=0
walker_winner depth=1 role=AXGroup score=5 appSemanticsCount=0
content_root_empty
The screen renders normally (5 product cards, "Buy Ducats" button, bullet copy, "Terms of Service" link), so the AX subtree exists in pixels but Flutter has not exposed Semantics widgets to the macOS AX bridge. ensureSemanticsActive (the simctl spawn defaults write com.apple.Accessibility ApplicationAccessibilityEnabled -int 1 path) does not repopulate the tree on its own; the dump still exits non-zero after the flag flip.
Surface B — AX-frame to iOS-point coordinate mismatch
On a screen where AX is populated (e.g. the bottom-tab Home page, per the original report), app_tree returns the My element at logical frame x=522.75, y=1317.39, width=174.25, height=138.67, center (609.875, 1386.73) — these are macOS-screen-points relative to the device-content-root origin.
The simulator screenshot is 1206 × 2622 px (iPhone 17 Pro @3x).
iPhone 17 Pro iOS-points: 402 × 874. Ratio of AX-frame size (697×1515) to iOS-points (402×874): 697/402 ≈ 1.733, 1515/874 ≈ 1.733.
sim-hid-bridge.swift:336-349 documents that IndigoHIDMessageForMouseNSEvent consumes coordinates in iOS-points, divided by mainScreenScale when the runtime reports mainScreenSize in pixels (the Xcode 26+ regression fixed in test(input): SimulatorKitHID 통합 테스트 — 실 시뮬레이터 Settings.app 자동화 검증 #491).
app-tap-element.ts:211-212 builds rawCenterX/rawCenterY directly from match.frame.x + match.frame.width/2 and forwards to backend.tap(deviceId, centerX, centerY, …) without any macOS-pt → iOS-pt conversion. On iPhone 17 Pro at the Simulator zoom in use, the dispatched coordinates are ~1.733× larger than the iOS target on each axis.
Observed
app_tap_element returns status: tapped but the UI does not change.
The post-tap context probe surfaces POST_TAP_CONTEXT_PROBE_FAILED because the dump that follows the tap hits the same DEVICE_CONTENT_ROOT_EMPTY exit as the pre-tap dump.
app_tap_element(label: "My") converts the AX frame from macOS-points to the iOS-point input space the SimHID backend expects, and the dispatched tap lands on the visible bottom-tab center.
Post-tap verification recovers the AX tree on Flutter screens that don't cold-expose semantics, OR returns a typed diagnostic that distinguishes "Flutter Semantics not yet active" from "AX server crashed" from "app backgrounded".
Root-cause hypothesis
Surface A
ensureSemanticsActive's simctl-defaults activation (ApplicationAccessibilityEnabled) writes the flag but does not currently force Flutter's SemanticsBinding.ensureSemantics callback on a screen the framework already considers settled. The Dart VM Service fallback (Strategy C in src/native/semantics-activator.ts:14-19) is the only mechanism that materialises the tree from a settled state via debugDumpSemanticsTreeInTraversalOrder, and that path requires a debug or profile build. Production / release builds — the App Review verification target — exhaust the activator's strategy ladder and surface DEVICE_CONTENT_ROOT_EMPTY to the wrapper.
Surface B
app-tap-element.ts:211-212 treats match.frame as iOS-points but the AX bridge reports it in macOS-screen-points after subtracting the device-content-root origin (src/native/ax-bridge.swift:234-235). On a Simulator window scaled so 1 iOS-pt ≠ 1 macOS-pt — observed at 1.733× on iPhone 17 Pro / iOS 26.4 in this session — the conversion factor is missing. The bridge already knows the device-content-root size in macOS-pt; SimHID already knows iOS-pt size via getScreenSize. Joining the two in either layer (bridge emits the conversion factor on every dump, OR app_tap_element queries the size and divides) closes the gap.
Plan
WU1 — Diagnostic surfacing (no behaviour change): when the wrapper observes DEVICE_CONTENT_ROOT_EMPTY, surface the walker --debug data (top candidates + overlay roles + window enumeration) in the thrown error message instead of the bare Command failed: … tail. Forces the diagnostic onto the user's tool output without requiring a manual re-run. Depends on PR feat(ax-bridge): walker candidate diagnostics on --debug (#660) #691.
WU2 — Surface A: production-build Semantics activation. Investigate whether the simctl spawn defaults write com.apple.Accessibility ApplicationAccessibilityEnabled -int 1 flag combined with a process re-foregrounding nudge (or a user-initiated tap on a non-Semantics surface) reliably fires SemanticsBinding.ensureSemantics on release builds. If it does, retry once after the activation step in ensureSemanticsActive Strategy B. If it does not, document the limitation in a recipe and surface a typed FLUTTER_SEMANTICS_INACTIVE error code so the wrapper can offer a structured fallback message rather than the current opaque DEVICE_CONTENT_ROOT_EMPTY.
WU3 — Surface B: AX-frame to iOS-point conversion. Emit the device-content-root macOS-pt size from ax-bridge.swift on the dump root (alongside chromeOnly), have app-tap-element.ts look up iOS-pt size via simctl runtime info and scale centerX / centerY per axis before calling backend.tap. Apply the same conversion to app_double_tap and any other coordinate-from-AX-frame call site.
WU4 — Live integration test for both surfaces, opt-in gated similar to test(integration): gated ko-KR push-permission live suite (#660) #692. Asserts a tap on a known label lands on the visible target (verified via post-tap AX recovery + screenshot diff), and that the dump on a Flutter sub-screen returns a non-empty tree under each tier of the activator ladder.
WU5 — Recipe documenting the AX-frame coordinate space and the production-build Semantics-activation guarantee level (debug vs profile vs release).
Exit criteria
app_tap_element { label: "My" } on a populated Home tab dispatches a tap whose post-tap context probe confirms the navigation (Home → My).
app_tree on the Ducats Shop sub-screen returns a tree whose flattened node count exceeds MIN_NODE_THRESHOLD (5) on both debug and release builds, OR the wrapper surfaces a typed FLUTTER_SEMANTICS_INACTIVE error so the caller can branch.
Walker --debug data is included verbatim in the error returned by ax-bridge wrapper when DEVICE_CONTENT_ROOT_EMPTY fires (no manual re-run required).
WU4's gated integration test passes against an iPhone 17 Pro / iOS 26.4 simulator with the omofictions app cold-launched.
Summary
OpenSafari's
app_tap_elementandapp_double_tapcannot reliably drive a Flutter app on an iPhone 17 Pro / iOS 26.4 simulator. Two failure surfaces are entangled and both must be closed before the App Review verification flow (settings, notification opt-in, IAP, account deletion, UGC controls) can run autonomously:Ducats Shoproute).ax-bridge-native dumpexits non-zero withDEVICE_CONTENT_ROOT_EMPTY; the matched device window has descendants but every candidate the walker scores reportsappSemanticsCount: 0.Environment
com.omofictions.omofictionsAppD7D26213-C3E9-4623-BCCB-984CDF5D0793/Users/jh0927/opensafari/dist/ax-bridge-nativeReproduction
Tested live in this session against the booted device above. PR #691's walker
--debugevents confirm both surfaces; quoted output is verbatim from the failingdumpinvocation.Surface A — AX dump empty on the Ducats Shop screen
xcrun simctl launch <UDID> com.omofictions.omofictionsApp. App restores to the in-appDucats Shoproute.ax-bridge-native dump --device <UDID> --max-depth 12 --debug.DEVICE_CONTENT_ROOT_EMPTY. The new walker debug events from feat(ax-bridge): walker candidate diagnostics on --debug (#660) #691 show the precise failure shape:Semanticswidgets to the macOS AX bridge.ensureSemanticsActive(thesimctl spawn defaults write com.apple.Accessibility ApplicationAccessibilityEnabled -int 1path) does not repopulate the tree on its own; the dump still exits non-zero after the flag flip.Surface B — AX-frame to iOS-point coordinate mismatch
app_treereturns theMyelement at logical framex=522.75, y=1317.39, width=174.25, height=138.67, center(609.875, 1386.73)— these are macOS-screen-points relative to the device-content-root origin.1206 × 2622px (iPhone 17 Pro @3x).402 × 874. Ratio of AX-frame size (697×1515) to iOS-points (402×874):697/402 ≈ 1.733,1515/874 ≈ 1.733.sim-hid-bridge.swift:336-349documents thatIndigoHIDMessageForMouseNSEventconsumes coordinates in iOS-points, divided bymainScreenScalewhen the runtime reportsmainScreenSizein pixels (the Xcode 26+ regression fixed in test(input): SimulatorKitHID 통합 테스트 — 실 시뮬레이터 Settings.app 자동화 검증 #491).app-tap-element.ts:211-212buildsrawCenterX/rawCenterYdirectly frommatch.frame.x + match.frame.width/2and forwards tobackend.tap(deviceId, centerX, centerY, …)without any macOS-pt → iOS-pt conversion. On iPhone 17 Pro at the Simulator zoom in use, the dispatched coordinates are ~1.733× larger than the iOS target on each axis.Observed
app_tap_elementreturnsstatus: tappedbut the UI does not change.verified: false,effect: "verification_unavailable".POST_TAP_CONTEXT_PROBE_FAILEDbecause the dump that follows the tap hits the sameDEVICE_CONTENT_ROOT_EMPTYexit as the pre-tap dump.app_treecalls intermittently fail with the sameDEVICE_CONTENT_ROOT_EMPTYerror and PR feat(ax-bridge): walker candidate diagnostics on --debug (#660) #691's walker events confirmappSemanticsCount=0across all candidates.Expected
app_tap_element(label: "My")converts the AX frame from macOS-points to the iOS-point input space the SimHID backend expects, and the dispatched tap lands on the visible bottom-tab center.Root-cause hypothesis
Surface A
ensureSemanticsActive's simctl-defaults activation (ApplicationAccessibilityEnabled) writes the flag but does not currently force Flutter'sSemanticsBinding.ensureSemanticscallback on a screen the framework already considers settled. The Dart VM Service fallback (Strategy C insrc/native/semantics-activator.ts:14-19) is the only mechanism that materialises the tree from a settled state viadebugDumpSemanticsTreeInTraversalOrder, and that path requires a debug or profile build. Production / release builds — the App Review verification target — exhaust the activator's strategy ladder and surfaceDEVICE_CONTENT_ROOT_EMPTYto the wrapper.Surface B
app-tap-element.ts:211-212treatsmatch.frameas iOS-points but the AX bridge reports it in macOS-screen-points after subtracting the device-content-root origin (src/native/ax-bridge.swift:234-235). On a Simulator window scaled so 1 iOS-pt ≠ 1 macOS-pt — observed at 1.733× on iPhone 17 Pro / iOS 26.4 in this session — the conversion factor is missing. The bridge already knows the device-content-root size in macOS-pt; SimHID already knows iOS-pt size viagetScreenSize. Joining the two in either layer (bridge emits the conversion factor on every dump, ORapp_tap_elementqueries the size and divides) closes the gap.Plan
DEVICE_CONTENT_ROOT_EMPTY, surface the walker--debugdata (top candidates + overlay roles + window enumeration) in the thrown error message instead of the bareCommand failed: …tail. Forces the diagnostic onto the user's tool output without requiring a manual re-run. Depends on PR feat(ax-bridge): walker candidate diagnostics on --debug (#660) #691.simctl spawn defaults write com.apple.Accessibility ApplicationAccessibilityEnabled -int 1flag combined with a process re-foregrounding nudge (or a user-initiated tap on a non-Semantics surface) reliably firesSemanticsBinding.ensureSemanticson release builds. If it does, retry once after the activation step inensureSemanticsActiveStrategy B. If it does not, document the limitation in a recipe and surface a typedFLUTTER_SEMANTICS_INACTIVEerror code so the wrapper can offer a structured fallback message rather than the current opaqueDEVICE_CONTENT_ROOT_EMPTY.ax-bridge.swifton the dump root (alongsidechromeOnly), haveapp-tap-element.tslook up iOS-pt size viasimctl runtime infoand scalecenterX / centerYper axis before callingbackend.tap. Apply the same conversion toapp_double_tapand any other coordinate-from-AX-frame call site.Exit criteria
app_tap_element { label: "My" }on a populated Home tab dispatches a tap whose post-tap context probe confirms the navigation (Home → My).app_treeon the Ducats Shop sub-screen returns a tree whose flattened node count exceedsMIN_NODE_THRESHOLD(5) on both debug and release builds, OR the wrapper surfaces a typedFLUTTER_SEMANTICS_INACTIVEerror so the caller can branch.--debugdata is included verbatim in the error returned byax-bridgewrapper whenDEVICE_CONTENT_ROOT_EMPTYfires (no manual re-run required).Related
mainScreenSize(the upstream of the SimHID coordinate-space contract)