Skip to content

fix(a11y-android): carry the device's own node ref, don't infer it - #354

Merged
xxx merged 1 commit into
masterfrom
fix/android-carries-the-devices-own-node-ref
Aug 6, 2026
Merged

fix(a11y-android): carry the device's own node ref, don't infer it#354
xxx merged 1 commit into
masterfrom
fix/android-carries-the-devices-own-node-ref

Conversation

@xxx

@xxx xxx commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

The on-device-service reader assumed AxNodeId(n) equalled the companion's ref n, on the grounds that both sides number the same nodes in the same pre-order. That holds only while the host keeps every node the device sent. The Depth and Siblings caps drop a subtree and then continue with later siblings, so past the first pruned subtree a host id is lower than the device's own ref — an action dispatched by it reaches a different node and reports success.

The companion has emitted ref on every node since its first release, and resolves an action against its own freshly re-read, untruncated tree. The ref namespace was therefore never the host's kept set.

What changed

  • json_to_node reads each node's ref and records it in the pre-order assign_ids numbers by; tree_from_json returns a RefTree pairing the tree with that table.
  • invoke and set_value dispatch by RefTree::device_ref(..) — what the device calls the node — instead of the host id. set_value addresses the node its guard approved rather than the id the caller named.
  • A node with no ref fails the snapshot, like malformed bounds: dropping it would shift every later id, and inferring it is the bug this removes.
  • The guard refusing a native click on a Depth/Siblings-truncated tree is gone. Such a tree now actuates natively rather than falling back to a pointer click.

The uiautomator reader carries no equivalent inference — it acts by tap coordinates and consults truncation only to explain an element missing from its own re-read tree.

Verification

cargo test --workspace, cargo clippy --workspace --all-targets -- -D warnings and cargo fmt --check are clean.

Both action paths are covered end-to-end against the fake device, over a tree shaped root > [Card > (c1, c2, c3), trailing] where the trailing node is device ref 5:

  • depth: 1 makes it host id 2 — a click by host id would actuate c1.
  • siblings: 2 makes it host id 4 — a write by host id would land in c3.

Both assert the exact ref on the wire, and both were confirmed to fail against the id-dispatching code.

The Android device suite is schedule-only, so PR CI does not exercise this on a real emulator. Default limits (depth 30, siblings 4096) do not fire on a real app, so an on-device run would cover the untruncated path as a regression check rather than the fix itself.

Fixes #288

🤖 Generated with Claude Code

The service reader assumed `AxNodeId(n)` equalled the companion's `ref` n,
because both sides number the same nodes in the same pre-order. That holds
only while the host keeps every node the device sent. The `Depth` and
`Siblings` caps drop a subtree and carry on with later siblings, so past the
first pruned subtree a host id is lower than the device's own ref, and an
action dispatched by it reaches a different node and reports success.

The companion has emitted `ref` on every node since its first release and
resolves an action against its own freshly re-read, untruncated tree, so the
ref namespace was never the host's kept set. Read it instead of inferring it:
`json_to_node` records each node's ref in the pre-order `assign_ids` numbers
by, and `RefTree` pairs the tree with that table so `invoke` and `set_value`
dispatch by what the device called the node. A node with no `ref` fails the
snapshot, like malformed bounds — dropping it would shift every later id.

That removes the class rather than one path, so the guard refusing a native
click on a `Depth`/`Siblings`-truncated tree goes; such a tree now actuates
natively instead of falling back to a pointer click.

Fixes #288

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@xxx
xxx marked this pull request as ready for review August 6, 2026 03:57
@xxx
xxx merged commit d03d83c into master Aug 6, 2026
20 checks passed
@xxx
xxx deleted the fix/android-carries-the-devices-own-node-ref branch August 6, 2026 04:05
xxx added a commit that referenced this pull request Aug 6, 2026
)

The convention is that a changelog entry lands with its PR. Auditing all 101
commits since v1.1.0 for whether they touched CHANGELOG.md found five that
changed what a user sees and carried no entry:

- #354 a native Android action through the companion reached a different
  element than the one named, on any truncated tree, and reported success
- #353 a failing device was classified by matching words in a message whose
  tail is the device's own output, so a wedged emulator read as "not ready"
- #330 an element whose screen had been destroyed was reported as changed,
  advising a re-snapshot that cannot help
- #310 a subtree dropped by a failed child read left the tree looking complete
- #283 glass_clipboard_set on the iOS Simulator returned ok for a write that
  was still in flight

All five are corrections to what glass reports rather than additions, so they
join ### Fixed. No code changes.

cargo test --workspace: 1938 passed, 0 failed. cargo fmt --check clean.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Android host ids are inferred to equal device refs, and truncation breaks that

1 participant