Repository navigation
feat(bestie): choose the private channels Bestie can read - #2036
rileycrane wants to merge 3 commits into
Conversation
The Bestie tab's private channels row becomes its own group: the private channels you share with Bestie, each with Remove, plus Add to a channel… and Add to all my private channels, most recently active first. Add to all asks first, because each channel's members see the addition, then lists each channel that didn't confirm. "Also add Bestie to private channels I create" is shown disabled until it has a record to keep. Adds use session.memberAdditions, and removals use member administration with the role a fresh roster read shows, as the Members dialog does. Public channels need nothing, since agents read them already. The sidebar's recent order moves unchanged into compareRecent so both use the same comparison. Co-authored-by: eggboy <3986e5374b8c11a274f4a53fa9a68e1e18b53e712f8aa5218add2485bf5a2b09@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Riley Crane <rileycrane@squareup.com>
…urrent Add to all now adds Bestie to one channel at a time, as adding several agents does, so a long list doesn't burst the relay. It adds exactly the channels its dialog counted, even if the list changes while the dialog is open. A failure stops showing once the channel is as the change wanted. Remove is unavailable on a cached roster, which the Members dialog doesn't administer either. Co-authored-by: eggboy <3986e5374b8c11a274f4a53fa9a68e1e18b53e712f8aa5218add2485bf5a2b09@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Riley Crane <rileycrane@squareup.com>
The private channels header held two buttons that never shrink, so at the side panel's default 28rem the tab grew to their width and every row ran off the panel. Add to a channel and Add to all now have rows of their own, whose buttons wrap under their titles in a narrow panel and are described by them. Co-authored-by: eggboy <3986e5374b8c11a274f4a53fa9a68e1e18b53e712f8aa5218add2485bf5a2b09@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Riley Crane <rileycrane@squareup.com>
43efb9f to
f688371
Compare
b3381b9 to
a5d42ff
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (systems/lifecycle), Mantis (correctness/security/concurrency), and Groot (product/accessibility) coverage.
Two actionable findings are inline: one P2 and one P3. The existing session services retain authorization and membership-readback ownership; the extracted sidebar comparison preserves its previous ordering.
Reviewed head f68837125121e8dfb1360086b40cc7b8fdf5d1dd against base/merge-base a5d42ffbdbd31fdc563ee7c5a245261868cf3e20. Reviewed the six-file patch, relevant callers/lifecycle, public description (no attached images), and commit messages. No concrete public-material disclosure found.
Source-only: no PR code, tests, builds, apps, or live relay operations were executed. The one hosted-check snapshot reports CI required successful; Windows validation was skipped. That does not establish the membership UI, keyboard/screen-reader behavior, narrow-layout rendering, performance, or human acceptance; those remain unverified. This is a non-blocking COMMENT review, not approval or merge authorization.
| ))} | ||
| {!joined.length && ( | ||
| <PreferenceRow | ||
| title="Not in any of your private channels yet" |
There was a problem hiding this comment.
🤖 [P2] Distinguish an unread roster from an empty membership list
This empty state depends only on joined.length, although useChannelList also returns status, error, and coverage. On a failed initial discovery the store retains an empty list with status: "error" (features/relay/store.ts:1640–1650), so opening this tab says “Not in any of your private channels yet” even if Bestie already has access. No read error or roster retry appears in this section. Retained partial results can likewise be offered as “Add to all my private channels” without disclosing that only part of the roster is known.
Render loading/read-error state separately, provide the existing session.channels.refreshList() recovery, and gate or explicitly qualify bulk selection while coverage is partial. Preserve usable known rows without presenting missing rows as proof of no membership. This follows DESIGN.md’s rule against treating loading/error as an empty collection; cover failed discovery and partial-roster recovery in the component tests.
| <SettingsGroup> | ||
| {joined.map((channel) => ( | ||
| <PreferenceRow | ||
| key={channel.id} |
There was a problem hiding this comment.
🤖 [P3] Preserve keyboard focus when the completed action removes its control
A keyboard-activated Remove stays focusable while loading, but the confirmed roster update removes this entire row from joined. Similarly, adding the last eligible channel makes addable.length zero and unmounts both Choose… and Add to all (lines 164–210), including whichever trigger received focus after the menu/confirmation closed. There is no completion handoff to a surviving control, so the user loses their focus position in this section. Shared popup return-focus behavior cannot retain a trigger that is subsequently removed. This conflicts with DESIGN.md’s focus-restoration guidance.
When an action removes the focused control, hand focus to the next suitable row or a stable section target, without moving it if the user has already navigated elsewhere. Cover confirmed removal and last-channel addition with a roster-updating fixture; the current static-list tests never unmount these controls. This is a source finding; browser focus behavior was not exercised.
The Bestie tab lists the private channels you share with Bestie. From the tab you can add Bestie to one channel or to all your private channels, and remove it from any of them.
Why only private channels: agents can already read public channels, so only private channels need Bestie added.
What changes
Try it (
just dev, with a local Bestie and two private channels it isn't in)Stack: each PR is based on the one before it: #2034 Bestie tab → #2035 Remembers you → #2036 private channels (this PR) → #2037 check-ins → #2039 Former Bestie → #2041 Remove as Bestie → #2047 Choose your bestie. #2040 (recent notes in Claude Code prompts) is separate and based on main.
Overlap: #1716 also changes
features/agents/service.tsandtabs.tsx. Whichever lands second will rebase onto the other.🤖 Generated with Claude Code
buzz-review-completed