Repository navigation
feat(search): show member counts on channel results - #2333
Merged
kazemek21 merged 3 commits intoNov 4, 2026
Merged
Conversation
koltenrobison
marked this pull request as ready for review
November 3, 2026 21:57
kazemek21
approved these changes
Nov 4, 2026
kazemek21
left a comment
Contributor
There was a problem hiding this comment.
Kyle reviewed this PR and the Nexus review-tracker notes. Approved with no comments.
Posted by Codex (gpt-6.1-sol) on behalf of kylez.
Channel rows in the search palette now end with their member count, e.g. "Public channel · not joined · 3 members". Joined rows count the roster discovery already holds. Unjoined public rows read their relay-signed 39002 rosters through a new display-only channels.memberCounts read that never reaches discovery, so it grants no access. Co-authored-by: Wumbo <cba8cea567445730d41093b28915fac9a55aba4315a05864995e81bc53bfc724@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kolten Robison <kolten@squareup.com>
Co-authored-by: Wumbo <cba8cea567445730d41093b28915fac9a55aba4315a05864995e81bc53bfc724@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kolten Robison <kolten@squareup.com>
…eader Main now has channels.readMemberCounts and useMemberCounts for the Channels tab, so search rows reuse them instead of a second reader. Co-authored-by: Wumbo <cba8cea567445730d41093b28915fac9a55aba4315a05864995e81bc53bfc724@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Kolten Robison <kolten@squareup.com>
koltenrobison
force-pushed
the
kolten/search-channel-member-counts
branch
from
November 4, 2026 18:34
4add9a6 to
ade5617
Compare
johnmatthewtennant
added a commit
that referenced
this pull request
Nov 4, 2026
Signed-off-by: John Tennant <jtennant@block.xyz> Co-authored-by: Damon <8a08290d889f1c68921542bfe9631d4cd671be9bea5f1114951f386819e4c10a@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * origin/main: feat(agents): identify plugins in native hook output (#2299) [4/5] Add protection to the Codex harness plugin (#1879) feat(search): show member counts on channel results (#2333) Fix the mute icon flashing when selecting a channel (#2287) feat(agents): open a working agent's transcript from its activity (#2276) test(agents): refuse a real relay workflow mention in the harness kit (#2444) fix(agents): catch up every event watch on the kinds it watches (#1852) Keep local artifact collections current (#1507) feat(members): add several people to a chat in one write (#2010)
bradseiler
added a commit
that referenced
this pull request
Nov 4, 2026
Co-authored-by: Honor <e0bf479eeebe47dde1e35705212a4df70616a7bc834d907ddfb18cf0405f8b1b@seiler-test.communities.public.builderlabstaging.xyz> * origin/main: feat(agents): identify plugins in native hook output (#2299) [4/5] Add protection to the Codex harness plugin (#1879) feat(search): show member counts on channel results (#2333) Fix the mute icon flashing when selecting a channel (#2287) feat(agents): open a working agent's transcript from its activity (#2276) test(agents): refuse a real relay workflow mention in the harness kit (#2444) fix(agents): catch up every event watch on the kinds it watches (#1852) Keep local artifact collections current (#1507) feat(members): add several people to a chat in one write (#2010) Signed-off-by: Brad Seiler <seiler@squareup.com>
tulsi-builder
added a commit
that referenced
this pull request
Nov 4, 2026
Picks up #2333's channel member counts alongside the search heading status. Co-authored-by: BasilBot <399581b6165ab01343e3a1324774b1024d76942aea563468f93c975e5ee5193c@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: tulsi <tulsi@block.xyz>
michaelneale
added a commit
that referenced
this pull request
Nov 4, 2026
* origin/main: Open any Inbox filter view by route (#2394) fix(bestie): deleting an agent saves no Bestie record where there was none (#2420) fix(unread): never treat agents-audience messages as unread (#1552) feat(focus): add an optional Focus plugin that quiets everything except Solo chats (#2277) [5/5] Add protection to the Goose harness plugin (#839) feat(agents): identify plugins in native hook output (#2299) [4/5] Add protection to the Codex harness plugin (#1879) feat(search): show member counts on channel results (#2333) Fix the mute icon flashing when selecting a channel (#2287) feat(agents): open a working agent's transcript from its activity (#2276) Signed-off-by: Michael Neale <michael.neale@gmail.com> # Conflicts: # crates/plugin-manager/tests/management.rs # src/app/pages.integration.test.mjs # src/features/agent-harness/acp/process.ts
wpfleger96
added a commit
that referenced
this pull request
Nov 4, 2026
…-service * origin/main: (73 commits) fix(bestie): deleting an agent saves no Bestie record where there was none (#2420) fix(unread): never treat agents-audience messages as unread (#1552) feat(focus): add an optional Focus plugin that quiets everything except Solo chats (#2277) [5/5] Add protection to the Goose harness plugin (#839) feat(agents): identify plugins in native hook output (#2299) [4/5] Add protection to the Codex harness plugin (#1879) feat(search): show member counts on channel results (#2333) Fix the mute icon flashing when selecting a channel (#2287) feat(agents): open a working agent's transcript from its activity (#2276) test(agents): refuse a real relay workflow mention in the harness kit (#2444) fix(agents): catch up every event watch on the kinds it watches (#1852) Keep local artifact collections current (#1507) feat(members): add several people to a chat in one write (#2010) feat(solo): let viewers join read-only spaces (#2301) [3/5] Add protection to the Claude harness plugin (#1878) fix(settings): keep the bottom padding on long sections (#2361) fix(mermaid): cap diagram output size (#2231) feat(agents): give plugin agent types the Buzz side of a turn (#2364) feat(plugins): pin buzz://plugin-install links to a path and commit (#2315) feat(plugins): tint currentColor SVG page icons like host icons (#2314) ... # Conflicts: # src-tauri/src/host_process.rs # src-tauri/src/lib.rs # src/bundled/claude-code/claude.ts # src/bundled/codex/rpc.ts # src/bundled/goose/goose.ts # src/bundled/goose/runtime.ts # src/bundled/goose/sessions.test.ts # src/bundled/goose/sessions.ts # src/features/agent-harness/acp/process.ts
gilgtm
added a commit
that referenced
this pull request
Nov 4, 2026
* origin/main: (22 commits) feat(mentions): quieter mention menu that marks agents and their owners (#2328) feat: add a Canvas View tab, History diffs and a 256 KiB limit (#2318) feat(plugins): let local agents write and reload plugins (#2435) feat(onboarding): offer Create a community after public Builderlab sign-in (#2397) Open any Inbox filter view by route (#2394) fix(bestie): deleting an agent saves no Bestie record where there was none (#2420) fix(unread): never treat agents-audience messages as unread (#1552) feat(focus): add an optional Focus plugin that quiets everything except Solo chats (#2277) [5/5] Add protection to the Goose harness plugin (#839) feat(agents): identify plugins in native hook output (#2299) [4/5] Add protection to the Codex harness plugin (#1879) feat(search): show member counts on channel results (#2333) Fix the mute icon flashing when selecting a channel (#2287) feat(agents): open a working agent's transcript from its activity (#2276) test(agents): refuse a real relay workflow mention in the harness kit (#2444) fix(agents): catch up every event watch on the kinds it watches (#1852) Keep local artifact collections current (#1507) feat(members): add several people to a chat in one write (#2010) feat(solo): let viewers join read-only spaces (#2301) [3/5] Add protection to the Claude harness plugin (#1878) ... Signed-off-by: Gil Birman <gil@squareup.com> Co-authored-by: Claude <noreply@anthropic.com>
wpfleger96
added a commit
that referenced
this pull request
Nov 4, 2026
…r-load-perf * origin/main: Open any Inbox filter view by route (#2394) fix(bestie): deleting an agent saves no Bestie record where there was none (#2420) fix(unread): never treat agents-audience messages as unread (#1552) feat(focus): add an optional Focus plugin that quiets everything except Solo chats (#2277) [5/5] Add protection to the Goose harness plugin (#839) feat(agents): identify plugins in native hook output (#2299) [4/5] Add protection to the Codex harness plugin (#1879) feat(search): show member counts on channel results (#2333) Fix the mute icon flashing when selecting a channel (#2287) feat(agents): open a working agent's transcript from its activity (#2276) Signed-off-by: Will Pfleger <wpfleger@block.xyz> # Conflicts: # src/features/agent-harness/acp/agent-sessions.ts
matt2e
added a commit
that referenced
this pull request
Nov 5, 2026
…own with a label-only Edit target (#2506) ## What changed In **Messages → channel → ⋯ Channel actions → View channel details**, the Description row now renders through `MessageMarkdown`, the same renderer messages use, instead of plain text. - URLs, Buzz links and `#channel` references are real links for every viewer: owners, admins and plain members. `#name` only links when it exactly matches a channel name, including case. - Bold, italics and other Markdown render, instead of showing raw `**` / `_` markers. - Only the **Description** label and its pencil open *Edit channel details*. Clicking the description text no longer opens the editor. The pencil still reveals on row hover, the label is a 24px target, and the row has no hover fill. "No description" and "Not available" stay plain text. Members and Channel ID keep their whole-row actions. - The panel's Escape handler now has ThreadPanel's guard: it skips events that are already handled or that come from a portalled layer. A description link's context menu or preview card can't close the panel. Link safety is the same as in messages: raw HTML is skipped, only Buzz, profile and http(s) URLs pass `safeLinkUrl`, images render as alt text, and external links get `target=_blank rel="noopener noreferrer"`. ## Why Before this change, the description was plain text, so its URLs and `#channel` references couldn't be followed. For owners and admins it was worse: the Edit button covered the whole row with an `::after` overlay, so clicking a URL in the description opened *Edit channel details*. `docs/channels.md` documented that whole-row behaviour on purpose, and this PR updates it. Lifting the links above the overlay was rejected, because DESIGN.md says to "keep an independent row action outside the row's primary click target". The label-only `SettingsDetail` variant follows that rule. ## Scope This PR covers channel details only. **⌘K and the join notice are left alone on purpose.** Unjoined channels still show only "Public channel · not joined" in ⌘K, and the join notice still says only "Join channel to send messages". Showing the description there edits the same lines as #2333 (member counts in channel results) and #2301 (joining read-only spaces), so it will be a separate follow-up PR stacked on those two. `BuzzLinkPreview` is also out of scope (see Open points). ## Before / after Both sides ran the same Playwright script against the same fixture, viewport (1280×800), light theme and timeline. Before is `0b2dfbf988` (this branch's base). After is `8e1ffa8dcc`; the last commit only changes tests. The fixture description is `**R2 ops** on-call. Runbook: https://example.test/r2-runbook · escalate in #Beta · _quiet hours after 22:00 UTC_`. https://github.com/user-attachments/assets/07c62edb-25f5-4033-a0c8-7c29b332c76f *Each side gets the same clicks at the same moment. **Before:** clicking the URL (11.4s) and `#Beta` (15.9s) both open "Edit channel details". **After:** the URL opens a new window, `#Beta` goes to the Beta channel, and only the Description label (21.7s) opens the editor. The pointer, red click dot and action captions come from Playwright's `showActions` and are not app UI.*  *Pointer resting on "on-call". **Before:** raw `**` and `_` markers, the URL as plain text wrapping at its hyphen, and the whole row greyed because the row is one button. **After:** bold text, underlined URL and `#Beta` links, italics, and only the pencil next to "Description", with no row fill.* | Before (`0b2dfbf988`) | After (`8e1ffa8dcc`) | |---|---| |  |  | *Full-window stills behind the side-by-side image, captured losslessly with `page.screenshot` at the same moment (no pointer overlay).* ## Commits 1. **`fix(channels): link the description in channel details`** (43db4ea): `ChannelSettingsPanel` renders a non-empty description with `MessageMarkdown`, using a synthetic row, the session's reference directory and the page's `openLink` / `canOpenLink`. `ChannelsPage` passes the session and link handlers. A new label-only `SettingsDetail` variant drops the whole-row overlay for Description. CSS and `docs/channels.md` are updated, and Vitest and browser cases are added. 2. **`fix(channels): tighten the linked description row in channel details`** (8e1ffa8): follow-ups from the first AI review (below). 3. **`test(channels): prove the description row neither grows nor highlights`** (fce07e4): follow-ups from the second AI review (below). Test-only. ## AI review follow-ups **First review of 43db4ea** found no blockers and five polish suggestions. All five, plus the Escape guard, landed in 8e1ffa8: - Moved `useReferenceDirectory` and the synthetic row into a small `ChannelDescription` component. It mounts only for a non-empty description with a session and link handler. Profile and channel-list snapshots no longer re-render the whole panel and its editor surface for DM, solo or empty-description channels. - Dropped `aria-description` from the label-only Edit button. It made screen readers read the raw Markdown source right before the rendered value. - Gave the label a 24px minimum target, as DESIGN.md asks for inline targets, with a negative block margin so the row doesn't move. The pencil reveals on row hover again, including under reduced motion, but only the label takes the click. - Set `line-height: inherit` on the description cell, so it lines up with Members and Channel ID instead of using message line height. - Restored "no animation for reduced motion" in the Channel ID docs, and described the row-hover reveal and the label target. - Guarded the panel's Escape handler like ThreadPanel's. **Second review of 8e1ffa8** found no merge blockers and confirmed all six follow-ups. It raised two test-strength suggestions, both applied in fce07e4: - The 24px label-target check would still have passed if the negative `margin-block` stopped applying, because the row would just grow. It now checks that the Description value sits at the same offset inside its row as Members' value (40px inside the top border). - The "no row highlight" check hovered the padding and read the background straight away. It now hovers the value text, waits for the row-hover pencil reveal as proof the row is hovered, then checks the background. It also hovers the label, which catches a highlight rule that loses its label-only exclusion. ## Tests - **Vitest** (`ChannelDetailsEditor.test.tsx`): an editor and a member each get link roles, `onOpenLink` targets, an editor that stays closed on link clicks, and a label that still opens it. `ChannelSettingsPanel.test.tsx` covers the Escape guard and the missing accessible description. - **Browser case added**: "description links" → "an owner's description links open their targets, not the editor" in `tests/browser/channel-settings.spec.mjs`. It's a browser case because it checks layout: which element gets a click at a given point, the 24px label target, row offsets and hover paint. jsdom can't model any of that. - **Browser case updated**: `tests/browser/channel-header-menu.spec.mjs` now expects the Description row to be label-only (pencil revealed, no row fill). Members still expects a whole-row action. No browser cases were removed. - **Fail-then-pass**: the commit 1 Vitest and browser cases fail on `0b2dfbf988`. The commit 2 Escape and accessible-description assertions fail on 43db4ea. Each commit 3 assertion fails under a deliberate CSS break in Chromium and WebKit; for example, removing the margin gives 44 vs 39.98. ## Checks run On fce07e4 (the PR head), locally on a Blox workstation: - typecheck, lint, design checks and Biome: pass - Vitest on `src/bundled/channels`: pass (1,000 tests across 60 files) - `tests/browser/channel-settings.spec.mjs` and `tests/browser/channel-header-menu.spec.mjs`: pass in Chromium and WebKit (18 runs) Not run: - The full browser suite. - Any checks against current `main`. This branch is based on `0b2dfbf988`. `git merge-tree` shows a clean merge with `main`, but `main` has since changed `docs/channels.md`, `Channels.module.css`, `ChannelsPage.tsx` and `channel-header-menu.spec.mjs`. Hosted CI is the first run on a merged tree. ## Open points 1. **The `#channel` preview card can cover the Description label.** Hovering `#Beta` in the description shows MessageLink's existing channel preview card. The card can hide the "Description" label and pencil, which is now the only way to start editing. In the recording the card had dismissed before the pointer reached the label, so the click worked, but this interaction is new to the panel. 2. **The panel now parses descriptions as Markdown, but `BuzzLinkPreview` still shows them raw.** The same channel can look different in the two places; for example, `# Ops` becomes a heading in the panel. This trade-off was accepted for this PR, and `BuzzLinkPreview` was out of scope. 3. **Not yet tried in the native app.** Only Vitest, the browser specs and headless Chromium recordings have exercised this change. The human test step below is still pending. ## How to test 1. Open a channel you own. Choose **⋯ Channel actions → Edit channel details** and set the description to `**Ops** runbook: https://example.com · see #<another channel's exact name> · _quiet hours_`. 2. Choose **⋯ Channel actions → View channel details**. Expected: bold and italics render, and the URL and `#channel` are underlined links. Hovering the row shows the pencil next to "Description" but doesn't fill the row. 3. Click the URL. Expected: it opens in the browser, and the editor doesn't open. 4. Click the `#channel` link. Expected: it goes to that channel. 5. Go back, reopen details, and click the "Description" label. Expected: *Edit channel details* opens. 6. Repeat steps 2–4 as a plain member. Expected: the same links, and no pencil. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Signed-off-by: mtoohey <mtoohey@squareup.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Channel results in the search palette now show a member count at the end of the detail line, e.g. Public channel · not joined · 3 members or Conversation · 12 members.
Screenshots use the browser test fixture, with channel names and member counts copied from the internal community: buzz-community 99, buzz-help 14, buzz-development 138.
How it works
channel.members). No extra read.#p: [viewer]). The newchannels.memberCounts(ids)instore.tssends one bounded39002read without that filter and counts distinct well-formedpentries in each channel's newest relay-signed roster. The relay already serves full rosters of public channels to non-members.useChannelMemberCountswaits for the same 300 ms typing pause as public channel search and reads each channel only once while the palette is mounted. If the read fails, the row just has no count.Testing
SearchResults.test.tsxchecks the joined, unjoined and singular/plural labels, the exact read, and that discovery is untouched.store.test.tschecks the newest roster wins, that duplicate, malformed, wrong-author and wrong-channel entries are ignored, and that an oversized answer is rejected.global-search.spec.mjsnow asserts the count on the unjoined row. The fixture serves that channel's roster for an unfiltered read.global-search.spec.mjs: 20/20 passed across Chromium and WebKit.vitest run(all files): 2 failures, neither caused by this change.media-preparation.integration.test.mjs(tiled HEIC) fails onmainon my machine too.store-system-discovery.test.tspasses when run alone.tsc --noEmitand Biome on changed files are clean.Human test steps
just desktopwith VPN on, signed in to the internal community.buzz-dev.Public channel · not joined · N members. N should match the channel's member list once you open it. Joined channels readConversation · N members.🤖 Generated with Claude Code