LearnNewsExamplesServices
Frontmatter
id17582
titlelist.Buffered prepend anchor never reaches painted scroll
stateClosed
labels
bugaitestingregressioncore
assigneesneo-gpt-emmy
createdAtAug 23, 2026, 12:24 AM
updatedAtAug 23, 2026, 2:49 AM
githubUrlhttps://github.com/neomjs/neo/issues/17582
authorneo-gpt-emmy
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 17550 Activity stream: scrollable buffered list, honest counts, per-row local times
closedAtAug 23, 2026, 2:49 AM

list.Buffered prepend anchor never reaches painted scroll

Closed Backlog/active-chunk-18 bugaitestingregressioncore
neo-gpt-emmy
neo-gpt-emmy commented on Aug 23, 2026, 12:24 AM

Context

PR #17557 landed Neo.list.Buffered; #17550 is its first 500-record product adoption. The headed prepend witness now proves the worker-only anchor test from the original implementation is insufficient.

With a 52px fixed row and the operator scrolled into history, live state immediately before a two-record prepend was:

  • App-Worker anchor: e2e:488;
  • within-row offset: 48px;
  • Store and painted list otherwise coherent.

After two newer records joined, restoreScrollAnchor() advanced worker scrollTop by the expected 2 × 52px. The main-thread list element did not move. Its next native scroll capture sent the old physical pixel value back to the App Worker, which rebound the anchor to e2e:486 at the same 48px offset.

The Problem

Buffered#onStoreLoad() and #onStoreFilter() call restoreScrollAnchor() and createItems(). The restore updates only App-Worker scrollTop plus VDOM state. It does not perform the main-thread scroll effect needed to preserve what the operator is looking at.

The same class already contains the working cross-thread contract: scrollToIndex() rebuilds the range and then invokes Neo.main.DomAccess.scrollTo({id, value, windowId}). Store-driven anchor restoration stops one effect short of that precedent.

The current unit test asserts only Store/worker truth, so it greenlights the split brain: the named record is preserved before a physical scroll capture has a chance to overwrite it.

The Architectural Reality

  • src/list/Buffered.mjs:716-729 owns Store filter/load refresh.
  • src/list/Buffered.mjs:767-785 owns logical record + pixel-offset restoration.
  • src/list/Buffered.mjs:630-668 already defines the correct worker-to-main scroll effect in scrollToIndex().
  • Main-thread DOM is the physical scroll authority; App-Worker state remains the semantic anchor authority. A preservation contract is complete only when both converge before the next native capture.

No app-local scroll adjustment belongs in #17550. The engine list owns the Store, anchor, mounted range, VDOM scroll state and DOM target identity.

The Fix

  1. Extract/reuse one bounded Buffered helper that sends the current scrollTop to Neo.main.DomAccess.scrollTo for this list/window.
  2. Keep scrollToIndex() on that helper.
  3. After Store load/filter restores an anchor and rebuilds its bounded range, synchronize the restored pixel value to the mounted main-thread list.
  4. Add a unit receipt that requires the exact DomAccess.scrollTo envelope after a prepend/filter restoration.
  5. Retain the headed #17550 witness joining the pre/post logical anchor, within-row offset, worker scroll, and painted element scrollTop.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback Docs Evidence
Store-driven anchor restoration first visible Store record + anchorOffset load/filter restores logical anchor and delivers the derived pixel to the mounted DOM before native capture can overwrite it missing anchor clamps to the existing max-scroll fallback and delivers that bounded value inline method summary exact main-thread envelope unit + headed prepend
programmatic list scroll effect Buffered list/window identity one helper owns DomAccess.scrollTo({direction:'top', id, value, windowId}) for scrollToIndex and Store restoration unmounted lists keep worker/VDOM truth without an external effect helper JSDoc existing focus/selection tests + new anchor witness

Decision Record impact

None — restores the shipped cross-thread scroll/anchor contract.

Acceptance Criteria

  • A mounted two-record prepend preserves the exact first visible eventId and within-row pixel offset in App-Worker state and the painted list.
  • Worker scrollTop and DOM scrollTop both advance by insertedBeforeAnchor × itemHeight before the next native scroll capture.
  • Store filter restoration uses the same bounded main-thread scroll helper and remains clamped when the anchor disappears.
  • scrollToIndex() retains its existing physical scroll behavior through the shared helper.
  • The exact DomAccess.scrollTo payload names direction: 'top', the list id, restored value, and exact windowId.
  • Existing scroll, resize, selection, focus, record-change, nested-row and fixed-pool tests remain green.
  • #17550's headed 500-record witness preserves the history reader and shows N new events ↑ without a viewport jump.

Out of Scope

  • Scroll anchoring in Grid or arbitrary containers.
  • Variable-height rows.
  • Fixed-DOM-order recycling (#17563).
  • App-local scroll retries or delayed correction.
  • Resize delivery (#17579) and nested item repaint depth (#17581).

Avoided Traps

  • Assert only the worker anchor. That is the false green this ticket exists to remove.
  • Set DOM scroll from the activity pane. It duplicates engine ownership and breaks every later consumer.
  • Suppress the native scroll capture. The capture is physical truth; the repair must make it agree, not silence it.
  • Add a timeout. The missing operation is known and already has a synchronous engine precedent.

Related

#17550 · #17554 · #17563 · #17579 · #17581 · PR #17557

Live latest-open sweep: checked the latest 20 open issues at 2026-08-22T22:24:23.813Z; no equivalent found.
A2A in-flight sweep: checked the latest 30 all-state messages at 2026-08-22T22:24:23.813Z; no overlapping claim found.

Origin Session ID: a502fc35-3ddd-439b-9797-7d6629b3b5f3

Retrieval Hint: query_raw_memories("list.Buffered prepend anchor worker scrollTop painted DOM capture e2e:488 e2e:486")

🪡 Emmy (GPT-5.6 Sol Ultra, Codex)

tobiu referenced in commit 68505df - "feat(agentos): buffer the Fleet activity history (#17550) (#17585) on Aug 23, 2026, 2:49 AM
tobiu closed this issue on Aug 23, 2026, 2:49 AM