Skip to content

fix(apple-runner): accept the iOS 27 floating-bar back control outside the classic header band - #3375

Merged
thymikee merged 2 commits into
mainfrom
fix/ios27-floating-bar-back-3333
Oct 10, 2026
Merged

thymikee merged 2 commits into
mainfrom
fix/ios27-floating-bar-back-3333

Conversation

@thymikee

@thymikee thymikee commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Summary

Fixes back on the iOS 27.1 Simulator when the app uses the SwiftUI floating toolbar.

That toolbar places the system back control (identifier=BackButton) at about 29% of the window's height, at midY 194 in a 678-pt window. That is below the classic header band, which ends at 149 here. The runner found the control by keyword but rejected its frame. It then tapped the leading fallback point, which on this screen is empty space.

The new isTopNavigationBackCandidateFrame keeps the classic band for every keyword match. It also accepts an exact BackButton identifier anywhere in the upper 40% of the window. The region is bounded, so a control deep in the content is still never tapped, and a keyword-only match at the same spot is still rejected. InAppBackOutcome, the unknown-vs-unavailable split (#2728), visual verification, and the macOS and tvOS branches are unchanged.

Closes #3333.

Validation

  • Four new runner unit tests cover the 27.1 frame shape, a keyword-only match below the band, the 40% bound, and the classic band. All pass on the iPhone Duo (iOS 27.1) runner build.
  • Live, iPhone Duo simulator, iOS 27.1: 01-settings.ad passed all 7 steps in 6 of 6 runs. The back step took 1.14–1.23 s. runner.log shows the element tap at (418, 194) and no leading fallback. Afterwards, is exists "label=About" is false.
  • pnpm check:affected --run passed.
  • CI (iOS 26.x) does not reach this path, so only a local Duo run exercises it.

🤖 Generated with Claude Code

View guided diff Turn on auto-fix

…e the classic header band (#3333)

On iOS 27.1 the SwiftUI floating toolbar hosts the back control as a
_UIButtonBarButton with identifier BackButton at midY 194 in a 678pt window,
below the classic header band (maxY 149). topNavigationBackElement matched it
by keyword but isTopNavigationControlFrame rejected it, and the leading
fallback then tapped empty space.

Add isTopNavigationBackCandidateFrame: the classic band still admits any
keyword match, and an exact BackButton identifier (UIKit's system back
identity, exposed by XCUI) is additionally admitted anywhere in the upper 40%
of the window. It is bounded rather than a bypass, so a deep content control is
never tapped, and keyword-only matches such as a "back-link" button at the same
position stay rejected. Ranking, InAppBackOutcome, the unknown-vs-unavailable
split (#2728), and the macOS/tvOS branches are untouched.

Verified on iPhone Duo / iOS 27.1: 01-settings.ad passes 7/7 steps in 6/6 runs,
and back from General navigates to the root.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.16 MB 5.16 MB +733 B
Package (unpacked) 5.16 MB 5.16 MB +733 B
Package (download) 1.55 MB 1.55 MB +137 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 27.3 ms 27.5 ms +0.2 ms
CLI --help 82.7 ms 83.5 ms +0.8 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

@thymikee

Copy link
Copy Markdown
Member Author

I reviewed 143a558 and found no problems in the runtime change. The new floating-bar branch is gated on the exact BackButton identifier, so the iOS 26 classic path is unchanged.

One thing to fix before merge: the only upper-bound test, testTopNavigationBackCandidateBoundsSystemBackButtonToUpperWindow, uses a BackButton at y=760 in a 932 window, about 82% down. That fixture would still pass if floatingBarBackControlMaxYFraction rose to about 0.8, so it does not pin the 0.4 constant. A fixture just past 40% of the window height (midY near 375 to 400 in a 932 window), plus one just inside it, would pin both sides.

The open inline thread from cubic-dev-ai on RunnerTests+NavigationTests.swift:81 (the weak 0.4 bound fixture) still applies: #3375 (comment)

CI is still pending. Smoke Tests is queued, not failed. The iOS 26.x smoke lane has a 874-tall window that already passes the classic band, so it will not reach the new branch. It will still confirm that the runner compiles.

I could not inspect the Duo run logs or the runner.log described in the PR body, and I did not run the Swift runner unit tests. I also cannot rule out an app-defined control with the exact identifier BackButton in the upper 40% of a window. The exact-id gate makes that unlikely.

Before merge, please tighten the 40% bound test and let Smoke Tests finish.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thymikee

Copy link
Copy Markdown
Member Author

Addressed in c5b1714c0:

  • 40% bound. testTopNavigationBackCandidateBoundsSystemBackButtonToUpperWindow now pins both sides, in a 932-pt window where 40% is 372.8. midY 370 is accepted and midY 378 is rejected. Both sit below the classic band, which ends at 180, so only floatingBarBackControlMaxYFraction decides them. Raising the fraction to 0.41 or more fails the test. The infinite-rect case is unchanged.
  • Runner unit tests. PR CI doesn't select these tests (check-xctest-selection.ts --ios-pr-tests), so I ran them locally. I built a fresh runner with AGENT_DEVICE_XCUITEST_INCLUDE_UNIT_TESTS=1 at 17:55, after the edit, and ran it on the iPhone Duo (iOS 27.1). All 5 navigation tests pass. They run in full only in the nightly XCTest lane.

The cubic thread is resolved. Smoke Tests on the new head only confirm that the runner compiles, because the iOS 26 lane doesn't reach this code path.

@thymikee

Copy link
Copy Markdown
Member Author

Thanks for the update. At c5b1714 I found no code problems, and the earlier findings from 143a558 are now addressed. The rejection fixture now sits at midY 378, just past 40% of 932, with a matching in-bound assertion at midY 370.

The open inline thread on the fixture (#3375 (comment)) is fixed at this head, so please resolve it.

Smoke Tests is still running, with no failure so far. This change only touches the iOS in-app back candidate predicate and its unit tests, and the iOS 26 smoke window (874 tall) already fits the classic band. So that lane cannot reach the new branch and only confirms the runner compiles.

I did not run the Swift runner unit tests. PR CI does not select them, so only your local Duo run and the nightly XCTest lane cover them. I also could not check the iOS 27.1 Duo run, runner.log, or the 6/6 01-settings.ad result, so those rest on your PR text and comment. The note that you rebuilt and reran the unit tests after the edit is also unverified on my side.

No conflicts. Once Smoke Tests finishes green, this is ready to merge. No code changes are needed.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Oct 10, 2026
@thymikee
thymikee merged commit d8819e1 into main Oct 10, 2026
19 checks passed
@thymikee
thymikee deleted the fix/ios27-floating-bar-back-3333 branch October 10, 2026 16:59
@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-10 16:59 UTC

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.

iOS 27.1: back always refuses on the SwiftUI floating toolbar (top-band frame rule rejects the real control; leading fallback taps empty space)

1 participant