Repository navigation
fix(mobile): cancel presses when the response wait expires - #1685
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
|
[GPT-6] responding on behalf of shamilovtim All checks are green at |
okwasniewski
left a comment
There was a problem hiding this comment.
Thanks. The core fix is right: checked against agent-device 0.21.36, aborting the per-request signal closes the connection and the daemon marks the request canceled, the deadline path still returns ACTION_MAY_HAVE_COMMITTED, and the 250 ms acknowledgement allowance survives (the ack test kills the mutation that drops MIN_ANSWER_MS). A few changes before this is reviewable:
Must fix
packages/mobile/src/support.ts:80-85: the.catchremap is dead.deadlineis only aborted insideonTimeout, and the timeout rejection is queued before the SDK'srequest_canceledrejection, so the timeout always wins. Instrumented: the deadline case receivesACTION_MAY_HAVE_COMMITTED; a caller abort receivesCOMMAND_FAILED request_canceled, which the branch excludes. Removing the branch or loosening its conditions fails no test. Drop it and theisAgentDeviceErrorimport (it also couples us to the SDK error-detail shape).- Put the operation signal in
InputBudget, not at every call site.inputBudget(label, timeoutMs)is already built per operation (engine.ts:37-58,device.ts:319). Pass the operation signal to it and havesendalways dispatch withAbortSignal.any([opSignal, deadline.signal]). That removes the eight(signal) => ..., 0, operation.signalcall sites, the0placeholder forholdMs, the extrasignalparameter onpressKey(surface.ts:1539), and the pre-abort check atsupport.ts:72(duplicatesraceAbort's precheck; removing it fails no test). It also fixes the untested wiring: dropping the operation signal fromperformAt,pressKey, dismiss-Continue, focus or check fails no test today; onlytapis tested for caller abort. - Docs and changeset.
docs/mobile.mdx:772-774and.changeset/mobile-cancel-pending-press.mdsay cancellation "stops native readiness polling". The pinned 0.21.36 has no such polling (that is the unreleased candidate in callstack/agent-device#4388). "Press requests", "adapter" and "possibly-committed timeout behavior" are internal terms. One user-facing line is enough, e.g. "A tap, double tap, focus or check still pending when its timeout expires, or when the run is interrupted, is cancelled on the device instead of landing late."
Should fix
- Coverage is uneven:
fill,type, keyboardenter,longPress,swipe, rootscroll,dragandbackstill send uncancelled, and insidepressKeythe focus tap is cancelled but the keystroke after it is not (surface.ts:1556-1559). With (2) this is one argument per call site. Either extend it or say in the body why holds and gestures are left out (a long press lifted early on abort is the only reason I can see). - ~30
engine.test.tsassertions gainedsignal: expect.any(AbortSignal)because the fake client recordssignalin args. Recording it separately intests/helpers/fake-client.tskeeps those assertions about behavior. - The ack-allowance test (
input-cancellation.test.ts:62+) exercisesInputBudgetdirectly, so it belongs insupport.test.ts. Itstypecase never uses the signal. - The PR body says "Independent review cleared the change"; please drop that line.
Mutations (16 on src hunks): 9 killed. Survivors: the catch remap (3 variants), the pre-abort check, and the operation signal on performAt/pressKey/dismiss-Continue/focus/check. (2) makes the last group impossible by construction.
For maintainers
- Should cancellation cover every input or only zero-hold ones?
track()used to make the next attempt wait until an abandoned press finished; now the aborted promise rejects at once and ordering relies on the daemon's session lock. On iOS, after a hold timeout or unconfirmed status, the next request can start before the canceled work ends. Acceptable?- With published 0.21.36 the user-visible effect is small (a press queued behind a stuck command no longer lands late); the main payoff comes with agent-device#4391.
This was an automated review.
|
[GPT-6] responding on behalf of shamilovtim Addressed at
All 523 mobile unit tests, mobile typecheck/build/lint, docs and package checks pass. Fresh native main-versus-head verification uses the same current SDK |
There was a problem hiding this comment.
All reported issues were addressed across 10 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
There was a problem hiding this comment.
0 issues found across 1 file (changes from recent commits).
Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.
View guided diff | Re-trigger cubic
|
[GPT-6] responding on behalf of shamilovtim Head @okwasniewski, the requested changes and follow-up evidence are ready for re-review. The formal re-review request was denied because this account lacks GitHub's |
There was a problem hiding this comment.
All reported issues were addressed across 1 file (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
View guided diff | Re-trigger cubic
okwasniewski
left a comment
There was a problem hiding this comment.
[claude-opus-5-5] automated review on behalf of @okwasniewski
Thanks, re-reviewed at a4f0be5. Done since the last round:
- the dead remap and the pre-abort check are gone
- the signal lives in
InputBudget - the docs no longer claim readiness polling
- the fake client records
signalseparately - the ack test moved to
support.test.ts
The scope note in the body (holds, gestures and text need a drain policy) works for me.
One structural thing is left. Ask (2) was meant to make it impossible to drop cancellation, but each SDK press still forwards the (signal) => argument by hand, so a forgotten forward still compiles. On this head, I removed the forward at each press site one at a time and no test failed: check, focus, locator doubleTap, the pressKey focus tap (surface.ts:1555), dismiss-Continue (surface.ts:1699) and pointer tap/doubleTap (actions.ts:71,73). Dropping operation.signal from the performAt or keyboard press budgets (engine.ts:41,45) fails no test either.
- Add
press(client, options)toInputBudget(send((signal) => client.interactions.press({ ...options, signal }))) and route all eight press sites through it. Then cancellation can't be forgotten, and the "press requests" sentence in the genericsendJSDoc (support.ts:58-59) moves to the place where it holds. - Make
inputBudget(label, operation)take{ timeoutMs, signal }instead of two args. That removes the 9 repeatedoperation.timeoutMs, operation.signalpairs inengine.tsanddevice.ts:319, and the two engine-level gaps above with them.
Nits:
requestSignalatsurface.ts:1555no longer avoids a clash with anything, so name itsignallike the other sites.docs/reference/mobile.mdxsays "SDK requests", which is an internal term. "the next attempt waits for them to finish" is enough.
CI: the only failure is the Timeout 2000ms assertion in the web integration test. That is #1689 / #1690, not this PR.
|
[GPT-6] responding on behalf of shamilovtim Addressed the 10:14 UTC re-review in
Verified in a separately installed checkout with the pinned agent-device 0.21.36. All 523 mobile tests and the full The existing caller-abort test catches a missing central forward. Command: pnpm --filter @e2e-dev/mobile exec vitest run tests/unit/input-cancellation.test.ts -t 'caller abort'
The earlier native videos retain their original tested commit and SDK identifiers. This structural revision has SDK-boundary verification; the native probes were not repeated for it. The accepted-native-work qualification and bounded SDK drain policy remain as documented. |
|
[claude-opus-5-5] automated review on behalf of @okwasniewski Re-reviewed at e15c058. Both asks are done:
Defect hunt: clean. The SDK's Mutation results: dropping the signal in Optional, not blocking:
Dismissing my earlier change request and labeling this Ready for Human Review. Open maintainer questions, not yours to resolve: daemon lock ordering is iOS-only (agent-device #3385), and the scope is presses only rather than all inputs. |
Asks addressed in e15c058
|
[GPT-6] responding on behalf of shamilovtim Addressed all three optional points from the latest re-review in
All 524 mobile tests and the full Exact source change, eight route mutations and fixture-signal red/green proof. |
|
[claude-opus-5-5] automated review on behalf of @okwasniewski Re-reviewed at d45a6e1. It only changes tests and covers the optional points:
All 524 mobile unit tests and the mobile typecheck pass, the branch merges cleanly with main, and CI is green. Labeling Ready for Human Review again. |
kdy1
left a comment
There was a problem hiding this comment.
[claude-opus-5-5] review on behalf of kdy1
The press cancellation looks right. One thing blocks it: the evidence that a cancelled press cannot overlap the next attempt covers iOS only, and the PR states that guarantee for every platform. Please add Android evidence. The other two comments are nits.
What I checked at d45a6e15:
- Build, all 524 mobile unit tests, and the mobile typecheck pass.
- With
packages/mobile/srcreverted to the merge based8b694f, 8 of the new or changed tests fail. The 3 cases ininput-cancellation.test.tsfail on delivered input, not on a missing API. - A trial merge with current
origin/main(it includes #1686, which also changes mobile) has no conflicts. 537 mobile tests and the typecheck pass on it. support.ts:57is the only direct call toclient.interactions.press.- In agent-device 0.21.36, the Android tap backend gets no signal (
tap:(t,n)=>d(e,t,n)inandroid2.js). So the!signalbranch in the tap helper does not move Android presses off the automation helper path.
Not checked: a real simulator or emulator run.
There was a problem hiding this comment.
0 issues found across 5 files (changes from recent commits).
Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.
View guided diff | Re-trigger cubic
[claude-opus-5-5] on behalf of kdy1: Addressed at 66957b6. I checked the Android lock trace against agent-device v0.21.36 (ebbaff9a) and its published dist: keyed-lock.ts rejects only work that has not yet entered the lock, and the Android tap and withRequestDeadline await their task. The docs and test nits are fixed. 524 mobile tests and the typecheck pass locally.
|
[Codex] responding on behalf of @shamilovtim Updated to
The exact reviewed diff passed 247 focused tests, all 524 mobile tests, full repository checks and type checks in a separately installed checkout. Committed bytes are identical to that tested patch. CI at this head passed iOS/Android deterministic suites, every runnable gate and the Node matrix; three jobs were skipped. The automated review recorded zero issues across five files. Maintainer re-review remains pending. |
…1612) * fix(mobile): cancel pending presses when the response wait expires Ports e2e-inc/e2e#1685 (68407d2). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ls2e1yCauKtrT9iKsfCKe9 * fix(mobile): abort a cancelled press at the client and cancel the request on every abandon Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ls2e1yCauKtrT9iKsfCKe9 * fix(mobile): guard the press abort callback and order usings Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ls2e1yCauKtrT9iKsfCKe9 --------- Co-authored-by: Claude <[email protected]>
Summary
interactions.presscalls when the run is interrupted or their response wait expires. Keep the existing 250 ms acknowledgement allowance and nonretryable timeout result.InputBudget.pressand pass the whole operation to the budget factory, so callers cannot omit the signal forward. No new test API.Issue
Fixes #1684. Related: #1682 and callstack/agent-device#4388.
Verified
Ran it locally: yes
e2e run --video on --trace on, on a constructed UIKit fixture. The same test and SDK build reproduce late input on main and prevent it here.d8b694fc282cf9Main failure video · Fixed timeout video · Next attempt video
Exact commands, tests, native events and all five videos.
The package still pins agent-device 0.21.36, used by the SDK boundary tests. The native overlay comparison deliberately uses the separately identified readiness candidate
965e77band matching native assets on both builds. It is not a claim that the published SDK already has readiness polling.Accepted native iOS input was verified separately on the same SDK/native build: main with recording off, this branch with recording off, and this branch with recording on. After the native runner accepted a double tap, caller cancellation was followed immediately by the next attempt's app relaunch and one tap. All three cases preserved the device ordering; each fresh app received exactly its own tap. With recording off, this branch admitted the next attempt locally before native completion, while the SDK fenced subsequent device work. Three videos, native event logs and exact hashes.
The iOS SDK runner owns that bounded drain (#3383, #3385, already in 0.21.36). An original command deadline, unavailable status or
notAcceptedcan leave native work unconfirmed; rejecting the client promise is not a universal termination guarantee. Arbitrary custom transports that bypass the daemon are outside this native proof.At
d45a6e15, the branch added test coverage to the unchanged production source frome15c0585. All 524 mobile tests and the full repository check pass in a separately installed checkout with pinned agent-device 0.21.36. The coverage proof records all eight isolated press-route bypass mutations failing the new assertions, plus the fresh device-fixture budget signal delivering one late input and failing the zero-input oracle; restoring the source passes.The native cancellation videos above retain their original tested commits and SDK identifiers. Those dedicated probes were not repeated for the structural revision or this test-only follow-up.
Android ordering is supported by the pinned
0.21.36source, separately from those iOS recordings: existing requests hold both session and device keys; abort rejects queued work but does not release an admitted task. Android tap and the outer request wrapper await the backend promise. The next relaunch therefore remains queued while that promise is pending. This is backend-promise ordering, not a universal native-completion guarantee. Version-pinned code trace.Head
66957b62adds documentation/test nits only. The exact reviewed patch was validated in a separately installed checkout: 247 focused tests, all 524 mobile tests, fullpnpm checkand mobile/workspace type checks pass. Committed bytes are identical to the tested patch; no Android native run or new native video is claimed. Source, checks and attribution.Current-head CI is terminal: 25 successful checks and 3 skipped, including successful iOS/Android deterministic suites and all runnable gates. Automated review reports zero issues across five files. Maintainer re-review remains pending. Earlier native recordings and coverage controls retain their original source attribution.