Commit Graph

5 Commits

Author SHA1 Message Date
Wes
7b11c6147d test: fix recurring CI flakes in desktop and Postgres lanes (#8343)
Fixes the CI flakes that failed most often across `main` and unrelated
branches from Oct 5 to Oct 14. The counts come from 150 failed `CI`
runs. Every change except the nextest config is test-only.

| Flake | Runs hit | Root cause | Fix |
|---|---|---|---|
| `probe_codex_acp_version_…descendant_holds_pipe_open` | 5 | The test
writes its script in-process and then execs it. If a sibling test spawns
a process in that window, the child inherits the open write fd and Linux
rejects the exec with `ETXTBSY`. The probe swallows that error as
`None`. | Write probe scripts from a short-lived `/bin/sh`
(`write_executable_script`) |
| `empty-edit-delete.spec.ts` (`:51`, `:74`, `:95`) | 11 | The seeded
`mock-general-welcome` id isn't hex, so `formatTimelineMessages` can't
overlay its edit. The post-subscribe window refetch then reverts the
row. | Emit a per-test message with a real id after the exact channel
subscription is live |
| `huddle-transcription.spec.ts:798` | 9 | The composer's delayed
initial autofocus (TipTap `focus()` runs in rAF) lands after the voice
popover opens and dismisses it. | Wait for `message-input` focus before
opening the popover |
| `voice-note.spec.ts:906` | 6 | The fixture `ping.mp3` is about 1s
long, so playback can end before `paused === false` is observed. | Loop
the fixture audio |
| `workflows.spec.ts:291` | 5 | The `Message text` fill runs before the
inspector swaps to the new step, so it fills the trigger's condition
instead. | Scope the fill to the step inspector after its `Action` menu
appears |
| `cluster_global_probe_commits_tokens_and_sessions_prove_coverage` | 3
| The `max-threads = 1` group only serializes these tests with each
other. Other tests' sessions still mask cluster-wide `pg_stat_activity`
(`MaskedActivity`). | `threads-required = "num-test-threads"` |
| `test_usage_metrics_lock_has_single_owner_and_releases_on_drop` | 1
(this PR) | The test retries the lock right after dropping the leader.
Postgres releases a session advisory lock only after the backend notices
the disconnect, so the retry can run first. | Poll for the successor,
bounded at 5s |

### Validation
- **ETXTBSY:** a Linux experiment with Rust 1.95 using the same
write→chmod→spawn pattern under concurrent spawns hit `ETXTBSY` 4–7% of
the time. Using the `sh` writer, it hit 0 of 6,000. I did not catch it
inside the full suite locally, so this fix is based on the mechanism,
not an in-suite repro. All 11 probe tests pass on Linux and macOS.
- **empty-edit-delete:** the baseline failed 16 of 45 runs
(`--repeat-each`, 6 workers, no retries). The spec fix alone brings that
to 4/45. The remaining failures come from a separate product race,
described below.
- **huddle:** the baseline failed 5 of 10 runs. With the fix, 60 of 60
passed without retries.
- **nextest:** checked with a throwaway crate on 0.9.136. A grouped test
with `num-test-threads` ran alone, even with `max-threads = 1`.
- I have not stress-run the voice-note and workflows fixes; they rely on
CI.

### Not in this PR
- **Product bug in `useChannelLinks`:** the debounced `#channel`
autocomplete close lets Enter accept a stale suggestion for about 120ms.
That accounts for the remaining `empty-edit-delete` failures, and it
affects users too. With the spec fix plus a synchronous close, the spec
passed 120 of 120 runs. This needs a product decision, so it's held for
a separate PR.
- **Composer autofocus race:** the delayed autofocus can close an
overlay the user just opened. The real fix is to revalidate when the
delayed focus runs. Overlaps #6530.
- **`thread-head-stale-edit.spec.ts:55`:** a forced click on a
pointer-events-none action bar is suspected, but I couldn't reproduce
it.
- **Remaining ETXTBSY exposure:** about 40 other Rust tests write and
then exec scripts, and 17 specs still edit the non-hex seed message.

Root-cause lanes by Princess Donut, Mongo and Mordecai. Carl integrated
and verified them.

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

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <5d97ac8c272fa949af56c71e586146b1706e5c4c3daa125db5b4625e12e0686c@buzz.block.builderlab.xyz>
Co-authored-by: Mongo <81cabd2ca1792494372ed410283c00770c0cf35afca052e940fa522883212b45@buzz.block.builderlab.xyz>
Co-authored-by: Mordecai <f314b6033b6e13df94c8e9e1187d3666b6b9ce54e66ee986e31cdaa909e8dcbc@buzz.block.builderlab.xyz>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:32:00 -06:00
Max Lampert
eb484387d5 chore(desktop): drop unused ORIGINAL_CONTENT from empty-edit-delete spec (#6517)
`biome check` fails with `lint/correctness/noUnusedVariables` on
`ORIGINAL_CONTENT` in `desktop/tests/e2e/empty-edit-delete.spec.ts`,
which fails `pnpm check` (Desktop Core) for **every PR touching desktop
paths** — e.g. it currently blocks #6460. It presumably landed while
Desktop Core was path-skipped on the introducing PR.

One-line removal; the constant has no remaining references (the
assertions use `RENDERED_ORIGINAL_CONTENT`).

Signed-off-by: Max Lampert <maxwell@squareup.com>
2026-08-21 18:20:19 -06:00
Taylor Ho
5acb930821 feat(desktop-messages): render compact Buzz permalink chips (#5638)
**Category:** improvement
**User Impact:** Buzz channel, message, repository, pull request, and
issue links now open reliably and display recognizable context in the
desktop app.
**Problem:** Buzz links could appear as raw or ambiguous URLs, and
navigation links received during startup or community transitions could
be dropped before the UI was ready. Repository and issue shares in
particular required hover context to understand at a glance.
**Solution:** Queue desktop channel/message navigation until the UI is
ready, then render bare Buzz permalinks as icon-prefixed chips with
concise entity context while preserving user-authored Markdown labels as
ordinary links.

<details>
<summary>File changes</summary>

**desktop/src-tauri/src/deep_link.rs**
Adds validated channel-link parsing and a deduplicated, acknowledged
queue so navigation survives frontend startup.

**desktop/src-tauri/src/lib.rs**
Registers the pending-navigation state and commands with the desktop
application.

**desktop/src/features/communities/useCommunityInit.ts**
Resets queued navigation safely across community boundaries without
leaking stale destinations.

**desktop/src/features/messages/lib/channelLink.test.mjs**
Covers valid, malformed, and canonical channel permalink forms.

**desktop/src/features/messages/lib/channelLink.ts**
Defines strict parsing and detection for `buzz://channel/<uuid>` links.

**desktop/src/features/messages/lib/composerMessageLinkNode.test.mjs**
Extends composer-node coverage for normalized Buzz link content.

**desktop/src/features/messages/lib/composerMessageLinkNode.ts**
Keeps composer link-node handling aligned with the expanded Buzz link
surface.

**desktop/src/features/messages/lib/remarkChannelDeepLinks.test.mjs**
Verifies bare channel URLs become renderable deep-link nodes without
touching code.

**desktop/src/features/messages/lib/remarkChannelDeepLinks.ts**
Transforms eligible bare channel links into dedicated Markdown nodes.

**desktop/src/features/messages/lib/remarkEntityLinks.test.mjs**
Covers bare repository, pull-request, and issue detection and code-span
exclusions.

**desktop/src/features/messages/lib/remarkEntityLinks.ts**
Adds dedicated Markdown nodes for bare Buzz project entities.

**desktop/src/shared/deep-link.test.mjs**
Exercises queued navigation, acknowledgement, serialization, and
community-switch behavior.

**desktop/src/shared/deep-link.ts**
Serializes pending deep-link drains and acknowledges destinations only
after successful navigation.

**desktop/src/shared/styles/globals/markdown.css**
Aligns permalink icon geometry and spacing with agent mention chips.

**desktop/src/shared/ui/markdown.test.mjs**
Adds integration coverage for every permalink chip, authored labels,
fallbacks, icons, and static rendering.

**desktop/src/shared/ui/markdown.tsx**
Routes channel and entity nodes through the shared presentation path
while preserving authored link text.

**desktop/src/shared/ui/markdown/BuzzLinkChip.tsx**
Introduces the shared interactive/static permalink chip and
authored-label inline-link components.

**desktop/src/shared/ui/markdown/ChannelDeepLink.tsx**
Renders channel shares and references with Hash icons, names, and
shortened-ID fallbacks.

**desktop/src/shared/ui/markdown/MessageLinkPill.tsx**
Renders ordinary message shares with message icons and channel/message
context while retaining sent-from-thread behavior.

**desktop/src/shared/ui/markdown/entityLinks.tsx**
Maps repositories, pull requests, and issues to Projects-aligned icons
and contextual labels.

**desktop/src/shared/ui/markdown/nodeCache.ts**
Includes entity-link rendering in cached Markdown node handling.

**desktop/src/shared/ui/markdown/utils.ts**
Allows validated channel links through the Buzz URL transform.

**desktop/src/shared/useMessageDeepLinks.ts**
Drains queued navigation links safely and clears them during teardown.

**desktop/src/testing/e2eBridge.ts**
Extends the mock bridge with pending-navigation command behavior.

**desktop/tests/e2e/community-rail.spec.ts**
Verifies queued links do not cross community boundaries.

**desktop/tests/e2e/navigation.spec.ts**
Covers channel/message deep-link navigation during startup and active
sessions.

**desktop/tests/helpers/bridge.ts**
Adds reusable deep-link mock state and acknowledgement helpers.


</details>

## Reproduction steps
1. Run the desktop app and open a channel containing bare
`buzz://channel`, `buzz://message`, `buzz://repo`, `buzz://pr`, and
`buzz://issue` URLs.
2. Confirm each bare URL renders as one cohesive chip with a type icon,
a useful name or shortened identifier, and no duplicated channel `#`
character.
3. Add an authored Markdown link such as `[design
discussion](buzz://issue?...)` and confirm the supplied label remains an
ordinary link rather than becoming a chip.
4. Select channel and message links and confirm they navigate correctly
in warm and cold-start states.

## Screenshots / demos
Houston dark theme with custom purple accent (`#a855f7`), captured from
rebased visual implementation `ad411cc06`; current head `0aafa144f` only
adjusts E2E expectations for the visible mention-label behavior shown
here.

**Composer — channel, message, repository, pull request, and issue
pills**

![Composer with all Buzz permalink pill types in Houston dark theme and
purple
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/5638/composer-all-permalink-pills-dark-purple.png)

**Message list — channel, message, repository, pull request, and issue
pills**

![Message list with all Buzz permalink pill types in Houston dark theme
and purple
accent](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/5638/message-list-all-pill-types-dark-purple.png)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
2026-08-14 10:18:18 -07:00
Will Pfleger
7bcfe7e0a1 fix(desktop): widen post-Enter timeouts in empty-edit-delete spec (#4792)
## Summary

Increases three Playwright assertion timeouts in
`tests/e2e/empty-edit-delete.spec.ts` from 5s to 10s to fix a
shard-composition flake introduced by PR #4694.

## Root Cause

PR #4694 added `huddle-transcription.spec.ts` (477 lines, 22+ tests) to
the Desktop Smoke E2E suite, shifting shard 2 composition so that
`empty-edit-delete` now runs with significantly more accumulated browser
state. The three affected assertions all wait for a React state update
triggered by pressing Enter in edit mode:

- `alertdialog` becoming visible after an empty edit (tests 1 and 2)
- `edit-target` hiding after a successful non-empty edit (test 3)

These transitions go through the React scheduler. In isolation they
complete in milliseconds. In a loaded headless shard with accumulated GC
pressure, the 5s window became insufficient — test 3 failed 3/3 times in
CI run
[30946444168](https://github.com/block/buzz/actions/runs/30946444168)
with `edit-target` still visible after Enter.

No product code is changed. The empty-edit-delete flow is correct and
untouched by #4694. This is a test-environment timing adjustment only.

## What Changed

- `tests/e2e/empty-edit-delete.spec.ts` — three `{ timeout: 5_000 }` →
`{ timeout: 10_000 }` for the post-Enter React-update waits

## Validation

- `just desktop-check` — passed
- `just desktop-test` — 4194 passed, 0 failed

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-08-04 17:08:29 -04:00
Sam W
d88313f369 feat(desktop): delete a message by clearing its edit to empty (#3813)
## What

Clearing an edit to empty and hitting accept now **deletes the message**
instead of hanging. One of Sam's frequent workflows is to delete a
message by editing it, clearing the text, and pressing Enter — which
previously no-op'd (a deliberate guard blocked empty edits).

## How

Pure client-side wiring — **no relay, schema, or Rust changes.**

1. **`MessageComposer.tsx`** — the edit path had a guard that *blocked*
empty edits (`if (!trimmed && !hasMedia) return;`). That guard is simply
**removed**, so empty content flows through the normal edit path to
`onEditSave("", [], [])`. `buildOutgoingMessage("")` is a safe no-op.
2. **`handleEditSave` in `useChannelPaneHandlers.ts`** — when an edit is
submitted with empty text and no media tags, it exits edit mode and
opens the **same "Delete message?" confirmation** the Delete menu action
shows, rather than publishing an empty edit.
3. **`DeleteMessageConfirmDialog.tsx`** — the confirmation dialog,
extracted into **one shared component**. `MessageActionBar` renders it
for the Delete menu action (previously inline), and `ChannelScreen`
renders it for the empty-edit path. No duplicated dialog UI. **Delete**
runs the existing `deleteMutate`; **Cancel** leaves the message
untouched.

Because both the main timeline and the thread panel already route
edit-save through `handleEditSave`, this covers both surfaces with a
single dialog at the `ChannelScreen` level — no per-composer plumbing.

- Image-only edits (empty text but attachments present) still publish
normally — only a *fully* empty edit prompts to delete.
- An empty edit can never publish an empty body: `handleEditSave`
returns before the edit mutation.

## Review history

This PR was reworked three times in response to review — each pass made
it smaller:

1. First cut wrapped this in a new "Delete message?" `AlertDialog`
rendered from a composer hook — a verbatim duplicate of the confirmation
already in `MessageActionBar.tsx`. Removed.
2. Second cut threaded a dedicated `onDeleteEditTarget` callback down
`ChannelScreen → ChannelPane → MessageComposer / MessageThreadPanel`.
Also redundant — the delete decision moved entirely into
`handleEditSave`, which every edit-save already flows through.
3. Third cut added a special-case empty branch to the composer, which
pushed `MessageComposer.tsx` over the file-size ratchet and led to an
unrelated emoji-helper extraction to make room. Both gone: deleting the
pre-existing guard (rather than adding a branch) is net-negative, so
there's no ratchet pressure and **nothing emoji-related in this PR**.
`MessageComposer.types.ts` is back to baseline too.
4. Fourth pass (this one): an unconfirmed, no-undo delete was too sharp.
The empty-edit path now routes through the same **"Delete message?"
confirmation** as the menu action — shared as one
`DeleteMessageConfirmDialog` component (so it's reuse, not the duplicate
dialog from cut #1).

## Testing

- **E2E:** `desktop/tests/e2e/empty-edit-delete.spec.ts` (Playwright,
smoke project), three tests, all passing locally:
- *clearing an edit to empty prompts to delete, then deletes on confirm*
— edits the mock identity's own `#general` message, clears it, Enter →
the **"Delete message?"** dialog appears; Delete → the row disappears
and edit mode exits.
- *cancelling the empty-edit delete keeps the message* — same up to the
dialog, then Cancel → the message survives.
- *a non-empty edit still edits and never deletes* — guards the other
direction (no dialog).
- `pnpm typecheck`, biome, file-size + px-text guards all clean; full
desktop unit suite (3847 tests) passing locally.

> Heads-up for the reviewer: pushed with `--no-verify` because the
pre-push hook runs the Rust **integration** suite, which needs Docker
(Postgres/Redis) that isn't available in this environment — it doesn't
apply to this desktop-only change. CI runs the real gates.

---

🐝 Built by Bumble in Buzz, from a conversation in #test-swesterman.

---------

Signed-off-by: Sam Westerman <swesterman@squareup.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 22:34:12 -07:00