Skip to content

fix(schema): Preserve video fps constraints

8206d7b
Select commit
Loading
Failed to load commit list.
Sign in for the full log view
Merged

feat(ui-automation): Add rs/1 runtime automation parity #416

fix(schema): Preserve video fps constraints
8206d7b
Select commit
Loading
Failed to load commit list.
GitHub Actions / warden: find-bugs completed May 19, 2026 in 34m 5s

11 issues

find-bugs: Found 11 issues (5 medium, 6 low)

Medium

Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure - `src/cli/register-tool-commands.ts:31`

In tap.ts, swipe.ts, type_text.ts, and batch.ts, captureRuntimeSnapshotAfterAction is called inside the same try block as the primary UI action. If the snapshot call throws (e.g. RuntimeSnapshotParseError when AXe returns an empty accessibility tree during an animated screen transition), the catch block returns createUiActionFailureResult with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Also found at:

  • src/mcp/tools/ui-automation/batch.ts:200-204
  • src/mcp/tools/ui-automation/batch.ts:211-218
  • src/mcp/tools/ui-automation/shared/post-action-snapshot.ts:19-24
  • src/mcp/tools/ui-automation/swipe.ts:149-155
  • src/mcp/tools/ui-automation/tap.ts:21
  • src/mcp/tools/ui-automation/tap.ts:123-130
  • src/mcp/tools/ui-automation/type_text.ts:154-170
  • src/types/domain-results.ts:252-253
  • src/types/domain-results.ts:644
`batchElements` is never populated, making batch guidance permanently dead - `src/mcp/tools/ui-automation/shared/runtime-next-steps.ts:222`

At line 349 batchElements is initialised as an empty array and never filled, so every batchElements.length >= 2 guard (lines 366, 369, 391, 398) is permanently false and the batch-tap next step is never emitted.

Also found at:

  • src/mcp/tools/ui-automation/shared/runtime-next-steps.ts:351
createSheetSwipeFrame produces a degenerate 2px swipe target when the sheet grabber is near the bottom of its container - `src/mcp/tools/ui-automation/shared/runtime-snapshot.ts:346-361`

When grabberFrame.y + grabberFrame.height + 120 exceeds containerFrame.y + containerFrame.height * 0.85, top is greater than bottom, causing height = Math.max(2, bottom - top) to collapse to 2px — resulting in a nearly unusable swipe target that passes the isVisible check (height > 0) but cannot produce meaningful scroll behavior.

Stale runtime snapshot not cleared before post-action capture in tap success path - `src/mcp/tools/ui-automation/tap.ts:21`

Unlike every other action tool (swipe.ts, long_press.ts, touch.ts, etc.), tap.ts never calls clearRuntimeSnapshot(simulatorId) before captureRuntimeSnapshotAfterAction. If the post-capture throws a non-AxeError (e.g. a JSON parse error), the pre-tap stale snapshot survives and will silently serve outdated element positions to subsequent calls.

Post-action snapshot failure misreported as action failure in tap, swipe, type_text, and batch

If captureRuntimeSnapshotAfterAction throws (e.g. AXe describe-ui fails after the action), the catch block reports the entire operation as failed with a misleading message like "Failed to simulate tap on elementRef X" — even though the underlying action succeeded. Wrap captureRuntimeSnapshotAfterAction in its own try/catch and return a success result without capture when the snapshot step fails.

Low

`readState` guard is always true — function never returns `undefined` - `src/mcp/tools/ui-automation/shared/runtime-snapshot.ts:238`

The Object.keys(state).length > 0 check is dead code: state is always initialised with enabled and visible (at minimum 2 keys), so the function always returns the state object and never undefined. The caller's conditional spread ...(state ? { state } : {}) will always include state, making the branch that omits it unreachable.

`null` param values silently serialize to the string "null" in CLI next-step formatting - `src/types/common.ts:22`

null is now a valid NextStepParamValue, but hasComplexCliParamValue returns false for null (correct by intent), so a null top-level param falls through the per-key CLI loop and is formatted as the literal string "null" via JSON.stringify, generating --flag-name null — a string, not an omission.

Also found at:

  • src/utils/responses/next-step-formatting.ts:27-28
`wait_for_ui` with `predicate: 'gone'` and `elementRef` fails with ELEMENT_REF_NOT_FOUND when the element is already absent - `src/types/ui-snapshot.ts:125-131`

In wait_for_ui.ts (~lines 179–189), resolveElementSelector is invoked for every non-settled predicate before the polling loop. For 'gone', when elementRef points to an element that is no longer in the latest stored runtime snapshot (e.g., after a tap that triggered captureRuntimeSnapshotAfterAction and removed the target element from the UI), resolveElementSelector (wait-predicate.ts:153–163) returns { ok: false, error: { code: 'ELEMENT_REF_NOT_FOUND' } }. The executor then returns a capture failure, even though the element being absent is exactly the condition the caller is waiting to verify. This contradicts the PR's stated workflow of chaining wait_for_ui(predicate: 'gone', elementRef: ...) after a UI action that removes the element.

Hidden elements with `swipeWithin` leak into the scroll areas section - `src/utils/renderers/domain-result-text.ts:1279-1281`

isScrollableRuntimeArea returns true for elements that isHiddenRuntimeTarget marks as hidden (e.g. 'sheet grabber'), because !isLikelyRuntimeTarget is true for hidden elements regardless of their actions. Add !isHiddenRuntimeTarget(element) as a guard in isScrollableRuntimeArea.

Wrong fallback error message shown when `capture.type === 'runtime-snapshot-unchanged'` errors with no `result.error` - `src/utils/renderers/domain-result-text.ts:1476-1482`

When isRuntimeSnapshotUnchanged is true but isRuntimeSnapshot is false and result.error is null, the ternary falls through to 'Failed to get accessibility hierarchy.' instead of the intended runtime-snapshot message.

`HIDDEN_RUNTIME_TARGET_LABELS` filter not applied to the `scroll` array in `toRuntimeSnapshotCompactCapture` - `src/utils/structured-output-envelope.ts:49`

Elements suppressed via HIDDEN_RUNTIME_TARGET_LABELS (e.g. 'sheet grabber') are filtered from targets but not from scroll, so a sheet grabber with a swipeWithin action (but no tap/typeText) will appear in the compact capture's scroll array.


⏱ 30m 23s · 12.6M in / 490.4k out · $19.21

Annotations

Check warning on line 31 in src/cli/register-tool-commands.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 204 in src/mcp/tools/ui-automation/batch.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 218 in src/mcp/tools/ui-automation/batch.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 24 in src/mcp/tools/ui-automation/shared/post-action-snapshot.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 155 in src/mcp/tools/ui-automation/swipe.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 21 in src/mcp/tools/ui-automation/tap.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 130 in src/mcp/tools/ui-automation/tap.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 170 in src/mcp/tools/ui-automation/type_text.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 253 in src/types/domain-results.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 644 in src/types/domain-results.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[ESB-TCV] Post-action snapshot failure is caught by the primary-action catch block and returned as an action failure (additional location)

In `tap.ts`, `swipe.ts`, `type_text.ts`, and `batch.ts`, `captureRuntimeSnapshotAfterAction` is called inside the same `try` block as the primary UI action. If the snapshot call throws (e.g. `RuntimeSnapshotParseError` when AXe returns an empty accessibility tree during an animated screen transition), the `catch` block returns `createUiActionFailureResult` with a message like "Failed to simulate tap on elementRef X" — even though the tap/swipe/type/batch already succeeded. Agents that see this failure result will likely retry the action, causing unintended double-taps or double-swipes.

Check warning on line 222 in src/mcp/tools/ui-automation/shared/runtime-next-steps.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

`batchElements` is never populated, making batch guidance permanently dead

At line 349 `batchElements` is initialised as an empty array and never filled, so every `batchElements.length >= 2` guard (lines 366, 369, 391, 398) is permanently false and the batch-tap next step is never emitted.

Check warning on line 351 in src/mcp/tools/ui-automation/shared/runtime-next-steps.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

[8RA-FF6] `batchElements` is never populated, making batch guidance permanently dead (additional location)

At line 349 `batchElements` is initialised as an empty array and never filled, so every `batchElements.length >= 2` guard (lines 366, 369, 391, 398) is permanently false and the batch-tap next step is never emitted.

Check warning on line 361 in src/mcp/tools/ui-automation/shared/runtime-snapshot.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

createSheetSwipeFrame produces a degenerate 2px swipe target when the sheet grabber is near the bottom of its container

When `grabberFrame.y + grabberFrame.height + 120` exceeds `containerFrame.y + containerFrame.height * 0.85`, `top` is greater than `bottom`, causing `height = Math.max(2, bottom - top)` to collapse to 2px — resulting in a nearly unusable swipe target that passes the `isVisible` check (`height > 0`) but cannot produce meaningful scroll behavior.

Check warning on line 21 in src/mcp/tools/ui-automation/tap.ts

See this annotation in the file changed.

@github-actions github-actions / warden: find-bugs

Stale runtime snapshot not cleared before post-action capture in tap success path

Unlike every other action tool (`swipe.ts`, `long_press.ts`, `touch.ts`, etc.), `tap.ts` never calls `clearRuntimeSnapshot(simulatorId)` before `captureRuntimeSnapshotAfterAction`. If the post-capture throws a non-`AxeError` (e.g. a JSON parse error), the pre-tap stale snapshot survives and will silently serve outdated element positions to subsequent calls.