Skip to content

Count a successful visualClick as browser progress - #214

Merged
DavertMik merged 2 commits into
mainfrom
fix/visualclick-counts-as-progress
Sep 15, 2026
Merged

DavertMik merged 2 commits into
mainfrom
fix/visualclick-counts-as-progress

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

What happened

Session WeeDefeatedHarlequin414 (Langfuse trace bbc695626bbc6d049f40869e15f727c2), scenario "Create a manual plan with the Run Automated as Manual option disabled…", failed after 76 seconds without ever pressing Save.

The target control is <button class="group" role="switch"><span class="flex-col"></span></button>no accessible name at all (axe flagged button-name and aria-input-field-name on that page). Four click attempts and two xpathCheck probes missed, correctly. The tester then did exactly what the rules at src/ai/tester.ts:819 tell it to do and reached for visualClick, twice, successfully — the ariaDiff proves the toggle really flipped (added: switch [checked]removed: switch [checked]).

It was cut off anyway, with No further browser progress on unchanged page; requesting final review.

Root cause

shouldStopForStalledExecution (src/ai/tester.ts:501) builds its action set from ACTION_TOOLS + DELEGATED_ACTION_TOOLS + SPECIAL_CONTEXT_ACTION_TOOLS. Against the tools actually registered in src/ai/tools.ts (click, hover, pressKey, form, exitIframe, interact, visualClick), that set covers every browser-mutating tool except visualClick — the one the rules mandate when click fails.

Bucketing the trace's tool calls into the tester's invoke spans:

iteration tools counter
#2 formformsee reset
#3 click✗ ×3 1
#4 xpathCheckcontext clickxpathCheckvisualClick 2
#5 verifyseevisualClick 3 → bail

Both iterations that drove the counter to MAX_STALLED_ITERATIONS contained a successful visualClick.

Fix

One line. DELEGATED_ACTION_TOOLS is read only by that check — not by the prompt (line 527 interpolates ACTION_TOOLS) and not by actionPerformed (line 351) — so visualClick stays a fallback in the model's eyes and only starts counting as progress. It sits beside interact because both delegate the click to another model.

Trade-off

visualClick reports success for any coordinate click, including one that lands on nothing, so a run clicking empty space can keep itself alive longer. The existing "don't repeat visualClick in a row" rule and state-hash dead-loop detection remain the bound.

Tests

New case in tests/unit/tester-focus-scope.test.tsTester stalled execution: a successful visualClick between empty iterations must reset the counter. Verified it fails on main and passes with the fix.

bun test tests/unit/ — 1415 pass, 0 fail.
bun test tests/integration/ — 144 pass; the 3–4 overlay-modal-browser timeouts are order-dependent and reproduce on main unchanged (that file passes 14/14 on its own).

Not fixed here

Two more findings from the same trace, each deserving its own change:

  • The plan was titled Pilot Persistence Plan 2026-09-11-003, not the session name. The experience recipe's code block is a frozen data literal — I.fillField('#plan-title', 'Pilot Persistence Plan 2026-09-11-003') — and a ready-made command beats the abstract instruction at tester.ts:875 ("include ${task.sessionName} in the value"). A sibling recipe had frozen another run's session name outright. Writer is src/ai/historian/experience.ts:339, which passes candidate.success.output.code verbatim into writeAction.
  • This switch will keep costing a run. visualClick executes I.clickXY(x, y), and isNonReusableCode (src/utils/step-analyzer.ts:36) deliberately drops coordinate clicks, so the resolution is never written to experience. Right call on its own terms — but the real fix is an accessible name on the control in the app under test.

🤖 Generated with Claude Code

The tester's stall guard built its action set from ACTION_TOOLS +
DELEGATED_ACTION_TOOLS + SPECIAL_CONTEXT_ACTION_TOOLS, which covers every
browser-mutating tool except visualClick. So the one escape hatch the rules
mandate when click() fails — "when click() fails on an element you can see,
you MUST try visualClick()" — was invisible to the progress check.

On a page whose control has no accessible name, every locator-based click
misses by design and visualClick is the only way through. The tester used it,
successfully, twice; the counter kept climbing anyway and the run went to
final review before the form was ever submitted.

DELEGATED_ACTION_TOOLS is read only by that check, so visualClick stays a
fallback in the prompt and only starts counting as progress.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@DavertMik
DavertMik merged commit 5c50123 into main Sep 15, 2026
2 checks passed
@DavertMik
DavertMik deleted the fix/visualclick-counts-as-progress branch September 15, 2026 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants