Repository navigation
feat!: flat mobile a11y tree without layout-only nodes - #44
Draft
cryptotavares wants to merge 2 commits into
Draft
cryptotavares wants to merge 2 commits into
cryptotavares wants to merge 2 commits into
Conversation
cryptotavares
force-pushed
the
cryptotavares/remove-ax-tree-path
branch
from
August 13, 2026 22:37
02dbd3a to
3bbda5c
Compare
cryptotavares
marked this pull request as ready for review
August 14, 2026 06:47
Ticket P1-14 (supersedes the Android-only first version of #44). Mobile a11y nodes carried `path`, the list of every ancestor's role. On mobile those roles are widget class names (`android.widget.FrameLayout`, `RCTViewComponentView`), with no targeting value; on Android wallet home they were 21,658 entries in a ~1 MB describe-screen. Most nodes were also unnamed layout views. - `A11yNodeTrimmed.path` is optional. Mobile nodes have no `path` (iOS and Android). Browser nodes keep it (ancestor dialog/heading names, used to collapse identical rows). The observation diff and row collapse treat two absent paths as equal. - Mobile: layout-only elements (no label, value or testId, not an actionable control, not a progress indicator) are dropped and replaced by their kept descendants, in order. Containers with a testId, named elements, controls and progress indicators stay, and so do unlabelled React Native leaf views of at least 24x24 (icon buttons without a label, such as the wallet action buttons), listed with bounds. - Refs exist for nodes with a testId, label or value (as before); listed nodes without one (unlabelled tap targets, progress indicators) have bounds for tap-coordinates. Ambiguity marking covers only listed nodes. - `bounds` keys are always in x, y, width, height order (idb returns them in varying order, which made unchanged screens look changed). - One busy-type regex shared by the settle logic and the filter. BREAKING CHANGE: `A11yNodeTrimmed.path` is optional and absent on mobile nodes; mobile trees no longer include unnamed layout nodes, so mobile ref numbers change.
cryptotavares
force-pushed
the
cryptotavares/remove-ax-tree-path
branch
from
October 25, 2026 17:41
3bbda5c to
f38073e
Compare
cryptotavares
changed the base branch from
main
to
cryptotavares/mmcli-p1-13-mobile-snapshot
October 25, 2026 17:41
cryptotavares
marked this pull request as draft
October 25, 2026 17:41
cryptotavares
added this pull request to stack #63
October 25, 2026 17:41
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack context: mm CLI v2, Phase 1, P1-14. Base:
cryptotavares/mmcli-p1-13-mobile-snapshot; review #67 first. Next: #68.Problem
Mobile
pathrepeats native ancestor class names, such asandroid.widget.FrameLayoutandRCTViewComponentView. These names add no ref or testId targeting information. Unnamed layout wrappers further inflatedescribe-screenand observation payloads.Browser ancestry has a different purpose: it holds dialog and heading context used by row collapse. Shared comparison code must accept mobile nodes without losing that browser context.
On iOS, idb also varies frame key order. Equivalent frames can therefore produce different serialized screen signatures.
Solution
Apply pruning at the
MobilePlatformDrivernormalization boundary, after the app-hierarchy filtering introduced by P1-13.flowchart TD A[App-filtered mobile snapshot] --> B[Prune layout-only elements; hoist kept descendants] B --> C[Keep unlabelled RN leaves at least 24 x 24] C --> D[Flatten in pre-order; number kept nodes] D --> E[Omit path; write bounds in fixed key order] E --> F[Build refMap for testId, label, or value] F --> G[Mark duplicate stable identifiers as ambiguous] A --> H[Read test IDs and locators from app-filtered hierarchy] classDef new fill:#fff3cd,stroke:#d39e00 class B,C,D,E newYellow boxes are new.
pruneLayoutOnlykeeps elements with a label, value, identifier, actionable type, or progress-indicator type. It matches exact iOS AX types and Android class-name suffixes. Removed wrappers give way to their kept descendants, in order. The function preserves input objects and returns unchanged subtrees by reference.A plain React Native view also stays when it has no kept children and measures at least 24×24. This retains unlabelled icon buttons, including the iOS wallet action buttons. These nodes expose
boundsfortap-coordinates.Both
getAccessibilityTreeandcaptureScreenuse this normalization. Test IDs and generated locators still use the app-filtered hierarchy before layout pruning. Browser nodes retain theirpath. Shared diffing and row collapse treat two absent paths as equal, but distinguish absence from an empty array.The filter and screen-settle logic share
MOBILE_BUSY_TYPE, so pruning retains the progress indicators that settling detects. Normalization writes bounds inx, y, width, heightorder to stabilize screen signatures.What changes
pathpath; flatten kept nodes in pre-orderrefMapentryx, y, width, heightThe original Android-only change (
3bbda5c) now covers both mobile platforms under P1-14. The protected proof below records payload reductions and the remaining size-target gaps.Decisions for reviewers
clickableflag. The leaf rule retains unlabelled wallet action buttons while removing wrappers.pathfield optionalReview guide
Read the files in this order:
src/tools/types/discovery.ts,src/tools/utils/constants.tspath, shared progress-indicator matching, and the minimum tap-target size.src/platform/mobile-hierarchy.tssrc/platform/mobile-platform-driver.tssrc/server/observation-compaction.ts,src/server/screen-settle.ts,src/tools/utils/discovery.tssrc/platform/mobile-hierarchy.test.ts,src/platform/mobile-platform-driver.test.ts,src/platform/__fixtures__/ios-wallet-home-preorder.jsonREADME.md,SKILL.mdTesting
Fresh checks at remote head
f38073e4ad5732ade9c22d5f6e486753a44b6416:EACCESdiagnostic.yarn lintpasses.yarn tsc --noEmit --project tsconfig.build.jsonpasses for production source.yarn tsc --noEmit --project tsconfig.jsonfails with 11 existing test-file diagnostics at this unchanged head. They include mock typing, top-levelawait, and a missingToolContextimport.ProofandUnit testssections below retain historical evidence. The fresh checks above cover focused tests, lint, and production types.Breaking changes
A11yNodeTrimmed.pathis optional and absent on mobile nodes.Consumer impact
test/e2e/playwright/llm-workflow/node.path)tests/llm-workflow/node.path)Proof
Tested with yalc builds in Mobile (dev client) on the iOS simulator (idb nested format, MetaMask/device-mcp#38) and the Android emulator (MetaMask/device-mcp#39). Before = the #67 build. Byte counts are for the pretty JSON the CLI prints, with compact JSON in brackets.
describe-screenpathentriesdescribe-screenpathentriestap-coordinatesat its boundsAcceptance criteria not fully met:
describe-screenis ≤ 15 kB only as compact JSON. The CLI pretty-prints with a 2-space indent, which is ~41 % of the bytes.The remaining size is named content (token rows, banners, prices) and
bounds(~30 % of mobile bytes). Possible next steps: compact CLI output, dropping or roundingbounds, and a cap on long lists.Known issues not caused by this PR:
findElementaccepts a substring testId match and takes the first one, soclick --testid account-pickeron Android tapsaccount-picker-avatar.Unit tests
New tests:
pruneLayoutOnly,isLayoutOnly,isActionableElement: iOS exact types,XCUIElementTypeprefix not actionable, Android suffixes incl.CheckedTextView/RatingBar, progress indicators, unlabelled leaf ≥ 24×24.pathon mobile (iOS and Android); browserpathkept; diff and collapse with absentpath.