Files
callstack__agent-device/test
Michał Pierzchała 1d1367311a perf(find): reuse snapshot index once per ranking pass (#1971)
* refactor(find): isolate match ranking policy

Move find's candidate ordering — on-screen preference, actionability
scoring, and the area/input-order tie-break — out of find-match-resolution.ts
into a focused sibling. Pure move: no score input, ranked order, ambiguity
refusal, or --first/--last behavior changes.

The extraction establishes the seam the follow-up indexing change needs,
so the ranking pass has one production entry point to build a topology in.

Refs #1690

* perf(find): index snapshot topology once per match ranking pass

Ranking a mutating find's candidates asked the whole tree the same three
questions once per candidate: same-rect descendants filtered every node,
the nearest hittable ancestor rebuilt a full index map, and the
overly-broad-ancestor check re-filtered every node for viewport roots.
With m matches over an n-node capture that is O(m x n) full-tree work
before find can act or refuse.

buildActionableTouchTopology reads those three collections in one pass
(nodes by index, children by parent index, normalized viewport-root
rects). preferOnscreenMatches builds exactly one per multi-match pass and
threads it through every score; resolveActionableTouchResolution takes it
as an optional argument so one-off interaction callers keep the cheap
two-argument shape. findNearestAncestor gained the same optional prebuilt
map its snapshot-presentation sibling already accepted, and
classifyActionableTouchCandidates now reuses one topology instead of
building a bare index map and re-resolving per candidate.

Score inputs, ranked order, area and input-order tie-breaks, ambiguity
refusal, and --first/--last are unchanged; only the derivation is shared.

Observed red first: with the wiring hunk removed, the new ranking
regression reports builder calls 0 (expected 1) and 64 filter + 32 map
whole-tree scans (expected 0) over 32 candidates.

The topology docstring records two seams the reviewer asked for. #1690
names src/snapshot/snapshot-processing.ts as findNearestAncestor's home;
that path is gone and packages/contracts/src/snapshot-tree.ts is the seam
that replaced it, so the issue's file list is drifted rather than a second
site to change. And viewportRootRects is not interchangeable with
snapshot-visibility's precomputedViewportRects: normalizeRect drops
negative width/height where hasValidRect keeps them, which changes which
rect wins pickLargestRect.

Refs #1690

* refactor: hide actionable touch indexing
2026-08-23 11:00:22 +02:00
..