Repository navigation
fix(ios): press the navigation bar back button and speed up the back swipe - #4508
Conversation
…swipe iOS back now reads the current tree in the same runner call for the navigation bar's system back button (identifier BackButton inside a navigation bar). When exactly one is hittable, it taps it with a synthesized tap and the daemon's settle observes the pop. No other button counts, whatever its label; an unreadable or penalized tree, or two candidates, fall back to the swipe. The swipe is resolved before settling, samples 0.05 s apart, and measures the change from the last still sample, so the separate just-before capture is gone. The post-swipe delay drops from 0.25 s to 0.1 s, which also shortens the required still span. The settle budget is 2 s. The gap-coverage and baseline checks still decide every success. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Size Report
Startup median (7 runs, lower is better):
|
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Apex Sentinel · ✅ ApproveSince the last review: 0 new · 0 still open · 0 no longer raised No open findings. 2 findings below 💬 Talking to SentinelIn a finding's thread, reply with:
On this conversation, comment Earlier reviewsReview of
|
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift:278 |
A post-tap bar layout shift with no navigation is reported as a successful back |
Reviewed with this repository's own Sentinel configuration (b68ed4bf285a).
Review of d1ffd66a · 13.11.2026 18:22 UTC
Apex Sentinel · ✅ Approve
Since the last review: 0 new · 0 still open · 2 no longer raised (2 marked fixed)
No open findings.
1 new finding away from what changed since 3728f47 not shown. Comment @apex-sentinel-staging review for a full review.
Reviewed with this repository's own Sentinel configuration (b68ed4bf285a).
Review of 3728f47e · 13.11.2026 17:51 UTC
Apex Sentinel · ✅ Approve
Since the last review: 0 new · 0 still open · 0 no longer raised
2 finding(s): 2 minor.
🟡 Minor (2)
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift:321 |
First settle round of the bar read takes both reads back-to-back, so an animating bar can pass |
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift:392 |
Bar-state confirmation credits a self-updating bar title to the back tap |
1 finding below minor hidden (user_settings.min_severity).
Reviewed with this repository's own Sentinel configuration (b68ed4bf285a).
Review of 3728f47e · 13.11.2026 17:41 UTC
Apex Sentinel · ✅ Approve
Since the last review: 0 new · 0 still open · 0 no longer raised
No open findings.
1 new finding away from what changed since dbf400b not shown. Comment @apex-sentinel-staging review for a full review.
Reviewed with this repository's own Sentinel configuration (b68ed4bf285a).
Review of dbf400b9 · 13.11.2026 16:48 UTC
Apex Sentinel · ✅ Approve
Since the last review: 0 new · 0 still open · 0 no longer raised
No open findings.
2 findings below minor hidden (user_settings.min_severity).
Reviewed with this repository's own Sentinel configuration (cc3b99b38530).
Review of 0d17e3ec · 13.11.2026 16:19 UTC
Apex Sentinel · ✅ Approve
Since the last review: 0 new · 0 still open · 0 no longer raised
No open findings.
Reviewed with this repository's own Sentinel configuration (cc3b99b38530).
Review of 32278daa · 13.11.2026 15:14 UTC
Apex Sentinel · ✅ Approve
Since the last review: 0 new · 0 still open · 1 no longer raised (1 marked fixed)
No open findings.
Reviewed with this repository's own Sentinel configuration (cc3b99b38530).
Review of 722671b8 · 13.11.2026 14:40 UTC
Apex Sentinel · ✅ Approve
1 finding(s): 1 minor.
🟡 Minor (1)
| Where | Finding |
|---|---|
apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift:344 |
2 s settle budget makes a still screen never settle on a host with ~1 s captures |
Reviewed with this repository's own Sentinel configuration (80075b6412df).
There was a problem hiding this comment.
Apex Sentinel: 1 new finding on 722671b. The verdict and the full list are in the summary comment.
The navigation bar back button path now reads the bars again after the tap (one XCTest snapshot per bar, 0.05 s apart, up to 1 s) and answers BACK_SWIPE_UNVERIFIED when neither the bar title nor its back button changed; a delivered tap is never followed by the swipe. The button log line is emitted on every exit. The back swipe settle budget grows with the slowest capture so a still screen can settle on a slow host. Help, flag text, command docs, and the runner protocol describe when the button is pressed and what each fallback answers. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
All reported issues were addressed across 7 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
…plainly Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Thanks for this. At 32278da the back-button path has one defect that should be fixed before merge. The 22 checks pass, including the smoke The optional button probe reads Not blocking, and you can take or leave these: the button path takes its before-state with no stillness check, so a push still animating can look like a confirmed pop (two equal reads before the tap would fix it); the bar-state entry misses apps with a constant bar identifier and The earlier bot threads on single-screen scope, the settle budget, the swipe fallbacks and the log line are fixed at this head or answered. You can resolve r4564566869, r4564566875, r4564566942, r4564566951, r4564566963, r4564566971, r4564566978, r4564636721, r4564897969, r4564897988 and r4564898010. The one on single-screen scope (r4564566875) is correct as written: that scope applies only when the window is unresolved (ADR 0025). There are no conflicts. Before merge, please contain the lookup and confirmation reads in |
Every navigation bar read in iOS back runs inside holdingRecordedIssues. A held issue before the tap means no button, so back swipes. A held issue after the tap proves nothing, so back answers back_swipe_unverified and never swipes. This replaces the suppressed-issue counter diff, which only saw kAXError issues. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Re #4508 (comment): fixed in 0d17e3e.
Validation: 23 back runner tests pass on an iOS 27.0 simulator (iPhone 17 Pro). check:xctest-selection, typecheck, lint, and focused vitest pass. Latency is unchanged: I did not take the non-blocking notes in this push. |
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
…uery - Read the bar state twice around the button lookup and tap only when the two reads agree and contain the button's frame, so a push still animating is not taken for the state a pop changes. - Compare bar count, identifier, static-text titles, and back buttons. - Start bar reads only within the 1 s budget, checked before each bar's snapshot; document what bounds a read in flight. - Re-record a hung XCTest query as record(_:) requires, send nothing more: BACK_SWIPE_NOT_DISPATCHED before the tap, BACK_SWIPE_UNVERIFIED after it. - Drop the dead outcome guard on the XCTest tap fallback. - Correct the visionOS back hints; a thrown tap no longer logs a swipe verification error line. - Test the two-candidate rule, the skipped read, and hung queries; the lookup-issue test asserts the swipe path, not a swipe outcome. - Name the simulator still span in commands.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Device verification at dbf400biPhone 17 Pro simulator, iOS 27.0, Xcode 27.1. The CLI is this worktree's build ( AD="node bin/agent-device.mjs --state-dir /private/tmp/ad-pr4508-state --platform ios --udid BC54AC11-CD86-4C10-8C4E-5101C773FFEB --session pr4508"Button path: Settings > General$AD open com.apple.Preferences --relaunch
$AD snapshot -i # @e6 [cell] "General"
$AD press @e6 --settle
$AD back --json{ "success": true, "data": { "action": "back", "mode": "in-app", "message": "Back" } }Runner log ( 8 more runs of press General, then The stillness check costs one more bar read. The lookup median went from about 113 ms at 0d17e3e to 154 ms. Confirmation did not change. Swipe path: Safari history (no navigation bar back button)$AD open com.apple.mobilesafari https://example.com
$AD fill @e35 "https://www.iana.org/help/example-domains" # address bar
$AD keyboard return
$AD backCleanup: Runner tests (same simulator)All 30 methods in Mutation check. I removed the |
|
Thanks. I applied every non-blocking note in dbf400b.
Also in this push, from two new Cubic threads:
All 30 navigation runner tests pass on an iPhone 17 Pro simulator (iOS 27.0). check:xctest-selection, typecheck, lint, and focused vitest pass. The old bot threads you listed were already resolved. |
There was a problem hiding this comment.
All reported issues were addressed across 8 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
A live XCUIElement frame is in screen points, but a snapshot frame of a placed iPad window starts at the window's own top-left (#3899). The pre-tap still check compared the two directly, so on a placed window the button was never found still: back spent the 1 s read budget and swiped. The check now places the snapshot frames at the window's screen origin through the synthesized coordinate context's SurfaceGeometry. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The macOS runner test build has no navigationBarBackState. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
@apex-sentinel-staging review |
There was a problem hiding this comment.
Apex Sentinel: 2 new findings on 3728f47. The verdict and the full list are in the summary comment.
|
This PR is ready at 3728f47: I found no new problems, and the earlier findings (#4508 (comment)) are now fixed. A hung query is re-recorded and no longer held, the lookup-issue test asserts the trace found=unreadable / pressed=false, and snapshot frames are offset by the window origin before the live-frame compare. Not blocking, and you can take or leave these: NavigationBarBackReadPhase is only read by the unit-test injection filter (RunnerTests+Navigation.swift:418), so its doc comment describes caller behaviour it does not control; the hung re-record could live in holdingRecordedIssues so the other held reads obey the record() contract too (RunnerTests+Navigation.swift:444); and the iPad evidence predates e946cb3, so a placed (non-full-screen) iPad Settings window run showing AGENT_DEVICE_RUNNER_BACK_NAVIGATION_BAR_BUTTON found=yes pressed=yes confirmed=yes would close that gap. The three earlier review threads on the hung query, the lookup-issue test and the snapshot frame offset are fixed at this commit, so you can resolve them: #4508 (comment), #4508 (comment), #4508 (comment). I did not run the runner tests or a live device; the test and mutation results are from your posted output. 21 checks pass, and the only one not finished is an external review check, which exercises no build or device route. No conflicts are known. Once that check finishes, nothing else stands in the way of merging. |
…reads The still check before the tap now needs two agreeing bar reads started at least one read interval apart, so two reads drawn in one animation frame no longer pass for a still bar. The bar state is now each bar's back button label and frame, so a bar title that updates itself, such as a countdown, can no longer confirm a tap that navigated nowhere. The placed-window button match allows 0.5 pt per edge for fractional frame values. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
@apex-sentinel-staging review |
There was a problem hiding this comment.
Apex Sentinel: 1 new finding on d1ffd66. The verdict and the full list are in the summary comment.
…els and counts A tap that navigates nowhere can still move the back button, such as by a large-title collapse, so its frame no longer confirms a pop. Frames stay in the still check before the tap and in matching the button to press. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
I found no correctness problems at cee9941, but the iOS checks have not finished, so I can't call this ready yet. The earlier review at 3728f47 was clean, and the changes since then fix the three open inline threads: the loop now does one read per round before The three inline threads are fixed at this head and can be resolved: #4508 (comment), #4508 (comment) and #4508 (comment). Not blocking, and you can take or leave these: (1) Could you delete the timed-state type, the timing clause and the tolerance (and its test) unless you can cite a run where exact equality failed? That would remove about 30 production lines and keep the two real fixes, the label-only confirmation and the one-read-per-round loop. After that, the remaining step before merge is a passing iOS smoke run on the final head. |
…button check Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Applied the simplification at f443d78:
Non-test production lines vs main go from +455/−67 to +430/−67. Validation: 29 back runner tests pass on an iPhone 17 Pro simulator (iOS 27.0). Settings > General gives |
This comment has been minimized.
This comment has been minimized.
…udget A snapshot of a navigation bar that vanished mid-pop spends about 2 s in XCTest's element-resolution retries before it raises a held issue. On a slow CI simulator that read used up the 1 s confirmation budget, so no read saw the settled screen and back answered back_swipe_unverified after a real pop. One read now always follows the budget, with no deadline, so the screen after the pop is read. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
The iOS smoke failure in run 43488040274 ( Fix in 1154091: after the 1 s budget, the confirmation always takes one more read with no deadline, so it always reads the screen after the pop. Zero bars or zero back buttons already counts as a change. Confirmation still uses labels only, and a hung query still answers at once. A new runner test replays this: two slow reads that fail inside the budget, then the pop must be confirmed. It fails without the final read. The 22 back runner tests and the local iOS smoke tier pass on an iPhone 17 Pro simulator. |
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
…ount The slow-host confirmation test failed two reads that each slept 0.6 s, so whether the final read came third depended on real read speed. The confirmation loop now tells the test hook which read starts after the budget, so the test fails every read in the budget and succeeds only on that final read. The protocol and command docs now say the final read can also fail or hang, and that a read can run well past the 1 s. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
iPad placed-window verification at 40c2f69This run uses the label-only confirmation. 40c2f69 changes only a doc comment from f1799d6, and the daemon built the runner from this source. Setup: iPad Pro 11-inch (M5) simulator, iOS 27.0, Xcode 27.1. Multitasking is set to Windowed Apps. The CLI is this worktree's build ( AD="node bin/agent-device.mjs --state-dir /private/tmp/ad-pr4508-ipad-state --platform ios --udid 9D075F91-A796-49ED-8C83-947137E611B6 --session pr4508ipad"
$AD open com.apple.Preferences --relaunch
$AD press @e10 --settle # Multitasking & Gestures
# Shrink Settings from full screen into a placed window by dragging the bottom-right resize grip:
$AD gesture pan 816 1192 -200 -350 1200
$AD back --jsonAfter the drag, the Settings window sits at about x 197–637, y 230–865 on the 834×1210 screen, with the wallpaper and Dock visible around it. Its back button is in the window's own bar. { "success": true, "data": { "action": "back", "mode": "in-app", "message": "Back" } }Then I ran 4 more rounds of Cleanup: |
|
Re #4508 (comment), at 40c2f69:
Validation: check:xctest-selection passes, the macOS runner test build succeeds (TEST BUILD SUCCEEDED), and lint passes. I did not change the swipe constants or the confirmation logic. |
|
This PR is ready at f1799d6. The trims requested in the earlier review (#4508 (comment)) landed, and I found no new problems in the code. The newer head 40c2f69 only rewrites the All 22 checks pass on f1799d6. Smoke Tests (ios) exercises the changed back route, and it is the job that failed on the run before the earlier fix. No conflicts. No further action is needed before merge. The three earlier review threads are fixed at this head, so you can resolve them: the phase-gated test (#4508 (comment)), the RUNNER_PROTOCOL.md wording on the final read (#4508 (comment)), and the "within 1 s or on one more read" wording in the docs, help and runner message (#4508 (comment)). What I did not check: I did not run the runner unit tests or a device, so the claim that the new test fails without the final read comes from reading the code and your report. I also did not open the CI runner log for the ~2 s snapshot figure. The exact frame equality that replaced the 0.5 pt tolerance was validated on a placed iPad window only at e946cb3, not at this head. A mismatch there would fall back to the swipe after 1 s and would not misreport. |
* fix(ios): restore the back swipe's pre-swipe still span #4508 cut the post-swipe delay to 0.1 s, and the required still span was derived from that delay, so it shrank to about 0.4 s on a simulator. A screen that repaints itself every 0.4-0.55 s could then look still, and its own repaint after a no-op swipe read as a back. The still span now uses its own 250 ms margin instead of the delay: about 0.55 s on a simulator again. The 0.1 s after-sample and the 0.05 s sampling pause stay. The settle budget derives from the span, so it follows. The slow-toggle fixture repaints every 0.5 s again and runs without the edge pan, so the swipe is a no-op; back is tried at five timer phases and must answer back_swipe_unverified each time. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(ios): start each slow-repaint back attempt at a real repaint phase The `--slow-toggle` fixture now repaints on whole multiples of 0.45 s of the wall clock, which the test process shares, so the test waits for each target offset (0, 0.09, 0.18, 0.27, 0.36 s) instead of cumulative sleeps. The 0.45 s period leaves 100 ms below the ~0.55 s still span and stays above the old ~0.4 s span. The shade steps through 8 grays by wall-clock period, so samples on either side of a capture stall cannot alias. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(ios): floor the repaint fixture's shade so a late callback keeps its own slot Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(back): name the 0.45 s repaint the runner tests use Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
* origin/main: fix(android): carry an empty text on a node labelled by its content description (#4531) fix(ios): restore the back swipe's pre-swipe still span (#4515) fix(daemon-client): type the remote daemon refusal with its dispatch fact (#4532) fix: prepare reinstall artifacts before removing apps (#4533) fix(ios): classify the keyboard dismiss refusal by what it dispatched (#4536) fix(ios): press the navigation bar back button and speed up the back swipe (#4508) refactor(gestures): one owner for the densified gesture path; drop the unproven sendevent transport (#4239) fix(host-kit): keep Linux process identity stable across clock steps (#4438) fix(ios): bound the idle wait of keyboard dismiss and keyboard enter (#4454) fix(device-selection): fail an ambiguous device request with only the tied devices (#4506)
Summary
iOS
backpresses the navigation bar's system back button (identifierBackButton) only when exactly one live, hittable one is read from a readable tree, and two bar reads one 0.05 s pause apart agree and hold it (frames placed at the window's screen origin, so a placed iPad window matches). After the tap it re-reads the bars for up to 1 s, then once more with no deadline, so a slow host whose reads of a vanishing bar spend the 1 s in XCTest retries still reads the settled screen. It answersokonly when the bar count, the back button count, or a back button label changes. Bar titles and back button frames never count, so a countdown title or a large-title collapse cannot confirm a tap. A pop that keeps the same back button labels and bar count answersback_swipe_unverified. It never swipes after a delivered tap, and a hung query sends nothing more. Otherwise it falls back to the swipe, which now settles faster. Help, runner protocol and command docs are updated.Validation
iOS 27.0 simulator, Settings > General, median of 8 runs:
back(button)back --settleAt 1154091 the 22
testInAppBack*runner tests pass on an iPhone 17 Pro simulator; the new slow-host test fails without the final read. The local iOS smoke tier passes, 7 of 7 scenarios (automation-inputback:confirmed=yes confirmMs=372). The macOS runner test build, typecheck, lint, xctest-selection and focused vitest pass. At 40c2f69 the placed (non-full-screen) iPad Settings window givesfound=yes pressed=yes confirmed=yesin 5 of 5 runs with the label-only confirmation (iPad evidence). Device evidence.Risks: no physical-device run.
🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.