Repository navigation
[compass] Fix: seat_observe repeats a window's tree while a Chrome drop-down list is open - #29
Conversation
seat_element set_value on a Chrome <select> returned {"performed":"set_value"}
while the list kept its old choice, so an agent could submit a form with the
wrong option. AccessibilityReader.Act now reads the control back after
SetValue: it waits up to 1.5 s for the value to change (Chrome shows a change
a moment later), fails with a message that says to expand the list and select
the option when the control still reads its old value, and returns what the
control reads when it reformatted the value.
The new quick check "desktop ignored set_value" drives a hidden in-process
ComboBox that applies, ignores, applies late or reformats the value. The
hidden-window plumbing of "desktop unknown control types" moves into a shared
Hosted helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e may have taken (review) From the second review of B-026's fix: - The failure quoted up to 200 characters of the control's old value, and the pipe server logs every handler error to anode.log, so a field's contents could reach the log. The message no longer quotes the control. - It now says the control may have taken the change and kept its old text (a tag field, a value it rewrites, a change still pending), and gives the "expand the list" advice only for a ComboBox. - A COMException, timeout or InvalidOperationException while reading back, after the value was sent, now answers "performed" with a note instead of a failure that reads as "nothing happened". - The text summary shows what a reformatting control now reads (sanitised), and PROTOCOL.md lists the optional `value`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck catch and a text field (review) From the re-check of b884144: the text summary quotes what a reformatting control reads, or says "(empty)", so app text isn't read as part of Anode's note. The quick check now also covers a control that goes away after the value is sent (performed, "could not be read back") and a text field that ignores the value (no drop-down advice). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t is open (B-027) With a <select> list open, Chrome reported the window's Pane again below itself, and AccessibilityReader.Observe's breadth-first walk followed the copy over and over (depths 1, 2, 3, 4...) until the element budget ran out, so the list's options never appeared. The walk now remembers each element's UIA runtime ID and skips one it has already described, without walking it again or counting it against maxElements, and a warning says how many repeats were skipped. Sibling indexes still count the skipped repeats, so the paths that seat_element follows stay valid. The new quick check "desktop repeated elements" builds a hidden in-process fragment tree with a loop (C lists its ancestor A) and a control under two parents (B under A and D), then acts on C through its observed path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efore queueing (review) From the second review of B-027's fix: - The "Skipped N controls" warning made seat_wait's "missing" state, which needs a tree without warnings, time out on any window with a repeat. The count is now `skippedRepeats` and a summary line, not a warning. - A repeat is caught when its parent lists it, so it no longer takes a queue slot from real controls; a control whose description fails leaves room for a later copy; a null runtime ID no longer throws. - The CHANGELOG and DESKTOP-TOOLS.md describe what Anode does, not Chrome's internals, which still need a seat to confirm; PROTOCOL.md lists the field. - The check now has a second provider object with B's runtime ID before a unique control E, acts on E through a path that counts the skipped repeat, uses a budget one above the unique count, and asserts that a wait for a missing control still matches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…ad IDs unique (review) From the re-check of fed1aea: since a skipped repeat takes no budget, a provider whose next sibling comes back to a control it already gave (X -> X, or X -> Y -> X) kept the walk going until the 4 s limit, then dropped the rest of the tree. A parent's list of children now ends at the first control it gives twice. Also: an ID read only when a control is described is checked against the ones already listed; a failed description frees that ID either way; and Act compares a null runtime ID as empty instead of throwing. The check's E now says it is its own next sibling; without the stop, the check fails with only A and D listed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compass merge: ready to merge, merging now #28 landed as a squash ( Tested by compass-merge on 2026-10-09, on that same tree, so this is the code that lands:
Review (whole diff):
Second opinion: the fixer's fresh Claude subagent reviews (three passes; the last found no P1 or P2). The Codex CLI is at its usage limit until 22:15 today, and Codex's GitHub review hit the same limit here. Undo: |
…down notes compass-merge's review of #27 (2026-10-09): - GUIDE-DESKTOP-APP: build before `lease acquire`, so a long build can't expire the 120 s lease; mention `--ttl 600`; scale screenshot points back to seat pixels before `seat_click`. - GUIDE-ANDROID, ANDROID, TROUBLESHOOTING: `anode android` needs Anode running; only `--json` shows `booted`. - GUIDE-WEB-FORM: sign in before the agent takes the lease; take the next element ID from `seat_wait`'s own reply; scale screenshot points back; #28 and #29 fix the `<select>` behaviour in the next release; one `seat_type` call types at most about 3,000 characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Report
C:\DEV\Compass\ANODE-BUGS.md, B-027 (compass-followup, 2026-10-08, while recording the C-042 web-form guide in the seat). On installed Anode 0.11.0, the followup ranseat_element expandon a Chrome<select>in an--appwindow. Thenseat_observe(maxDepth 16, maxElements 60) returned the window's Pane "Sample sign-up form" nested inside itself at depths 1, 2, 3 and 4. Each copy repeated the caption buttons and the address bar until the element budget ran out ("Tree truncated"). The list's options never appeared. Before the expand, and after the list closed, the same call returned the normal 32-element tree.Root cause
AccessibilityReader.Observewalks the window breadth-first and trusted the app's tree to have no loops or shared controls. The report shows the window's tree nested inside itself, so with the list open Chrome lists the window, or a pane holding its controls, below itself. The walk followed that loop until the budget ran out, so the rest of the tree, including where the options would be, was never reached. B-014's log shows something similar: Chrome's toolbar and bookmarks listed twice in a normal window.Not confirmed: public results strip runtime IDs, so the report can't show that Chrome's copies share one. The review judged that they very likely do, about 75% sure. Chromium gives each node a stable
[UiaAppendRuntimeId, unique id]. If they don't, the guard still ends the loop after at most one extra copy, since only two windows (the app window and the popup) take part. A seat run settles it; see below.Fix
The walk reads each child's UIA runtime ID when its parent lists it. A child with an ID already queued is skipped: it isn't queued, described or walked, so it takes no room from real controls. The new
skippedRepeatsfield says how many were skipped, with a line in the text summary.seat_wait'smissingstate needs a tree without warnings.seat_elementfollows (first child, then next sibling by index, then a runtime-ID check) still lead to the observed control.docs/DESKTOP-TOOLS.md,docs/PROTOCOL.mdand the CHANGELOG say this.How it was tested
New quick check
desktop repeated elements. It uses a hidden in-process window (never shown) with a UIA fragment tree:It observes with a budget of 7 for 6 controls. It expects A, D, B, C and E once each, no truncation,
skippedRepeats: 3, the summary line, no warnings, and aseat_wait-style "missing" match. Then it invokes E through its path[1,1], which counts the skipped repeat before it, and only E must be invoked.Mutation runs (file restored byte for byte after each, then rebuilt):
A,D,B,C,B,Ewithtruncated: True, the reported symptom in small.A,Dandtruncated: True, after the 4 s limit.FOCUS test command (
scripts\build.ps1 -QuickTest -OutputDirectory artifacts\pkg-build): 97/97 pass at each commit, with [compass] Fix: seat_element set_value reports "performed" on a Chrome drop-down list that ignores it #28's check. Debugselftest --quick: pass.No seat, lease, input, browser or Android command was run, and the installed Anode's
anode-controlpipe wasn't touched.Second review: a fresh read-only Claude subagent (Codex is at its usage limit until Fri 9 Oct 22:15). First pass, on the first commit: no P1. Two P2s:
seat_wait state=missing.Both are fixed in fed1aea, with its P3s:
It also noted that WPF and WinForms build some runtime IDs from 26-bit hash codes, so two distinct controls could rarely share one (about 0.03 to 0.2% chance in a 200 to 500 element walk). That's left as a P3. A re-check of fed1aea found one new P2, fixed in 0acceef. Repeats take no budget, so a provider whose next sibling loops back (X to X) kept the walk going until the 4 s limit and dropped the rest of the tree. A parent's list of children now ends at the first control it gives twice. Its P3s are fixed in the same commit: a re-read ID is checked against those already listed, a failed description frees its ID, and
Actcompares a null runtime ID as empty. A last look at 0acceef found no new P1/P2. Two P3s are left: two distinct provider objects with one runtime ID under the same parent would end that parent's list early, and the test doesn't cover the dequeue-time re-read or freeing an ID after a failed description.Still to check in a real seat
C:\DEV\Compass\scratch\compass-followup\c042-media\form\index.html. The tree should list each control once, withskippedRepeatsset and no repeated window pane. This confirms or refutes the shared-ID assumption above.selecton one works through its path.🤖 Generated with Claude Code