mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
ee6ca5fa28bce04dfecb6717de65b08a57f2ac47
2482 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ee6ca5fa28 |
Remove public relay signing key fallback (#6729)
Require `BUZZ_RELAY_PRIVATE_KEY` on every relay startup and remove the shared public fallback key completely. For local development, `just bootstrap` now generates a random key once in the gitignored `.env` file. Re-running bootstrap preserves that key, so relay-authored events retain the same identity across restarts. The local relay recipes reload `.env`, while isolated CI and E2E launchers pass explicit per-run keys. Startup fails immediately when the key is missing or invalid, before connecting to Postgres or Redis. Deployed relays continue to receive their stable key from the existing chart-managed secret. Validated with: - `./scripts/test-ensure-local-relay-key.sh` - `cargo test -p buzz-relay --bin buzz-relay` - `cargo clippy -p buzz-relay --bin buzz-relay -- -D warnings` - `cargo fmt --all -- --check` - `shellcheck scripts/ensure-local-relay-key.sh scripts/test-ensure-local-relay-key.sh` Signed-off-by: Jordan Mecom <jm@squareup.com> |
||
|
|
22f32c99e2 |
docs(nest): make commit attribution policy-neutral (#6707)
## Summary - replace the generated Nest's unconditional human author/sign-off rules with portable guidance that separates authorship, material co-authorship, DCO certification, and cryptographic signing - defer attribution to repository-local policy, forbid inferred or guessed identities, and require inspection of every outgoing commit - bump the Nest template version so existing installations refresh, with regression coverage for fresh generation and upgrade preservation ### Related issue None found. Related runtime identity work exists in #6177, but this PR is intentionally limited to the generated Nest guidance and its refresh behavior. ### Testing - `bin/just desktop-tauri-clippy` - `bin/just desktop-tauri-test` - `bin/just file-size-check` - `git diff --check` - pre-push hooks: `push-head-scope`, `branch-skew`, `file-size-check`, and `desktop-tauri-checks` Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> |
||
|
|
b58de7cfa7 |
fix(desktop-messages): preserve inline agent mentions with persistent addressing (#6793)
**Category:** fix **User Impact:** Users can place `@Agent` mentions inline anywhere in a message—even when that agent is also persistently addressed—so separate agents can receive separate instructions in the same message. **Problem:** Selecting an agent from the composer mention picker could turn the selection into persistent addressing instead of leaving an inline `@Agent` at the cursor. That made persistent addressing and inline composition mutually exclusive: once the persistent behavior took over, users lost the clear `@Agent A do X, @Agent B do Y` message structure they previously had. **Solution:** Make inline mentioning and persistent addressing independent behaviors: - Ordinary mention selections always insert `@Agent` inline at the current cursor position. - This remains true when the agent is already persistently addressed; the persistent audience never blocks or replaces an inline mention. - If **Automatically mention agents** is enabled, a successfully sent inline mention may additionally make that agent persistent for later messages. Existing saved preferences remain respected. - The dedicated automatic-mention control and primary+Shift+Enter shortcut continue to add or remove persistent addressing directly. - For users without a saved preference, **Automatically mention agents** now defaults off. <details> <summary>File changes</summary> **desktop/src/features/messages/lib/autoPinMentionedAgentsPreference.test.mjs** Updates preference coverage for the default-off behavior while preserving explicit saved choices. **desktop/src/features/messages/lib/autoPinMentionedAgentsPreference.ts** Defaults automatic post-send persistence off when no valid preference exists. **desktop/src/features/messages/ui/useAgentAddressLockPicker.test.mjs** Verifies ordinary picker selections insert inline mentions without changing or pulsing the persistent audience, including when the selected agent is already persistently addressed. **desktop/src/features/messages/ui/useAgentAddressLockPicker.ts** Keeps ordinary mention selection on the existing inline insertion path while preserving the separate persistent-address controls and shortcut. **desktop/tests/e2e/persistent-agent-audience.spec.ts** Covers separately targeted inline instructions to two agents, signed outgoing recipients, default-off persistence, and explicit opt-in persistence scenarios. </details> ## Reproduction steps ### Inline mentions without persistence 1. Open a channel with at least two available agents and leave **Automatically mention agents** disabled. 2. Use the composer mention picker to insert the first agent, type an instruction, then insert a second agent and type a different instruction. 3. Confirm the draft reads like `@Agent A review this, @Agent B test that` and neither agent appears in the persistent addressed-agent controls. 4. Send the message and confirm both agents are recipients while neither remains persistently addressed for the next draft. ### Inline mentions with persistence 1. Enable **Automatically mention agents**, then mention an agent inline and send successfully. 2. Confirm that agent becomes persistently addressed for later messages. 3. In a new draft, select the same agent from the mention picker again. 4. Confirm a new inline `@Agent` is inserted at the cursor while the agent remains persistently addressed. 5. Confirm the dedicated automatic-mention control and primary+Shift+Enter shortcut can still add or remove persistent addressing directly. ## Demo - **Before:** Persistent addressing could consume an ordinary picker selection, preventing users from placing that agent inline in the message. - **After:** Ordinary selection always produces an inline `@Agent`; persistence is a separate optional behavior that can coexist with inline mentions. ### Related issue N/A — reported through the Buzz feature room. ### Testing At `a4579c0a666f3644abe44271614186d7b9356bf5`: - Post-push hooks passed desktop check, desktop typecheck, and the full desktop unit suite. - The two persistence-focused Playwright smoke scenarios passed after explicitly opting into **Automatically mention agents**. - The one-time multi-agent targeting and persistent-address shortcut Playwright scenarios passed on the feature changes before the final rebase; CI is validating the rebased branch. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
f249710331 |
Qualify canonical relay images for staged delivery (#6781)
## Why Phase 1 of progressive delivery needs an explicit eligibility boundary before deployment automation can safely consume relay artifacts. Today the canonical Docker workflow can publish independently of the same-SHA CI result, the relay does not expose its source/build identity, and the Helm chart only renders mutable tags. The existing `ghcr.io/block/buzz-staging-dev` workflow in #6709 remains a manual pre-merge preview lane. It is deliberately not a canonical promotion input. ## What changed - Compile the full source SHA and GitHub Actions build identity into canonical relay binaries and expose it at `/_status`. - Gate creation of tagged multi-architecture manifests on the latest exact-SHA `CI` push run succeeding on `main` or `release`. - Preserve the signed SLSA provenance and add a signed, release-only deployment-eligibility predicate with source, build run, qualifying CI run, and exact compatible Buzz chart version. - Keep failed or pending builds untagged and without an eligibility predicate; preview-package artifacts are excluded by package and signer contract. - Add optional `image.digest` support to Buzz chart `0.1.8`, retaining the existing tag fallback. - Document verification and the intentional boundary between image/chart compatibility and backwards-compatible database migrations. ## Risk and rollout Canonical tag publication now waits for same-SHA CI and fails closed when the newest matching run fails. Per-architecture blobs may remain untagged after a failed qualification; they are not promotion inputs. Debug manifests are also CI-gated but do not receive the release eligibility predicate. There are no migration or schema-orchestration changes. The companion BuilderBot infrastructure PR will pin a separately verified canonical main digest and expose a read-only running-deployment inspection command. Signed eligibility is available only for artifacts published after this workflow lands. ## Verification - Blox: workflow contract fixtures, including stale-green rejection and predicate structure - Blox: `actionlint` for `ci.yml` and `docker.yml` - Blox: 46 Helm unit tests, chart lint, and production/quickstart render fixtures - Blox: relay status unit test, workspace Rust formatting, and workspace all-target clippy - Live evidence: exact-SHA CI selector chose successful run `32857345153` for source `9d1e4b257657f382d3111ce748f3da8d063b7671` - Existing canonical digest provenance verified with `gh attestation verify` ## References - #6709 — developer-preview precursor; intentionally separate from canonical eligibility - `docs/deployment-identity.md` *Generated with Codex* --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com>chart-v0.1.8 |
||
|
|
7ba1197aa6 |
feat(desktop): persist agent addressing across composer messages (#6714)
**Category:** improvement **User Impact:** Agent addressing now stays visible, editable, and consistent across messages, so users can keep talking to the same agents without rebuilding the audience for every send. **Problem:** Agent addressing was transient and easy to lose track of. Inline mention text, persistent audience state, restored drafts, and sent-message rendering could drift apart, making it unclear who would receive the next message. **Solution:** The composer now treats addressed agents as a persistent audience represented by ordinary editable mention chips and a synchronized compact avatar tray. Eligible agent mentions can become persistent immediately, addressing survives sends without flashing empty, and removing or explicitly unpinning an agent updates that state without disturbing unrelated draft text. `Cmd+Shift+M` / `Ctrl+Shift+M` provides a fast way to toggle the current recipient or select the best eligible agent. - Promotes eligible inline agent mentions into persistent addressing when the preference is enabled; human mentions remain unchanged. - Keeps inline chips, restored drafts, persistent recipient state, and the avatar tray synchronized. - Clears an agent's persistent address when its final chip is removed, while explicit unpinning preserves unrelated draft text. - Restores persistent mentions optimistically during send and restores the full draft on failure, avoiding an empty-composer flash. - Deduplicates inline mentions and tag-backed address prefixes in sent messages. - Toggles the current addressed agent with `Cmd+Shift+M` / `Ctrl+Shift+M`, or selects the best eligible agent when none is addressed. - Ranks shortcut selection by recent explicit stream mentions, then active/member/runnable/stable fallbacks; DM participant fan-out does not affect recency. - Adds focused avatar feedback for entry/exit, send bounce, and failure shake, with reduced-motion and narrow-layout support. - Applies the same behavior to main and thread composers while preserving draft restoration, channel switching, and send-race correctness. ### Screenshots #### Light theme The compact tray keeps persistent recipients visible alongside editable mention chips.  #### Dark theme Persistent addressing remains clear and legible in the dark composer treatment.  ### Reproduction steps 1. Open a channel with an eligible agent and enable persistent agent addressing. 2. Insert an agent mention and confirm it appears as a normal editable chip while the compact avatar tray reflects the same persistent recipient. 3. Send several messages and confirm the agent remains addressed, the mention restores without an empty-composer flash, and sent messages do not duplicate the addressed agent. 4. Remove the agent's final mention chip and confirm its persistent address clears. Add unrelated draft text, explicitly unpin the agent, and confirm that text remains intact. 5. Press `Cmd+Shift+M` on macOS or `Ctrl+Shift+M` on Windows/Linux. Confirm it toggles the current addressed agent or, when none is selected, chooses the most relevant eligible agent. 6. Verify recent explicit channel/thread mentions influence shortcut selection, while DM participant fan-out does not. 7. Force a failed send and confirm the addressed avatars shake and the full draft is restored; confirm successful sends use the subtler bounce feedback. 8. Repeat in the thread composer, after switching channels, in dark theme, with reduced motion enabled, and at a narrow composer width. <details> <summary>File changes</summary> **desktop/src/features/channels/ui/ChannelPane.tsx** Feeds recent explicit agent mentions into the composer for contextual shortcut selection. **desktop/src/features/messages/lib/getVisibleAgentAddressPubkeys.ts** and tests Show tag-backed addressed agents only when they are not already represented by inline mention text. **desktop/src/features/messages/lib/mentionCandidates.ts**, **mentionRanking.ts**, **mentionSuggestionMapping.ts**, **recentMentionPubkeys.ts**, and tests Model agent eligibility and activity, derive explicit mention recency, and rank the best shortcut recipient. **desktop/src/features/messages/lib/useActiveAgentPubkeys.ts**, **useDefaultAgentSuggestion.ts**, and **useMentions.ts** Expose active-agent and recent-recipient context to mention consumers. **desktop/src/features/messages/ui/ComposerAddressControls.tsx** and tests Render the compact persistent-recipient tray with enter, exit, send, failure, layout, and reduced-motion states. **desktop/src/features/messages/ui/MessageComposer.tsx** and **MessageComposer.types.ts** Connect editable mention text, persistent addressing, shortcut selection, draft restoration, and send feedback. **desktop/src/features/messages/ui/MessageRow.tsx** Avoids duplicating addressed agents already rendered as inline mentions. **desktop/src/features/messages/ui/MessageThreadPanel.tsx** Brings the same recency and persistent-addressing behavior to thread composers. **desktop/src/features/messages/ui/useAgentAddressLockPicker.ts**, **useAutoPinMentionedAgents.ts**, **useAlwaysAddressShortcut.ts**, **useMentionSendFlow.ts**, and tests Synchronize mention chips with persistent audience state, promote eligible mentions, handle shortcut toggling/default selection, and preserve recipients through send success or failure. **desktop/src/features/messages/ui/useComposerPasteHandler.ts** and **useMentionSendFlow.types.ts** Keep the composer implementation focused by extracting paste and send-flow boundaries. **desktop/src/shared/lib/keyboard-shortcuts.ts** Documents `Cmd+Shift+M` / `Ctrl+Shift+M` as the agent-address shortcut. **desktop/tests/e2e/persistent-agent-audience.spec.ts** Exercises persistence, selection, removal, sends, failures, restored drafts, themes, reduced motion, and narrow layouts. </details> --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
a526dca9bc |
feat: navigate images across message threads (#6705)
## Summary - navigate across image occurrences that are currently rendered inside the nearest explicit open-thread DOM scope - preserve rendered DOM order, duplicate occurrence identity, hidden-spoiler exclusion, preview-image entry parity, and the exact thumbnail return target - keep adjacent timeline messages and unrelated surfaces as separate galleries - make Copy and Download follow the current gallery item, including when a link-preview image opens the gallery first and navigation reaches Markdown media ## Scope contract This is intentionally a **rendered-media gallery only**. Gallery membership comes from mounted image triggers under the nearest explicit thread scope. Collapsed or unloaded descendants are excluded until the thread UI renders them. There is no canonical thread fetch, descendant projection, depth override, or synchronous message reparse in this change. `ChannelPane.tsx` remains unchanged from `main`; `useSearchHighlightProps` and both timeline/thread prop spreads are preserved. ## Verification - rebased onto `origin/main` at `113a33b7e49b7173ee1767c49ef2f49c63803034` - `git diff --check` - differential file-size check - full Desktop check (Biome plus text/pubkey policy checks; only pre-existing informational diagnostics) - Desktop TypeScript typecheck - Desktop production build and rebuilt E2E build - full Desktop JS units: `5,476/5,476` passed - full Desktop Tauri workspace tests passed (one documented native performance test ignored) - rebuilt image gallery Playwright spec: `14/14` passed - search-highlight smoke cases: `5/5` passed - reduced diff: 6 files, `+417/-41`; production `+78/-41`, E2E `+339/-0` The four added E2E cases cover cross-message thread navigation and return targeting, rendered-only exclusion of collapsed descendants, preview-first current-item actions, and adjacent-message isolation. --------- Signed-off-by: Kalvin Chau <kalvin@block.xyz> Signed-off-by: am <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz> Co-authored-by: am <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz> |
||
|
|
113a33b7e4 |
Add database pressure observability (#6700)
## Why Database pressure currently collapses several distinct delays into one symptom. This adds the evidence layer needed to distinguish pool acquisition wait, logical database operation time, advisory-lock wait, and selected transaction duration before changing timeout or retry policy. This is the phase 2 Lane A observability bundle for [#26](https://github.com/TheSentinel454/buzz/issues/26), [#28](https://github.com/TheSentinel454/buzz/issues/28), and [#33](https://github.com/TheSentinel454/buzz/issues/33). It is stacked on #6668. ## What - Record explicit reader/writer checkout wait and acquisition outcomes with `buzz_db_pool_acquire_wait_seconds` and `buzz_db_pool_acquisitions_total`. - Extend the compile-time `#[datastore_span(name = "...")]` seam with `buzz_db_operation_duration_seconds`, so operation labels remain static source literals instead of request data. - Route correctness-critical replacement, membership, push-gate, deletion, and migration/schema-safety advisory locks through one observer without changing their SQL, order, scope, or blocking behavior. - Measure six internally owned transaction lifetimes with `buzz_db_transaction_duration_seconds`, starting after `BEGIN` succeeds and ending after explicit commit/rollback or scope exit. - Emit root slow-operation warnings at 500 ms, logging the first slow completion and then 1/100 per call site with only `operation`, `outcome`, and `elapsed_ms`. - Document names, units, fixed label vocabularies, measurement boundaries, and blind spots in this PR description. Fixed labels are deliberately small: - `pool_role`: `writer`, `reader` - `lock_type`: `replacement`, `membership`, `push_gate`, `deletion`, `migration_schema_safety` - `outcome`: `success`, `error`, `timeout` where SQLx/PostgreSQL can distinguish it accurately - `operation`: compile-time datastore names plus the six closed transaction operation names documented in the runbook No metric or slow warning contains community IDs, event IDs, event kinds, coordinates, d-tags, SQL/query text, query IDs, returned errors, or event content. ## Coverage boundaries - Operation duration is the complete annotated logical function body, not pure SQL execution; it may include implicit checkout, lock wait, nested operations, and application work. Cancelled futures do not reach its completion hook. - Pool timing covers explicit helper checkouts, including proved-reader routing and selected writer-owned transactions. Implicit SQLx checkout through `&PgPool` remains folded into operation duration. - Lock timing covers application-side blocking locks in the five named families. Trigger/stored-procedure locks, channel-TTL locking, the usage try-lock, and the audit service session lock remain outside this slice. - Transaction timing covers only the six wholly owned boundaries documented in the runbook. It excludes pool wait, `BEGIN`, asynchronous rollback cleanup after an early return, and caller-owned `Db::begin_transaction` lifetime. ## Relationship to #6229 #6229 is the incident-driven timeout precursor. This PR does not add or change `statement_timeout`, `lock_timeout`, `idle_in_transaction_session_timeout`, retries, audit durability, or client-visible conflicts. It provides the missing distributions needed to evaluate those policies later and intentionally leaves #6229's open audit retry/durability finding untouched. The branches overlap in `crates/buzz-db/src/lib.rs` and `crates/buzz-db/src/migration.rs`, so a later rebase may need textual conflict resolution, but the behavior is complementary rather than duplicated. ## Risk assessment Moderate-low. The primary risk is instrumentation overhead and added static series. Cardinality is source-bounded, slow logs are sampled/redacted root events, and the lock/transaction changes wrap existing awaits without changing policy or ordering. ## Verification Author workstation: `buzz-tornquist-db-pressure-observability` (`2010927`), exact head `d7cf833e26c528adfcde3917ded80daf6f4ddac9`, parent `6f50e6b2b2a996349149af61d35bdd6a355f77fd`. - `cargo fmt --all --check` — passed - `cargo clippy -p buzz-datastore-tracing -p buzz-db -p buzz-audit -p buzz-search -p buzz-relay --all-targets -- -D warnings` — passed - `cargo test -p buzz-datastore-tracing --quiet` — 4 passed - `cargo test -p buzz-db --quiet` — 109 passed, 200 ignored - `cargo test -p buzz-audit -p buzz-search --quiet` — 16 passed, 25 ignored - `cargo test -p buzz-relay --lib --quiet -- --test-threads=1` — 906 passed, 48 ignored - Native PostgreSQL focused tests for pool success/timeout/error, lock success/contention/timeout/error, replacement, membership serialization, push ordering, deletion fencing, migration/schema exclusion, and reader fallback — 8 passed The default-parallel relay run passed once; subsequent runs exposed the existing load-sensitive `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo` 504 at the end of the suite. That test passes in isolation and the full relay suite passes serially. Independent exact-head review workstation: `buzz-tornquist-db-pressure-observability-review` (`2013067`). Formatting, the same all-target clippy command, datastore instrumentation tests, DB unit tests, source privacy guards, and diff/non-goal audits passed; no review findings. Generated with Codex --------- Signed-off-by: tornquist <tornquist@squareup.com> |
||
|
|
12f3fea26e |
revert fixed mention highlight (#6716)
**Category:** fix **User Impact:** Mention chips now follow the active theme without a fixed yellow fill and remain readable across every supported theme. **Problem:** PR #6696 added an opaque yellow treatment to every human and agent mention. Removing that fixed color exposed another issue: some theme accent colors do not provide WCAG AA contrast when also used as normal-size mention text. **Solution:** Remove the fixed yellow treatment and keep the active theme accent as a subtle translucent chip background. Mention labels use a light/dark semantic foreground selected for readable contrast, including hover states. <details> <summary>File changes</summary> **desktop/src/shared/styles/globals/markdown.css** Removes the fixed-yellow treatment, retains theme-derived accent backgrounds, and applies an AA-safe semantic foreground to human and agent mention labels in resting and hover states. **desktop/src/shared/styles/globals/theme.css** Removes the obsolete fixed-yellow tokens and defines light/dark mention foreground values. **desktop/tests/e2e/mentions.spec.ts** Adds rendered contrast coverage for resting and hover states across every supported syntax theme, compositing translucent chip and ancestor surfaces before checking the 4.5:1 threshold. </details> ## Reproduction steps 1. Open a message containing a human or agent mention. 2. Confirm its subtle background follows the active theme accent rather than using yellow. 3. Hover the mention and confirm the accent background strengthens while the label remains readable. 4. Switch between default, light, dark, and custom syntax themes and confirm mention labels remain legible. This is a focused visual rollback of the mention treatment introduced by #6696. The channel notification badge changes from that PR remain intact. --------- Signed-off-by: tulsi <tulsi@block.xyz> |
||
|
|
29f2054c69 |
highlight search terms in results and messages (#6702)
**Category:** fix **User Impact:** Search terms are now highlighted in yellow in Cmd+F results and in the message opened from a result. **Problem:** Search returned relevant messages, but users still had to reread each preview and destination message to discover where the query appeared. Search navigation could also lose or misapply highlighting during rapid typing, repeated navigation, thread opening, and forum navigation. **Solution:** Render match-focused previews with a shared yellow treatment, carry the result query through navigation, and apply it only to the clicked destination. The implementation binds highlights to the debounced result set, supports token and prefix matching, and handles channels, threads, forums, diffs, and wave messages consistently. <details> <summary>File changes</summary> **desktop/src/app/AppShell.tsx** Carries the active result query into search-hit navigation so the destination can preserve the user's context. **desktop/src/app/navigation/searchHitEventCache.ts** Adds a bounded, one-shot cache for result queries and navigation IDs alongside cached search-hit events. **desktop/src/app/navigation/searchHitNavigation.test.mjs** Covers query retention, one-shot consumption, repeated same-route activations, forum posts, forced routing, and cancellation. **desktop/src/app/navigation/searchHitNavigation.ts** Associates each search-result activation with a unique navigation ID and forwards it to channel or forum destinations. **desktop/src/app/navigation/useAppNavigation.ts** Extends channel and forum navigation to carry search navigation state without changing normal navigation behavior. **desktop/src/app/routes/ChannelRouteScreen.tsx** Consumes search highlight state after navigation and retains it for the selected message while clearing it on community context changes. **desktop/src/app/routes/channels.$channelId.posts.$postId.tsx** Validates and forwards forum search-navigation IDs. **desktop/src/app/routes/channels.$channelId.tsx** Validates and forwards channel search-navigation IDs. **desktop/src/features/channels/ui/ChannelPane.tsx** Passes the selected result ID and query into the main timeline and open thread panel. **desktop/src/features/channels/ui/ChannelPane.types.ts** Defines the destination-highlight contract for channel panes. **desktop/src/features/channels/ui/ChannelScreen.tsx** Routes destination highlighting to either forum content or the channel timeline. **desktop/src/features/channels/ui/ChannelScreen.types.ts** Defines route-level search-highlight inputs. **desktop/src/features/channels/ui/ForumChannelContent.tsx** Forwards search context into expanded forum threads. **desktop/src/features/forum/ui/ForumThreadPanel.tsx** Highlights the matching text in the selected forum post or reply. **desktop/src/features/forum/ui/ForumView.tsx** Carries selected-result context from forum routing into the thread panel. **desktop/src/features/messages/ui/DiffMessage.tsx** Forwards the query into diff descriptions and rendered diff content. **desktop/src/features/messages/ui/DiffViewer.tsx** Provides a highlighted match excerpt for structured diffs and highlights fallback raw diff text. **desktop/src/features/messages/ui/MessageRow.tsx** Passes destination queries into normal Markdown, diff, and wave message renderers. **desktop/src/features/messages/ui/MessageThreadPanel.tsx** Applies highlighting to the selected thread head or reply without tinting unrelated messages. **desktop/src/features/messages/ui/WaveMessageAttachment.tsx** Highlights matches in wave-message fallback text. **desktop/src/features/search/lib/searchMatch.test.mjs** Covers case-insensitive, literal, multi-term, prefix, one-character, and late-preview matches. **desktop/src/features/search/lib/searchMatch.ts** Centralizes token extraction, case-insensitive match splitting, and match-focused preview generation. **desktop/src/features/search/ui/HighlightedSearchText.tsx** Adds the reusable accessible mark renderer used by result and specialized message surfaces. **desktop/src/features/search/ui/TopbarSearch.tsx** Highlights result previews, centers excerpts around matches, strips search operators, and hides stale results during debounce transitions. **desktop/src/features/sidebar/ui/AppSidebar.types.ts** Updates the sidebar search callback contract to include the result query. **desktop/src/features/sidebar/ui/AppSidebarPinnedHeader.tsx** Passes query-aware result selection through the pinned search entry point. **desktop/src/shared/lib/rehypeSearchHighlight.ts** Uses the shared matcher to mark every matching Markdown text segment while leaving code blocks untouched. **desktop/src/shared/lib/searchHighlightStyle.ts** Defines one yellow highlight treatment for light and dark themes. **desktop/src/shared/ui/markdown/nodeCache.test.mjs** Verifies all Markdown matches, one-character scoped searches, code exclusion, and transient cache behavior. **desktop/src/shared/ui/markdown/nodeCache.ts** Enables transient highlighting for one-character scoped searches without polluting the Markdown cache. **desktop/tests/e2e/smoke.spec.ts** Exercises result and destination highlighting, same-route forum activation, and stale-query suppression end to end. </details> ## Reproduction steps 1. Open a channel containing a longer message and press Cmd+F. 2. Search for a word that appears later in the message, such as `mentions`. 3. Confirm each matching result shows the word in yellow and keeps the match visible in its preview. 4. Open a result and confirm the same term is highlighted in yellow in the destination message. 5. Repeat with a thread reply or forum post, and quickly change the query to confirm stale results are not selectable. ## Demo - **Before:** Search results returned the correct message but offered no visual indication of where the query matched; opening the message also showed no match treatment. - **After:** Every matching term is marked in yellow in the result preview and in the exact channel, thread, or forum message opened from that result. --------- Signed-off-by: tulsi <tulsi@block.xyz> |
||
|
|
9b6a637d01 |
fix(desktop): make lightbox zoom controls interactive (#6710)
## Summary - Replace the decorative lightbox zoom icons with accessible Zoom out and Zoom in buttons. - Route button clicks through the existing 1x–3x, 5% stepped zoom state without dismissing the viewer, including clicks on SVG icon descendants. - Extract the zoom toolbar so `markdown.tsx` remains below the repository file-size threshold. ### Testing - `pnpm build:e2e` - `pnpm exec playwright test image-attachment-gallery.spec.ts --project=smoke` — 10 passed - `pnpm typecheck` - Full desktop unit suite — 5,397 passed - Pre-push checks — file-size gate, desktop checks, typecheck, and desktop tests passed - `git diff --check` ### UI evidence The rendered lightbox flow is covered by the gallery smoke test: Zoom out is disabled at 100%, Zoom in changes the value to 105%, Zoom out returns it to 100%, and the dialog remains open throughout. Existing gallery, keyboard, spoiler, context-menu, and close-path assertions also pass. --------- Signed-off-by: sanic <c57bf9b4275088b2b33db7f746975407210f159fbd8bd733c0e532375f69aa80@buzz.block.builderlab.xyz> Signed-off-by: Kalvin Chau <kalvin@block.xyz> Co-authored-by: sanic <c57bf9b4275088b2b33db7f746975407210f159fbd8bd733c0e532375f69aa80@buzz.block.builderlab.xyz> Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz> |
||
|
|
d12dea4e67 | Support community deletion in versioned media buckets (#6738) | ||
|
|
bb5b9357a7 |
Fix TipTap editor mount race (#6779)
## Summary - wait for TipTap's mount lifecycle before reading the selection formatting tray's editor DOM - detach and reattach DOM listeners across editor view unmounts and remounts - cover mounted and pre-mount editor view access ## Testing - `just ci` - `pnpm build:e2e` - `pnpm exec playwright test tests/e2e/composer-selection-formatting.spec.ts --project=smoke --grep "right-clicking selected composer text"` - Native Builderlab staging: reproduced the pre-mount crash on main, relaunched with the fix, and verified the composer mounts without the TipTap error Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
a1219070fa |
feat(buzz-agent): gate LLM tool calls on session/request_permission (#5712)
## Summary
buzz-agent now authorizes every LLM-issued MCP tool call through
`session/request_permission` before it executes, instead of running
tools unconditionally. The agent **always asks**; the client applies
`BUZZ_ACP_PERMISSION_POLICY` and answers. buzz-agent never reads the
policy — this keeps the policy decision on the client side, matching the
layering of the other ACP harnesses, and avoids duplicating policy logic
that would drift.
### Broker
A crate-local `PermissionBroker`
(`crates/buzz-agent/src/permission.rs`), owned by `App` for the
connection lifetime, owns the full request-correlation lifecycle:
- **Process-wide admission.** A global `Semaphore`
(`BUZZ_AGENT_MAX_PENDING_PERMISSIONS`, default 32, validated `>= 1`) is
acquired *before* any correlation entry is inserted. This is the
security bound: the per-turn tool semaphore is constructed fresh per
turn and `max_sessions` is unbounded by default, so neither bounds
simultaneously-outstanding asks process-wide.
- **Abort-safe cleanup.** A successful admission returns a
`PendingPermission` lease that owns the admission permit and the
correlation id. Its `Drop` synchronously removes the still-pending entry
and releases the slot, covering task abort/panic that bypasses the
normal `run_prompt` tail.
- **At-most-once resolution.** `deliver` claims (removes) the entry
*before* waking the waiter, so each id resolves once and a later lease
`Drop` is a no-op. Unknown/late ids are logged and dropped; only ids the
broker minted (`perm-<n>`) are recognized.
- **Single absolute deadline.** Admission, request enqueue, and response
wait all share one deadline (`BUZZ_AGENT_PERMISSION_TIMEOUT_SECS`,
default 330s, validated `>= 1`), so a saturated call cannot outlive one
timeout window even when a stalled writer blocks the enqueue.
Cancellation races inside every wait — resolution never depends on the
outer abort drain. A writer that dies mid-connection (stdout closed, or
a blocking write that only surfaces its error at flush) is
connection-fatal: it cancels all sessions, which resolves any ask
waiting on a reply that can never be written.
### Wire
`request_permission_params` (`crates/buzz-agent/src/wire.rs`) is
version-aware, keyed on the protocol version negotiated at `initialize`
and stored on `App` for the connection lifetime (never derived from a
later mutable session field). v2 nests the tool call under `subject:
{type: "tool_call", toolCall}` with top-level `title`/`options`; v1 uses
the legacy top-level `toolCall`. No hybrid shape. Both offered options
(`allow_once`, `reject_once`) carry `optionId == kind`, so the client's
`kind`-based selection and this side's `optionId`-based predicate agree
without a lookup table.
### Gate
In each spawned tool task (`crates/buzz-agent/src/agent.rs`) the
sequence is: acquire per-turn permit → argument-shape validation →
broker admission + request + wait → cancellation recheck →
`emit_in_progress` → `mcp.call`. Argument-shape validation is hoisted
out of `mcp.rs::do_call` into `validate_arg_shape` so a malformed
non-object argument is rejected locally without prompting for a call
that could never execute.
Authorization is fail-closed, stated once in `evaluate`: execute IFF
`outcome.outcome == "selected"` **and** the selected `optionId` equals
the offered allow option. Every other shape (reject, cancelled, JSON-RPC
error, malformed, unknown outcome, wrong/unknown option, timeout,
wire-channel closure) denies with a synthetic tool error, and the turn
continues.
### Scope
Only LLM-issued MCP calls are gated. The built-in `load_skill` tool and
`call_hooks` lifecycle calls (`_Stop`, `_PostCompact`) are exempt — they
are not model-issued. `readOnlyHint` is never treated as a security
boundary.
First cut ships `allow_once`/`reject_once` only; session-scoped grants
are deliberately out of scope.
### Related issue
Part of #4938. This PR and
[#5106](https://github.com/block/buzz/pull/5106) jointly implement the
feature: #5106 is the client-side policy engine and permission cards;
this PR is buzz-agent's asking side (`session/request_permission`).
Neither closes #4938 alone.
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Hayt <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
|
||
|
|
931747c9c4 |
Add staging dev relay image workflow (#6709)
## Summary Adds a manual, collaborator-triggered workflow for publishing pre-merge Buzz relay runtime images for bb-block staging. - Resolves a canonical `block/buzz` branch or tag to an immutable commit SHA before checkout. - Builds the relay runtime image for `linux/amd64` and `linux/arm64` and publishes a single OCI index. - Tags each publication uniquely as `dev-sha-<full SHA>-run-<run_id>-<run_attempt>` so no tag is ever reused against the immutable-tag utility ECR pull-through cache (rebuilds of the same SHA aren't byte-identical, given mutable base tags and `apt-get update`). - Uses the staging-only `ghcr.io/block/buzz-staging-dev` namespace, which maps to a distinct utility ECR pull-through path. - Restricts dispatch to `block/buzz` on `refs/heads/main`; job permissions are narrowly scoped to `contents: read` plus `packages: write`. - Emits a deployment summary with the exact BPCI `repository` and unique `tag` values, plus the merged manifest digest. - Keeps the existing production/release image workflow unchanged. The first real workflow run must confirm GHCR package creation/access and utility ECR pull-through import for the new package. --------- Signed-off-by: Brad Seiler <seiler@squareup.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: tornquist <tornquist@squareup.com> Co-authored-by: coder 0 <d97ebdbb198c7237c94f84ea8bb8a73583ea067407eebd0062abbb3962527fb1@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: tornquist <tornquist@squareup.com> |
||
|
|
9d1e4b2576 |
Extract community persistence (#6668)
## Why Community persistence is the next incremental `buzz-db` store extraction, keeping tenant lifecycle SQL, records, tests, and instrumentation out of the database runtime module without changing behavior. ## What - Move community records and the existing `impl Db` operations into `community.rs` while preserving crate-root re-exports. - Move focused PostgreSQL tests with the implementation and enforce single ownership for each method and datastore span. ## Risk Assessment Low — this is a structural move of the existing records, SQL, method bodies, and focused tests; database runtime concerns, schema, and behavior remain unchanged. ## References - Architecture guardrail: https://github.com/TheSentinel454/buzz/issues/34 - Incremental tracker: https://github.com/TheSentinel454/buzz/issues/2 - Primary task: https://github.com/TheSentinel454/buzz/issues/5 - Stacked on: https://github.com/block/buzz/pull/6660 Generated with Codex Signed-off-by: tornquist <tornquist@squareup.com>mobile-v0.14.0-rc.5 mobile-v0.15.0-rc.1 |
||
|
|
8b812017a7 |
Fix mobile Huddle agent voice turn states (#6611)
## Summary - ignore mobile room tone when deciding whether human speech should interrupt agent audio - show a subtle waiting animation while an agent prepares a response, then restore its avatar when speech begins - render agents from Huddle membership before their audio peer starts transmitting ## Testing - `just ci` - signed Release build installed and launched on a physical iPhone - live Huddle voice and agent-membership behavior verified on device --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz> |
||
|
|
9aa332af03 |
Add inline profile camera capture (#6680)
## Summary - add inline profile photo capture with smooth avatar-to-viewfinder transitions - add close, flip, shutter, retry, and use-photo states with haptics and front-camera mirroring - polish iOS liquid-glass controls and avatar editor motion ### Related issue Follow-up to #6583. ### Testing - just mobile-check - just mobile-test (1,743 tests) - built, installed, and launched on Pixel 10 and iPhone Air --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Code Reviewer <037593536284cf40e221c96c931e9877d4166d54f6bb84e5341a86d7fd5d05a4@buzz.block.builderlab.xyz> Signed-off-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz> Signed-off-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz> Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Signed-off-by: Meli <5aaa86bce934fc3445fc254aab560a40923f10252f92107e665073dede0e04d3@buzz.block.builderlab.xyz> Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> Co-authored-by: Code Reviewer <037593536284cf40e221c96c931e9877d4166d54f6bb84e5341a86d7fd5d05a4@buzz.block.builderlab.xyz> Co-authored-by: Tyler <109685178+tlongwell-block@users.noreply.github.com> Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz> Co-authored-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz> Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Co-authored-by: Meli <5aaa86bce934fc3445fc254aab560a40923f10252f92107e665073dede0e04d3@buzz.block.builderlab.xyz> Co-authored-by: morgmart <98432065+morgmart@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
822c5ab231 |
Hide Huddles in mobile agent DMs (#6676)
## Summary - hide the mobile Huddle action in one-to-one agent DMs - preserve Huddles for human DMs and group DMs ## Testing - just mobile-check - flutter test (1,662 tests) --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> |
||
|
|
a8e1c66c4a |
fix(desktop): polish inline chip states (#6718)
**Category:** fix **User Impact:** Inline chips now read more consistently, fit cleanly in the composer, and show deleted message links with the same calm muted treatment as unresolved links. **Problem:** Deleted message links looked like destructive actions even though they are informational, while mention chips had slightly uneven vertical spacing, a high-set human icon, and could clip inside the composer. **Solution:** Unify unavailable-state styling, tighten chip spacing, optically align the human icon, and give composer chips enough line height to paint without changing caret behavior. <details> <summary>File changes</summary> **desktop/src/shared/styles/globals/markdown.css** Makes chip padding vertically symmetric, moves only the human `@` icon down by 1px, and shares muted colors between deleted and unresolved message links while preserving their separate semantic classes. **desktop/src/shared/styles/globals/composer.css** Adds a composer-only line height derived from the text size and chip padding so inline chips no longer clip while retaining inline caret behavior. **desktop/tests/e2e/entity-link-recipient-cards.spec.ts** Verifies deleted and unresolved chips have matching computed colors while deleted links retain their tooltip, semantics, and navigation behavior. **desktop/tests/e2e/mentions.spec.ts** Covers composer chip height, clipping, the human icon's 1px optical offset, and visual capture. </details> ## Reproduction steps 1. Open a channel containing a link to a definitively deleted message and compare it with a transient or unresolved message link; both should use the muted unavailable treatment, while the deleted link still says “Message deleted” and navigates to its fallback destination. 2. Insert a person mention in the composer; the chip should have balanced vertical spacing and paint fully without clipping. 3. Compare a person mention with a channel chip; only the human `@` icon should sit 1px lower. ## Screenshots ### Deleted message link | Before | After | | --- | --- | |  |  | ### Composer chip polish | Before | After | | --- | --- | |  |  | ## Validation - `desktop/tests/e2e/entity-link-recipient-cards.spec.ts` + `desktop/tests/e2e/mentions.spec.ts`: 82/82 passed - Desktop unit suite: 5,397 passed - Formatting and lint checks passed (existing informational warnings only) - Pre-push desktop check, typecheck, and unit tests passed at `0255c3fd49f9244cd727d0dd20c514b3a1812152` --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> |
||
|
|
8d2d0ff5ad |
Centralize replaceable event persistence (#6660)
## Why Command persistence duplicated NIP-33 replacement SQL in the relay and obscured the boundary between database runtime concerns and domain-store behavior. This proving slice establishes that boundary inside the existing `buzz-db` crate. ## What - Centralize parameterized-replaceable coordinate locking, ordering, replacement, watermark, and mention-indexing behavior in `buzz-db` - Expose transaction-required replacement and ordinary-insertion seams while keeping transaction ownership and workflow conflict messages in the relay - Cover concurrency, same-second ties, stale writes, replay, caller rollback, nested rollback recovery, and mention-index atomicity with focused PostgreSQL tests ## Risk Assessment Medium — this changes core event persistence and command idempotency paths, while intentionally preserving NIP-33, NIP-RS, mesh, and workflow conflict semantics. ## References - Architecture guardrail: https://github.com/TheSentinel454/buzz/issues/34 - Proving slice: https://github.com/TheSentinel454/buzz/issues/3, https://github.com/TheSentinel454/buzz/issues/4, https://github.com/TheSentinel454/buzz/issues/6 Generated with Codex --------- Signed-off-by: tornquist <tornquist@squareup.com> |
||
|
|
e760c51820 |
feat(workflows): discover trigger filter values (#6712)
**Category:** improvement **User Impact:** Workflow authors can discover people and messages while configuring trigger filters, then see readable enriched labels instead of raw identifiers. **Problem:** Author and message filters required users to know and paste raw public keys or event IDs, and configured workflows surfaced those opaque values afterward. **Solution:** Add network-backed pickers and presentation enrichment while keeping deterministic local public-key and event-ID fallbacks authoritative whenever discovery is unavailable or untrusted. Related issue: none found. <details> <summary>File changes</summary> **desktop/src/features/workflows/ui/WorkflowAuthorPicker.tsx** Adds channel-aware author discovery, profile search, keyboard navigation, loading states, and deterministic public-key fallback selection. **desktop/src/features/workflows/ui/WorkflowCard.tsx** Uses enriched trigger presentation when building the workflow card’s readable summary. **desktop/src/features/workflows/ui/WorkflowDialog.tsx** Keeps Escape scoped to an active filter picker before allowing the inspector or dialog to close. **desktop/src/features/workflows/ui/WorkflowFormBuilder.tsx** Threads channel context into trigger filters and renders enriched author/message summaries in the workflow sequence. **desktop/src/features/workflows/ui/WorkflowMessagePicker.tsx** Adds paged channel-history discovery, message search, exact event lookup, profile labels, keyboard navigation, and bounded results. **desktop/src/features/workflows/ui/WorkflowRichTriggerDescription.tsx** Renders compact author identity details and loading presentation inside trigger summaries. **desktop/src/features/workflows/ui/WorkflowTriggerConditions.tsx** Connects author and message filter accordions to their pickers while preserving selected and excluded condition semantics. **desktop/src/features/workflows/ui/useWorkflowAuthorPresentation.ts** Resolves configured author keys to trusted display labels with deterministic fallbacks. **desktop/src/features/workflows/ui/useWorkflowTriggerPresentation.ts** Enriches configured message IDs only after validating the fetched event and channel. **desktop/src/features/workflows/ui/workflowAuthorCandidates.test.mjs** Covers author candidate normalization, ordering, deduplication, and fallback behavior. **desktop/src/features/workflows/ui/workflowAuthorCandidates.ts** Builds stable author candidates from channel members, profiles, and raw public keys. **desktop/src/features/workflows/ui/workflowConditionExpression.ts** Allows message IDs to participate in basic trigger-filter parsing. **desktop/src/features/workflows/ui/workflowDefinition.ts** Accepts enriched trigger text when generating workflow card labels. **desktop/src/features/workflows/ui/workflowMessageCandidates.test.mjs** Covers event validation, source merging, deterministic ordering, and exact-lookup enrichment boundaries. **desktop/src/features/workflows/ui/workflowMessageCandidates.ts** Validates message candidates by event kind, channel, and exact event ID before permitting enrichment. **desktop/src/features/workflows/ui/workflowTriggerDescription.test.mjs** Covers readable selected/excluded author and message descriptions plus loading fallbacks. **desktop/src/features/workflows/ui/workflowTriggerDescription.ts** Builds concise enriched trigger descriptions while retaining stable raw-ID fallbacks. **desktop/tests/e2e/workflow-local-controls.spec.ts** Exercises picker discovery, selection toggles, Escape ownership, bounded scrolling, and enriched workflow summaries. </details> ## Reproduction steps 1. Open Workflows and create a workflow for a channel with members and message history. 2. Choose **Reaction Added** as the trigger and expand **Author**. 3. Confirm channel members and fetched profile results are discoverable, searchable, and keyboard accessible; choose one. 4. Expand **Message**, confirm recent channel messages appear in a bounded list, and choose one. 5. Toggle either selected filter between **is** and **is not**, then collapse the inspector and confirm the sequence summary stays readable. 6. Add a send-message step and create the workflow; confirm its card uses the resolved author and message labels. 7. Repeat while discovery is unavailable and confirm raw public keys/event IDs remain selectable and authoritative. ## Screenshots ### Author discovery  ### Message discovery  ### Selected filter summaries  ### Enriched workflow card  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <0366ccd5ee09c2779a9d6bd6683daa17c16a508a51f6a7e7314018dab8fdc49b@buzz.block.builderlab.xyz> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> |
||
|
|
c5166f2164 |
feat(desktop): simplify the message action rail (#6529)
## Problem Quick-reaction shortcuts make the message action rail visually noisy. Reaction access should remain first-class, but through one predictable Add reaction action rather than several learned emoji shortcuts. ## Change The rail is now: **Add reaction · Reply · Copy link · More** - Remove all quick-reaction shortcut buttons and their divider from the message action rail. - Preserve Add reaction as the first action, including its existing picker, tooltip, accessible name, keyboard behavior, reaction behavior, and feedback. - Keep the existing Link2 Copy link action, shared copy handler, More-menu entry, eligibility guards, and success feedback unchanged. - Keep reveal, positioning, responsiveness, styling, and remaining menu paths unchanged. The now-unused quick-reaction rendering component and imports were removed from `MessageActionBar`; shared reaction learning remains intact for other reaction surfaces. ## Tests Focused smoke coverage verifies: - zero `React with …` shortcut buttons; - exact ordered rail: `Open reactions → Reply → Copy link → More actions`; - the rail and More-menu paths emit the same canonical thread-aware `buzz://message` URL; - existing success feedback; - pending and huddle rows omit both copy-link surfaces; - the action bar stays within the open thread panel. The zero-shortcut contract was mutation-checked by restoring the prior production action bar: the focused test failed causally with expected 0 versus received 3 quick-reaction buttons. ## Validation At `4f062e0d60b0f0c16b6862cdea7137c287060947`: - `pnpm exec biome check src/features/messages/ui/MessageActionBar.tsx tests/e2e/message-copy-link.spec.ts` — passed. - `pnpm exec tsc --noEmit` — passed. - `pnpm test` — 5,432 passed, 0 failed. - `pnpm build:e2e` — passed; existing dynamic-import and chunk-size warnings only. - `pnpm exec playwright test tests/e2e/message-copy-link.spec.ts --project=smoke` — 2 passed. - Pre-push `file-size-check`, `desktop-check`, `desktop-typecheck`, and `desktop-test` — passed. Vogue’s design review: **SHIP** — reaction remains discoverable and accessible as the visible first action; the extra click is an intentional efficiency tradeoff for the simpler hierarchy. --------- Signed-off-by: Trace (Engineer) <9d485ff0c62915e08a801162c195c7ef096f6f6143925407255cea2656c4b8a4@buzz.block.builderlab.xyz> Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> Signed-off-by: Rivet <a08d9a8418c7ff03afe19964724c8fd87bf1776ab9e9b9cafb8cc920edd02a6e@buzz.block.builderlab.xyz> Co-authored-by: Trace (Engineer) <9d485ff0c62915e08a801162c195c7ef096f6f6143925407255cea2656c4b8a4@buzz.block.builderlab.xyz> Co-authored-by: Rivet <a08d9a8418c7ff03afe19964724c8fd87bf1776ab9e9b9cafb8cc920edd02a6e@buzz.block.builderlab.xyz> |
||
|
|
30d2fc52f9 |
fix(desktop): restore icon-only remote marker (#6491)
## Summary - restore the agreed icon-only cloud marker for an agent from another setup - retain the accessible `From another Buzz setup` label and native hover title - keep the merged loading, hover/focus persistence, and exact-pubkey routing safeguards unchanged - update the duplicate-agent E2E to require an icon with no visible marker text in both autocomplete and Channel members ## Why PR #6401 accidentally changed the agreed compact icon treatment into a visible `Other setup` badge during review hardening. This is the smallest correction and is intended for the active release. ## Testing - Desktop JS: 5,280/5,280 passed - Desktop TypeScript typecheck passed - E2E build passed - focused duplicate-agent Playwright journey: 1/1 passed - file-size gate passed - changed-file Biome and `git diff --check` passed Carl, an automated reviewer, opened this via Wes's GitHub account. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> |
||
|
|
69b1225923 |
fix(ci): prevent poisoned Rust caches (#6618)
## Summary - roll every `Swatinem/rust-cache` use back from v2.9.2 to the last known-good v2.9.1 digest - give Unit Tests a new `sherpa-cache-v1` key so it cannot restore the existing poisoned artifact - pin Renovate to v2.9.1 and add CI contracts that reject unsafe cache actions or a misplaced generation key ## Why After #5441 upgraded rust-cache to v2.9.2, warm-cache `main` Unit Tests runs began failing while linking `buzz-voice` with `could not find native static library sherpa-onnx-c-api`. The failed run at `db5617dd1` restored the same 1.4 KB cache generation that had already failed at `01091c15a`; the preceding cold run at `26f4c3ed3` downloaded sherpa 1.13.4 and passed. v2.9.2 changed target cleanup, while `sherpa-onnx-sys` treats its prebuilt `lib/` directory as proof that the native archive exists. Rolling back the action and invalidating the affected key removes both sides of that failure state without disabling target caching. ## Validation At `6da0037a0407fc498cd482fcbbb74c7a15907e9f`: - `scripts/test-rust-cache-contract.sh` - `scripts/test-rust-cache-contract-regressions.sh` - negative fixtures reject a bad digest in a newly named `.yaml` workflow and a generation key moved outside the cache action's `with` block - YAML parse for all workflows - release, desktop candidate, mobile release, mobile candidate, and mobile worktree source contracts - `just file-size-check` - pre-commit and pre-push hooks The PR Unit Tests run proves the cold-cache path because pull requests restore but do not save Rust caches. The first successful `main` run after merge will save the new Unit Tests key; the following `main` run will exercise the warm restore. ## Related issue None found. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> |
||
|
|
02dc49f0e6 |
docs(security): route reports through private advisories (#6728)
Routes security researchers to GitHub's private vulnerability reporting workflow instead of a public issue or email-first disclosure. - Makes the private advisory form the primary path in `SECURITY.md`, with email retained as a fallback. - Adds private-reporting links to the contributor guide, issue chooser, and bug template. Checked with `git diff --check`, Ruby YAML parsing, and confirmation that private vulnerability reporting is enabled for `block/buzz`. Signed-off-by: Jordan Mecom <jm@squareup.com> |
||
|
|
f79d346a17 |
fix(composer): wrap Buzz chip labels without orphaning icons (#6581)
**Category:** fix **User Impact:** Long Buzz link chips now wrap without overflowing in composers and sent messages; icon-bearing stubs use at most five graphemes when no earlier separator exists, so some sent chips attach the icon to a shorter prefix than before. Labels over 48 graphemes are visibly truncated in the composer while their full identity remains available to assistive technology and in the tooltip. **Problem:** Long repository, issue, pull request, and channel chip labels could orphan their icon or overflow narrow composers and sent messages. **Solution:** Keep the icon with a bounded, grapheme-safe leading fragment while allowing the remaining text to break anywhere; cap visible labels at 48 graphemes without changing the full tooltip or accessible identity. <details> <summary>File changes</summary> **desktop/src/features/messages/lib/composerMessageLinkNode.ts** Splits composer chip content into an icon-bearing leading fragment and a freely wrapping remainder while preserving the semantic label and link metadata. **desktop/src/features/messages/lib/composerMessageLinkNode.test.mjs** Updates renderer assertions for the fragment structure and verifies every supported Buzz link kind retains the intended visible label. **desktop/src/shared/styles/globals/composer.css** Keeps ordinary composer mention decorations inline while relying on the existing shared markdown chip wrapping rules for Buzz links. **desktop/src/shared/ui/mentionChip.ts** Centralizes grapheme-aware leading-fragment boundaries and label truncation so composer and sent chips share the same visible identity. **desktop/src/shared/ui/markdown/BuzzLinkChip.tsx** Uses the shared grapheme-aware boundary when rendering sent-message chip fragments. **desktop/tests/e2e/navigation.spec.ts** Covers increasing wrap depth across constrained widths, icon attachment, the sent-message wrap, accessible labeling, and tooltip positioning over both edge fragments. </details> ## Reproduction steps 1. Open a desktop channel and paste a Buzz link with a long repository or channel name into the composer. 2. Narrow the composer until the chip spans two or more lines. 3. Confirm the label breaks mid-string while the icon remains attached to the first label fragment. 4. Send the message, hover both the first and last rendered fragments, and confirm the tooltip follows the hovered fragment. ## Screenshots **Before — the icon drops onto a separate line from its chip label**  **After — the icon stays attached while the remaining label wraps** The same long repository chip at three composer widths. Its label gains line breaks as space contracts, while the icon remains attached to the leading fragment. **420px — one line**  **210px — two lines**  **150px — three lines**  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
f6e6617a9d |
fix(desktop): bound thread /query and surface load errors, not false-empty (#6447)
Long threads in the desktop app sometimes never load (stuck on a
skeleton until you close and reopen the panel), and a failed load
silently renders as "No replies in this branch yet" — presenting a
broken fetch as an authoritative empty thread with no way to recover.
This fixes both, the two IMPORTANT findings from the thread-load
investigation.
## Defect 1 — unbounded `/query` request
The shared `reqwest::Client` in `relay.rs` sets no timeout, and neither
`/query` request builder set a per-request `.timeout(...)`. A stalled or
half-open connection (headers or body never arrive) leaves the request
pending forever, so a thread-history load hangs on the skeleton
indefinitely.
Fix: a 30s per-request deadline on both `/query` builders, funnelled
through one `send_query_request` helper so the timeout can never be
applied to one builder and dropped from the other. Scoped per-request
rather than client-level because the same client also serves STT/TTS
model downloads, builderlab auth, and the media proxy — a client-level
timeout would cut those off. The deadline sits above the 25s WS
`HISTORY_TIMEOUT_MS` so a slow-but-live relay isn't cut off before the
WebSocket path would be. A timeout surfaces through
`classify_request_error` as the stable `"relay unreachable: request
timed out"` string.
`send()` resolves as soon as response headers arrive, so a relay that
returns headers and then stalls the body trips the deadline during body
consumption, not at `send()` — and that consumption happens on two
paths: `parse_json_response` for 2xx, and `relay_error_message` for a
non-success status (500/429/…). Both paths route their body-consumption
error through one shared `classify_body_timeout` helper so they can't
drift: a stalled body surfaces the stable `"relay unreachable: request
timed out"` string on either path rather than the malformed-response
bucket (2xx) or a bare `"relay returned 500"` status label (non-2xx). A
genuinely non-stalled error still keeps its status classification.
## Defect 2 — terminal error painted as empty
`ChannelScreen` consumed only `isPending`/`data` from the thread-replies
query. Once React Query exhausted its one retry, `isPending` was false
and the zero-length data fell through `selectDeferredListRenderState` to
the `"empty"` state — indistinguishable from a genuinely empty branch,
with no retry affordance.
Fix: plumb `isError` + `refetch` through `ChannelScreen` → `ChannelPane`
→ `MessageThreadPanel`. A pure `selectThreadRepliesSurface` helper
decides the paint in strict precedence — the load-bearing invariant is
that a terminal error **never** resolves to `"empty"`, and cached
replies stay visible non-destructively under a later error (the error
card only surfaces when there is nothing to show). The panel renders an
explicit "Couldn't load replies" + Retry card (testids
`message-thread-replies-error` / `message-thread-replies-retry`).
`ProjectConversationPanel` is a second producer of the same shared panel
and used to hard-code `threadRepliesPending={false}` with no
error/retry, so a failed load in a Projects conversation still painted
the false-empty. It now propagates the same
`isPending`/`isError`/`refetch` from its `useThreadReplies` query.
The multi-root `useThreadRepliesForRoots` hook (the Huddle transcript
and Projects-agent conversation surfaces) had the same gap in its
`useQueries` `combine`: it returned only `{ events, isPending }`, so a
failed reply subtree contributed zero rows and vanished. The combine is
now a pure, unit-testable `combineThreadRepliesResults` that exposes
aggregate `isError`/`error` plus a `refetch` that re-runs only the
failed subtrees. Both multi-root consumers render the shared "Couldn't
load replies" + Retry card when a subtree fails: the Projects-agent
conversation after its transcript, and the Huddle transcript as a
non-destructive banner above the timeline. `useHuddleChannelMessages`
used to read only `.events` and discard the aggregate state, so one
summarized root failing left the flattened transcript presenting as
complete; it now propagates `threadRepliesError`/`onRetryThreadReplies`
through `ChannelScreen` into `ChannelPane`, where successful rows stay
visible and `onRetry` re-runs only the failed subtrees.
The error card carries `role="alert"` so its asynchronous appearance is
announced to assistive tech — without a live region a screen-reader user
parked in the composer never learns the load failed or that Retry became
available.
## Tests
- `stalled_query_request_times_out_with_classified_error` — a loopback
server that never responds; asserts the stable classified timeout
string.
- `stalled_response_body_times_out_with_classified_error` — a loopback
server that writes valid 2xx JSON headers then stalls the body past the
deadline; asserts the classified timeout string, not the malformed
bucket.
- `stalled_error_response_body_times_out_with_classified_error` — a
loopback that writes `500` headers promising a body it never sends;
asserts the classified timeout string rather than the `500` status
label.
- `non_stalled_error_response_yields_status_message` — a promptly-served
`500` still surfaces `"relay returned 500 Internal Server Error"`,
pinning that timeout preservation is scoped to actual timeouts.
- `selectThreadRepliesSurface` — pending→skeleton, terminal error→error
(never empty), page-2 failure never empty, cached rows stay visible
under error, successful-empty→empty, retry-success→list,
streaming→pending, and huddle-transcript collapse.
- `MessageThreadReplyState` mounted test — terminal error renders the
error card (asserting `role="alert"`), never the empty card.
- `combineThreadRepliesResults` — multi-root aggregation/order, a failed
subtree surfaces the aggregate error and never drops rows, aggregate
pending, refetch re-runs only failed queries, all-success yields no
error.
- `thread-load-failure.spec.ts` (smoke E2E) — binds the real
channel-thread panel wiring: forces a terminal `get_thread_replies`
failure at the IPC boundary, asserts the error card renders (never the
false-empty) and Retry recovers.
- `project-conversation-load-failure.spec.ts` (smoke E2E) — the same
guard for the Projects conversation producer, driven through the
Projects Channels-tab row.
- `huddle-thread-load-failure.spec.ts` (smoke E2E) — the consumer-level
guard the combine unit test can't provide: drives the real Huddle wiring
(`useHuddleChannelMessages` → `ChannelScreen` → `ChannelPane`) with two
summarized roots, fails one subtree's fetch at the IPC boundary, asserts
the surviving root's reply stays visible while the retry alert surfaces,
then Retry recovers the failed subtree and clears the alert.
## Structure
To stay under the desktop file-size ratchet, `relay.rs`'s inline test
module moved to `relay/tests.rs`, and two pure pieces were extracted
from the panel: the empty/error reply cards (`MessageThreadReplyState`)
and the per-row branch-highlight derivation
(`selectThreadRowHighlight`).
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
mobile-v0.14.0-rc.4
|
||
|
|
4bf80978f5 |
fix(messages): route edits to the owning composer (#6575)
**Category:** fix **User Impact:** Editing a channel message now opens in the main composer, while editing a thread reply stays in the thread composer, with focus ready for typing. **Problem:** When a thread was open, Buzz treated its root message as thread-owned and opened edits in the thread composer. Menu-driven edits also lacked regression coverage for immediate focus. **Solution:** Carry the message's semantic root/reply classification into the edit target, route only actual replies to the thread composer, and use the menu primitive's selection event for a reliable handoff. End-to-end tests cover placement and focus for both paths. <details> <summary>File changes</summary> **desktop/src/features/channels/ui/ChannelPane.tsx** Routes edit targets by semantic thread ownership rather than membership in the open thread panel. **desktop/src/features/channels/ui/ChannelPane.types.ts** Uses the shared composer edit-target type so routing metadata stays attached to the target. **desktop/src/features/messages/lib/draftMentionRefs.ts** Classifies each edit target as a root or true thread reply from its event tags. **desktop/src/features/messages/lib/draftMentionRefs.test.mjs** Covers semantic ownership for root and reply edit targets. **desktop/src/features/messages/ui/MessageActionBar.tsx** Handles Edit through the dropdown menu's selection event so focus restoration and edit startup share the intended lifecycle. **desktop/src/features/messages/ui/MessageComposer.types.ts** Adds semantic thread ownership to the edit-target contract. **desktop/tests/e2e/messaging.spec.ts** Verifies root edits use and focus the main composer, while reply edits use and focus the thread composer. </details> ### Reproduction Steps 1. Send a channel message and open its thread. 2. From the thread panel, edit the root message; confirm its content loads in the main composer and the editor is focused. 3. Send a reply in that thread. 4. Edit the reply; confirm its content loads in the thread composer and the editor is focused. ### Screenshots **Editing a channel-root message uses the main composer**  **Editing an actual thread reply uses the thread composer**  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
6eff84d127 |
fix(mobile): join starter channels after accepting invite (#5915)
### What changed? After a new mobile invite claim succeeds, Buzz now best-effort ensures membership in the same public starter channels as desktop: - `#general` - `#welcome-everyone` Missing starters use desktop's deterministic per-relay IDs and exact public channel configuration, so concurrent mobile and desktop setup converges safely. Setup failures do not invalidate or retry an already successful invite claim, and failure for one starter does not block the other. The success sheet offers **Continue to #welcome-everyone** when that channel is available. Mobile does not create the private `Welcome` channel because it cannot provision the desktop Welcome agents that make that channel useful. This PR is stacked on #6145 because it deliberately reuses that PR's open-channel directory and join behavior. Once #6145 merges, this PR can be retargeted to `main` without changing its BUZZ-12 diff. Fixes [BUZZ-12](https://linear.app/squareup/issue/BUZZ-12/bug-community-appears-empty-after-using-invite-link-on-mobile). ### How is it tested? - Desktop/mobile deterministic starter-ID parity coverage. - Existing-channel join and missing-channel creation coverage. - Duplicate-create convergence and per-channel failure isolation coverage. - Invite success remains successful when starter setup fails. - Widget coverage for continuing directly into `#welcome-everyone`. - Focused invite/deep-link tests: 23 passed. - `just mobile-check`: passed. - `just mobile-test`: 1,483 passed. - Pre-push repository checks: passed. --------- Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Codex <noreply@openai.com>mobile-v0.14.0-rc.3 |
||
|
|
a0298539f7 |
Add mobile profile editing (#6583)
## Summary - add mobile editing for display name, profile description, and profile photo - support image positioning, emoji backgrounds, and animated avatar capture with native iOS controls - refine settings navigation, profile motion, and the connection identity row ## Testing - `just mobile-check` - `just mobile-test` (1,685 tests) --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> |
||
|
|
9f55bf6745 |
fix(desktop): align jump-to-latest pill with composer height (#6606)
**Category:** fix **User Impact:** Jump to Latest now stays above the composer as a draft grows to multiple lines. **Problem:** On WebKit, the pill's transform could retain a stale inherited composer-height value after the composer expanded, leaving the control stranded inside the composer. **Solution:** Position and animate the pill with its absolute bottom offset, which consumes the live composer height through layout rather than a promoted transform layer. A smoke test now verifies that the pill rises by the full composer growth and remains clear of the composer. <details> <summary>File changes</summary> **desktop/src/features/messages/ui/MessageTimeline.tsx** Anchor Jump to Latest with a live bottom offset instead of a translated compositor layer so composer resizing reliably moves it. **desktop/tests/e2e/smoke.spec.ts** Add coverage that expands a detached timeline's composer and checks the pill tracks the full height increase without overlapping it. </details> ## Reproduction steps 1. Open a channel with enough messages to scroll. 2. Scroll away from the newest message until Jump to Latest appears. 3. Add several lines to the composer without sending. 4. Confirm Jump to Latest rises with the composer and remains directly above it. ## Validation - `pnpm --dir desktop test` — 5,397 passed - `pnpm --dir desktop check` — passed with four existing informational warnings outside this diff - `pnpm --dir desktop typecheck` — passed - `pnpm --dir desktop exec playwright test tests/e2e/smoke.spec.ts --project=smoke` — 26 passed - Push hooks — desktop check, typecheck, and tests passed ## Screenshots / Demos Both captures use the same long, mid-history timeline and the same four-line composer. | Before | After | | --- | --- | | The stale pill position overflows into the expanded composer. | The pill tracks the live composer height and stays clear above it. | |  |  | Signed-off-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> |
||
|
|
db5617dd15 |
fix(desktop): emit singular mention feed category so alerts route correctly (#6665)
## Problem Mentions — and thread replies that @-mention you — played the **Needs action** sound instead of the **@Mentions** sound. Reported by @morgmart: "I set Needs action to a different sound and it's the only one I ever hear." ## Root cause Two vocabularies got conflated in #475: - **Section / filter vocabulary** (plural): `mentions`, `needs_action`, `activity`, `agent_activity` — the shape of `FeedSections`, the `--types` filter, and the agent-facing CLI docs. - **Per-item category vocabulary** (singular for mention): `mention`, `needs_action`, `activity`, `agent_activity` — the `FeedItemCategory` contract in `desktop/src/shared/api/types.ts`, unchanged since #12. The Tauri feed builder reused the filter string `"mentions"` as each mention item's `category`. Only one word differs between the vocabularies, so only mentions broke. Every frontend consumer compares against the singular, so real mentions never matched and fell through to the resolver's `needs_action` fallback. The E2E mock bridge emits the singular form, so tests never saw the drift. ### Symptoms this fixes (all from the one mislabel) - Mentions and mentioning thread replies played the Needs-action sound - Mention notifications used the Needs-action title format - Mentions in muted channels were suppressed (the mute-bypass never fired) - Inbox / Home feed labelled mentions "Channel update" - Channel activity popover's mentions list was always empty ## Fix **Fix the owner, not the symptoms.** `FeedItemInfo.category` becomes a `FeedItemCategory` enum whose serde form is exactly the TS union, so a misspelled category can't compile at the producer. A serialization test pins each variant to its wire string. **Frontend:** `slotForFeedKind` maps every known category explicitly. The `needs_action` fallback for unknown categories is **kept on purpose** — a contract drift should cost the user the wrong sound, not a missed alert — but it now `console.warn`s so the drift is visible to developers instead of masquerading as intended behavior. `e2eBridge.ts` and `tauri.ts` now derive the category type from `types.ts` instead of retyping it. Not touched: the plural `--types` filter and `FeedSections` keys. Those are the section vocabulary and are correct as-is. ## Verification - `just ci` green (file-size ratchet, Rust/Tauri/desktop/mobile tests, desktop + web builds) - New tests: 2 Rust (`feed_item_category_serializes_to_frontend_contract`, `feed_item_from_event_carries_singular_mention_category`), 3 TS in `sound.test.mjs` incl. one that feeds the old `"mentions"` string and asserts fallback + warning - **Runtime, dev build against the production relay:** controlled test from an agent identity into a test channel — - mention in channel → @Mentions sound, inbox shows "Mentioned in" ✅ (was Needs-action) - thread reply with mention → @Mentions sound, once ✅ (was Needs-action) - plain thread reply in the channel being viewed → silent, as designed ✅ ## Reviewers - @tlongwell-block — #475 introduced the plural category; please confirm it wasn't intentional - @wesbillman — owner of the original `FeedItemCategory` contract (#12) and most of the feed builder - @taylorkmho — owner of the sound-slot model and resolver (#968); the fallback-with-warning shape is the part to weigh in on - cc @klopez4212 --------- Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
01091c15a1 |
fix(mobile): recover stale and shuffled messages (#6691)
## Summary - refetch mounted mobile thread replies after relay reconnect, preserving the previous reply list during recovery - auto-dispose route-scoped relay reply caches so reopening a thread queries current relay state - invalidate live replies through both the channel-window and legacy websocket-history paths - preserve optimistic-reply confirmation when the route closes before its deferred cleanup - stabilize rapid same-second messages using desktop's existing split contract: channel timelines render `(created_at ASC, id DESC)` while threads render `(created_at ASC, id ASC)` - retain late live rows after a channel window is exhausted instead of dropping same-second tail messages Closes #4404. Closes #4830. Closes #6204. ## Context The broad all-channel/all-DM stale-session defect reported in #4402 is already addressed on current `main` by #4372 and #3053. Two distinct mobile gaps remained: 1. `threadRepliesProvider` was a process-lifetime one-shot query, so replies missed while the socket was stale remained absent after reconnect or after closing and reopening the thread. 2. Mobile had inconsistent timestamp-only and event-id ordering across channel producers. Rapid messages routinely share Nostr's one-second timestamp, so later hydration/live reconciliation could reshuffle them. Desktop deliberately has two render contracts: channel windows reverse the relay's composite order to `(created_at ASC, id DESC)`, while thread replies use `(created_at ASC, id ASC)`. This consolidates the current-main portions of #4831 and #3243 rather than reviving stale overlapping branches. ## Validation Exact pushed head: `be92d9542c6cd1342733bdc5e8359664b511ce02` - focused channel-provider/window/thread suites: 48/48 passed - incident regression: a mounted thread misses a reply while disconnected, reconnects, and renders the recovered reply - route regression: closing and reopening a thread performs a fresh authoritative query - websocket fallback regression: live reply invalidates the mounted thread even without the channel-window path - disposal regression: optimistic confirmation survives provider disposal between rebuild and deferred cleanup - ordering regressions: channel window/live, websocket fallback, optimistic sends, deep links, both pagination paths, and thread merges preserve their desktop-compatible same-second order - boundary regression: exhausted windows admit late same-second live rows without weakening open-page cursor boundaries - independent adversarial review: no production blocker; source contract verified across all producers and relay cursor semantics unchanged - pre-push Mobile lane passed at exact head, including analysis, file-size/branch checks, and full Flutter suite: 1,675/1,675 passed - `git diff --check` --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> |
||
|
|
26f4c3ed30 |
feat(mobile): browse and join open channels (#6243)
Mobile previously exposed no way to browse or join channels. Users can now browse and join eligible open channels from the Home quick-actions menu. The public directory loads on demand when Browse channels opens, while the existing kind 9021 join path refreshes membership after success. | Browse channels | Join channel | | --- | --- | | <img width="320" alt="Browse channels" src="https://github.com/user-attachments/assets/f12c46c8-8bf4-487d-8a45-7bec63b16028" /> | <img width="320" alt="Join channel" src="https://github.com/user-attachments/assets/d6306ca7-9ab8-4f5a-8131-7f9b2a76d653" /> | ### How is it tested? Manually tested (see screenshots) and added tests: - [`channels_provider_test.dart`](https://github.com/block/buzz/blob/main/mobile/test/features/channels/channels_provider_test.dart) covers access filtering, independently paginated membership and directory queries, relay-capped pages, repeated-page termination, hard page caps, on-demand directory loading, load failures, retry, and cached-channel retention. - [`channels_page_test.dart`](https://github.com/block/buzz/blob/main/mobile/test/features/channels/channels_page_test.dart) covers browse eligibility, loading and retry states, quick-action layout, and scrolling and joining from a 500-channel directory. - [`search_page_test.dart`](https://github.com/block/buzz/blob/main/mobile/test/features/search/search_page_test.dart) covers discoverable open-channel results without presenting unknown membership counts as zero. Local validation: - `just mobile-check` - `just mobile-test` (1,560 tests) - full pre-push gate --------- Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: leader <71e9f2c44a6932b6772caaaccda1911d010463c3e2c6c40410b8329956046801@buzz.block.builderlab.xyz> |
||
|
|
0e69b3fd7c |
show mention counts in channel notifications (#6696)
**Category:** fix **User Impact:** Stream and forum channels now show an accessible numeric badge for unread mentions while mention chips remain clear in every theme. **Problem:** Mention notifications contributed to the app and Dock badge, but inactive stream and forum rows only became bold, making it difficult to see where multiple mentions were waiting. Mention styling and generic destructive colors could also lose contrast or visual meaning in some themes. **Solution:** Use the same app-badge projection for non-DM channel mention counts, while preserving regular unread bolding, thread activity dots, DM counts, and manual unread behavior. Dedicated notification and opaque mention-highlight tokens keep the new treatments stable and readable across syntax themes. <details> <summary>File changes</summary> **desktop/src/features/channels/useUnreadChannels.ts** Projects app-badge-eligible mention and broadcast counts into stream and forum channel rows while retaining DM-specific counting and manual-unread semantics. **desktop/src/features/sidebar/ui/SidebarSection.tsx** Renders an accessible numeric notification pill on inactive non-DM channels and preserves the thread activity dot fallback. **desktop/src/shared/styles/globals/markdown.css** Applies the shared opaque yellow highlight to human and agent mention chips, including hover treatment. **desktop/src/shared/styles/globals/theme.css** Adds fixed notification and mention-highlight tokens with theme-independent contrast. **desktop/tailwind.config.js** Exposes the notification token pair through semantic Tailwind utilities. **desktop/tests/e2e/badge.spec.ts** Covers aggregated mention counts, broadcasts, unchanged unread tiers, exact accessible text, and badge contrast under an adversarial theme. **desktop/tests/e2e/mentions.spec.ts** Covers human and agent mention styling, hover behavior, dark mode, and WCAG text contrast. </details> ## Reproduction steps 1. Open a stream or forum channel, then navigate to another channel. 2. Receive two messages that mention you in the inactive channel. 3. Confirm the inactive row is bold and shows a red `2` pill matching the two notifications added to the app or Dock badge. 4. Receive a regular channel message and confirm the row only becomes bold, without a numeric pill. 5. Receive a reply in an interested thread and confirm the channel retains its activity dot instead of a mention count. 6. Switch between light and dark themes and confirm human and agent mention chips remain yellow with near-black readable text, including on hover. ## Screenshots Screenshots are posted in the PR discussion using immutable repository-hosted image URLs. Signed-off-by: tulsi <tulsi@block.xyz> |
||
|
|
2f13e30e88 |
fix(desktop): hide selection formatting tray on composer right-click (#6683)
## Summary Right-clicking a text selection in the message composer left the selection formatting tray floating over the native context menu. This suppresses the tray for the duration of the right-click interaction. ## Changes - `SelectionFormattingTray.tsx`: a `contextmenu` listener on the editor DOM sets a suppression ref, cancels any queued rAF reposition, and hides the tray. Suppression clears on the next left-click `pointerdown` or `keydown` in the editor, which reschedules a normal position update. - `scheduleUpdate`/`updatePosition` both honor the suppression ref, so editor `selectionUpdate`/`transaction`/`focus` events fired during the right-click can't bring the tray back. - Extracted `cancelScheduledUpdate` to replace the duplicated rAF-cancel logic, and reset suppression on editor change / cleanup. - E2E coverage in `composer-selection-formatting.spec.ts`: double-click to select, assert the tray shows, right-click and assert the tray hides *and* that `contextmenu` is not `defaultPrevented` (the native menu still opens), then re-select and assert the tray returns. ## Testing `just` pre-push gate ran green: `desktop-check`, `desktop-typecheck`, `desktop-test` (5397 passing), `file-size-check`. ## Demo https://github.com/user-attachments/assets/2fdfced9-6cd6-4eb2-a6df-c03164ab1c42 Signed-off-by: Matt Toohey <contact@matttoohey.com> |
||
|
|
72ba987c36 |
fix(desktop): stabilize members dialog scrolling (#6670)
## Summary - Keep virtualized member rows measurable by removing `content-visibility: auto` from the measured row subtree. - Use the member card's 60px baseline as the virtualizer estimate while retaining deferred rendering for eager search and archived-member lists. - Cover large rosters with a regression test that checks stable scroll extent across the list and verifies the final member remains reachable. ### Related issue None found. ### Testing - `just ci` - `pnpm -C desktop build:e2e` - `pnpm -C desktop exec playwright test tests/e2e/channels.spec.ts --grep 'members sidebar virtualizes large channel rosters' --repeat-each=5` #### Before https://github.com/user-attachments/assets/a5fcc040-6872-4200-bcc3-7b4197a4dd23 #### After https://github.com/user-attachments/assets/f2c97f2f-3719-4c2a-b17a-2450c6c70a55 Signed-off-by: Matt Toohey <contact@matttoohey.com> |
||
|
|
17af15effa |
fix(desktop): keep member runtime status off the UI thread (#6445)
## Summary - skip managed-agent runtime discovery when the members sidebar has no local managed bots - run runtime listing disk, process, and mutex work on Tauri’s blocking pool - preserve local managed-bot status and Start/Stop behavior with positive and negative E2E coverage Opening Add people in a human-only channel could invoke synchronous native runtime discovery before the sidebar painted, leaving the macOS app beachballed. Human invites do not depend on that data. ## Why I have seen slowness opening this dialog in the UI https://github.com/user-attachments/assets/1955eb5e-ee47-4edf-8e5c-606d11ffbc25 ### Related issue Related overlap: #4851 is a broader managed-agent lifecycle change that includes a similar native offload. This draft is intentionally limited to the sidebar critical path and adds the human-only query gate. ### Testing I have verified the pause in the video goes away after this change. - `just ci` - `just desktop-check` - `just desktop-test` (5,241 passed) - `just desktop-tauri-fmt-check` - `just desktop-tauri-clippy` - `just desktop-tauri-test` (2,702 passed; 18 ignored) - focused Playwright: human-only sidebar skips runtime discovery - focused Playwright: local managed bot retains status and Stop/Start controls No visual styling changed, so screenshots are not applicable. Signed-off-by: Matt Toohey <contact@matttoohey.com> |
||
|
|
2d280376ad |
perf(desktop): persist channel heads, collapse thread reads and reply sends (#6572)
## Summary
Lands the build-now items from the desktop latency plan
(#ui-performance-deep-dive) as one change. Every perceived-latency hot
path a user hits on launch, channel open, thread open, and reply send
drops one or more round trips.
**A1 — persisted channel heads (the big one).** Native WAL SQLite cache
(`desktop/src-tauri/src/channel_head_cache.rs`) keyed by `{pubkey,
relayUrl}` scope, 32 rows/scope LRU, 1 MiB per-row drop cap,
schema-version reset, corrupt-row tolerance, checkpointed on shutdown.
Three blocking-pool commands: `channel_head_cache_load` / `_store` /
`_clear`. On the renderer side, `CommunityQueryProvider` kicks off
hydration of up to 12 heads when it constructs the query client — the
app, splash and relay preconnect mount immediately; only
`useChannelMessagesQuery` awaits the seed (`channelHeadHydration`), then
consumes a one-shot hydrated gate so a hydrated channel pays **zero**
`get_channel_window` calls on mount and exactly **one** on the
post-subscription refresh, whose response replaces page zero wholesale.
That refresh fires whether live-subscription setup succeeds or fails,
and is sequenced behind hydration so it is always a distinct
authoritative fetch (see Review follow-ups). Bounds-only persisted heads
(zero rows) are not hydrated and take the cold loading path. The
timeline loading latch recognizes native-hydrated rows as restart-safe
so they paint immediately instead of holding a skeleton. The cache is a
paint accelerator only — the relay response is always authoritative.
Replaces the legacy localStorage `messageSnapshot.ts` (removed, -401
lines).
Kill switch: `VITE_BUZZ_CHANNEL_HEAD_CACHE=off` at build time or
`localStorage["buzz-channel-head-cache"] = "off"` at runtime. Cache is
cleared on community removal and scoped per identity, so a replaced
signer never sees the previous identity's rows.
**B1 — thread aux in one response.** Relay thread filters accept
`include_aux`; the bridge appends the same authorized two-hop
reactions/edits/deletions closure a channel window gets
(`build_aux_query` shared with the window path). Renderer
`useThreadReplies` drops its two follow-up aux fetches. `next_cursor` is
computed from reply-kind rows only since aux rows are unpaged.
Documented in `docs/bridge-channel-window.md`. Thread queries keep
`staleTime: 0` (`bcfe04e2f`): an earlier revision raised it to 30s,
which CI's `thread-unread.spec.ts` caught — once the user leaves a
channel, the live subscription stops feeding that thread's cache, so a
reopen must always take the (now single) authoritative read.
**B2 — cached root on reply send.** `send_channel_message` gains
`root_event_id`; when the renderer already holds the parent (channel or
thread cache) it passes the NIP-10 root, and native signs without the
relay round trip that `resolve_thread_ref` used to make. Strict hex
parse; `root_event_id` requires `parent_event_id`; absent root falls
back to the existing relay resolution. The renderer never sends a
guessed root.
**B4** general HTTP pool idle 10s→300s, max idle per host 1→2. **B5**
relay preconnect fires as soon as identity is ready instead of waiting
for `requestIdleCallback`. One e2e test (`relay-reconnect.spec.ts`
"service restart close resets accumulated backoff") had been relying on
the idle-callback batching to skip past its own seeded dial failures
before the channel list painted; `8133d70bb` makes it wait for the
connected state instead (test-only, still fails with the 1012 backoff
reset disabled). **B6** profile freshness 60s→10 min (both the in-memory
entry check and the query `staleTime`). Tradeoff: another user's
display-name/avatar edit can take up to 10 min to propagate to a client
that already holds their profile (relay reconnect refetches
`users-batch` but resolves from the still-fresh per-pubkey entry); your
own edits still evict the entry immediately (`evictUsersBatchEntries` in
`useUpdateProfileMutation`).
### Related issue
Follows #6456/#6457/#6459/#6460 (already merged). #6455 is the
measurement instrument and is intentionally not folded in. No duplicate
PR found.
### Review follow-ups
Addressing Carl's reviews
[5001114109](https://github.com/block/buzz/pull/6572#pullrequestreview-5001114109)
and
[5002596542](https://github.com/block/buzz/pull/6572#pullrequestreview-5002596542),
each pushed as new commits (no rebase):
- `4f06b7770` fix(desktop): mount app while channel heads hydrate;
always revalidate — provider no longer gates children on the cache load;
`refreshAfterSubscribe` runs on subscribe failure too; bounds-only heads
skipped at seed; seed merges into an existing window store. +3 tests.
- `35834cb31` fix(relay): drain aux closure hops across the page clamp —
`query_all_pages` walks the `(created_at, id)` keyset via
`until`/`before_id` until a short page (`AUX_PAGE_LIMIT` =
`DEFAULT_MAX_PAGE_LIMIT`, `AUX_MAX_PAGES` = 64 warn+truncate) so
one-shot `limit: 1000` newest-first no longer drops the oldest
edits/deletions. +3 tests; `docs/bridge-channel-window.md` updated.
- `db21b0531` merge of `origin/main` `e23632941` (#6558, #6312 — no
overlap).
- `5a5566c0f` fix(desktop): sequence post-subscribe refresh behind
channel head hydration — `refreshChannelWindowMessages` awaits
`channelHeadHydration()` and, for a hydration-seeded query (`data !==
undefined && dataUpdatedAt === 0`), the in-flight snapshot fetch before
invalidating. Without this, a subscription that settles before the
SQLite load invalidated a data-less in-flight query; TanStack dedupes
that onto the existing fetch (`query-core` `fetch()` only cancels when
`state.data` exists), which returned the seeded snapshot — 0
authoritative fetches. Regression test reproduces Carl's exact ordering
(fails at `35834cb31` with 0 calls), plus a cold-channel guard that the
fix does not double-fetch.
- `b129231c8` fix(desktop): let concurrent post-hydration refreshes
share one window fetch — found independently by Max and Wren reviewing
`5a5566c0f`: subscribe settlement + reconnect both wake on the same
snapshot promise and both invalidate; the second (default
`cancelRefetch: true`) cancelled and replaced the first authoritative
fetch (3 queryFn calls, not 2, and the cancelled Tauri invoke still hits
the relay). The seeded branch now invalidates with `cancelRefetch:
false` so a second waker joins the in-flight fetch; cold/warm keep the
default (`test_canceled_stale_fetch_cannot_overwrite_catch_up_window`
relies on it). Concurrent regression test fails at `5a5566c0f` with 3.
### Testing
At `b129231c8` (PR head; verified in one shell with `git rev-parse HEAD`
= `b129231c8`): `pnpm check`, `tsc --noEmit`, desktop unit 5,393 / 0,
Playwright `boot-splash` + `channel-head-restart` + `relay-reconnect` +
`relay-reconnect-affordance` + `thread-unread` 34 / 34 on a fresh
`build:e2e`, pre-push hooks green.
At `5a5566c0f`: `pnpm check`, `tsc --noEmit`, desktop unit 5,392 / 0,
Playwright `boot-splash` + `channel-head-restart` + `relay-reconnect` +
`relay-reconnect-affordance` + `thread-unread` 34 / 34 on a fresh
`build:e2e`, pre-push hooks green.
At `35834cb31`: desktop unit 5,390 / 0; `cargo test -p buzz-relay --lib`
910 / 0; fmt + clippy `-D warnings` clean; Playwright 32 / 32 (same
specs minus affordance); GitHub CI green on every job except Smoke (3)
(unrelated project-review row-count + messaging timing flake, per Carl)
and Unit Tests (sherpa cache skeleton, below).
Earlier, all at `8133d70bb` (this PR head is `0c492366d` =
|
||
|
|
0720f5380c |
Downgrade desktop Huddles to audio protocol v2 (#6610)
## Summary - negotiate Huddle audio protocol v2 on desktop - decode the released one-byte peer-index prefix - retain roster-driven playout resets and document the missing v3 epoch fence ## Testing - `just desktop-tauri-fmt-check` - `just desktop-tauri-clippy` - `just desktop-tauri-test` Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
e236329413 |
Polish Huddle participant interactions (#6312)
## Summary - arrange Huddle participants in a responsive, equal-weight cluster with spring enter/exit motion and a `+N` overflow - spotlight tapped participants over a blurred call surface, with a roster for hidden participants and no self-avatar action - add selection haptics across full-screen and drawer controls, including both end-call buttons <img width="1080" height="2424" alt="Screenshot_20260819-151448" src="https://github.com/user-attachments/assets/00b7fdca-2304-4788-9952-e07224798513" /> <img width="1080" height="2424" alt="Screenshot_20260819-151422" src="https://github.com/user-attachments/assets/a0cfc861-0519-44ff-bb56-4c983ed6344c" /> ## Validation - `just mobile-check` - focused participant, drawer-control, and full-screen end-call widget tests - Huddle-focused widget suite (15 tests) - full mobile Flutter suite (1,538 tests) ## Dependency Built on #6056 and contains only the follow-up interaction work. Merge after #6056 lands. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: leader <71e9f2c44a6932b6772caaaccda1911d010463c3e2c6c40410b8329956046801@buzz.block.builderlab.xyz> Co-authored-by: Tom Brow <tomb@block.xyz> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz> |
||
|
|
4baccd5394 |
Downgrade mobile Huddles to audio protocol v2 (#6558)
## Summary - downgrade Mobile Huddle authentication and native media configuration from protocol v3 to the currently deployed relay's v2 contract - restore the released one-byte relay peer prefix while retaining later reconnect, roster, and playout-reset reliability fixes - update Android, iOS, protocol documentation, and focused tests together Protocol v2 does not carry v3's occupancy epoch on audio frames, so it cannot fence the narrow delayed-packet/peer-index-reuse race. This is an intentional compatibility tradeoff until the relay v3 rollout is ready. ### Related issue None found. ### Testing - `just mobile-check` - `just mobile-test` — 1,661 tests passed - Android debug build installed and launched on Pixel 10 as `xyz.block.buzz.mobile.sprout_mobile_profile_settings`; foreground process verified - signed iOS Release build installed and launched on iPhone as `com.buzz.buzzMobile`; running process verified A live two-device Huddle audio call remains a manual verification step. Signed-off-by: kenny lopez <klopez4212@gmail.com>mobile-v0.14.0-rc.2 |
||
|
|
040b203f73 |
perf(desktop): make the Projects surface render-cheap (#6460)
Diagnostic profiling on a large community (101 issues, 258 PRs) showed Projects tab switches taking 2.5–3.6s, dominated by single React commits of 0.5–1.5s and per-render recomputation — fetch work was already off the main thread; the cost was building the UI. ### Measured: tab click → painted, per tab | tab | before | after | |---|---|---:| | projects | 3,608ms | 320–580ms | | repositories | 3,126–3,534ms | 310–410ms | | tasks | 395–1,101ms | ~115ms | | reviews | 322–2,603ms | ~96ms | | activity | 597–741ms | ~148ms | Single-commit ceiling dropped from 1,541ms to ≤200ms (growth steps 25–40ms). Fixes in profiled-cost order: - **Profile popover body mounts only while open.** `UserProfilePopover` carried seven query subscriptions plus interaction hooks per instance even when closed; grids mount hundreds (five per card in people stacks, one per row author) — measured **~40ms per card**, the dominant share of the 1.2s card-tab commits. The always-mounted shell is now just the Radix root + trigger; trigger markup, hover timing, and keyboard handling are unchanged, and hover/tooltip event continuity is preserved because the trigger never remounts. - **Incremental row mounting.** The first 12 cards / 30 rows render in the first commit; the rest stream in 36–60-per-frame low-priority transitions. Grouped lists trim across group boundaries via a pure, tested slicer; the mounted count survives in-place refetches. - **Activity feed**: was rebuilt unmemoized on every render, markdown-flattening every issue/PR/comment body in the community just to sort and keep 30 items (~360+ flattens per render on the measured community). Now memoized, and bodies stay raw until after the sort+slice — 30 flattens, once per data change. - **Contribution graph** (always-visible rail, so every tab paid for it): ~180 day cells each wrapped in a Radix tooltip with per-cell Intl date formatting per render. Now memoized, cells precomputed once per data change, native `title` tooltips. (The activity-bar segments keep their styled Radix tooltips — pinned by an existing spec.) - **Rows/cards memoized with identity-stable props**: per-row selection arrays were rebuilt per row per render (O(n²) — 258 PRs × 258-item arrays each render) and are now hoisted and shared; people arrays derive inside the memoized cards; the rail's stat walk over every issue/PR is memoized. - **`content-visibility: auto`** on cards and rows so offscreen entries skip layout and paint; **tab switches run in a React transition** so the click stays responsive while the new tree mounts. Remaining known cost (out of scope): cold-entry data readiness — the work-item and activity queries ship thousands of events to compute counts (2–4s on a large community; see the fan-lifecycle PR). The structural fix is a relay-side aggregate; tracked as follow-up. --------- Signed-off-by: Max Lampert <maxwell@squareup.com> |
||
|
|
f99532585a |
refactor(acp): clarify agent prompt sections (#6501)
## Why The ACP prompt puts a machine-specific Workspace prefix before static base guidance and labels the user-facing agent instruction layer as the generic System section. Because the cwd varies by launch and worktree, leading with it reduces reusable prompt-prefix stability. `[Workspace]` was added in [PR #1194](https://github.com/block/buzz/pull/1194) as a defensive fix after a broken `~/.sprout` → `~/.buzz` migration caused agents to scan `$HOME` and trigger macOS TCC prompts. This change retains that grounding while shrinking it to the current working directory and moving dynamic environment context after the static Base prompt. ## What - Emit the prompt in Base → Workspace → Agent Instructions order - Reduce Workspace to `Current working directory: <absolute path>` - Resolve cwd as an absolute native-platform path and preserve Windows drive/UNC paths instead of checking for a leading `/` - Emit Agent Instructions for persona and standalone agent instructions across modern and legacy ACP paths - Preserve parsing for archived observer frames that used System or the former Workspace-before-Base order, and align the persona catalog label ## Risk Assessment Medium-low — this changes prompt framing for every newly created agent session. Existing archived observer frames remain parseable, and execution still uses the same ACP working directory. Cwd resolution now fails clearly instead of substituting `/` when the process directory cannot be resolved. ## References - https://github.com/block/buzz/pull/1103 - https://github.com/block/buzz/pull/1194 Generated with Codex --------- Signed-off-by: Salman Mohammed <smohammed@squareup.com> |
||
|
|
8c0f42e187 |
Add mobile Huddles voice MVP (#6056)
## Summary - add foreground mobile Huddles on Android and iOS with native Opus capture/playback, mute, speaker routing, participants, lifecycle, and minimized drawer UI - keep mobile Huddle cards and roster state live, including ended rooms, relay-resolved profiles, and agents - broadcast desktop agent TTS through the existing Huddle audio protocol ## Scope Foreground human-to-human voice MVP only. Agent setup/transcripts, background calling, recording, and advanced device controls remain out of scope. ## Validation - `just mobile-check` - `just mobile-test` — 1,500 passed - `just desktop-check` and `just desktop-test` — 4,957 passed - desktop typecheck, strict Clippy, and Tauri tests — 2,445 passed, 15 ignored - mobile worktree identity contract checks - physical Pixel/iPhone behavior reviewed during development --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Tom Brow <tomb@block.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: leader <71e9f2c44a6932b6772caaaccda1911d010463c3e2c6c40410b8329956046801@buzz.block.builderlab.xyz> Co-authored-by: Tom Brow <tomb@block.xyz> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz>mobile-v0.14.0-rc.1 |
||
|
|
a2d8be5efa |
feat(desktop-messages): keep agents addressed across messages (#6315)
**Category:** new-feature **User Impact:** Users can keep selected agents addressed across consecutive messages without retyping their handles. **Problem:** Repeated conversations with agents require manually typing the same mentions on every turn, which adds friction and makes recipients easy to omit. **Solution:** The composer can now keep agents automatically addressed per channel, either from the mention controls or after a successful inline mention. Addressed agents remain visible in the toolbar, apply to channel threads, survive send failures safely, and never cross community boundaries. ## Changes <details> <summary>File changes</summary> **desktop/src-tauri/src/events/message_tags.rs** Preserves the automatic-address marker on validated mention reference tags. **desktop/src/features/channels/ui/ChannelPane.tsx** Wires the appropriate channel, thread, inbox, or forum composer context without leaking audiences across surfaces. **desktop/src/features/communities/useCommunityInit.ts** Clears composer audience state when the active community changes. **desktop/src/features/forum/ui/ForumComposer.tsx** Wires the appropriate channel, thread, inbox, or forum composer context without leaking audiences across surfaces. **desktop/src/features/forum/ui/ForumComposerAutocompletes.tsx** Wires the appropriate channel, thread, inbox, or forum composer context without leaking audiences across surfaces. **desktop/src/features/home/ui/InboxDetailPane.tsx** Wires the appropriate channel, thread, inbox, or forum composer context without leaking audiences across surfaces. **desktop/src/features/messages/lib/agentAddressMention.d.mts** Defines helpers and types for marked automatic-address mention tags. **desktop/src/features/messages/lib/agentAddressMention.mjs** Defines helpers and types for marked automatic-address mention tags. **desktop/src/features/messages/lib/agentAddressMention.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/lib/applyEditTagOverlay.mjs** Preserves automatic-address metadata when edited message tags are overlaid. **desktop/src/features/messages/lib/applyEditTagOverlay.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/lib/autoPinMentionedAgentsPreference.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/lib/autoPinMentionedAgentsPreference.ts** Stores the preference that keeps explicitly mentioned agents addressed for later messages. **desktop/src/features/messages/lib/extractMentionPersonas.ts** Separates persona recipients from the composer mention orchestration. **desktop/src/features/messages/lib/persistentAgentAudience.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/lib/persistentAgentAudience.ts** Maintains bounded, in-memory, channel-scoped automatic agent audiences. **desktop/src/features/messages/lib/useMentionSelection.ts** Centralizes mention picker selection state and agent-first selection behavior. **desktop/src/features/messages/lib/useMentions.ts** Exposes explicit picker origins and selection controls while preserving inline mention behavior. **desktop/src/features/messages/ui/ComposerAddressControls.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/ComposerAddressControls.tsx** Renders compact addressed-agent avatars and the automatic-mention management entry point. **desktop/src/features/messages/ui/MentionAutocomplete.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/MentionAutocomplete.tsx** Adds automatic-mention controls and options to the existing mention picker. **desktop/src/features/messages/ui/MessageAgentAddressPrefix.tsx** Shows which agents were automatically addressed on a sent message. **desktop/src/features/messages/ui/MessageComposer.tsx** Integrates automatic audiences, picker controls, accessible feedback, shortcuts, and send behavior. **desktop/src/features/messages/ui/MessageComposer.types.ts** Defines the simplified channel audience context shared by composer hosts. **desktop/src/features/messages/ui/MessageComposerToolbar.tsx** Places automatic-address controls in the composer toolbar without crowding narrow layouts. **desktop/src/features/messages/ui/MessageRow.tsx** Displays automatic-address metadata alongside sent message content. **desktop/src/features/messages/ui/MessageThreadPanel.tsx** Wires the appropriate channel, thread, inbox, or forum composer context without leaking audiences across surfaces. **desktop/src/features/messages/ui/composerAgentKeyboard.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/persistentAgentAudienceHosts.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/useAddressMentionPulse.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/useAddressMentionPulse.ts** Provides success and failure animation signals for addressed-agent controls. **desktop/src/features/messages/ui/useAgentAddressLockPicker.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/useAgentAddressLockPicker.ts** Coordinates adding, removing, and announcing automatically addressed agents. **desktop/src/features/messages/ui/useAlwaysAddressShortcut.ts** Implements the platform-aware shortcut for toggling automatic addressing. **desktop/src/features/messages/ui/useAutoPinMentionedAgents.ts** Promotes successfully sent inline agent mentions and provides a single undoable notification. **desktop/src/features/messages/ui/useComposerMentionPicker.ts** Opens the mention picker without rewriting the current draft. **desktop/src/features/messages/ui/useMentionSendFlow.helpers.test.mjs** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. **desktop/src/features/messages/ui/useMentionSendFlow.helpers.ts** Merges automatic and inline recipients, marks outgoing tags, and restores failed sends safely. **desktop/src/features/messages/ui/useMentionSendFlow.ts** Merges automatic and inline recipients, marks outgoing tags, and restores failed sends safely. **desktop/src/features/messages/ui/usePersistentAgentMentionHydration.ts** Removes the prior draft-text hydration approach now that automatic audiences stay at composer ingress. **desktop/src/features/settings/ui/AgentsSettingsPanel.tsx** Replaces the old global behavior with explicit composer-level automatic-mention controls. **desktop/src/features/settings/ui/PreventSleepSettingsCard.tsx** Replaces the old global behavior with explicit composer-level automatic-mention controls. **desktop/src/shared/lib/keyboard-shortcuts.ts** Defines the user-facing automatic-address keyboard shortcut label. **desktop/src/shared/ui/VideoReviewCommentMarkdown.tsx** Allows automatic-address prefixes to compose with video review timecodes. **desktop/tests/e2e/persistent-agent-audience.spec.ts** Covers the automatic-address behavior and its failure, keyboard, layout, or persistence boundaries. </details> ## Reproduction Steps 1. Open a channel with one or more agents and open the mention picker from the composer. 2. Select an agent for automatic mentions, then send several messages without retyping the handle; confirm the agent remains in the composer control and receives each message. 3. Mention another agent inline, send successfully, and confirm the agent becomes automatically addressed; use the notification's Undo action to reverse it. 4. Open a thread in the same channel and confirm the same addressed agents are available there. 5. Remove an agent from the composer control and confirm later messages stop addressing it. 6. Switch communities and confirm addressed agents do not carry into the other community. ## Screenshots All states below use the dark Buzz theme with a selected lilac accent. ### Addressed composer Selected agents stay visible at the composer ingress without adding handles to the draft.  ### Open mention menu The @ ingress opens the existing mention menu and shows which agents are already addressed.  ### Mention options The inline options pane controls whether a successful one-time agent mention carries into later messages.  ### Agent settings The same preference is available in **Settings → Agents → Conversations**.  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
723affe5d1 |
fix(desktop): remove Buzz entity link previews (#6512)
**Category:** improvement **User Impact:** Buzz-native project, repository, issue, and pull request links now appear once as compact inline chips, with their details available on hover. **Problem:** Buzz-native entity links rendered both an inline chip and a standalone preview card, repeating the same metadata and adding visual noise to conversations. **Solution:** Exclude Buzz-native links from the shared standalone-preview extractor while leaving entity parsing intact for chip tooltips and preserving external web previews and attachment cards. <details> <summary>File changes</summary> **desktop/src/shared/lib/linkPreview.ts** Stops Buzz-native preview candidates after parsing, including same-relay git clone URLs that normalize to repository entities, while allowing external URLs through the existing snapshot path. **desktop/src/shared/lib/linkPreview.test.mjs** Covers project, repository, issue, pull request, markdown-labeled, same-relay clone, and mixed external-link extraction behavior. **desktop/src/shared/ui/markdown/useMessageLinkPreviews.test.mjs** Confirms sent messages no longer merge a standalone Buzz entity card while external sender snapshots still render. </details> ## Reproduction steps 1. Open a desktop channel containing a `buzz://project`, `buzz://repo`, `buzz://issue`, or `buzz://pr` link. 2. Confirm the link renders as an inline entity chip without a second standalone Buzz card below the message. 3. Hover the chip and confirm its entity metadata remains available. 4. Post an external HTTPS link and confirm its web preview still renders. 5. Paste a same-relay `/git/<owner>/<repo>` clone URL and confirm it uses the repository chip without a duplicate card. ## Screenshots | Before | After | | --- | --- | | Inline chip plus redundant standalone Project card | Inline chip is now the sole presentation | |  |  | **After — rich metadata stays available on hover**  ## Verification At commit `3fa74cdd342ac1f6721b7d56a7f111af31e0e6e9`: - focused link-preview + Markdown unit suites — 119/119 passed - targeted registered smoke E2E — 8/8 passed, including labeled same-relay clone metadata, ordinary-link presentation, and in-app navigation - `cd desktop && pnpm exec tsc --noEmit` — passed - `git diff --check origin/main...HEAD` — passed - pre-push hooks — desktop check, TypeScript, and full desktop unit suite passed --------- Signed-off-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Rizz <302abe414ca6e3134763d2539bfcf145aea2a63fe5f8455204ed602fd40cf381@buzz.block.builderlab.xyz> Co-authored-by: Mongo <5398c5fd039b963ce132b3e078e7c4af097dd997517bb5e14c2682fe68c25197@buzz.block.builderlab.xyz> |
||
|
|
074561233e |
fix(composer): preserve caret when inserting mentions mid-message (#6531)
**Category:** fix **User Impact:** Users can insert mentions earlier in a draft and continue typing without the caret corrupting the rest of the message. **Problem:** Caret correction ran after every document change, so typing a mention before existing text repeatedly advanced across the mention separator and interleaved spaces into the draft. **Solution:** Limit correction to the autocomplete settlement it was designed for, with transaction-level and browser-level regression coverage for known and unregistered mentions. <details> <summary>File changes</summary> **desktop/src/features/messages/lib/mentionHighlightExtension.ts** Restricts trailing-space caret advancement to an armed autocomplete settlement instead of every document change. **desktop/src/features/messages/lib/mentionHighlightExtension.test.mjs** Exercises the real ProseMirror plugin state and verifies mid-draft mention typing, unknown tokens, end-of-message typing, and completed-mention separators. **desktop/tests/e2e/mentions.spec.ts** Reproduces the reported composer workflow in Chromium and covers the same corruption path for an unregistered `@token`. </details> ## Reproduction steps 1. Open a channel and enter `hello world` in the composer. 2. Move the caret between `hello` and ` world`. 3. Type ` @bo`, select `bob` from autocomplete, and continue typing `abc`. 4. Confirm the composer reads `hello @bob abc world` with the caret after `abc`. 5. Repeat with an unregistered token such as ` @zzq` and confirm the existing text remains intact. ## Before / After | Before | After | | --- | --- | | Typing after a mid-draft mention walks the caret through the existing message. | Continued typing stays after the inserted mention. | |  |  | --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Mongo <5398c5fd039b963ce132b3e078e7c4af097dd997517bb5e14c2682fe68c25197@buzz.block.builderlab.xyz> |
||
|
|
f794216737 |
chore(deps): update rust crate async-trait to v0.1.92 (#6094)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [async-trait](https://redirect.github.com/dtolnay/async-trait) | dependencies | patch | `0.1.91` → `0.1.92` | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/1) for more information. --- ### Release Notes <details> <summary>dtolnay/async-trait (async-trait)</summary> ### [`v0.1.92`](https://redirect.github.com/dtolnay/async-trait/releases/tag/0.1.92) [Compare Source](https://redirect.github.com/dtolnay/async-trait/compare/0.1.91...0.1.92) - Resolve double\_must\_use clippy lint in generated code ([#​303](https://redirect.github.com/dtolnay/async-trait/issues/303)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/block/buzz). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yOS41IiwidXBkYXRlZEluVmVyIjoiNDQuMzkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> |
||
|
|
2d93ea0955 |
chore(deps): update dependency sonner to v2.0.8 (#6093)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [sonner](https://sonner.emilkowal.ski/) ([source](https://redirect.github.com/emilkowalski/sonner)) | [`2.0.7` → `2.0.8`](https://renovatebot.com/diffs/npm/sonner/2.0.7/2.0.8) |  |  | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/1) for more information. --- ### Release Notes <details> <summary>emilkowalski/sonner (sonner)</summary> ### [`v2.0.8`](https://redirect.github.com/emilkowalski/sonner/compare/v2.0.7...ecce1841c55e4a72dfe139a8992b56498660125e) [Compare Source](https://redirect.github.com/emilkowalski/sonner/compare/v2.0.7...v2.0.8) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/block/buzz). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yOS41IiwidXBkYXRlZEluVmVyIjoiNDQuMzkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> |