Files
buzz/desktop
Salman Mohammed f99807a79a fix(desktop): skip a starter channel the relay will never create (#8911)
## Problem

On first run, the desktop app creates the `general` and
`welcome-everyone` starter channels at fixed ids when it can't find a
live one. Some refusals of that create are permanent. Any refusal
aborted setup **before it joined the new member to the starters that do
exist**:

| Starter state in the community | Relay reply to the create | Before |
|---|---|---|
| Another listed channel holds the name, archived ones included (with
#8782) | 400 `invalid: an open channel named "…" already exists` | setup
fails, not joined to `general` |
| An archived channel holds the fixed id | 400 `invalid: channel is
archived` (the relay checks this before duplicates) | setup fails |
| The channel at the fixed id was renamed or retyped | `duplicate` |
waits, then `starter channels created but metadata not yet available` |
| The channel at the fixed id was deleted | `duplicate`, and no metadata
is ever served | same |

Onboarding itself continues, because public starter failures are already
caught in `initializeStarterChannels`. The new member just never gets
joined to the other starter. With #8782 this happens to every new
desktop user in a community whose `welcome-everyone` or `general` is
archived.

## Fix

`ensure_starter_channels_inner` now skips a starter it can never create
or use, clears its pending-owner mark, and still joins the rest. Before
skipping after a permanent refusal, it reads the open directory again,
from the writer rather than a replica, once for all refused starters: a
live starter created after the first read is joined instead of skipped.
Any other refusal still fails setup. On the TS side,
`ensureStarterChannels` reports a skipped starter as `null` instead of
throwing. No caller reads those fields.

## Testing

- New tests run `ensure_starter_channels_inner` against a stub relay
that sends the relay's real reply shapes, one per row above, plus one
checking that other refusals still fail, and one where a live
`welcome-everyone` is listed only by writer-pinned reads (a lagging
replica) and gets joined. Each test fails when its guard is removed.
- Ran the real starter-setup code as a new user against a local relay
built from #8782, one community per row:
  - On `main`, rows 1, 2 and 4 fail.
- On this branch, all four join `general`, and each community still has
exactly one `welcome-everyone`.
  - A rerun is a no-op.
- `cargo test` (desktop Tauri, full), clippy, fmt, desktop `pnpm test`,
`tsc`, and biome all pass.

Related: #8782 (relay refuses taken names), #8807 (the same fix for
mobile). The archived-id case returns `channel is archived`, not
`duplicate`, so mobile needs to handle that reply too.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Co-authored-by: opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 12:59:28 -05:00
..
…

Buzz

Desktop chat shell with:

  • Tauri + React + TypeScript + Vite
  • Tailwind CSS
  • shadcn/ui-ready shared components
  • Biome (lint/format/check)
  • Feature-driven frontend structure

Scripts

  • pnpm dev - run the web frontend
  • pnpm tauri dev - run the desktop app
  • pnpm build - typecheck and build frontend
  • pnpm typecheck - TypeScript checks
  • pnpm lint - Biome lint
  • pnpm format - Biome format (write)
  • pnpm check - Biome check

Structure

  • src/shared - reusable app-wide code (ui, lib, styles)
  • src/features - feature modules (vertical slices)
  • src/app - top-level app composition