Skip to content

Maestro compat: waitForAnimationToEnd between two taps fails with a stability-generation mismatch (upstream passes) #1326

Description

@thymikee

Found while building the inert fixture control for #1300: the layer-3 differential caught a real engine divergence, on a four-line flow that uses only stock fixture screens.

Repro

appId: com.callstack.agentdevicelab
---
- launchApp:
    clearState: true
- tapOn:
    text: Settings
- waitForAnimationToEnd
- tapOn:
    text: Catalog
Engine Result
Maestro 2.5.1 pass — all four steps COMPLETED
agent-device failReplay failed at step 4 (tapOn "Catalog"): Maestro stability generation 2 does not match 3.

Verified on iPhone 17 Pro Max, iOS 26.2, against com.callstack.agentdevicelab. Removing the waitForAnimationToEnd line makes the same flow pass on agent-device, so that command is the trigger.

Cause

A navigating tapOn defers a stability requirement: tapTargetAndSettle ends with snapshots.requireStability(context.generation) (daemon-runtime-port.ts:392), which parks stabilityRequiredGeneration = N.

That requirement is only consumed by the next command that declares requiresSettledPredecessor, via snapshots.settlePending(...) (daemon-runtime-port.ts:366). waitForAnimationToEnd is declared false in MAESTRO_COMMAND_REQUIRES_SETTLED_PREDECESSOR (runtime-port-commands.ts:91), so it does not consume the parked requirement — but the generation still advances past it. The next tapOn then calls settlePending at generation N+1, hits the guard in daemon-runtime-port-snapshot-source.ts:87-93, and throws.

So the guard is firing on a legitimate sequence rather than on a bug it was meant to catch.

assertVisible is also declared false and does NOT reproduce this (tapOn -> assertVisible -> tapOn passes), so the fix is presumably not simply flipping the flag — worth understanding why the two differ before choosing between consuming the requirement, carrying it forward, or letting waitForAnimationToEnd settle it.

Impact

waitForAnimationToEnd between two taps is an extremely common Maestro idiom, and it is in our supported subset ("basic pressKey, back, animation waits, and stopApp"). Any flow using it after a navigating tap fails on agent-device where it passes upstream.

Not covered by a differential scenario yet

The tap-retry scenario originally carried a waitForAnimationToEnd step and would have caught this, but it did not need it (a navigating tap already defers stability for the next tap to settle), so the #1300 fix drops the step rather than encoding a workaround. A dedicated scenario for this idiom would be the natural regression guard once it is fixed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions