Skip to content

docs(ios): record #2659 spike — can the AX bridge guest read the app's interface orientation? - #2667

Closed
thymikee wants to merge 1 commit into
mainfrom
t3code/implement-issue-2659
Closed

thymikee wants to merge 1 commit into
mainfrom
t3code/implement-issue-2659

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Answers the #2659 spike: can the Simulator AX bridge guest learn the foreground app's UIInterfaceOrientation cheaply and reliably at capture time? Measured on iPhone 17 Pro (iOS 26.2) with throwaway guests compiled exactly like the bridge and xcrun simctl spawn.

  • Route 1XC_kAXXCAttributeApplicationOrientation resolves (id 1503) via the bridge's own resolver, but userTestingSnapshotForElement: returns 0 on the root and every window in all orientations, and XCTest's appOrientationForElement:error: fails kAXErrorServerNotFound from a remote-access guest.
  • Route 3 — the only working service read, BKHIDServicesGetCurrentDeviceOrientation (~0.015 ms), is device orientation; it diverges from the app's interface orientation on a rotation-locked app (Settings stayed portrait (0,0,402,874) while the device read 3). SpringBoard interface getters return a fixed non-enum 0x10000003.
  • Route 2 — a keyboard-band heuristic only; blind without a keyboard and cannot name arbitrary rotated surfaces.

No route delivers a cheap, reliable interface-orientation fact, so #2653's detect-and-refuse layer and the ADR 0004 refusal stand; the delete-the-layer plan is not unlocked. Deliverable is the doc docs/ios-bridge-orientation-read-spike.md (Appendix carries the reproduction commands). No production code merged. Docs-only, one file.

Closes #2659.

Validation

Docs-only: pnpm check:affected --run → "Docs-only changes ignored … all runnable checks passed." No runtime path changed, so no device/runtime validation applies. The measurements behind the findings were taken live on the iPhone 17 Pro Simulator (iOS 26.2) via simctl spawn guests and agent-device orientation; the runner's own interfaceOrientationForApplication: value is not host-exposed, so interface-space truth was witnessed by captured window geometry plus a rotation-locked Settings divergence — recorded as a scope limit in the doc. Physical devices out of scope (bridge is Simulator-only).

…ion from the AX bridge

Measures all three candidate routes on iPhone 17 Pro (iOS 26.2). Route 1's
XC_kAXXCAttributeApplicationOrientation resolves to id 1503 but the guest's
snapshot channel returns 0 and XCTest's own reader fails kAXErrorServerNotFound;
route 3's only working read (BKHIDServicesGetCurrentDeviceOrientation) is device
orientation, which diverges from the foreground app's interface orientation on a
rotation-locked app; route 2 is a keyboard-band heuristic. No cheap, reliable
interface-orientation fact is available to the guest, so #2653's detect-and-refuse
layer and the ADR 0004 refusal stand.
@thymikee

Copy link
Copy Markdown
Member Author

Reviewed 2965c9c. This is a docs-only spike record, and its conclusion follows from the measurements it reports: none of the three routes gives a cheap, reliable interface-orientation fact, so the #2653 refusal stays. I did not re-run the Simulator measurements; the appendix commands make them reproducible. Checks are green, and this is ready for human review.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 18, 2026
@thymikee

Copy link
Copy Markdown
Member Author

Closing unmerged on purpose: the answer is a negative result and does not need a 200-line doc in the repo. The verdict and the bar for a future attempt are recorded on #2659; ADR 0004 and window-coordinate-space.ts point there from #2664. This diff stays readable here as the full write-up with the probe source. Thanks, this was exactly the measurement the issue asked for.

@thymikee thymikee closed this Sep 18, 2026
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-18 11:24 UTC

thymikee added a commit that referenced this pull request Sep 18, 2026
The ADR 0004 refusal decision and the TS detector's header now cite the measurement (issue #2659,
diff of #2667): the AX orientation attribute resolves but reads 0 through the guest channel, and
the only cheap service read is device orientation, which diverges on a rotation-locked app.
thymikee added a commit that referenced this pull request Sep 19, 2026
…ackage (#2664)

* refactor(ios): declare bridge failure scope at the throw site, disclose unplaced surface hosts, move the coordinate-space rule into the presentation package

Three follow-ups to #2653 (fixes #2612), each inside that PR's own new code.

Failure scope. Whether a Simulator AX bridge failure retires the app generation from the bridge
was decided at the route by matching the failure's kind and code. SnapshotSourceFailure now
carries scope: 'capture' | 'generation', set where the failure is thrown
(snapshotSourceCaptureError for bridge-preparation-pending and window-coordinate-space-unresolved),
and the route reads the field.

Disclosure. A runner capture that could not turn a rotated surface host back (orientation
unreadable, or the private-AX tier anchored on the bridge's root box) published two coordinate
spaces silently. SnapshotGeometrySpace.unplacedSurfaceHostCount runs after every tier's
acquisition; a non-zero count rides the quality verdict as unresolvedCoordinateSpaceWindows and the
daemon renders a warning.

Package. The rotation table and the coordinate-space rule are pure CoreGraphics and now live in
AgentDeviceSnapshotPresentation (SnapshotCoordinateSpace.swift) with their tests in the package
suite, so swift test replays the golden table without a simulator. isSurfaceHost takes booleans
instead of an XCUIElement.ElementType.

* docs(ios): point the bridge refusal decision at the #2659 spike verdict

The ADR 0004 refusal decision and the TS detector's header now cite the measurement (issue #2659,
diff of #2667): the AX orientation attribute resolves but reads 0 through the guest channel, and
the only cheap service read is device orientation, which diverges on a rotation-locked app.

* refactor(ios): make failure scope a required constructor arg; count a turned surface once

Addresses review on #2664.

- SnapshotSourceError takes scope as a required argument (no 'generation' default), positioned
  after the required code. The two factories name their side: snapshotSourceError -> 'generation',
  snapshotSourceCaptureError -> 'capture'. New errors.test.ts pins both.
- unplacedSurfaceHostCount folds a turned surface's window and the surface directly under it into
  one host, so the disclosed count is a surface count even when both report the turned box. Package
  test covers the both-turned pair.

* refactor(ios): keep only the coordinate-space package move; drop scope field and unplaced-geometry disclosure

Measured against main with npm pack: the earlier +9.3 kB was 6.8 kB of prose comments in the moved
file (the presentation package is written comment-free and copied verbatim), ~1.5 kB of disclosure
plumbing, and ~0.2 kB for the failure scope field. The move alone is now +25 bytes.

- Moved file rewritten in the package's comment-free style; rationale stays in ADR 0004 and the
  golden fixture description.
- Failure scope field reverted to main's opensGenerationCircuit predicate: a required constructor
  argument guarded what the route tests already guard, for two fixed codes.
- unresolvedCoordinateSpaceWindows disclosure reverted: its only trigger path (private-AX tier with
  a penalized XCTest channel) anchors on a root frame whose space is unmeasured and is infinite in
  the common case, so the count is zero by construction there; never observed live. Noted on #2661
  where one-pass normalization makes it a measured one-liner if ever wanted.
- ADR 0004 keeps the #2659 spike pointer and the package-home sentence only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spike(ios): can the Simulator AX bridge guest read the app's interface orientation?

1 participant