Skip to content

fix(desktop): consolidate prepend scroll correction - #2855

Merged
wesbillman merged 8 commits into
mainfrom
carl/scroll-ownership-consolidation
Jul 25, 2026
Merged

wesbillman merged 8 commits into
mainfrom
carl/scroll-ownership-consolidation

Conversation

@wesbillman

Copy link
Copy Markdown
Collaborator

Summary

  • make Virtua the sole prepend geometry/correction owner by accumulating active prepend ResizeObserver corrections from the live DOM offset
  • retire prepend reconciliation on ordinary reader wheel input, while preserving it for Ctrl+wheel browser zoom
  • remove Buzz's competing three-second semantic-anchor watcher and corrective scrollBy loop
  • keep ESM/CJS Virtua patch behavior equivalent and update the patch lock hash

Why

Buzz admitted prepended rows using seeded estimates, then Virtua received multiple measurement corrections for the same transaction. Each correction was based on the same stale model offset, so a later write replaced an earlier correction instead of accumulating it. In the reproduced first page, that resurrected 452px of anchor drift; the app-level watcher merely corrected the lost virtualizer write afterward.

This fixes the correction inside Virtua and deletes the competing app writer, following the single-owner geometry invariant used by Berd rather than copying its spacer implementation.

Validation

  • watcher-off desktop virtualization matrix: 11/11 passed, including 15 cascading prepends, continued wheel input, detached rich-row growth, channel switching, bottom follow, and buffered live arrivals
  • focused cascading prepend/Ctrl+wheel regression passed
  • desktop typecheck passed
  • desktop unit suite passed: 3,495 tests
  • Biome passed on changed desktop files
  • git diff --check clean

Manual behavior

Load older history repeatedly while scrolling upward, then wheel downward during/after a prepend. The visible anchor should stay within the existing 5px contract during reconciliation, and deliberate reader movement should not be pulled back. Ctrl+wheel during the prepend commit must not cancel reconciliation.

wesbillman and others added 3 commits July 25, 2026 09:20
Wait for the virtualized timeline API before consuming initial channel
positioning, and do not mistake measurement-driven offsets for reader
input while the virtualizer is settling at the floor.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <[email protected]>
Replace the timeout-based bottom chase with explicit bottom intent driven by
Virtua content and viewport geometry. Reader navigation, targeted messages,
and history prepends still take ownership immediately, while late row reflow
and composer typing no longer strand the timeline above its newest message.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <[email protected]>
Let Virtua own prepend measurement reconciliation by accumulating resize
corrections from the live DOM offset. Retire that reconciliation on reader
wheel input while preserving it for browser zoom, and remove the competing
app-level semantic anchor watcher.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <[email protected]>
@wesbillman
wesbillman requested a review from a team as a code owner July 25, 2026 15:53
wesbillman and others added 5 commits July 25, 2026 10:08
Keep bottom intent through zoom and ordinary row interactions, while retiring
it for demonstrated timeline navigation and every virtualized target move.
Cover mounted-target geometry races and listener ownership transitions.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <[email protected]>
Do not let completion of an older history request cancel a later jump-to-bottom
command. Align smoke assertions with explicit reader ownership and the
ResizeObserver-to-animation-frame settle boundary.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: Wes <[email protected]>
Programmatic scrollTop writes do not represent reader input. Dispatch an
ordinary wheel event on the timeline before synthetic positioning so detached
history tests exercise the production ownership contract.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <[email protected]>
Use a dedicated Virtua store action to leave shift reconciliation when real
wheel input takes ownership. Unlike ACTION_SCROLL_END, the transition does
not idle scroll direction, clear frozen state, flush jumps, or emit the
public scroll-end callback used by timeline retention.

Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <[email protected]>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: Wes <[email protected]>
@wesbillman
wesbillman merged commit 25e7864 into main Jul 25, 2026
50 of 52 checks passed
@wesbillman
wesbillman deleted the carl/scroll-ownership-consolidation branch July 25, 2026 23:49
wesbillman added a commit that referenced this pull request Jul 26, 2026
## Summary

- retire Virtua prepend reconciliation on every ordinary reader wheel
event, including events suppressed from its separate wheel-timing
heuristic
- keep Ctrl+wheel browser zoom excluded from reader ownership
- arm the Ctrl+wheel prepend probe before pagination begins so it cannot
miss the commit
- preserve ESM/CJS patch parity and update the patch lock hash

## Why main was red

PR #2855 added the reader-wheel retirement action after Virtua's
existing suppression guard. Once the first event set that guard, later
events in the same wheel burst returned before retiring prepend mode. A
late ResizeObserver correction could then pull the viewport backward by
20–40px. The same test had failed twice on #2855 but passed its final
retry, so the PR job appeared green; the merge commit lost all three
retries.

The separate Ctrl+wheel failure was a test race: its MutationObserver
was registered after the request had already been triggered and could
miss the prepend commit.

## Validation

- desktop full unit suite: 3,515 passed
- pre-push desktop check: passed
- `git diff --check`: passed
- CI is the E2E verification; no local E2E was run

Fixes the main-branch failure in CI run 30180099007.

Signed-off-by: Wes <[email protected]>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
morgmart added a commit that referenced this pull request Jul 26, 2026
…ding-harnesses

* origin/main: (47 commits)
  fix(desktop): remove bundled libsystemd from AppImage (#2353)
  docs: document required DCO sign-off and add commit-msg sign-off hook (#2993)
  fix(desktop): make agent definition authoritative for model/provider/prompt (#1968)
  chore(desktop): delete dead persona catalog UI cluster (#2886)
  fix(desktop): surface install failures hidden by curl-pipe exit codes (#2892)
  fix(mobile): validate invite relay destinations (#2986)
  Refactor managed-agent runtime into cohesive modules (#2974)
  Refine mobile settings and themes (#2844)
  Fix formatting in README.md diagram (#2284)
  fix(desktop): make Linux AppImage GStreamer work on non-Debian distros (#2176)
  refactor(desktop): remove Agent directory section from Agents page (#2290)
  fix(desktop): enable arboard Wayland backend so Linux copies reach the Wayland clipboard (#2904)
  fix(desktop): supervise and re-arm relay-mesh runtime (#2823)
  fix(agents): run live Databricks discovery instead of the fallback list (#2890)
  fix(desktop): retire prepend mode on every reader wheel (#2913)
  fix(desktop): consolidate prepend scroll correction (#2855)
  docs(buzz-acp): correct agent key generation instructions (#2875)
  fix(desktop): track concurrent agent turns up to the harness maximum (#2882)
  docs(contributing): trim to goose-scale minimal intake surface (#2780)
  fix(relay): preserve reconnect backoff (#2759)
  ...

Signed-off-by: morgmart <[email protected]>

# Conflicts:
#	desktop/src/features/onboarding/ui/SetupStep.tsx
wesbillman added a commit that referenced this pull request Jul 28, 2026
## Buzz Desktop release v0.5.0

### Changes since v0.4.26:

- feat(invites): add use-limited invite links
([#3141](#3141))
([`d500c2d5c`](d500c2d))
- fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0
floor ([#3218](#3218))
([`98a7b1334`](98a7b13))
- fix(desktop): preserve thread anchor through layout reflow
([#3212](#3212))
([`9810d8545`](9810d85))
- feat(search): parse from:/in:/after:/before: and pass them in the
filter ([#2871](#2871))
([`cb2a265b5`](cb2a265))
- fix(desktop): fetch join policies through native networking
([#2862](#2862))
([`0019f8076`](0019f80))
- fix(desktop): republish agent identity records when a persona rename
propagates ([#2607](#2607))
([`7ca0bbd94`](7ca0bbd))
- fix(desktop): keep project Inbox previews compact
([#3193](#3193))
([`de1396050`](de13960))
- Inbox refactor ([#2045](#2045))
([`2bd4c24b7`](2bd4c24))
- Fix composer selection formatting and drop overlay
([#3172](#3172))
([`99da5b7eb`](99da5b7))
- Refine pending message status
([#3153](#3153))
([`75588eaff`](75588ea))
- fix(desktop): recover full local storage on startup
([#3182](#3182))
([`174c38e4b`](174c38e))
- fix(desktop): keep collapsed table separators out of spoilers
([#3169](#3169))
([`4d8b676bb`](4d8b676))
- feat(desktop): redesign agent runtime settings
([#3093](#3093))
([`d98da7389`](d98da73))
- fix(desktop): use forward slashes for git credential.helper on Windows
([#3023](#3023))
([`899531684`](8995316))
- chore(desktop): add AgentCreationPreview file-size override to unblock
main CI ([#3154](#3154))
([`b92a1f4bf`](b92a1f4))
- fix(desktop): make the test loader work on Windows
([#2758](#2758))
([`8bb43d519`](8bb43d5))
- fix(desktop): make lint and unit-test gates work on Windows
([#2943](#2943))
([`545bb46b8`](545bb46))
- feat(desktop): add search to agent emoji picker
([#2630](#2630))
([`313f793c8`](313f793))
- fix(desktop): keep identity key help dialog readable in dark mode
([#2854](#2854))
([`be275cfc6`](be275cf))
- feat(acp): title agent sessions from the agent and channel name
([#3028](#3028))
([`f2fe3b63c`](f2fe3b6))
- feat(git): use agent display name as git author name
([#3040](#3040))
([`18eef633d`](18eef63))
- fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote
DoS) ([#3135](#3135))
([`31e2de196`](31e2de1))
- fix(desktop): read the newest pair-scoped harness log
([#3134](#3134))
([`654f38490`](654f384))
- feat(desktop): handle project work from Inbox
([#3117](#3117))
([`c5c4f390b`](c5c4f39))
- fix(desktop): clarify identity key button when key exists
([#2357](#2357))
([`87b3fcd3c`](87b3fcd))
- Restore Goose and Buzz Agent to onboarding harness selection
([#2731](#2731))
([`7fc0cc82d`](7fc0cc8))
- fix(desktop): render rich project work item content
([#3100](#3100))
([`afb272bb7`](afb272b))
- feat(acp): bring your own harness (BYOH) — generic ACP runtime seam +
settings gallery ([#2773](#2773))
([`95fdf9788`](95fdf97))
- feat(desktop): use collective mesh routing for Auto
([#2825](#2825))
([`16d4ec335`](16d4ec3))
- fix(desktop): strip legacy baked team instructions from stored prompts
([#3035](#3035))
([`aee631448`](aee6314))
- feat(agents): lower default agent parallelism from 24 to 10
([#3038](#3038))
([`5d8ede446`](5d8ede4))
- Polish community rail and mobile pairing
([#2972](#2972))
([`e6c90bb7c`](e6c90bb))
- fix(desktop): remove bundled libsystemd from AppImage
([#2353](#2353))
([`a31fc4d2f`](a31fc4d))
- fix(desktop): make agent definition authoritative for
model/provider/prompt ([#1968](#1968))
([`8c0e8cb16`](8c0e8cb))
- chore(desktop): delete dead persona catalog UI cluster
([#2886](#2886))
([`8e67cf399`](8e67cf3))
- fix(desktop): surface install failures hidden by curl-pipe exit codes
([#2892](#2892))
([`166c6655e`](166c665))
- Refactor managed-agent runtime into cohesive modules
([#2974](#2974))
([`74b63e184`](74b63e1))
- fix(desktop): make Linux AppImage GStreamer work on non-Debian distros
([#2176](#2176))
([`cc6c4d347`](cc6c4d3))
- refactor(desktop): remove Agent directory section from Agents page
([#2290](#2290))
([`5d1233e84`](5d1233e))
- fix(desktop): enable arboard Wayland backend so Linux copies reach the
Wayland clipboard ([#2904](#2904))
([`ab7aa8b12`](ab7aa8b))
- fix(desktop): supervise and re-arm relay-mesh runtime
([#2823](#2823))
([`aa51dab9d`](aa51dab))
- fix(agents): run live Databricks discovery instead of the fallback
list ([#2890](#2890))
([`8eb6e3eb6`](8eb6e3e))
- fix(desktop): retire prepend mode on every reader wheel
([#2913](#2913))
([`07d0265cf`](07d0265))
- fix(desktop): consolidate prepend scroll correction
([#2855](#2855))
([`25e7864b3`](25e7864))
- fix(desktop): track concurrent agent turns up to the harness maximum
([#2882](#2882))
([`20bff5910`](20bff59))
- fix(relay): preserve reconnect backoff
([#2759](#2759))
([`499c5d349`](499c5d3))
- refactor(relay): expose reconnect timing policy
([#2310](#2310))
([`2f0041595`](2f00415))
- fix(desktop): clear stale working badges on agent stop/restart
([#2803](#2803))
([`a64cc71f6`](a64cc71))
- fix(desktop): surface agent rename relay profile sync failure as a
warning toast ([#2279](#2279))
([`5e3d2e484`](5e3d2e4))
- fix(discovery): inject PATH into Codex adapter planning
([#2767](#2767))
([`6ab3835f3`](6ab3835))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Wes <[email protected]>
calvadev pushed a commit to shopstr-eng/buzz that referenced this pull request Aug 3, 2026
## Buzz Desktop release v0.5.0

### Changes since v0.4.26:

- feat(invites): add use-limited invite links
([block#3141](block#3141))
([`d500c2d5c`](block@d500c2d))
- fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0
floor ([block#3218](block#3218))
([`98a7b1334`](block@98a7b13))
- fix(desktop): preserve thread anchor through layout reflow
([block#3212](block#3212))
([`9810d8545`](block@9810d85))
- feat(search): parse from:/in:/after:/before: and pass them in the
filter ([block#2871](block#2871))
([`cb2a265b5`](block@cb2a265))
- fix(desktop): fetch join policies through native networking
([block#2862](block#2862))
([`0019f8076`](block@0019f80))
- fix(desktop): republish agent identity records when a persona rename
propagates ([block#2607](block#2607))
([`7ca0bbd94`](block@7ca0bbd))
- fix(desktop): keep project Inbox previews compact
([block#3193](block#3193))
([`de1396050`](block@de13960))
- Inbox refactor ([block#2045](block#2045))
([`2bd4c24b7`](block@2bd4c24))
- Fix composer selection formatting and drop overlay
([block#3172](block#3172))
([`99da5b7eb`](block@99da5b7))
- Refine pending message status
([block#3153](block#3153))
([`75588eaff`](block@75588ea))
- fix(desktop): recover full local storage on startup
([block#3182](block#3182))
([`174c38e4b`](block@174c38e))
- fix(desktop): keep collapsed table separators out of spoilers
([block#3169](block#3169))
([`4d8b676bb`](block@4d8b676))
- feat(desktop): redesign agent runtime settings
([block#3093](block#3093))
([`d98da7389`](block@d98da73))
- fix(desktop): use forward slashes for git credential.helper on Windows
([block#3023](block#3023))
([`899531684`](block@8995316))
- chore(desktop): add AgentCreationPreview file-size override to unblock
main CI ([block#3154](block#3154))
([`b92a1f4bf`](block@b92a1f4))
- fix(desktop): make the test loader work on Windows
([block#2758](block#2758))
([`8bb43d519`](block@8bb43d5))
- fix(desktop): make lint and unit-test gates work on Windows
([block#2943](block#2943))
([`545bb46b8`](block@545bb46))
- feat(desktop): add search to agent emoji picker
([block#2630](block#2630))
([`313f793c8`](block@313f793))
- fix(desktop): keep identity key help dialog readable in dark mode
([block#2854](block#2854))
([`be275cfc6`](block@be275cf))
- feat(acp): title agent sessions from the agent and channel name
([block#3028](block#3028))
([`f2fe3b63c`](block@f2fe3b6))
- feat(git): use agent display name as git author name
([block#3040](block#3040))
([`18eef633d`](block@18eef63))
- fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote
DoS) ([block#3135](block#3135))
([`31e2de196`](block@31e2de1))
- fix(desktop): read the newest pair-scoped harness log
([block#3134](block#3134))
([`654f38490`](block@654f384))
- feat(desktop): handle project work from Inbox
([block#3117](block#3117))
([`c5c4f390b`](block@c5c4f39))
- fix(desktop): clarify identity key button when key exists
([block#2357](block#2357))
([`87b3fcd3c`](block@87b3fcd))
- Restore Goose and Buzz Agent to onboarding harness selection
([block#2731](block#2731))
([`7fc0cc82d`](block@7fc0cc8))
- fix(desktop): render rich project work item content
([block#3100](block#3100))
([`afb272bb7`](block@afb272b))
- feat(acp): bring your own harness (BYOH) — generic ACP runtime seam +
settings gallery ([block#2773](block#2773))
([`95fdf9788`](block@95fdf97))
- feat(desktop): use collective mesh routing for Auto
([block#2825](block#2825))
([`16d4ec335`](block@16d4ec3))
- fix(desktop): strip legacy baked team instructions from stored prompts
([block#3035](block#3035))
([`aee631448`](block@aee6314))
- feat(agents): lower default agent parallelism from 24 to 10
([block#3038](block#3038))
([`5d8ede446`](block@5d8ede4))
- Polish community rail and mobile pairing
([block#2972](block#2972))
([`e6c90bb7c`](block@e6c90bb))
- fix(desktop): remove bundled libsystemd from AppImage
([block#2353](block#2353))
([`a31fc4d2f`](block@a31fc4d))
- fix(desktop): make agent definition authoritative for
model/provider/prompt ([block#1968](block#1968))
([`8c0e8cb16`](block@8c0e8cb))
- chore(desktop): delete dead persona catalog UI cluster
([block#2886](block#2886))
([`8e67cf399`](block@8e67cf3))
- fix(desktop): surface install failures hidden by curl-pipe exit codes
([block#2892](block#2892))
([`166c6655e`](block@166c665))
- Refactor managed-agent runtime into cohesive modules
([block#2974](block#2974))
([`74b63e184`](block@74b63e1))
- fix(desktop): make Linux AppImage GStreamer work on non-Debian distros
([block#2176](block#2176))
([`cc6c4d347`](block@cc6c4d3))
- refactor(desktop): remove Agent directory section from Agents page
([block#2290](block#2290))
([`5d1233e84`](block@5d1233e))
- fix(desktop): enable arboard Wayland backend so Linux copies reach the
Wayland clipboard ([block#2904](block#2904))
([`ab7aa8b12`](block@ab7aa8b))
- fix(desktop): supervise and re-arm relay-mesh runtime
([block#2823](block#2823))
([`aa51dab9d`](block@aa51dab))
- fix(agents): run live Databricks discovery instead of the fallback
list ([block#2890](block#2890))
([`8eb6e3eb6`](block@8eb6e3e))
- fix(desktop): retire prepend mode on every reader wheel
([block#2913](block#2913))
([`07d0265cf`](block@07d0265))
- fix(desktop): consolidate prepend scroll correction
([block#2855](block#2855))
([`25e7864b3`](block@25e7864))
- fix(desktop): track concurrent agent turns up to the harness maximum
([block#2882](block#2882))
([`20bff5910`](block@20bff59))
- fix(relay): preserve reconnect backoff
([block#2759](block#2759))
([`499c5d349`](block@499c5d3))
- refactor(relay): expose reconnect timing policy
([block#2310](block#2310))
([`2f0041595`](block@2f00415))
- fix(desktop): clear stale working badges on agent stop/restart
([block#2803](block#2803))
([`a64cc71f6`](block@a64cc71))
- fix(desktop): surface agent rename relay profile sync failure as a
warning toast ([block#2279](block#2279))
([`5e3d2e484`](block@5e3d2e4))
- fix(discovery): inject PATH into Codex adapter planning
([block#2767](block#2767))
([`6ab3835f3`](block@6ab3835))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Wes <[email protected]>
BradGroux pushed a commit to BradGroux/buzz that referenced this pull request Aug 23, 2026
## Summary

- make Virtua the sole prepend geometry/correction owner by accumulating
active prepend ResizeObserver corrections from the live DOM offset
- retire prepend reconciliation on ordinary reader wheel input, while
preserving it for Ctrl+wheel browser zoom
- remove Buzz's competing three-second semantic-anchor watcher and
corrective `scrollBy` loop
- keep ESM/CJS Virtua patch behavior equivalent and update the patch
lock hash

## Why

Buzz admitted prepended rows using seeded estimates, then Virtua
received multiple measurement corrections for the same transaction. Each
correction was based on the same stale model offset, so a later write
replaced an earlier correction instead of accumulating it. In the
reproduced first page, that resurrected 452px of anchor drift; the
app-level watcher merely corrected the lost virtualizer write afterward.

This fixes the correction inside Virtua and deletes the competing app
writer, following the single-owner geometry invariant used by Berd
rather than copying its spacer implementation.

## Validation

- watcher-off desktop virtualization matrix: 11/11 passed, including 15
cascading prepends, continued wheel input, detached rich-row growth,
channel switching, bottom follow, and buffered live arrivals
- focused cascading prepend/Ctrl+wheel regression passed
- desktop typecheck passed
- desktop unit suite passed: 3,495 tests
- Biome passed on changed desktop files
- `git diff --check` clean

## Manual behavior

Load older history repeatedly while scrolling upward, then wheel
downward during/after a prepend. The visible anchor should stay within
the existing 5px contract during reconciliation, and deliberate reader
movement should not be pulled back. Ctrl+wheel during the prepend commit
must not cancel reconciliation.

---------

Signed-off-by: Wes <[email protected]>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
BradGroux pushed a commit to BradGroux/buzz that referenced this pull request Aug 23, 2026
## Summary

- retire Virtua prepend reconciliation on every ordinary reader wheel
event, including events suppressed from its separate wheel-timing
heuristic
- keep Ctrl+wheel browser zoom excluded from reader ownership
- arm the Ctrl+wheel prepend probe before pagination begins so it cannot
miss the commit
- preserve ESM/CJS patch parity and update the patch lock hash

## Why main was red

PR block#2855 added the reader-wheel retirement action after Virtua's
existing suppression guard. Once the first event set that guard, later
events in the same wheel burst returned before retiring prepend mode. A
late ResizeObserver correction could then pull the viewport backward by
20–40px. The same test had failed twice on block#2855 but passed its final
retry, so the PR job appeared green; the merge commit lost all three
retries.

The separate Ctrl+wheel failure was a test race: its MutationObserver
was registered after the request had already been triggered and could
miss the prepend commit.

## Validation

- desktop full unit suite: 3,515 passed
- pre-push desktop check: passed
- `git diff --check`: passed
- CI is the E2E verification; no local E2E was run

Fixes the main-branch failure in CI run 30180099007.

Signed-off-by: Wes <[email protected]>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
BradGroux pushed a commit to BradGroux/buzz that referenced this pull request Aug 23, 2026
## Buzz Desktop release v0.5.0

### Changes since v0.4.26:

- feat(invites): add use-limited invite links
([block#3141](block#3141))
([`b87b0f8e5`](block@b87b0f8))
- fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0
floor ([block#3218](block#3218))
([`26fa4b998`](block@26fa4b9))
- fix(desktop): preserve thread anchor through layout reflow
([block#3212](block#3212))
([`1fec177fe`](block@1fec177))
- feat(search): parse from:/in:/after:/before: and pass them in the
filter ([block#2871](block#2871))
([`a70b605f9`](block@a70b605))
- fix(desktop): fetch join policies through native networking
([block#2862](block#2862))
([`9d4c1db67`](block@9d4c1db))
- fix(desktop): republish agent identity records when a persona rename
propagates ([block#2607](block#2607))
([`0b3ff0ca2`](block@0b3ff0c))
- fix(desktop): keep project Inbox previews compact
([block#3193](block#3193))
([`995afd79f`](block@995afd7))
- Inbox refactor ([block#2045](block#2045))
([`3812fc6d9`](block@3812fc6))
- Fix composer selection formatting and drop overlay
([block#3172](block#3172))
([`d3b121922`](block@d3b1219))
- Refine pending message status
([block#3153](block#3153))
([`c244a0032`](block@c244a00))
- fix(desktop): recover full local storage on startup
([block#3182](block#3182))
([`8ed3e3071`](block@8ed3e30))
- fix(desktop): keep collapsed table separators out of spoilers
([block#3169](block#3169))
([`c0c971c88`](block@c0c971c))
- feat(desktop): redesign agent runtime settings
([block#3093](block#3093))
([`4597ad753`](block@4597ad7))
- fix(desktop): use forward slashes for git credential.helper on Windows
([block#3023](block#3023))
([`5ede34513`](block@5ede345))
- chore(desktop): add AgentCreationPreview file-size override to unblock
main CI ([block#3154](block#3154))
([`60da1f8e9`](block@60da1f8))
- fix(desktop): make the test loader work on Windows
([block#2758](block#2758))
([`7de427e92`](block@7de427e))
- fix(desktop): make lint and unit-test gates work on Windows
([block#2943](block#2943))
([`49bae5d4f`](block@49bae5d))
- feat(desktop): add search to agent emoji picker
([block#2630](block#2630))
([`72c572f78`](block@72c572f))
- fix(desktop): keep identity key help dialog readable in dark mode
([block#2854](block#2854))
([`6d533b24c`](block@6d533b2))
- feat(acp): title agent sessions from the agent and channel name
([block#3028](block#3028))
([`f89c51480`](block@f89c514))
- feat(git): use agent display name as git author name
([block#3040](block#3040))
([`f57ea796e`](block@f57ea79))
- fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote
DoS) ([block#3135](block#3135))
([`05af0ba4d`](block@05af0ba))
- fix(desktop): read the newest pair-scoped harness log
([block#3134](block#3134))
([`1f134f8cd`](block@1f134f8))
- feat(desktop): handle project work from Inbox
([block#3117](block#3117))
([`cb2982b22`](block@cb2982b))
- fix(desktop): clarify identity key button when key exists
([block#2357](block#2357))
([`ca5fde1ff`](block@ca5fde1))
- Restore Goose and Buzz Agent to onboarding harness selection
([block#2731](block#2731))
([`bcd15f640`](block@bcd15f6))
- fix(desktop): render rich project work item content
([block#3100](block#3100))
([`6037a701a`](block@6037a70))
- feat(acp): bring your own harness (BYOH) — generic ACP runtime seam +
settings gallery ([block#2773](block#2773))
([`46e6ca471`](block@46e6ca4))
- feat(desktop): use collective mesh routing for Auto
([block#2825](block#2825))
([`6c0e93e1f`](block@6c0e93e))
- fix(desktop): strip legacy baked team instructions from stored prompts
([block#3035](block#3035))
([`02227d761`](block@02227d7))
- feat(agents): lower default agent parallelism from 24 to 10
([block#3038](block#3038))
([`8baed7af9`](block@8baed7a))
- Polish community rail and mobile pairing
([block#2972](block#2972))
([`4c47d7af5`](block@4c47d7a))
- fix(desktop): remove bundled libsystemd from AppImage
([block#2353](block#2353))
([`05394d35c`](block@05394d3))
- fix(desktop): make agent definition authoritative for
model/provider/prompt ([block#1968](block#1968))
([`50a3dd7c4`](block@50a3dd7))
- chore(desktop): delete dead persona catalog UI cluster
([block#2886](block#2886))
([`7575f8d1c`](block@7575f8d))
- fix(desktop): surface install failures hidden by curl-pipe exit codes
([block#2892](block#2892))
([`806feb9ed`](block@806feb9))
- Refactor managed-agent runtime into cohesive modules
([block#2974](block#2974))
([`b1a983734`](block@b1a9837))
- fix(desktop): make Linux AppImage GStreamer work on non-Debian distros
([block#2176](block#2176))
([`53e1752d3`](block@53e1752))
- refactor(desktop): remove Agent directory section from Agents page
([block#2290](block#2290))
([`f756b5828`](block@f756b58))
- fix(desktop): enable arboard Wayland backend so Linux copies reach the
Wayland clipboard ([block#2904](block#2904))
([`3f2f3964e`](block@3f2f396))
- fix(desktop): supervise and re-arm relay-mesh runtime
([block#2823](block#2823))
([`c9607bd0f`](block@c9607bd))
- fix(agents): run live Databricks discovery instead of the fallback
list ([block#2890](block#2890))
([`7d64ca7a8`](block@7d64ca7))
- fix(desktop): retire prepend mode on every reader wheel
([block#2913](block#2913))
([`dba1ba5ae`](block@dba1ba5))
- fix(desktop): consolidate prepend scroll correction
([block#2855](block#2855))
([`797933e4d`](block@797933e))
- fix(desktop): track concurrent agent turns up to the harness maximum
([block#2882](block#2882))
([`3115a39a0`](block@3115a39))
- fix(relay): preserve reconnect backoff
([block#2759](block#2759))
([`cd949e75a`](block@cd949e7))
- refactor(relay): expose reconnect timing policy
([block#2310](block#2310))
([`94c91f110`](block@94c91f1))
- fix(desktop): clear stale working badges on agent stop/restart
([block#2803](block#2803))
([`da2580e51`](block@da2580e))
- fix(desktop): surface agent rename relay profile sync failure as a
warning toast ([block#2279](block#2279))
([`4d91195e9`](block@4d91195))
- fix(discovery): inject PATH into Codex adapter planning
([block#2767](block#2767))
([`795f2d12e`](block@795f2d1))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Wes <[email protected]>
richardokonicha pushed a commit to fugoku/buzz that referenced this pull request Oct 23, 2026
## Summary

- make Virtua the sole prepend geometry/correction owner by accumulating
active prepend ResizeObserver corrections from the live DOM offset
- retire prepend reconciliation on ordinary reader wheel input, while
preserving it for Ctrl+wheel browser zoom
- remove Buzz's competing three-second semantic-anchor watcher and
corrective `scrollBy` loop
- keep ESM/CJS Virtua patch behavior equivalent and update the patch
lock hash

## Why

Buzz admitted prepended rows using seeded estimates, then Virtua
received multiple measurement corrections for the same transaction. Each
correction was based on the same stale model offset, so a later write
replaced an earlier correction instead of accumulating it. In the
reproduced first page, that resurrected 452px of anchor drift; the
app-level watcher merely corrected the lost virtualizer write afterward.

This fixes the correction inside Virtua and deletes the competing app
writer, following the single-owner geometry invariant used by Berd
rather than copying its spacer implementation.

## Validation

- watcher-off desktop virtualization matrix: 11/11 passed, including 15
cascading prepends, continued wheel input, detached rich-row growth,
channel switching, bottom follow, and buffered live arrivals
- focused cascading prepend/Ctrl+wheel regression passed
- desktop typecheck passed
- desktop unit suite passed: 3,495 tests
- Biome passed on changed desktop files
- `git diff --check` clean

## Manual behavior

Load older history repeatedly while scrolling upward, then wheel
downward during/after a prepend. The visible anchor should stay within
the existing 5px contract during reconciliation, and deliberate reader
movement should not be pulled back. Ctrl+wheel during the prepend commit
must not cancel reconciliation.

---------

Signed-off-by: Wes <[email protected]>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
richardokonicha pushed a commit to fugoku/buzz that referenced this pull request Oct 23, 2026
## Summary

- retire Virtua prepend reconciliation on every ordinary reader wheel
event, including events suppressed from its separate wheel-timing
heuristic
- keep Ctrl+wheel browser zoom excluded from reader ownership
- arm the Ctrl+wheel prepend probe before pagination begins so it cannot
miss the commit
- preserve ESM/CJS patch parity and update the patch lock hash

## Why main was red

PR block#2855 added the reader-wheel retirement action after Virtua's
existing suppression guard. Once the first event set that guard, later
events in the same wheel burst returned before retiring prepend mode. A
late ResizeObserver correction could then pull the viewport backward by
20–40px. The same test had failed twice on block#2855 but passed its final
retry, so the PR job appeared green; the merge commit lost all three
retries.

The separate Ctrl+wheel failure was a test race: its MutationObserver
was registered after the request had already been triggered and could
miss the prepend commit.

## Validation

- desktop full unit suite: 3,515 passed
- pre-push desktop check: passed
- `git diff --check`: passed
- CI is the E2E verification; no local E2E was run

Fixes the main-branch failure in CI run 30180099007.

Signed-off-by: Wes <[email protected]>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
matt2e added a commit that referenced this pull request Nov 5, 2026
…9094)

## Summary

Re-entering a channel trims older pages from the head of the cached
timeline, and our Virtua patch compensates for each trim from the live
DOM `scrollTop`. When the reader was near the floor, the browser had
already clamped `scrollTop` to the shorter list, so the removal was
counted twice and the reader could be thrown to the top of history. That
is the `message-target-highlight.spec.ts` flake in Desktop Smoke E2E.
The patched scroller now remembers the last offset it observed or wrote,
and compensates from that offset when the live one was clamped to the
floor.

### fix(desktop): compensate head trims from the unclamped scroll offset

#### Problem

`message-target-highlight.spec.ts:79` ("message target flashes: ...")
flaked in Desktop Smoke E2E, always at :179: the linked row had left the
viewport 2.2 s after it flashed.
- main:
-
https://github.com/block/buzz/actions/runs/41568206043/job/125695139845
-
https://github.com/block/buzz/actions/runs/41669669478/job/126035103096
-
https://github.com/block/buzz/actions/runs/41669740241/job/126035480466
-
https://github.com/block/buzz/actions/runs/41694874145/job/126123865714
-
https://github.com/block/buzz/actions/runs/41728489172/job/126240193479
-
https://github.com/block/buzz/actions/runs/41737019256/job/126269365379
- branches:
-
https://github.com/block/buzz/actions/runs/41575774131/job/125719148353
-
https://github.com/block/buzz/actions/runs/41636043464/job/125918005733
-
https://github.com/block/buzz/actions/runs/41751914119/job/126319694104
-
https://github.com/block/buzz/actions/runs/41544910958/job/125618626645
-
https://github.com/block/buzz/actions/runs/41714110046/job/126190515793

All eleven runs predate #8742, and each failed in a case that picks
`mounted[2]`: the bottom-settle mode #8742 fixed by holding the target.
Run 41694874145 also failed once in "other channel, row near the
bottom", and that mode survives #8742: on current main it fails 40 of 60
local runs with tracing on, as CI's retries run.

#### Cause

Re-entering a channel refreshes its newest window, which drops the older
pages from the head of the cached timeline. #8660 passes that head trim
to Virtua as a `shift`, and Virtua compensates by the removed height in
a layout effect. Our Virtua patch (#2855) applies shift compensation to
the live DOM `scrollTop` so that corrections landing before the next
scroll event accumulate. But by then the browser has laid out the
shorter list and clamped `scrollTop` to the new floor whenever the
reader was within the removed height of it, so the removal is counted
twice. In a failing run the view sat 6px above the floor: trimming the
412px channel intro moved it 406px too far, and trimming 3054px of older
rows then wrote `scrollTop` -907, pinning it to the top.
`onStartReached` loaded older history and the target never came back. A
reader scrolled slightly above the bottom loses their place the same
way.

#### Fix

The patched Virtua scroller remembers the last offset it observed in a
scroll event or wrote itself. When a shift compensation finds the live
offset below that and pinned to the floor, the browser clamped it, so
the compensation starts from the remembered offset. Otherwise it keeps
#2855's live-offset base. The ESM and CJS builds change identically, and
the lockfile carries the new patch hash.

#### Validation

- New `virtuaShiftClampPatch.jsdom-test.mjs` drives the real patched
`VList` over a scroller that clamps like a browser. Head trims far from,
near and at the floor keep the reader's rows in place, a trim keeps a
scroll whose event has not fired yet, and a prepend then a trim before
any scroll event accumulate. Each part of the new condition fails a case
when removed: the previous patch line fails the near-floor, at-floor and
accumulation cases (3500 for 3900 and 4000, 3000 for 4000); upstream's
store-offset base fails the unreported-scroll and accumulation cases;
always using the remembered offset, or dropping the floor check, fails
the unreported-scroll case (2500 for 1900).
- "other channel, row near the bottom" with `--repeat-each=30
--workers=4 --trace=retain-on-failure`: 40/60 failed at :179 on current
main, 0/60 failed (60/60 passed) with the fix, and 25/30 failed again
with the previous patch reinstalled.
- All five cases, 20 traced runs each: 100/100 passed.
- scroll-history, virtualization, timeline-no-shift,
overscroll-boundary, dm-history-live-regression and
live-broadcast-reply-timeline, 3 runs each: 117/117 passed.
- Full smoke project: 1485 passed, 3 skipped, 2 failed.
`mentions.spec.ts:807` passed 5/5 on rerun. `messaging.spec.ts:2785`
(own-message avatar) fails the same way on a current-main build here
(3/3).
- `pnpm test`: 6933 unit and 176 jsdom tests passed.

**Rebased onto origin/main `64d12e297` (this branch):** Linux
workstation.

- `pnpm install --frozen-lockfile` installs virtua with the new patch
hash (`f4df71c39f6c...`).
- `pnpm typecheck` and `biome check --error-on-warnings` on the new
jsdom test are clean.
- Unit tests under `src/features/messages` and `src/features/channels`:
1990/1990. jsdom tests there: 12/12. Full desktop `pnpm test`: 6933 unit
and 176 jsdom tests passed.
- Playwright smoke after `pnpm build:e2e`, `--workers=4 --retries=0`:
messaging, send-channel-binding, scroll-history, virtualization,
timeline-no-shift, overscroll-boundary, live-broadcast-reply-timeline
and message-target-highlight, 137 passed and 1 failed. The failure is
`messaging.spec.ts:2785` (own-message avatar), which fails the same way
on main here, as noted above. `dm-history-live-regression.spec.ts` isn't
in the smoke project, so this run left it out.
- `message-target-highlight.spec.ts`, `--repeat-each=10`: 50/50 passed.
"other channel, row near the bottom", `--repeat-each=30`: 30/30 passed.

## Coordination

No open PR edits `patches/[email protected]`. The overlaps are all in
`pnpm-lock.yaml`:

- #5547 ("fix(desktop): ship a bundle that parses on WebKit without
lookbehind (Intel/macOS 12)") and #7074 ("chore(deps): update react
monorepo (patch)") merge cleanly with main but conflict with this branch
on the `[email protected]` patch-hash lines. #5547 adds a patched dependency
next to the virtua entry, and #7074 changes the React peer versions in
the same lines. Whichever lands second resolves it by taking both sides'
package changes and re-running `pnpm install` so the lockfile records
`patch_hash=f4df71c39f6c...` for virtua.
- #7073, #7080, #7633, #8218 and #8643 also touch `pnpm-lock.yaml` and
merge cleanly with this branch. Older open PRs that touch it already
conflict with main, and none gains a new conflict from this branch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: mtoohey <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant