Skip to content

fix(ios): gate alert activation on a fresh hittable read - #2506

Merged
thymikee merged 3 commits into
mainfrom
fix/ios-alert-activation-hittable-gate
Sep 12, 2026
Merged

fix(ios): gate alert activation on a fresh hittable read#2506
thymikee merged 3 commits into
mainfrom
fix/ios-alert-activation-hittable-gate

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Hardens the single, never-repeated alert activation. A snapshot can expose an alert button a beat before the owning app has made it hittable, and a starved CI host widens that window. The one activation tapped into that gap, so the press was dropped, the presentation never changed, and the whole budget rode an unchanged alert to ALERT_DEADLINE_EXCEEDED with First actions: 0 — the signature of the flaky alert-replacement runner regressions on main.

Now handleAlert waits for a fresh exists && isHittable read before the activation, still activating at most once (preserves #2326's no-retry guarantee).

One file, runner-test target only. No production, CLI, or cross-platform surface change.

Validation

Commit 0d89661f18.

  • iOS runner XCTest: full targeted PR list (89 tests) green, 0 failures; the three alert-replacement regressions pass.
  • macOS runner build: TEST BUILD SUCCEEDED (shared alert source, guards the fix(ios): confirm alerts without repeating activation #2326 macOS-compile pitfall).
  • pnpm check:affected --run: xctest-selection and packaged-runner-swift pass; Swift builds + device lanes are GitHub-authoritative (running on this PR).

Residual risk: the flake is CI-contention-specific (~10% of recent main runs, ~13x slowdown) and did not reproduce on warm machines, so the green device lanes confirm no regression rather than a reproduced-before/after. The webview-remote-content RUNNER_BUSY failures were the separate dominant cause and are addressed by the already-merged #2493.

A snapshot can surface an alert button before the owning app has made it
hittable, and a starved host widens that window. The single, never-repeated
activation tapped into that gap, dropping the button press, riding an
unchanged alert to ALERT_DEADLINE_EXCEEDED with First actions: 0, and
flaking the alert-replacement runner regressions under CI contention.

Wait for a fresh exists+isHittable read before the one activation; still
activates at most once.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.49 MB 4.49 MB +754 B
Package (unpacked) 4.49 MB 4.49 MB +754 B
Package (download) 1.32 MB 1.32 MB +87 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 24.0 ms 24.0 ms +0.0 ms
CLI --help 63.2 ms 66.3 ms +3.1 ms

@thymikee

thymikee commented Sep 11, 2026

Copy link
Copy Markdown
Member Author

The new readiness wait can activate an alert after the command deadline at 0d89661. It checks the deadline before the synchronous exists && isHittable query, but returns true even if that query finishes late; handleAlert then taps without checking time again. Recheck the deadline after the probe and add a regression where the probe completes late, asserting that no alert action occurs.

The earlier iOS smoke failure was an automation-longpress visibility check after scrolling and appears unrelated to this alert change; the rerun is pending. The reported green alert tests do not cover expiry during the new readiness query.

The hittable read is a synchronous query that a starved host can complete
past the command deadline. It previously handed back true unconditionally,
so handleAlert tapped once more after the budget was already gone. Only a
read that lands before the deadline buys back the single activation.

Route the read through a unit-test-overridable probe and add a regression
that completes the probe past the deadline and asserts, via the fixture's
own action counter, that no button is activated.
@thymikee

Copy link
Copy Markdown
Member Author

Good catch — fixed in 7bbb148. The hittable read is itself a synchronous query, so it now forfeits instead of buying back the single activation: waitUntilAlertButtonHittable returns Date() < deadline after the probe rather than unconditional true, so a read that lands late never reaches activateElement.

Regression testAlertHittableProbeCompletingAfterDeadlineLeavesTheOriginalUntouched drives handleAlert with a probe overridden to complete past the deadline and asserts the fixture's own First actions: 0; replacement actions: 0 label (typed activation count, no hint-text sniffing). Red/green proved locally: with the pre-fix return true it fails at First actions: 1; with the recheck it passes. Full targeted list green (90 tests, 0 failures); macOS runner build green; new method selected in the iOS PR lane (check:xctest-selection: 90 selected, 0 reachable by no lane).

@thymikee

Copy link
Copy Markdown
Member Author

The post-probe deadline check fixes the late activation at d1e1d1e. The selected regression forces a late successful probe and verifies the deadline error with zero original or replacement actions; current-head checks pass, including iOS and macOS, and there are no conflicts. Ready for human review and merge. Please also refresh the description’s old validation SHA, file count and test count to match this revision.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 12, 2026
@thymikee
thymikee merged commit cda7522 into main Sep 12, 2026
18 checks passed
@thymikee
thymikee deleted the fix/ios-alert-activation-hittable-gate branch September 12, 2026 16:22
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-12 16:22 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.

1 participant