Skip to content

test(sidebar): wait for the rolled-back row to leave Starred before collapsing Work - #2574

Merged
matt2e merged 1 commit into
mainfrom
mtoohey/flaky-nav-group-collapse
Nov 5, 2026
Merged

matt2e merged 1 commit into
mainfrom
mtoohey/flaky-nav-group-collapse

Conversation

@matt2e

@matt2e matt2e commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Summary

navigation-groups.spec.mjs "dismissing a failed move restores keyboard focus to its placement or a surviving row" clicked Work's header before the rolled-back row had left Starred. This adds the missing barrier to the journey's failMove helper. The change is test-only: no assertions are loosened, no retries, sleeps or timeouts change, and no product code changes.

Problem

WebKit's navigation-groups.spec.mjs:650 failed once, at await expect(beta).toBeHidden() after the click on Work's summary, on workflow-ui-issues (#2362), a branch that doesn't touch the sidebar.

In a scan of about 311 failed browser-journey jobs over about 25 hours, this journey failed only this once.

Cause

The trace (artifact browser-journeys-webkit-11) shows the following:

  • The 502 lands and the alert appears with the rollback render. The optimistic Starred copy of the row is then an AnimatePresence exit: it stays in the DOM and in layout (inert, aria-hidden, fading), and the row re-enters Work at once.
  • The journey's barrier, Work's beta row being visible, passes as soon as that row mounts at about 5% opacity (snapshot opacity 0.046), while the Starred copy is still there.
  • The click on Work's summary measured its box with the Starred copy still laid out (y 393–425). Removing the copy moved the header up by a row, to y ≈ 379 in the final screenshot.
  • Playwright's hit-target interceptor decides on the first event of a click and doesn't recheck later events. The press landed on the header, and the release landed on the Beta row that slid under it. The browser fired no click on the header, so toggle("group:work", false) never ran.
  • The trace is consistent with this. Work's <details> stays open and its content never gets hidden or inert in any snapshot after the click, and the call log shows no retry. The final screenshot has the pointer resting on the Beta row, which shows its hover highlight, and Alpha is still the current channel.

This is the same press-and-release mismatch that #2566 describes for Show more in channel-members-focus. Here the layout shift is an exit animation completing rather than a search result landing.

Fix

After the alert, failMove waits until the sidebar holds exactly one beta row. Only the rolled-back placement in Work remains, so the Starred exit has finished before the journey acts on the layout. The helper runs for both of the journey's failed moves.

Validation

  • Adversarial ordering, with requestAnimationFrame held from the 502 until the header press and a 300 ms press-to-release delay on the header click:
    • without the fix: 20/20 failed (10 Chromium, 10 WebKit) with the CI signature. The press landed on the Work summary with two beta rows in the DOM, the release landed on the Beta row, and the click fired on a common ancestor.
    • with the fix: 20/20 passed.
  • Whole navigation-groups.spec.mjs, --workers=4 --retries=0 --repeat-each=10: WebKit 100/100 passed (3.8 min) and Chromium 100/100 passed (3.9 min). The machine was heavily loaded (load average about 70), and the WebKit here is Playwright's macOS build, not the Linux build CI uses.
  • tsc --noEmit, biome check --error-on-warnings on the changed file and git diff --check are clean. node --test tests/integration/flake-check.test.mjs passes 10/10.

This changes the same file as #2566 (a different journey, so the hunks don't overlap). A green run doesn't prove the flake is gone, because it fired once in about 311 jobs. Hosted CI covers broad validation.

🤖 Generated with Claude Code

…ollapsing Work

WebKit's navigation-groups.spec.mjs:650 "dismissing a failed move restores
keyboard focus to its placement or a surviving row" failed once, at the
`expect(beta).toBeHidden()` after clicking Work's header
(https://github.com/block/buzz-app/actions/runs/41946923047/job/126981730084),
on a branch that doesn't touch the sidebar.

Cause: the failed move's alert arrives with the rollback render. The
optimistic copy of the row in Starred is then an AnimatePresence exit: it
stays laid out (inert, fading) until the exit ends, and the row re-enters
Work at once. The journey's barrier, Work's beta row being visible, passes as
soon as that row mounts at low opacity, so the click on Work's header can
start before the Starred copy leaves. Its removal moves Work's header up by a
row. In the CI trace the press landed on the header (two beta rows in the DOM)
and the release on the Beta row that slid under it, so the browser fired no
click on the header. Playwright's hit-target check only covers the first
event of a click. The trace shows the section never collapsed, the first
`details` kept `open`, and the final screenshot shows the pointer resting on
Beta.

Fix: after the alert, wait until the sidebar holds exactly one beta row, so
the Starred exit has finished before the journey acts on the layout. No
assertion, timeout or retry changes; test-only.

Validation: with requestAnimationFrame held from the 502 until the header
press and a 300 ms press-to-release delay, the journey failed 20/20
(Chromium and WebKit) with the CI signature without this change, and passed
20/20 with it. navigation-groups.spec.mjs in full, --repeat-each=10
--retries=0, passed 100/100 in WebKit and 100/100 in Chromium.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e
matt2e requested a review from a team as a code owner November 5, 2026 03:40
@matt2e
matt2e merged commit 6b697fa into main Nov 5, 2026
43 checks passed
@matt2e
matt2e deleted the mtoohey/flaky-nav-group-collapse branch November 5, 2026 04:12
ljknight added a commit to ljknight/buzz-app that referenced this pull request Nov 5, 2026
…nks-in-browser

* origin/main: (138 commits)
  feat(composer): optional ⌘/Ctrl+Enter to send (block#2331)
  test: use keyboard activation for reply caret setup (block#2557)
  test: settle member search before invitation expansion (block#2532)
  test(agent-command): isolate the copied executable writer (block#2537)
  test(sidebar): wait for the rolled-back row to leave Starred before collapsing Work (block#2574)
  test(browser): click timeline rows only after the timeline settles (block#2560)
  ci: split Vitest across six shards so 3/4 stops hitting the job cap (block#2571)
  feat(github): unfurl PR and issue links inline with their title (block#1693)
  fix(auth): style federated sign-in callback pages (block#2549)
  fix(agents): align creation model controls with identity (block#2480)
  Run native Codex through plugin execution targets (block#2150)
  test: split Solo access browser scenarios (block#2499)
  feat(agents): let any agent change Buzz settings through the MCP (block#2248)
  Let plugins decorate host sidebar rows and add sort modes (block#1853)
  NEXUS-4082: Keep the new-message boundary stable during a visit (block#2083)
  feat(agents): move version history into settings (block#2374)
  fix(agents): read goose Activity by saved session ids and render memory bodies on open (block#2466)
  feat(channels): starter channels: admin switch and relay auto_join flag (block#1624)
  fix(plugins): read Square HTTPS sources with the Block SSH agent (block#2465)
  feat(plugins): let agents request plugin changes for owner review (block#2280)
  ...

# Conflicts:
#	src/bundled/github/README.md
ljknight added a commit to ljknight/buzz-app that referenced this pull request Nov 5, 2026
* origin/main: (121 commits)
  feat(composer): optional ⌘/Ctrl+Enter to send (block#2331)
  test: use keyboard activation for reply caret setup (block#2557)
  test: settle member search before invitation expansion (block#2532)
  test(agent-command): isolate the copied executable writer (block#2537)
  test(sidebar): wait for the rolled-back row to leave Starred before collapsing Work (block#2574)
  test(browser): click timeline rows only after the timeline settles (block#2560)
  ci: split Vitest across six shards so 3/4 stops hitting the job cap (block#2571)
  feat(github): unfurl PR and issue links inline with their title (block#1693)
  fix(auth): style federated sign-in callback pages (block#2549)
  fix(agents): align creation model controls with identity (block#2480)
  Run native Codex through plugin execution targets (block#2150)
  test: split Solo access browser scenarios (block#2499)
  feat(agents): let any agent change Buzz settings through the MCP (block#2248)
  Let plugins decorate host sidebar rows and add sort modes (block#1853)
  NEXUS-4082: Keep the new-message boundary stable during a visit (block#2083)
  feat(agents): move version history into settings (block#2374)
  fix(agents): read goose Activity by saved session ids and render memory bodies on open (block#2466)
  feat(channels): starter channels: admin switch and relay auto_join flag (block#1624)
  fix(plugins): read Square HTTPS sources with the Block SSH agent (block#2465)
  feat(plugins): let agents request plugin changes for owner review (block#2280)
  ...

# Conflicts:
#	docs/buzz-help.md
wpfleger96 added a commit that referenced this pull request Nov 5, 2026
…-service

* origin/main: (68 commits)
  fix(messages): keep decoded images across virtualized remounts (#2474)
  Keep /leave commands out of channel history (#2476)
  fix(agents): ask before switching or closing drops unsaved edits (#2503)
  fix(agents): keep the Model settings row full width (#2577)
  feat(github): show a pull request's changes beside its summary (#2492)
  fix(search): open an existing DM from a ⌘K person row without a relay open; cover hidden-DM previews (#2519)
  feat(search): show channel type icon in search result subtitles (#2521)
  test(browser): bind port-0 fixture servers where the OS assigns (#2564)
  test(browser): fix six journey sync flakes from the 2026-11-04 sweep (#2566)
  fix(channels): ask before Escape, Close or an outside click drops a template draft (#2501)
  fix(channels): render the channel details description as linked Markdown with a label-only Edit target (#2506)
  fix(agents): let Back and Forward shortcuts traverse provider dialogs (#2509)
  Fix Inbox recovery after a delayed Retry (#2496)
  feat(composer): optional ⌘/Ctrl+Enter to send (#2331)
  test: use keyboard activation for reply caret setup (#2557)
  test: settle member search before invitation expansion (#2532)
  test(agent-command): isolate the copied executable writer (#2537)
  test(sidebar): wait for the rolled-back row to leave Starred before collapsing Work (#2574)
  test(browser): click timeline rows only after the timeline settles (#2560)
  ci: split Vitest across six shards so 3/4 stops hitting the job cap (#2571)
  ...

# Conflicts:
#	src/app/Settings.tsx
#	src/bundled/goose/sessions.test.ts
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.

2 participants