Repository navigation
fix(scroll): refuse an unreadable capture before scroll top/bottom decides - #3736
Conversation
…cides `scroll bottom` coerced a failed or sparse capture to an empty tree, which the edge analyzer reads as "no hidden content below", so it reported "Already at bottom" about a screen it never saw. Both scroll loops now run on one `runScrollPasses` loop in capture-kit: observe, refuse an unreadable observation, ask the caller's stop condition, then spend a pass. The daemon defines the refusal once (`scrollCaptureRefusal`, built on `sparseCaptureQuality`) and both `scroll top`/`scroll bottom` and `scroll --until` pass it in, so an edge scroll now fails with `scroll_edge_capture_unreadable` instead of an edge verdict. Fixes callstack#2468
|
The PR is ready at 62eb122. The refusal check now runs before scroll top/bottom decides, and I found no problems in the code. CI is green: 1 check, 0 not passing, and the author's receipt covers format, lint, typecheck, layering, fallow, build and vitest-related. There are no conflicts. Nothing else must happen before merge. Not blocking, and you can take or leave these: in I did not run the tests or the gate. I judged the regression tests by reading the pre-change path. I could not check the live Android run, which comes from the PR body. I did not check whether backends other than Android and iOS, such as Appium, ever declare quality sparse, so for them the refusal only fires on a missing or empty tree. I did not trace the note that scroll top after scroll bottom hits scroll_edge_no_progress on both main and the branch. One live iOS scroll bottom and top run would confirm the shared loop on xctest, if you have a device handy. |
Check sparse quality before the empty-tree refusal, so a backend that ships no nodes with a sparse verdict (the Android helper's presentation failure) reports its own reason rather than a generic no-capture. Read refused observations through one `isScrollEdgeRefused` guard, and hand the edge loop's scope to `settleAfterPass` so the daemon no longer tracks a second copy.
|
Thanks! I took both suggestions in d6eb2a9:
|
The separate scroll-capture-refusal module added one eagerly evaluated module to every daemon entry, which the eager-closure budget rejects. Define the refusal in scroll-until.ts, which scroll-runtime already imports, so both loops share it at no extra load cost.
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
A capture with no nodes field and a sparse verdict reported a generic no-capture. Check sparse quality against an empty tree first, so the backend's reason survives whether the tree is empty or missing.
Summary
scroll top/scroll bottomno longer report "Already at bottom" on a capture they could not read. A missing, empty or sparse tree now fails withscroll_edge_capture_unreadable(withcaptureRefusal: no-capture | sparse-tree) before any edge verdict or gesture.Following the shape in the issue, both scroll loops now run on one
runScrollPasses({ observe, refuse, shouldStop, passLimit, ... })in capture-kit.selectorsdepends oncapture-kit, so capture-kit can't importsparseCaptureQuality. The loop therefore takes arefusehook, and the daemon defines the refusal once (scroll-capture-refusal.ts) for bothscroll --untiland the edge scrolls.One ordering change: the edge loop now checks its 40-pass budget before the stuck window, as
--untilalready did. This only differs if a surface first reads as stuck on exactly pass 40.6 files, no scope expansion. Fixes #2468.
Validation
pnpm check:affected --run --head 62eb122: format, lint, typecheck, layering, fallow, build and vitest-related all pass (exactCommit=true).runScrollPassesunit cases (refusal before the stop condition, budget, stuck window, budget wins ties) andscroll bottomdaemon cases (no tree, empty tree, sparse tree, unreadable mid-loop). They failed before the change.scroll bottomtakes 1 pass to the bottom on bothorigin/main(4db1b7d) and this branch, in two runs each.scroll topright afterscroll bottomon Settings fails withscroll_edge_no_progress. Out of scope here; I can file it separately.