From 0e2de953abb29f739afad717a029da86b648d876 Mon Sep 17 00:00:00 2001 From: Sanket Sudake Date: Sun, 16 Aug 2026 22:54:53 +0530 Subject: [PATCH 1/2] fix: --by cell ranks candidates by grid, re-resolves replaced nodes, names the cover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three findings from a live timesheet run where `fill --by cell "Tue, …"` failed as `occluded` on one column in five, every week, while the other four columns filled fine. 1. cellLocatorJS picked candidates in document order after a geometric "in this column" filter. Workday's global "Search Workday" input sits above its Enter Time dialog with its centre inside one day column's tolerance; it comes first in the DOM and is under the modal overlay, so that column resolved to an off-grid, covered input and timed out. Rank candidates instead of filtering: the header's own grid beats the rest of the page, an unoccluded centre beats a covered one, document order breaks ties. Off-grid candidates stay as the last resort because some grids split header and body tables. TestCellAddressingPrefersGridOverOffGridField reproduces the wrong pick (value lands in the search box) on the old locator. 2. settledNodePoint polled one resolved node until the deadline. A grid that re-renders its row after a commit (Workday's does; input ids change) detaches that node, which then measures 0x0 forever and reads as `occluded`. The geometry probe now reports `detached`, and the new settledPointFor / coordClickSelector re-resolve the selector on it; Type, Fill, Key, the pointer verbs and drag targets all go through it. Only if the replacement never settles does the caller see a timeout, now with `detached: true`. TestCellAddressingReResolvesReplacedNode models the commit-veil-then- rebuild shape and fails on the old code with the exact message seen live. 3. `occluded: true` said nothing about WHAT was on top, so each failure had to be reproduced under instrumentation. OccludedError now carries the element found at the centre and puts it in the message ("its centre is covered by DIV name=\"modalOverlay\"", or "it measured 0x0"); the evidence rides in the message because errors cross the daemon as strings and the envelope must read the same on both paths. IsOccluded still matches. TestOccludedErrorMessage pins the format. Live-verified on the real grid with the rebuilt binary: a five-column session batch fills clean where the released binary failed the Tue cell. --- docs/cli-reference.md | 5 +- internal/chrome/cdp.go | 45 +++++++--- internal/chrome/cell_test.go | 148 +++++++++++++++++++++++++++++++ internal/chrome/geometry.go | 19 +++- internal/chrome/key.go | 6 +- internal/chrome/pointer.go | 15 ++-- internal/chrome/pointer_test.go | 40 +++++++++ internal/chrome/select.go | 130 ++++++++++++++++++++------- internal/cli/commands.go | 8 ++ skills/drive-chrome-cdp/SKILL.md | 7 ++ 10 files changed, 360 insertions(+), 63 deletions(-) diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 0d0b73a..8ae5364 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -92,7 +92,7 @@ On real apps, prefer `name` — it reads the accessibility tree, skips hidden/ut | `id` | an element id | | | `name` | an ARIA accessible name | prefer on real apps; pair with `--role` / `--nth` / `--match` | | `ref` | a `snap`-issued `e` ref | act on the exact node `snap` reported, no re-resolve | -| `cell` | a `[row\|]column` grid header | resolves the editable input in that grid cell | +| `cell` | a `[row\|]column` grid header | resolves the editable input in that grid cell; a candidate in the header's own grid beats one elsewhere on the page, and an unoccluded one beats a covered one | | `label` | a form control's visible label | for controls whose label isn't wired (no `aria-label` / `