Skip to content

feat: skip a11y ancestor path on android - #44

Open
cryptotavares wants to merge 1 commit into
mainfrom
cryptotavares/remove-ax-tree-path
Open

cryptotavares wants to merge 1 commit into
mainfrom
cryptotavares/remove-ax-tree-path

Conversation

@cryptotavares

Copy link
Copy Markdown
Collaborator

What is the current state of things and why does it need to change?

Every mm describe-screen / observation payload on Android returns a11y nodes carrying a path field — the chain of ancestor roles for each node. On Android these are widget class names, e.g.:

{ "ref": "e86", "role": "android.view.ViewGroup", "name": "", "path": [
  "android.widget.FrameLayout", "android.widget.LinearLayout", "android.widget.FrameLayout",
  "android.view.ViewGroup", "android.view.ViewGroup", "android.widget.ScrollView", ... ] }

The path array dominates the output and makes the a11y tree so noisy that reading it in practice requires piping through custom filters to extract the useful ref | testId | name columns. For an LLM agent consuming describe-screen, path is pure token bloat with no targeting value — mobile targeting is by a11y ref or testId only, never by path.

path is produced in normalizeSnapshot() (src/platform/mobile-platform-driver.ts), which was platform-agnostic and emitted the ancestor chain on every node for both iOS and Android.

What is the solution your changes offer and how does it work?

Thread the device platform into normalizeSnapshot and, on Android only, emit an empty path ([]) for every node instead of accumulating the ancestor roles during the recursive walk. iOS and browser behavior are intentionally left unchanged — the path there may still carry signal (iOS parity is unvalidated), so this change is scoped strictly to Android.

Key design points:

  • path stays required on the exported A11yNodeTrimmed type (public API contract preserved — no optional churn). Android simply emits path: [].
  • Because path: [] is a valid string[], the field's only functional consumers — observation de-dup (nodeChangedarraysEqual(a.path, b.path)), the browser-only collapseIdenticalRuns, and buildOptionSummary — require no changes. On Android, path just stops contributing to change detection (arraysEqual([], []) is true), which is correct since it carried no signal there.
  • Platform is sourced from this.#backend.platform (authoritative, resolved after snapshot()), gating on === 'android' so both ios and browser retain the previous [...path, role] accumulation.

Verification

  • Full test suite: 1549 tests passed, no type errors.
  • Added an Android test asserting every node in a nested widget hierarchy (FrameLayout > LinearLayout > 2 Buttons) receives path: []; existing iOS assertions (path: ['Window']) remain unchanged as a regression guard.
  • ESLint + changelog validation clean.

On Android the a11y snapshot ancestor-role `path` is a chain of widget class names (e.g. `android.widget.FrameLayout`) that dominates the `describe-screen` payload while providing no targeting value — mobile targeting is by a11y `ref` or `testId` only. Thread the device platform into `normalizeSnapshot` and emit an empty `path` for every node on Android instead of accumulating ancestry.

iOS and browser behavior are unchanged (iOS parity is unvalidated). `path` stays required on `A11yNodeTrimmed`, so de-dup/compaction consumers (`nodeChanged`, `collapseIdenticalRuns`, `buildOptionSummary`) need no changes.
@cryptotavares
cryptotavares force-pushed the cryptotavares/remove-ax-tree-path branch from 02dbd3a to 3bbda5c Compare August 13, 2026 22:37
@cryptotavares
cryptotavares marked this pull request as ready for review August 14, 2026 06:47
@cryptotavares
cryptotavares requested a review from a team as a code owner August 14, 2026 06:47
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.

1 participant