Skip to content

fix(channels): close the activity popover through Base UI actions - #891

Merged
kalvinnchau merged 1 commit into
mainfrom
ci-flake-3
Oct 13, 2026
Merged

kalvinnchau merged 1 commit into
mainfrom
ci-flake-3

Conversation

@matt2e

@matt2e matt2e commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the flaky me-workspace.spec.mjs "Me sidebar shares working activity" browser journey (WebKit).

ChannelActivityPopover closed itself by flipping its controlled open prop (setOpen(false)). That never emits a Base UI openchange event, so the hover interaction installed by openOnHover was never cleaned up. The safePolygon document mousemove handler, the stale openEvent, and the 150 ms close timer all stayed alive after the close. On WebKit the handler stayed armed until the next real pointer move. It then scheduled a trigger-hover close that landed inside the popup after the keyboard reopened it, and focus dropped to body. Chromium only avoided this because unmounting synthesizes a mouseleave.

This is a product defect in the popover's close path, not a test-timing problem, so the fix lives in the component and the spec is unchanged.

Changes

  • Every close the app starts (opening a working agent, opening agent activity, opening a thread, activity disappearing) now goes through actionsRef.current.close(), the same way TypingIndicator already does. Base UI then dispatches openchange and tears down its hover state.
  • The hasActivity effect also checks open, so it doesn't call an imperative close on a popover that's already closed.

Validation

All of this was run locally on macOS and covers only what's listed:

  • tsc --noEmit: clean
  • ChannelActivityPopover vitest: 4/4 pass
  • me-workspace.spec.mjs repeated 15 times: WebKit 15/15 pass (before the fix, 3 of 15 runs failed); Chromium 15/15 pass

Deferred:

  • Hosted CI on this branch
  • A human hasn't yet hovered over the popover, used the keyboard, or opened a thread in the running app
  • Agent review hasn't run

A green run doesn't prove every flake in this spec is gone.

🤖 Generated with Claude Code

@matt2e
matt2e marked this pull request as ready for review October 12, 2026 09:31
@matt2e
matt2e requested a review from a team as a code owner October 12, 2026 09:31

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes requested. The four app-initiated close paths now use Base UI’s action API, which dispatches the hover-cleanup event while preserving the existing navigation callbacks and controlled state.

Star Lord’s automated source review via Wes’s account — head a4e144cbd2db5f04def5200d56e1d8059714b7cb, base ecfbb8a5835dd0fb04e48111ef406a975f2c7715. Source-only: no tests or app execution; the one-time CI snapshot shows required checks passing (Windows native validation skipped), but live keyboard/pointer behavior and human acceptance remain unverified.

@kalvinnchau
kalvinnchau force-pushed the ci-flake-3 branch 2 times, most recently from 972da32 to 370c56d Compare October 13, 2026 18:22
Closing ChannelActivityPopover by flipping the controlled `open` prop
(`setOpen(false)`) never emits a Base UI `openchange` event, so the hover
interaction installed by `openOnHover` is not cleaned up: the safePolygon
document `mousemove` handler, the stale `openEvent`, and the 150 ms
close timer all outlive the close. On WebKit the handler stays armed
until the next real pointer move, then schedules a `trigger-hover` close
that lands inside the keyboard-reopened popup and drops focus to `body`
(me-workspace.spec.mjs "Me sidebar shares working activity", flaky in CI
and 3/15 locally). Chromium is saved only by a synthesized `mouseleave`
on unmount.

Route every app-initiated close through `actionsRef.current.close()`,
as TypingIndicator already does, so Base UI dispatches `openchange` and
tears down the hover state. The `hasActivity` effect additionally guards
on `open` to avoid imperative closes on already-closed instances.

Verified: `tsc --noEmit` clean; ChannelActivityPopover vitest 4/4;
me-workspace.spec.mjs x15 passes 15/15 on WebKit (was 3/15 failing) and
15/15 on Chromium.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: Matt Toohey <[email protected]>
Signed-off-by: cid <d9f92a72922bf45c17379a47d64dae84b6020397c2d5a52b5317d512068cd9d3@buzz.block.builderlab.xyz>

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes requested in this integration follow-up. The remaining activity-disappearance close uses Base UI’s action API and remains compatible with the newer keyboard/touch hover-dismissal guard; failed mark-read attempts retain the existing retry path.

Star Lord’s automated source review via Wes’s account — head 3f57cb357964e7a36f5f7305f1ec66f6cdb2d760, base 0ec0c1adbdceb480e49f218e261d9c38d44d2a3c. Source-only, including public PR material; no tests or app execution. The CI snapshot still had one WebKit shard running and Windows skipped; live keyboard/pointer and human/native acceptance remain unverified.

@kalvinnchau
kalvinnchau merged commit fb9cad1 into main Oct 13, 2026
23 checks passed
@kalvinnchau
kalvinnchau deleted the ci-flake-3 branch October 13, 2026 19:01
wpfleger96 pushed a commit that referenced this pull request Oct 13, 2026
* commit 'ad4040dee':
  Port IFC policy library and session APIs (#835)
  Give Codex the Buzz base prompt (#984)
  refactor(channels): remove personal groups (#988)
  Replace the agent icon with the Soft & wide mark (#932)
  fix(channels): close the activity popover through Base UI actions (#891)
  Restore model and effort picks when a background send is recovered (#887)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
johnmatthewtennant pushed a commit that referenced this pull request Oct 13, 2026
* origin/main: (22 commits)
  Polish workspace surfaces, split controls, and emoji composition (#801)
  fix: wake reading after an exact message reveal (#1006)
  feat(pairing): copy the pairing code from Settings (#1003)
  Ignore .scratch in the Vite dev watcher (#1001)
  fix(client): recover websocket connections across repeated rollouts (#996)
  test: fix six cross-branch CI flakes (#1002)
  feat(agents): run Buzz's own copies of Claude and Codex, kept on the latest release (#949)
  ci: repeat changed Vitest and Playwright tests 10x on pull requests (#890)
  feat(agents): restore agent memory (#982)
  Soften compact dropdown outlines (#991)
  fix(agents): manage providers, models, and agent migrations (#952)
  Port IFC policy library and session APIs (#835)
  Give Codex the Buzz base prompt (#984)
  refactor(channels): remove personal groups (#988)
  Replace the agent icon with the Soft & wide mark (#932)
  fix(channels): close the activity popover through Base UI actions (#891)
  Restore model and effort picks when a background send is recovered (#887)
  test(navigation): isolate the keyboard retry session gate (#981)
  fix(codex): log failed idle terminal clean instead of swallowing it (#878)
  feat(agents2): set the clicked conversation's session apart in agent tabs (#846)
  ...

Signed-off-by: Sol <49aa1f65411fd096d2e2ec144f1e7aa36fdc76d1b907cfdf7be000c66f9d3b8e@buzz.block.builderlab.xyz>
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.

3 participants