## Summary
Split out of the `flakey-monday` flake-fix series. Three Desktop E2E
tests in `persistent-agent-audience.spec.ts` and
`community-rail.spec.ts` acted on signals that could arrive before the
app state they implied (a logged command before the submit lock
rendered, a URL check that only held before a view transition landed).
They now wait on the readiness signals the product actually exposes. No
product code changes.
Commits (cherry-picked from `flakey-monday`, original messages kept):
- `test(desktop): wait for composer focus and send cycles in audience
specs`
- `test(desktop): assert the kept remembered route after the community
switch`
- `test(desktop): assert Home after repairing the remembered channel`
### Root cause and fix per test
**`a manual mention persists when automatic mentions are enabled`**
(`desktop/tests/e2e/persistent-agent-audience.spec.ts`):
`locator.fill("follow up")` failed because `message-input` was not
editable.
- Root cause (from the code and CI symptom; not reproduced locally): the
test sends with a 1.5 s mock send delay, during which the composer is
submit-locked and `contenteditable="false"`. The lock is React state and
renders a frame after Enter. The test's last two checks before the fill
could both pass before that frame: the outgoing-mention poll (the e2e
bridge logs a command when invoked, before the delay) and the
`contenteditable="true"` wait. Playwright's `fill` rejects a
`contenteditable="false"` element outright instead of waiting.
- Fix: after Enter, wait for the full send cycle: `data-submit-locked`
"true", `contenteditable` "false", `data-submit-locked` "false",
`contenteditable` "true". Only then assert on the editor. Existing
assertions are unchanged.
**`the mention setting is reachable and operable by keyboard`** (same
spec): the thread composer's mention autocomplete never became visible.
- Root cause (likely; not reproduced locally): opening a thread by URL
mounts both the channel composer and the thread composer.
`useComposerAutofocus` focuses whichever mounts first and TipTap defers
`focus()` by a frame. A click and fill that land before that deferred
focus lose it, and the focus-gated mention menu unmounts.
- Fix: a `readyThreadComposerInput()` helper waits for the thread editor
to be editable and for exactly one `message-input` to hold focus, so the
app's own autofocus has landed. Both tests use it.
**`community rail > does not repair a remembered channel until live
validation succeeds`** (`desktop/tests/e2e/community-rail.spec.ts`):
`expect(page).not.toHaveURL(/#\/channels\/general$/)` failed right after
the click on community B.
- Root cause (confirmed; 21/40 failures at 14 workers on the old spec):
`switchCommunity` runs inside a view transition whose callback writes
B's remembered channel into the hash and then activates B, a few frames
after the click returns. By design the URL is then `#/channels/general`,
which `AppShell` keeps until a live channel read succeeds and only then
repairs to Home. The inverted check from 631b05c88 passed only when its
first poll beat the view transition.
- Fix: wait for B's rail button to have `aria-current="true"` (switch
finished), then assert the URL is `#/channels/general`, the kept route.
After live validation repairs storage to Home, assert the Home route
`/#\/$/` (the follow-up commit; `not.toHaveURL(general)` would have
passed for any route, and `AppShell`'s repair calls `goHome({ replace:
true })`).
### Related issue
N/A (flake fixes; none found).
### Testing
Human testing is still required. This PR does not carry
`buzz-review-completed`; the author has not yet run these specs
themselves on this branch.
#### Verification
Verified locally on the `flakey-monday` branch before the split
(Playwright smoke project, e2e build):
- Audience spec: neither flake reproduced before the fix, even with CPU
throttled 4x and 8x. After the fix, both tests 40 repeats each at 10
workers: 80/80; whole spec: 35/35.
- Community-rail test: 40 repeats at 14 workers: 40/40; whole spec:
27/27 at 4 workers. The Home assertion was falsified: with the repair
temporarily sending the app to `#/channels/random`, the new assertion
failed and the previous `not.toHaveURL` check passed.
- The four desktop flake tests from the series together, 10 repeats each
at 8 workers: 40/40.
- Two untouched tests in these specs are pre-existing flakes a baseline
build fails as often, and are out of scope: community-rail "enters a
remembered channel before live validation completes" and
persistent-agent-audience "the mention button preserves manual
rementions after undo until explicitly readdressed".
Nothing was re-run on this split branch: the workstation was overloaded,
so no tests, builds or linters were executed after cherry-picking. CI on
this PR is the verification for the split branch.
🤖 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>
Buzz
Important
This is the deprecated Buzz 0.X desktop app. Buzz 1.0 lives in block/buzz-app. Build new desktop features there. Change this directory only to fix a 0.X bug, or to keep it compiling after a shared crate change.
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 frontendpnpm tauri dev- run the desktop apppnpm build- typecheck and build frontendpnpm typecheck- TypeScript checkspnpm lint- Biome lintpnpm 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
Required-community guidance in the legacy app
The legacy desktop reads public NIP-11 metadata before each WebSocket connection.
Only requires_nip_fi: true shows the upgrade card. Missing/false requirements
keep the existing connection flow. Discovery failures retain an unknown-policy
state and continue the existing socket/authentication path; the relay enforces
admission. Query/fragment URLs keep their existing socket behavior; the native
discovery reader rejects them before making an HTTP request. Credentials never
reach a fallback dial. Only explicit required policy opens the upgrade modal. Switching
communities invalidates pending checks. The card does not
reset keys or remove saved communities.
The upgrade card directs public users to the fixed https://buzz.xyz URL to
download an up-to-date Buzz client on every supported desktop platform and for
every required community.
If the browser cannot open, the card shows the public URL for manual navigation.
Discovery never supplies a trusted download or sign-in origin.