Repository navigation
Stabilize main: consolidate WebKit flake and bottom-follow fixes - #1039
Conversation
Co-authored-by: Mordecai <f314b6033b6e13df94c8e9e1187d3666b6b9ce54e66ee986e31cdaa909e8dcbc@buzz.block.builderlab.xyz> Signed-off-by: Wes <[email protected]> (cherry picked from commit 9e65023)
Co-authored-by: Mordecai <f314b6033b6e13df94c8e9e1187d3666b6b9ce54e66ee986e31cdaa909e8dcbc@buzz.block.builderlab.xyz> Signed-off-by: Wes <[email protected]> (cherry picked from commit c4dba54)
The "Live retry recovers an empty paused roster" journey asserts that Retry clicks inside the roster quota cooldown send nothing, but it installed a page clock that keeps running in real time. The app gates the roster read on performance.now() against a 3s deadline it arms when it consumes the refusal, so a slow WebKit runner reached the second "ignored" click after the cooldown had really expired and correctly sent a second roster read. Hold the first roster read at the page boundary until navigation (which needs a running clock) is done, pause the page clock, then release it so the app arms its deadline in controlled time. Only crossCooldown's runFor now passes that deadline. Co-authored-by: Wes <[email protected]> Signed-off-by: Wes <[email protected]> Co-authored-by: Princess Donut <5d97ac8c272fa949af56c71e586146b1706e5c4c3daa125db5b4625e12e0686c@buzz.block.builderlab.xyz> (cherry picked from commit ed128d8)
Thread activity in this test is lit only by typing, which stays fresh for 8s. The detour through the activity panel and back to the thread can take longer than that on slow WebKit runners, so the thread trigger was gone by the time the test focused it. Refresh typing before using the trigger, as each later section of the test already does. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]> (cherry picked from commit e9bb156)
"rejects a full attachment draft before opening the microphone" failed on WebKit with `"page.evaluate: Test ended." while running route callback`. WebKit defers the voice card's media reads until the first click and keeps issuing them, and the shared media route reads its bytes from the page with page.evaluate. This test ends about 200ms after its only click, so the context could close under a media read still in flight. Unroute in afterEach with `ignoreErrors`: those reads serve no assertion once the test body has finished. Co-authored-by: Wes <[email protected]> Signed-off-by: Wes <[email protected]> Co-authored-by: Princess Donut <5d97ac8c272fa949af56c71e586146b1706e5c4c3daa125db5b4625e12e0686c@buzz.block.builderlab.xyz> (cherry picked from commit 2607d39)
Co-authored-by: Mordecai <f314b6033b6e13df94c8e9e1187d3666b6b9ce54e66ee986e31cdaa909e8dcbc@buzz.block.builderlab.xyz> Signed-off-by: Wes <[email protected]> (cherry picked from commit 307debe)
A helper change makes the selector repeat every journey in the file. For layout.spec.mjs that is 18 journeys x 10, which ran past the 15-minute job timeout on WebKit (#1022: 148/170 passed, 0 failed, then cancelled). The selector now emits one shard per five journeys (at most eight), and the lane spreads the repetitions with --shard. Small selections keep one runner. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]> (cherry picked from commit 6a85f66)
…not a sync error Leaving a channel while its catch-up save is queued invalidates the save. That rejection set the global read-state health to error, so Inbox showed "Reading observation expired" with Retry inbox, and rows shifted 56px. CI caught it as an inbox-refresh-layout flake on slow WebKit runners. The caller still receives the rejection; explicit Inbox actions keep their own alert. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]> (cherry picked from commit 63f2a1a)
Treat position samples taken from Virtua list-height commits as layout-owned so an upward virtualizer correction cannot masquerade as reader input. Keep explicit upward gestures authoritative, and cover the panel-resize ordering at both the component and browser boundaries. Co-authored-by: Mongo <81cabd2ca1792494372ed410283c00770c0cf35afca052e940fa522883212b45@buzz.block.builderlab.xyz> Signed-off-by: Wes <[email protected]> (cherry picked from commit 85ea95ec179d77bb7ec48c8d2a06ef461370c894)
… bottom Wes's #1022 P1 case. It fails 120px from the bottom on main in both engines and passes with the ChannelTimeline bottom-follow fix. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
…not on row visibility Waiting for the alpha row before releasing still let the held setup cross the app's real 10s live-setup deadline on loaded WebKit runners: #1039's flake check failed 4 of the first ~120 repetitions at that wait. The first head request is the boundary the hold controls, so release there. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
…fore its restore frame #1039's CI still failed the panel-resizing journey 121px from bottom on WebKit: after a list-height commit, the browser delivered Virtua's 1px offset correction as a scroll event before the restore frame, and onScroll read it as reader movement. Treat scrolls in that window as layout-owned until the restore frame or the next reader input. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
kalvinnchau
left a comment
There was a problem hiding this comment.
🤖 no findings in the code review at 9a1e69e. reviewed the complete diff, read-save cancellation through storage/callers, timeline correction and reader-input paths, browser test synchronization, and flake-shard wiring.
minimalness: 10/10. elegance: 9/10 — correction ownership necessarily spans the mutation observer, scroll handler, and animation frame. correctness: 9/10 — no concrete defects found, but browser ordering and shard execution remain unverified at runtime.
code-only review using the adversarial-code-review skill; no tests run or hosted CI results assessed.
…tent Review follow-up: only open the window when the height commit was recorded for the current intent, so a reader drag that started first still cancels follow, and keep it across an effect re-run until the next effect's first frame so a correction scroll in between is still layout-owned. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
…to the first slot The removed photo tile leaves the accessibility tree when its exit starts but keeps its slot until the exit ends; the clip then slides left. A click straddling that slide pressed the button and released beside it. A forced 300ms press failed 6/6 before and passed 6/6 after. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
…enu journey "row menu moves and removes a channel optimistically…" failed on WebKit at openSort: the Sort menu item never appeared after clicking "More actions for Channels". The click came right after a reload, while the sidebar still showed "Loading Me placement…". Base UI opens the menu on mousedown and a document mouseup outside the trigger and popup cancels it; WebKit took ~500ms between press and release, the placement status left during it, the sidebar shifted up, and the release landed off the trigger. Open the menu from the keyboard, like openMove already does, and wait for Base UI to highlight Sort before focusing it. Pointer opening of the section menu stays covered by navigation-sorting. Co-authored-by: Wes <[email protected]> Signed-off-by: Wes <[email protected]> Co-authored-by: Princess Donut <5d97ac8c272fa949af56c71e586146b1706e5c4c3daa125db5b4625e12e0686c@buzz.block.builderlab.xyz> (cherry picked from commit fef8a23)
…imeline positions it On loaded Linux WebKit the timeline's own end alignment can land short of the DOM bottom while rows re-measure, and onScroll read that programmatic upward scroll as the reader leaving the bottom. Keep the timeline-owned position until the next reader input; a held pointer (scrollbar drag) still lets geometry infer upward movement. Instrumented in the pinned Linux Playwright image at 1.5 CPUs, the panel-resizing journey failed 5-7 of 30 before this change and 30/30 passed after. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
…text #828's SVG <text> numeral made every interaction near day dividers about 1.5x slower on Linux WebKit; the crowded nested-replies journey doubled in CI and pushed WebKit shard 4/8 past its 15 minute limit on main. Keep the SVG tile and lay an HTML numeral over it. Before/after WebKit screenshots of the divider match. Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Wes <[email protected]>
wpfleger96
left a comment
There was a problem hiding this comment.
🤖 Thanks for pulling these together. Blocking: two correctness issues in the bottom-follow change at cb5ca76e2a42d993a26ddf2ef0ab8bc83f9d3b48, both from 3c92ca64d ("only reader input can cancel bottom follow"). Everything else in the 4 commits since 8d14bee6 looks right to me.
1. A real upward scroll can stay classified as layout correction indefinitely
layoutCorrectionis now set on every bottom alignment the timeline does (ChannelTimeline.tsx:366,:570,:613) and cleared only ingesture()(:696). The restore frame no longer clears it, so for a reader who is following the bottom, the flag stays on until their next wheel/key/touch/pointer event on the section.- While it's on,
recordPositiongetslayoutOwned=true(:776-779), somovedUpneeds an upward gesture (:322-329) and the sample forcesbottom=true(:334-335), even hundreds of px from the bottom. The next append or row-height change end-aligns again. - That drops the geometry fallback
mainhas for upward scrolls that don't come through those handlers: screen-reader navigation,scrollIntoView, find-in-page. Those readers get snapped back to the bottom on the next message. - Fix: scope the exemption to outstanding positioning/correction work (e.g. clear it once the timeline's own alignment has landed or its
scrollendfires, rather than at the next reader input) so a settled upward move without an input event is still detected. Add a regression: settle at bottom, scroll up with no input event, then append, and separately grow a row.
2. The held-pointer guard is bypassed by the height observer
onScrollhonorspointerHeld, but the observer still callsrecordPosition(scroller.current, true)(:612). Ordering that still fails: pointer down, append re-arms the observer, the drag movesscrollTopup, and a row-height commit lands before the scroll event (the comment at:583notes input can move the DOM before its scroll event). The observer sample keepsbottom=trueand overwritesmeasuredPosition.offsetwith the dragged offset, so the later pointer-awareonScrollsees no upward delta and the queued restore snaps back.- Same for release-before-scroll:
onPointerUp(:745-747) clears the held state without sampling, so a re-armed correction can hide that delta too. - Fix: make the observer sample pointer-aware as well (
!pointerHeld.currentat:612) and cover the observer-before-scroll order. The new case atChannelTimeline.test.tsx:1005-1022releases the pointer before the correction, so it doesn't exercise the guard; the reversed order needs its own test.
Optional
pointerHeldonly clears onpointerup/pointercancelon the section (:745-750). Mouse pointers have no implicit capture, so releasing a drag outside the timeline leaves ittrueuntil the next click in the timeline, which puts that reader back on the pre-commit WebKit short-end-align behavior. A window-levelpointerup(or pointer capture) would close it.- Day divider (
MessageTimestamp.tsx): the numeral'scolorandfontSizeare inline while.dayCalendar [data-calendar-date]already exists inMessages.module.css; moving them there keeps the styling in one place. Footprint, decorative hiding, and the accessible day label all hold. - The description doesn't cover
3c92ca64d(reader-input-only cancel) orcb5ca76e2(day divider numeral), and still says hosted CI is pending. Nobuzz-review-completedyet.
CI: the re-run on this head (attempt 2) failed WebKit 4/8 on notifications.spec.mjs:327 (mention still unread after 10s, line 424); attempt 1 passed. I haven't tied it to this PR, and it isn't in main's recent WebKit failures. Re-running it.
… clock Main (#1039) now pauses the page clock before the roster refusal so the ignored Retry clicks land inside the cooldown. Automatic recovery shows its notice with the first retry, 1 s after the refusal, so run the paused clock that far before expecting the notice. Cross the cooldown in the broker's real time first, then on the page clock: the queued automatic retry sends as soon as the page deadline passes, and a second refusal would arm a deadline the paused clock never reaches. Listen for the observer update before crossing, because automatic recovery can read the roster during the crossing. Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Signed-off-by: Logan Johnson <[email protected]>
* commit '506ac5d4d': Fix JPEG uploads and attachment control overlap (#1068) feat(channels): read, write and look up channel labels (#964) refactor(agents): separate the agent runner and its reads from the desktop app (#961) fix(media): show desktop animated avatars and match dev broker media rules to native (#990) ci(browser): split journeys across nine shards per engine (#989) test(inbox): open the edit journey's conversation by name (#997) test(voice-notes): keep each attachment's own type in the upload fixture (#999) Stabilize main: consolidate WebKit flake and bottom-follow fixes (#1039) Push main-pane threads in and hold threads behind a loading mark (#1018) [buzz-app] Show full sent images and GIFs (#886) feat(agents): say the agents this copy runs are online (#862) [buzz-app] Keep the sidebar in place when switching to Personal space (#945) fix(builderlab): link the Buzz key before attesting remote agents (#978) feat(agents): show agents typing to everyone while they work on a mention (#998) Signed-off-by: John Tennant <[email protected]>
mainhas been red on WebKit browser journeys for hours, and the per-test fix PRs kept tripping over each other. This PR replaces all of them. It is the eight existing fixes cherry-picked ontomain(authorship and sign-offs preserved), plus the browser case from the #1022 P1.composer-attachments:562eager-upload playback and removal layout (test)live:234pause the page clock before the roster cooldown arms (test)agent-activity:872refresh thread typing before the keyboard section (test)voice-notes:385drop in-flight media route reads at teardown (test)sidebar-unread:180release the held alpha EOSE on its first head request, before the 10s live-setup deadline (test; #1033's row-visibility wait still failed 4 of ~120 flake repetitions here)inbox-refresh-layout:17a withdrawn read observation is a cancellation, not a sync error (app)layout:574list-height corrections no longer cancel bottom follow, including a correction scroll delivered before the restore frame (app; #1022 alone still failed 121px from bottom here)#1022 P1
The simultaneous shrink-above / grow-below case is now a permanent journey in
layout.spec.mjs. Locally it fails onmain(3/3 Chromium, stuck 120px from bottom) and passes with this PR'sChannelTimelinefix (3/3 Chromium + 3/3 WebKit). The fulllayout.spec.mjspasses 38/38 across both engines.Validation
src/features/messages/vitest: 1611/1611, including a new reverse-order correction test that fails without the fixunread.test.ts(read-state): passinglayout.spec.mjs:619panel resizing: 15/15 WebKit under 6 workers;sidebar-unread.spec.mjs:180: 30/30 under 6 workerstests/integration/flake-check.test.mjs: 9/9Product behavior changes to confirm: the Inbox no longer shows "Reading observation expired · Retry inbox" after leaving a channel mid-read, and the timeline stays pinned to the bottom when rows above and below change size together.
🤖 Generated with Claude Code