Skip to content

parseCDPAXTree silently discards root siblings when the root node is itself a presentational wrapper #877

Description

@sroussey

Defect

9a80450 fixed a real bug in packages/browser-control/src/task/CDPBrowserBackend.ts: buildNode returned null for any node whose role is in IGNORED_ROLES (:62"none", "generic", "ignored", "InlineTextBox") or that CDP marked ignored, and dropped its whole subtree. Chrome hangs a document off one or two such wrappers, so BrowserSnapshotTask returned a near-empty tree for most real pages. The fix is right, and the reasoning is stated at :95-99.

The new buildNodes (:101-118) returns an array and splices a meaningless node's children in where it stood. But the single caller does this — :175:

const built = buildNodes(rootNode)[0];

When rootNode is itself one of those wrappers, buildNodes returns N spliced children and [0] keeps the first, silently discarding the other N−1. That is precisely the case the change exists to handle: the old code could not hit it (it returned one node or null), the new code can.

rootNode is picked at :88-92 as the node no other node claims as a child (nodes.find((n) => !childIds.has(n.nodeId)) ?? nodes[0]), so it is whatever the CDP payload happens to root at — not guaranteed to be a RootWebArea.

Evidence

  • packages/browser-control/src/task/CDPBrowserBackend.ts:62IGNORED_ROLES
  • packages/browser-control/src/task/CDPBrowserBackend.ts:101-118buildNodes returns MutableAccessibilityNode[]
  • packages/browser-control/src/task/CDPBrowserBackend.ts:175buildNodes(rootNode)[0]

No test covers it, because there are none: find packages/browser-control -name "*.test.ts" → 0, and grep -rln "parseCDPAXTree\|queryAXTree" packages/test/src --include=*.test.ts → nothing.

Proposed fix

const built = buildNodes(rootNode);
if (built.length === 1) return built[0] as AccessibilityNode;
if (built.length > 1) {
  const ref = `e${++refCounter.count}`;
  refMap.set(ref, null);
  return { ref, role: "document", name: "", children: built };
}
// existing empty-tree fallback

…plus the first test in this package: a recorded Accessibility.getFullAXTree payload rooted at a generic wrapper with two meaningful children, asserting both survive. That fixture needs no browser and would also have caught the original subtree-dropping bug.

Why it matters

BrowserSnapshotTask's output is the only thing an agent sees of a page. Losing a sibling subtree is invisible — the snapshot looks plausible, just smaller — and there is no detector anywhere in the repo (see #875 and #701 for why the surrounding tests do not run).

Found during the 2026-08-31 review. Snapshot: workglow-dev/prdanalysis/grades/2026-08-31/libs-providers.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions