refactor(capture-kit): read AXe element trees in one shared adapter

Closed
GE
14h ago
+400-327

In #3960 Michał asked for one module that reads the AXe-keyed iOS element tree (the JSON Limrun's API and NoMac's phones both serve), so the same tree gives the same nodes whichever provider fetched it. This moves Limrun's reading of that tree into capture-kit with no behaviour change, so it can land before #3960.

  • @agent-device/capture-kit/ios-axe-tree exports flattenAxeTree, readAxeNodeRect, readAxeNodeChildren, readAxeTreeViewport and the AxeTreeNode type. The code is Limrun's snapshot.ts and the viewport-root mapping from ios-snapshot-adapter.ts, moved, not rewritten.
  • Limrun imports it and keeps only its own steps: fetching the tree, expanding a collapsed tab bar, and its device-info viewport fallback. snapshot.ts keeps toIosSelector. The rest of the Limrun diff is the IosTreeNode to AxeTreeNode rename.
  • The producer stays limrun-ios-tree. One neutral id for both providers (such as provider-ax-tree) is your call, since tests pin the id. For what it's worth, in my NoMac run on #3960 snapshot -i --json printed no producer, only snapshotDiagnostics.stats.backends: {"xctest": 1}. If you want the rename, I'll add it here.
  • Tests: the flatten cases moved from provider-limrun/src/snapshot.test.ts to capture-kit/src/ios-axe-tree.test.ts, plus viewport and nesting cases. Limrun's tests pass with only the type renamed.

Local gates at 950a32835 (pnpm check:affected --run --base upstream/main --head HEAD): format, lint, typecheck, layering, di-seams, fallow, native-dupes, mcp-metadata, build, package, integration-node, macos-coverage and vitest-related passed. unit had four 5 s timeouts outside this diff: the two fast-check cases in platform-android/src/manifest.test.ts, and one test each in scripts/__tests__/committed-source-tree.test.ts and eager-closure-budgets.test.ts. The first three time out on main at 3a218f573 on my laptop too, and the fourth passes on its own. check:affected stops at the first failure, so I ran the 26 checks it skipped with their own scripts, and all passed.