fix(daemon): consume the post-gesture record when its capture aborts - #2983
okwasniewski wants to merge 1 commit into
Conversation
A wait stable poll bounds each capture with a deadline abort. When that abort landed inside the post-gesture stabilization loop the pending record stayed armed, so every later capture on the session paid the whole loop again and wait stable kept timing out with zero captures. The record is now cleared once the loop has been entered, on settle, timeout and abort alike. Refs callstack#2885.
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
[claude-fable-5-1] responding on behalf of Oskar CI note: Smoke Tests failed at |
|
This looks unproven at 8bc857c. The only run in the description is scroll + wait stable where each wait settled with captures: 2, which is the normal return path through runPostGestureStabilizationLoop that main already clears the record on — see https://github.com/callstack/agent-device/blob/8bc857c/src/daemon/deferred-interaction-outcome.ts#L356. Nothing shows a wait-deadline abort thrown out of the loop followed by a later capture in the same session, and the tests don't reach it either: the new case calls capturePostGestureStabilizedResult directly with a hand-built DOMException instead of going through wait stable. Is the iOS AX-bridge or runner capture confirmed to throw when its signal aborts, rather than just returning? If it returns instead of throwing, the loop runs to its cap and clears the record on main too, and #2885 would have a different cause. To validate, open the fixture Automation lab (or Settings) on the iOS simulator at this head, run The failing CI step is replay step 1 (launchApp), where the simulator openurl of agent-device-test-app:///automation?event=cold.start failed with COMMAND_FAILED before any capture; the diff only changes when capturePostGestureStabilizedResult clears the record, which runs inside snapshot captures, so this failure looks unrelated to the change. I did not check the author's claim that this lane also fails on main. Not blocking: the new test could exercise the real wait-stable path (runWithinWaitDeadline -> captureSelectorSnapshot -> captureSnapshot -> resolveDeferredInteractionOutcome) instead of calling capturePostGestureStabilizedResult directly with a stub exception, and the inline comment at deferred-interaction-outcome.ts:328-330 could state the consume-on-entry rule instead of narrating the issue history, but either can be taken or left. The next thing needed before merge is that live simulator evidence: a run showing a wait-deadline abort mid-loop consumes the record, and the following wait stable settles with captures >= 2 without paying for another post-gesture loop. |
Summary
Refs #2885.
wait stablebounds each capture withrunWithinWaitDeadline, which aborts the capture's signal with aTimeoutErrorat the wait's deadline. After ascroll, that capture routes throughcapturePostGestureStabilizedResult, which runs the post-gesture stability loop (up to the 3.5 s distrust cap, each poll a full simulator capture) inside what the stable loop counts as one capture. When the wait deadline aborted the loop mid-poll, the throw skippedclearPostGestureStabilization, so the pending record stayed armed and every later capture on the session paid the same loop again: the nextwait stablealso stalled withcaptures: 0, and the one after it, which is the 15-of-16 pattern in the issue.The owner now clears the record in
finally: entering the loop consumes it, on settle, timeout and abort alike. No other behavior changes; the stalled timeout still reportscaptureStalled: trueandcaptures.Touched: 1 source file, 1 test file.
Validation
Tested commit
8bc857c52.pnpm check:affected --run: passed.post-gesture-stabilization.test.ts: a second capture that throws aTimeoutErrormid-loop leavessession.postGestureStabilizationundefined. Removing thefinallyfails it.open com.apple.Preferencesthen three rounds ofscroll down 0.75+wait stable: every wait settled withcaptures: 2in 1.5 s to 4.2 s. I could not force the zero-capture stall on this host; the unit test pins the mechanism.wait stableafter a gesture. That is a second seam (the stable loop is itself a settle observer and could skip the nested loop); this PR stops the stall from repeating.