mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
4b0744d7f3161d9f045dd67a73e014e4440c2b47
277 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c6ca9d94d9 |
Show status and huddle indicators beside names (#7112)
## Summary - show status and huddle emoji beside names in DMs and message rows - provide accessible tooltips, fallback status emoji, and profile-menu icon replacement - add the desktop status editor with preset durations, a ShadCN calendar, and a capped half-hour time menu ## Validation - desktop checks, typecheck, and file-size guard - 5,802 desktop tests - focused Playwright coverage (3 passed) - E2E build and native Builderlab staging verification Updated visual snapshots are attached in the PR comments. --------- 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> |
||
|
|
ac5a18697c |
feat(desktop): add persistent Bestie experience (#7223)
## Summary - add a persistent, community-scoped Bestie designation for local managed agents - surface the designated agent in the sidebar, agent library, profile actions, message toolbar, and draggable floating shortcut - bloom the floating shortcut into a lightweight compact composer that reuses the normal DM timeline, reactions, presence, and send behavior - support message handoff with a bounded snapshot and a full Buzz thread link so the agent can retrieve the complete conversation ## UX details - the floating avatar and expanded panel stay above app chrome and drag as one aligned surface - closing the expanded panel returns it to its top-right anchor - each mini-composer opening starts visually fresh while messages sent during that opening remain conversational - the designated Bestie's duplicate DM entry is hidden from the regular DM list - Bestie actions are suppressed inside the mini timeline to avoid recursive handoff ## Reliability and maintainability - preserve existing retention database paths across upgrades - serialize assignment and deletion, clearing matching assignments across community scopes before an agent is removed - fence async conversation resolution against workspace and assignment changes - validate stale assignments against existing local agents before hiding DMs - share lightweight assignment state across agent cards and keep protected-feature behavior out of the shared timeline API ## Testing - `pnpm --dir desktop test` — 5,886 tests passed - `pnpm --dir desktop exec tsc --noEmit` - `pnpm --dir desktop exec biome check ...` - `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets -- -D warnings` - focused native retention and Bestie assignment/command tests — 34 passed - `VITE_BUZZ_BESTIE=1 pnpm --dir desktop build:e2e` - `VITE_BUZZ_BESTIE=1 pnpm --dir desktop exec playwright test --project=smoke tests/e2e/bestie.spec.ts` - pre-commit and differential pre-push hooks ## Rollout The UI remains gated by the `bestie` build feature. Screenshots covering setup, empty, assigned, floating, and message-handoff states are included in the PR discussion. --------- Signed-off-by: Arjun Mahanti <arjun@squareup.com> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
2c99ee7af5 |
fix(desktop): resolve bundled sidecar on cheap path and bound login-shell spawns (#6904)
## Why PR #6330 split agent harness/runtime detection into a cheap (cache-only) path and a forced (spawning) path. Two regressions followed, both surfacing as every harness showing "(not installed)" / "CLI missing" across the agent create/edit picker, Agents > Agent defaults, and Settings > Agents — blocking agent create/edit until the user clicked Install in Settings > Agents. ## Root cause One underlying bug, two victims: - **Boot false-negative.** The resolve cache is in-memory, so it starts cold on every launch. `resolve_command_cached` (the cheap path) consulted only the Buzz-managed shim dirs plus that cold cache, and `buzz_managed_command_path`'s allowlist structurally excludes `buzz-agent`. The bundled sidecar could therefore never resolve on the cheap path until a forced pass warmed the cache, so cheap-path surfaces rendered all-missing at boot. App setup never warms the cache. - **"Check again" hang.** `run_in_login_shell` used an untimeouted `Command::output()`; a wedged login shell froze the whole forced pipeline, leaving "Check again" spinning forever. ## What - `resolve_command_cached` now also calls `resolve_workspace_command`, resolving the bundled sidecar via a filesystem stat (no spawn) — the same class of work the managed-shim check already performs. `buzz-agent` can no longer report missing, even inside the boot warm window. - New `discovery/bounded_command.rs` runs any discovery child under a hard wall-clock deadline, polling with `try_wait` rather than blocking on `wait()`. Stdout and stderr are piped to two drain threads whose buffers share an aggregate `CAPTURE_LIMIT`; a breach fails closed (kill the tree, return `None`), so a noisy or hostile probe can force neither unbounded memory nor disk fill. Tree teardown runs on every exit path — timeout, error, cap breach, *and* success — because a login-shell rc file or auth CLI can legitimately background a descendant that would otherwise outlive discovery. Ownership is deliberately asymmetric: - **Unix:** the child leads its own process group (`process_group(0)`); teardown is `SIGTERM` → bounded grace → `SIGKILL` on the group. A descendant that leaves the group (`setsid`/`setpgid`) while holding a pipe is not owned and may survive one probe, but can never hang or unbound the helper: the Unix drains read nonblocking and end on `WouldBlock` once teardown sets the stop flag, so the join returns promptly without waiting on an escaped writer's EOF. - **Windows:** the child is spawned `CREATE_SUSPENDED`, assigned to a kill-on-close Job Object while frozen, then resumed. The job owns the root before any descendant can exist and is created without breakaway, so no writer can escape — a hard whole-tree guarantee, and closing the job reaps the tree even after the root has exited. Any failure to create, assign, or resume is fail-closed: the child is terminated and reaped and the spawn returns `None` (discovery treats it as command-not-found) rather than running unowned. - Each login-shell candidate is bounded by a 10s timeout via that helper, falling through to the next candidate on timeout instead of aborting the resolve. The login-shell path cache is generation-aware: a probe that loses to a concurrent refresh or lands mid-refresh returns the authoritative cached value (or re-probes under the new generation) rather than its own rejected local result, so a losing thread can never settle the UI with a PATH-missing catalog while the cache holds a fresh success. - Warm the ACP runtime catalog once at `AppShell` mount and gate the cheap-path surfaces on that pass. A module-level boot-warm state (`idle` → `pending` → `settled`/`failed`, deduped per launch) lets `useAcpRuntimesQuery` present a cold catalog as *loading* while the first forced pass runs and as a *retryable error* (carrying the probe's real reason) if it fails, instead of blessing "every harness not installed" as authoritative. A non-empty catalog always wins, so a revalidation or later failure never blanks a good list; the gate only overlays once the warm has started, so onboarding (which renders before the warm) is unaffected. Deduping per launch also fixes the previous per-remount re-fire. ## Verification Unix teardown and the drain contract are runtime-proven by `#[ignore]`-free tests that record a backgrounded descendant's real PID and assert the helper returns promptly on both the success and timeout paths without blocking on that writer. The generation-aware login-shell cache is covered by deterministic tests through a `cfg(test)` injectable probe seam that assert the function's return value under both concurrent-refresh interleavings — the losing caller returns the peer's committed success, and a mid-probe refresh forces a re-probe to the fresh value. The Windows ownership contract has no CI lane, so `bounded_command.rs` carries two `#[ignore]`-gated tests (spawn/assign race, looped; and the timeout path) for a sanctioned run on a Windows host. The boot-warm gate is covered by unit tests for the pure overlay and the `startBootWarm` failure → retry → settle lifecycle. Origin: [Buzz thread](buzz://message?channel=5ef5d5bb-643f-4b87-bbf4-e8b64585ffeb&id=a4b1c4485de4d35cff0f914d4f4211c796f44f670e76de2ef9431f7e882c906e) Fixes #6872 Related #6662 --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
9733863516 |
perf(desktop): restore project context during startup (#6939)
## Summary Project channels previously appeared as ordinary channels until the community-wide project enumeration completed, delaying the contextual right rail most noticeably on larger relays. - Restore the last fully validated, relay-and-identity-scoped project collection immediately while keeping live relay data authoritative. - Resolve the active channel's project home through scoped `#buzz-channel` queries instead of waiting for the complete project scan. - Keep snapshots aligned with community removal and relay reconnect invalidation. ### Related issue None found. ### Testing - Pre-push `file-size-check`, `desktop-check`, `desktop-typecheck`, and `desktop-test` - Project enumeration, snapshot persistence, and relay invalidation unit coverage - Targeted Playwright scenario: project sidebar rows open the home channel and nest extra channels No numeric startup benchmark was captured; this draft validates the cache and scoped-query behavior while leaving timing measurement for review. --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
4d77175c78 |
fix(client): resurface hidden DMs from live activity (#6885)
## Summary Hiding a DM row and then receiving a new message from that person never brought the row back — the reader was left with only an Inbox notification. This restores the row from live activity, client-side, with no relay or database changes. - Subscribe to hidden-DM activity **channel-scoped** over the hidden-DM id set (`#h`), re-registering whenever the set changes. The relay's `fan_out_scoped` only routes channel-scoped events (every kind:9 DM carries a `channel_id`) to channel-scoped subscriptions, so a community-global `#p` filter receives nothing. Hiding a DM never drops membership, so `#h` authorization holds. Because every delivered event is already for a hidden DM the reader belongs to, eligibility no longer needs a self `p` tag — untagged CLI/agent DMs resurface too — and no per-event visibility fetch is required. - On qualifying activity, invoke the existing open-DM action (`41010`) to reopen the hidden one-to-one or group DM, validating that the relay reopened the same channel. - Preserve the existing relay-synced hide snapshot (`41012` → `30622`); hide state still syncs across devices. - Let Inbox reopen a hidden DM before navigating to its message, with a busy state on the row and context-menu "Open in channel" action while a reopen is pending. - Coalesce resurface attempts per channel: a concurrent message that arrives while a reopen is in flight triggers a retry on failure instead of being marked handled and dropped. - Write exact dm-visibility query keys on open/hide mutations. This is the client-only replacement agreed in the Buzz design thread and supersedes #6713. The accepted limitation is that resurface only fires while a client is running — a message that arrives while all clients are closed does not restore the row on next launch (the `30622` snapshot has no watermark); Inbox click-through is the safety net for that case. Addresses #6316. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> |
||
|
|
de188ebf06 |
feat(projects): add agent and CLI project-home support (#6590)
## Summary - inject bounded project-home identity and repository context into managed agent sessions - add project-aware CLI flows for creating projects, repositories, issues, and related channels - match project homes through the existing relay query surface, then filter channel metadata client-side This is Part 1 of the channel-first Projects stack. Part 2 contains project creation and model foundations. ## Testing - `cargo fmt --all -- --check` - `cargo clippy -p buzz-cli -p buzz-acp --all-targets -- -D warnings` - `cargo test -p buzz-cli -p buzz-acp` — 1,184 tests passed, 1 doc test ignored - full pre-push gate passed ## Post-Deploy Monitoring & Validation - validate project-home agent context and project-aware CLI commands against a staging relay - healthy signals: project context matches the active channel, explicit repo coordinates remain stable, and normal channels receive no project block - failure signals: cross-channel project context, duplicate project creation, or commands targeting an unrelated repository; mitigate by reverting this PR --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.xyz> Co-authored-by: Wrench <0eabe6ea5758c1e4c5b68cea4ac42b32c479072883cb28da8110b4a47c32b9a1@buzz.block.builderlab.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> |
||
|
|
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> |
||
|
|
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` =
|
||
|
|
97b1fee5c3 |
fix(desktop): restore true zoom by scaling the root rem (#6514)
## Summary Follow-up to #5644. Cmd +/- had become a text-only zoom: type scaled while rem-based padding, gaps, widths, avatars, and controls stayed frozen, which produced cramped layouts (see [#buzz-frontend thread](buzz://message?channel=a410ffde-c61f-416a-96e0-c296b5f5ecc9&id=1a758115cf07b00c097f6e988553908c045165325a57637519cfa7ed9c9accec)). Root cause: #5644 introduced a virtual typography rem so the **Font size** preference could change text without moving layout — a good decoupling — but it also routed **Cmd +/- zoom** through that same px-valued token and pinned the real root at 16px. One decision ("freeze layout") was applied to two dials that shouldn't share it. This PR gives each dial one owner and lets CSS compose them: | Control | Changes | How | |---|---|---| | **Cmd +/- zoom** | Everything — true zoom | Scales the real `<html>` font-size again (`useWebviewZoomShortcuts`) | | **Font size preference** | Text only | Sets `data-font-size`; `typography.css` maps it to a unitless `--buzz-type-scale`, mirroring how density already works | `--buzz-type-rem` becomes `calc(1rem * var(--buzz-type-scale))` — rem-relative, so it rides on zoom automatically. Resulting text px = `16 × zoom × scale × token-ratio`. The 13 / 14 / 15px conversation contract is unchanged at default zoom. Density and the type ramp from #5644 are untouched. The preference module no longer does px math or knows about zoom; the zoom hook no longer imports the preference module. Net deletion in production code. ## Validation - `pnpm test` — 5,308 desktop unit tests - `pnpm check:px-text`, `tsc --noEmit`, biome - Playwright: `top-chrome-zoom-clearance.spec.ts` (native-chrome clearance stays fixed under root zoom), `inbox-refactor-screenshots.spec.ts` (zoomed row padding now asserts `4.4px` instead of the frozen `4px`), and both `profile.spec.ts` zoom tests (composed zoom × preference, cross-window storage reset) - Before/after screenshots at 140% zoom in the comment below --------- Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
4e3c9e619c |
fix(desktop): sender names in notifications + macOS click-through routing (#6427)
> Opened by the agents Brain and Pinky on behalf of @wesbillman. Fixes two desktop notification issues (requested in Buzz channel `desktop-notification-improvements`): ## 1. Notifications now show who sent the message Live DM and thread-reply notifications showed only "Direct message" / "Reply in #channel", while home-feed mention toasts already carried the sender's name. All message-notification copy is now centralized: - **`formatMessageNotification`** (`notifications/lib/notificationFormat.ts`) — canonical title/body for all five sources (mention, approval, needs-action, DM, thread reply). Sender-first titles with neutral fallbacks, never a raw pubkey: - DM: `Taylor` instead of `Direct message` - Thread reply: `Taylor replied in #ship-room` instead of `Reply in #ship-room` - **`useNotificationSenderName`** — synchronous cache-only lookup (react-query users-batch entry cache → persisted label cache); a cold miss ships the fallback title immediately and warms the cache in the background. No toast delay, no new network machinery. - **`buildEventNotificationTarget` / `buildFeedItemNotificationTarget`** (`notifications/lib/target.ts`) — the click-through target payload is built in one place instead of three hand-rolled copies. ## 2. macOS notification clicks route to the target message (#3509) On packaged builds, clicking a notification focused the app but never navigated. Three independent gaps lined up behind one symptom: - **Reveal hang**: notification navigation now starts before the best-effort `unminimize → show → setFocus` chain, so a hung native invoke cannot gate click-through; the existing 1.5s reveal timeout remains as a secondary guard. - **Lost emit**: the Rust delegate queues the activation target *before* emitting `native-notification-activated`; a lost emit stranded the target with nothing re-draining the queue. The macOS listener now also drains on window `focus` / `visibilitychange` — delivered by WebKit independently of the Tauri event channel, and always produced by the click's own foregrounding. - **Silent no-op**: `commitNavigation` skips same-href destinations; `goChannel` / `goForumPost` / `openSearchHit` now accept `force`, and the notification activation handler passes it so a click always routes. Multiple queued activations are serialized FIFO with rejection containment so an older click cannot finish after and overwrite a newer one. Queue teardown now aborts already-running activations as well as pending ones; async forum-comment destination resolution rechecks ownership before community-scoped cache writes or routing. Diagnostic evidence from the macOS unified log (packaged v0.5.17): delegate confirmed live in release builds (`willPresent` honored — `(["list"])` presentations); a real click response at 09:25:06 reached `usernoted`, the app was fronted by LaunchServices, and no navigation followed. ## Verification - Fixed the stale relay-backed DM dedupe expectation: exactly one toast remains required, with sender-first title `alice` instead of channel title `alice-tyler` - Rebased onto `origin/main` and verified 15 focused activation/click tests, desktop typecheck/check, file-size gate, Biome on changed files, and `git diff --check` at `21a7e0cf5` - Notification + navigation + AppShell.helpers unit tests: 89/89 pass (includes new `notificationFormat.test.mjs`, `target.test.mjs`, `desktopActivations.test.mjs`) - Full desktop JS suite run by Pinky at f03979ea: 5133/5133 pass; pre-push hooks (desktop-test, desktop-typecheck, desktop-tauri-checks, rust-tests, file-size ratchet) all green on this branch - `tsc --noEmit` and changed-file Biome checks clean; re-verified after rebasing current `origin/main` - Click-through on a packaged build still needs human verification — @wesbillman, next release build is the real test. --------- Signed-off-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz> Signed-off-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Brain <1a02c72794dcd0f07058a353bc3a81f4028b8c77c92c87fce6d5c8b85970a20b@buzz.block.builderlab.xyz> Co-authored-by: Pinky <5f5ab050ec58ae208332edd544ebf705221e24c1b86d82a6ca07038a7a8f6ac9@buzz.block.builderlab.xyz> Co-authored-by: Carl <32a2e2c9d428ee08902cab75d956da2c1d235a22d4766b0dd4138bf6e2e5db1d@buzz.block.builderlab.xyz> |
||
|
|
54bbe7305b |
perf(desktop): resolve references without directory scans (#6328)
## Summary - keep the channel poll membership-only and fetch the open-channel directory only from explicit discovery surfaces - resolve unknown ID-bearing references through one bounded `get_channel_details` lookup, while name references remain limited to member channels plus a browse/search-warm directory cache - fail closed for fetched private metadata and cover cold lookup, warm/member hits, private visibility, and cached misses with mounted hook contracts --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> |
||
|
|
1934e83bf5 |
feat(workflows): add workflow editor (#6248)
**Category:** new-feature **User Impact:** Users can create, edit, duplicate, and deep-link to workflows in a responsive visual editor without losing unsupported YAML or unsaved work. **Problem:** Workflow editing was split across disconnected surfaces and lacked reliable URL state, lifecycle protection, and parity between Form and YAML modes. **Solution:** This adds a route-addressable editor foundation with stable pane identity, guarded dirty exits, lossless Form/YAML transitions, responsive workflow and channel controls, and matching reaction-filter execution support. <details> <summary>File changes</summary> **crates/buzz-workflow/src/lib.rs** Apply reaction trigger filters during workflow execution and cover target-message gating. **crates/buzz-workflow/src/schema.rs** Extend the reaction trigger schema with the editor-owned filter field. **desktop/src/app/navigation/useAppNavigation.ts** Add navigation helpers for explicit workflow create, edit, and duplicate editor modes. **desktop/src/app/routes/WorkflowsRouteScreen.tsx** Coordinate route state with the shared workflow library and editor dialog. **desktop/src/app/routes/lazyWorkflowsRouteScreen.ts** Share one lazy route component across workflow route entry points to avoid loading flashes. **desktop/src/app/routes/workflows.$workflowId.tsx** Parse workflow editor modes and pane deep links for workflow-specific URLs. **desktop/src/app/routes/workflows.tsx** Parse library-level create state and render the shared workflow route screen. **desktop/src/app/AppWorkflowEditorOverlayProvider.tsx** Host the shared workflow editor at the app-shell level so channel-originated workflow dialogs stay above the active channel instead of replacing it. **desktop/src/shared/context/WorkflowEditorOverlayContext.tsx** Expose route-independent open-existing and create-new workflow actions to channel settings. **desktop/src/features/workflows/ui/WorkflowEditorHost.tsx** Share editor loading, unavailable, and dialog lifecycle wiring between canonical workflow routes and the channel overlay. **desktop/src/features/channels/ui/ChannelManagementSheet.tsx** Add an experiment-gated, Canvas-style Workflows ingress below Canvas, including channel-scoped loading, error, empty, and list states plus open/create actions; disabled users issue no workflow query. **desktop/src/features/channels/ui/ChannelWorkflowsSection.tsx** Render the channel workflow list and New workflow action without pushing the existing settings sheet past its file-size ceiling. **desktop/src/features/workflows/ui/ChannelCombobox.tsx** Adopt the final channel presentation, portalled scrolling, and one-shot create-flow opening behavior. **desktop/src/features/workflows/ui/CreateWorkflowDialog.tsx** Remove the superseded create-only dialog in favor of the unified workflow editor. **desktop/src/features/workflows/ui/WorkflowCard.tsx** Open workflow cards in the detail and run-history modal while preserving explicit edit and duplicate actions. **desktop/src/features/workflows/ui/WorkflowDetailDialog.tsx** Present workflow Trigger/Steps in the shared modal chrome, with top-chrome ingress to a responsive right-side run-history inspector and an explicit edit action. **desktop/src/features/workflows/ui/WorkflowDialog.tsx** Unify create, edit, and duplicate lifecycle handling with URL panes, generated-name synchronization, dirty-exit guards, stale-write preservation, and protected webhook-secret handoff. **desktop/src/features/workflows/ui/WorkflowFormBuilder.tsx** Build the responsive Form/YAML editor shell, stable step selection, insertion and removal behavior, and lossless canonical-YAML synchronization. **desktop/src/features/workflows/ui/WorkflowStepCard.tsx** Align workflow step controls and presentation with the final editor interaction model. **desktop/src/features/workflows/ui/WorkflowUnavailableDialog.tsx** Show a non-disclosing loading or unavailable state for missing and inaccessible workflow links, with retry and close actions. **desktop/src/features/workflows/ui/WorkflowWebhookSecretDialog.tsx** Obscure one-time webhook secrets by default and require explicit confirmation before any close or navigation discards them. **desktop/src/features/workflows/ui/WorkflowsScreen.tsx** Connect library state and workflow actions to the route-addressable editor. **desktop/src/features/workflows/ui/WorkflowsView.tsx** Restore the responsive workflow library, create tile, cards, loading states, and shared action menu. **desktop/src/features/workflows/ui/workflowEditorPane.test.mjs** Cover pane parsing, serialization, and stable step-ID reconciliation. **desktop/src/features/workflows/ui/workflowEditorPane.ts** Define explicit trigger and stable step pane URL state. **desktop/src/features/workflows/ui/workflowYamlDocument.ts** Read and update header fields independently of full form validation so incomplete steps cannot clear or disable the workflow title. **desktop/src/features/workflows/ui/workflowYamlDocument.test.mjs** Cover document-level workflow header reads and writes for incomplete definitions. **desktop/tests/e2e/workflow-title-stability.spec.ts** Verify generated, renamed, saved, and duplicated titles remain stable while moving between trigger and step panes. **desktop/src/features/workflows/ui/workflowFormTypes.test.mjs** Cover lossless Form/YAML round trips and actionable fallback for unsupported fields. **desktop/src/features/workflows/ui/workflowFormTypes.ts** Own canonical workflow YAML conversion while preserving supported trigger and step fields. **desktop/src/shared/ui/PortalledScrollArea.tsx** Provide bounded scrolling for popovers rendered outside their dialog container. **desktop/src/shared/ui/popover.tsx** Allow workflow popovers to use the shared portalled scroll container. **desktop/tests/e2e/channels.spec.ts** Cover disabled and enabled Workflows experiment states, including suppressed queries while disabled, placement beneath Canvas, channel workflow listing and opening, channel-preselected workflow creation, and direct return to the channel Workflows panel after close, discard, or cancel. **desktop/tests/e2e/workflows.spec.ts** Exercise library actions, deep links, create/edit/duplicate lifecycle, dirty exits, responsive editor behavior, YAML safety, stale updates, and one-shot channel selection. </details> ## Reproduction steps 1. Open **Workflows** and confirm the responsive card library, create tile, card action menu, and card-to-detail/run-history modal navigation. 2. Open `?view=create`; confirm the channel chooser opens once, the trigger inspector stays hidden until a channel is selected, and closing the chooser does not make it reopen after unrelated edits. 3. Create a workflow, switch between Form and YAML, add and remove steps, refresh a pane deep link, and confirm the selected trigger or stable step remains addressable. 4. Edit or duplicate a workflow, make an unsaved change, and confirm close, Escape, browser navigation, and route target changes require discard confirmation while pane-only navigation does not. 5. Enter unsupported YAML and confirm Form mode gives an actionable fallback without rewriting the definition; verify reaction triggers preserve and execute their filter. 6. Open a channel’s settings, select **Workflows** below **Canvas**, and open or create a workflow; confirm the shared modal stays over the channel, the channel URL does not change, New workflow preselects that channel, and closing or discarding returns directly to the channel’s **Workflows** panel. 7. In create, edit, and duplicate modes, move between the trigger and incomplete step panes and confirm the generated or edited title remains visible and editable. ## Screenshots Fresh captures from product head `c5c3abc91a71fe511d43e6cc9168b1626d0c217c`; the later review-guidance fix does not alter these pictured states. ### Workflow library and actions  ### Workflow editor — wide  ### Workflow editor — narrow inspector overlay  ### Workflow editor — active channel overlay  ## Review feedback addressed - Corrected Diff Posted condition guidance to use the executor-supported `str_contains(trigger_text, "deploy")` syntax and added a visible Playwright regression assertion. - Scoped direct workflow-detail navigation coverage to the stable `Edit workflow` dialog name, asserted the workflow title separately, and retained the trigger-node assertion. - Routed dirty channel-overlay Duplicate/Edit transitions through the existing discard confirmation, preserving the original YAML draft when the user keeps editing. - Made workflow deletion await relay success, remain non-dismissible while pending, and retain the confirmation/editor/draft with an actionable inline error on rejection. - Gated the channel-settings Workflows ingress and channel workflow query behind the `workflows` experiment, with defensive rendering if the flag changes while that view is active. - Matched the Reaction Added trigger to the prototype reaction picker, preserving native/custom/legacy values in canonical YAML and providing an explicit clear action. - Kept a channel’s Workflows panel mounted beneath channel-origin editors so clean close, dirty discard, and create cancel return directly to that panel without changing the channel URL. ## Verification Verified at exact pushed head `76ebba7b7ace1e13445744200da70ca9da231b7e`: - Push hooks passed: destination-org policy, branch-skew, differential file-size, Desktop checks, TypeScript typecheck, and **5,115/5,115** Desktop unit tests - Focused channel lifecycle E2E passed: dirty edit discard returns directly to the channel Workflows panel; create cancel does the same while preserving the channel URL and preselected channel - E2E production build, standalone TypeScript typecheck, and Biome checks on both touched files passed - Reaction-picker regression spec remains recorded at `dc28ffa98ec34b4e0656757fb2dd93e60a84674d`: **5/5 passed** (picker interaction, canonical YAML persistence/clear, legacy-value preservation, save/reopen round trip, narrow viewport containment) - Blox existing workflow E2E regression set at `dc28ffa98ec34b4e0656757fb2dd93e60a84674d`: **36/36 passed** - Earlier blocker and review regressions remain recorded at `3760c3d657a525f5af98e8d0f98bdd03999d8e61`: **5,114/5,114** Desktop unit tests and the dirty-overlay/deletion focused checks - Working tree clean; local branch, remote branch, and PR head all match the exact SHA above ### Related issue None found. Closest prior work: #231. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
e5d1dfef7b |
feat(desktop): close Buzz window with Cmd+W (#6314)
## Summary - close the current Buzz window with `Cmd-W` on macOS - keep Buzz Term's existing `Cmd-W` behavior when the terminal owns input, where it closes the active terminal tab - cover the close-window chord and its modifier/composition guards with unit tests ## Why Buzz removes macOS's native Close Window menu accelerator so the webview can conditionally route `Cmd-W` to Buzz Term. That also left normal Buzz mode without the standard close-window shortcut. The new app-level bubble-phase handler restores window closing while allowing the terminal's capture-phase handler to retain priority. For the main window, the existing Rust close-request handler keeps the webview alive and hides it to the tray, matching the titlebar close button. ## Validation - desktop unit tests: 5,093 passed - focused close-window and terminal shortcut tests: 28 passed at `71a2cce42013440e297c7b237d45b20e9638078d` - desktop TypeScript: passed - desktop checks: passed (pre-existing warnings only) - workspace Rust clippy: passed - Tauri Rust clippy/check: passed - desktop build: passed - mobile format/analyze: passed - Tauri tests: 2,592 desktop tests and 26/27 terminal tests passed; the existing `default_prog_child_observes_the_login_argv0` PTY fixture repeatedly timed out waiting for `/bin/sh`, including when run alone, and is unrelated to this TypeScript shortcut change --------- Signed-off-by: Matt Kursmark <kursmark@squareup.com> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
2b7be9340d |
feat(desktop): make Projects workspaces selectable (#6368)
## Summary Projects workspaces now support consistent selection across projects, repositories, reviews, tasks, and channel results. A user can collect related entities, see the active selection without losing their current workspace, and pass a bounded, injection-safe representation to an agent for discussion. Detail views now keep repository source and availability explicit while preserving contextual actions across local and remote workspaces. Missing or inaccessible repositories produce a recoverable state instead of leaving the workspace ambiguous. This is part 2 of the Projects v6 stack. Parts 3 and 4 will add context-aware collaboration and navigation/detail-page polish. ### Related issue Related: #6335 ### Testing - Full pre-push gate passed: file-size checks, Biome, TypeScript, Desktop unit tests, Tauri checks, and Rust tests - Project context safety suite: 9/9 passed, including bounded untrusted overview data and hidden-context stripping - Updated Playwright coverage exercises grouped selection, selection-aware agent context, repository source states, and detail actions --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
bbd20fae75 |
perf(desktop): move five hot renderer paths from JS into Rust (#6024)
## Summary
Moves five hot renderer paths out of JavaScript and into Rust, targeting
main-thread CPU on the desktop app: wakeups, IPC round trips, and
per-frame JS work during reconnect storms, catch-up, and archive sync.
Five commits, reviewed and ratified sequentially (each round included
independent re-review, mutation-tested coverage, and full-suite gates at
pinned heads):
| Commit | Change |
|---|---|
| `9b5a82ab9` | Batch inbound relay frames in the native websocket
plugin: N run-loop wakeups collapse into 1 per 8ms window, byte-bounded
so no batch crosses the Tauri direct-execute threshold onto the slow
fetch path. NIP-42 AUTH bypasses the window. |
| `8b047a35e` | Move the local-archive subscription into Rust: the
renderer no longer sees archived frames at all. Shared
`native_relay_client` (one authenticated socket per relay+pubkey,
declarative subscription reconciliation, CLOSED-driven retry with
write-time eviction), lease/epoch lifecycle ownership so delayed IPC
cannot resurrect a stopped task. |
| `609962b49` | Move persona catalog fetching into Rust: signature
verification (~473us/event, ~0.24s per 500-event page formerly on the
webview thread) now runs under `spawn_blocking`; one projected DTO
crosses IPC. |
| `38afea0a0` | Batch unread catch-up behind one IPC call: N per-channel
REQs/awaits become one command with global two-pass classification,
making the completion-order race unrepresentable. Both IPC DTO surfaces
pinned by wire tests asserting serde output against the TS contract. |
| `b955f528b` | Move observed unread state into native SQLite: scoped
WAL DB with a sequence/revision protocol, transactional localStorage
migration, epoch-tagged rebuild detection. Membership no longer
serializes five capped arrays per catch-up: measured 327.4 KiB before,
0.2 KiB after at the same populated 5x1000 fixture. |
Full design rationale, failure matrices, and verification detail are in
the individual commit messages.
### Known behavior notes
- **Native migration is a one-way door per profile.** A successful
native open sets `migration_complete` in the first open's transaction
and the renderer then deletes the legacy localStorage key
(`useObservedUnreadPersistence.ts:290-294`,
`observed_unread.rs:420-442`). The one-release JS fallback remains for
native open *failure*, but a profile that has completed migration cannot
return to the JS store. Relevant if a rollback path is ever needed.
- **The membership seed is built unconditionally at startup**
(`useUnreadChannels.ts:245-250`), before any `isNative()` check. It is
ref-cached and small, but it is a startup cost the baseline does not
pay, including on the fallback path.
### Related issue
None found. Work originated and was reviewed in the Buzz
`buzz-gui-performance` channel.
### Testing
**Current head is `9a8128ba4d2d695113505162be388fcec62cda0a`. For
receipts at that head, see [Round-3
receipts](#round-3--final-head-9a8128ba4) at the bottom; the block
immediately below is the historical record for the original five-commit
pack and its counts are superseded.**
The receipts in this subsection are at exact pack head
`b955f528b6dfa34db089b21cc718fba0f9284d6f` (tree oid `dcc711a66`),
verified by three independent instruments (implementer, reviewer,
gatekeeper), with `git rev-parse HEAD` confirmed in the same shell as
each run:
- `cargo test --lib` (full package): **2511 passed / 0 failed** (+7 +3
in the other targets)
- `pnpm test`: **4920 / 4920 across 74 suites**
- `cargo fmt --check`, `clippy --all-targets -D warnings`, `tsc
--noEmit`, `pnpm check` (including the file-size ratchet): all clean
- Mutation controls on the final review round: badge-lane controls A and
B each turn exactly one test red; seed-guard (M4) and
anchor-monotonicity (M5) mutants each killed by exactly their own
witness; restored bytes fully green
- The observed-unread native path is exercised by an in-tree
`__TAURI_INTERNALS__` protocol rig (entry, replay, gap, rebuild,
markers, membership, badge lane), added after review found the original
suite never left the localStorage fallback
**Open gates, stated plainly (status as of head `9a8128ba4`):**
- **Paired CPU benchmark still not run.** The three-arm design (baseline
`78cbffeb6` vs pack vs a null arm with native forced off, fresh profile
per arm) was instrument-reviewed but never executed. The IPC payload
reduction above is measured; end-to-end CPU improvement is predicted,
not measured. This gate is unchanged since `b955f528b`.
- **Live-local gate at the final head: GREEN at `9a8128ba4`.** A real
native Tauri live-local pass per `TESTING.md` was run at this exact
head. Receipts:
`.scratch/pr6024-final-9a8128ba4d2d695113505162be388fcec62cda0a/`,
`MANIFEST.txt` `start_utc=2026-08-17T21:58:07Z`, `FINAL_ATTESTATION.txt`
`end_utc=2026-08-17T22:20:47Z` — both after the commit's `21:32:51Z`,
and the build worktree's HEAD is `9a8128ba4` with a clean tree. **I
opened these files rather than relaying the summary**, and recomputed 6
SHA256SUMS entries against the sealed manifest (6/6 match). From the raw
artifacts: historical catch-up persisted 10 seeded events into the
native `observed-unread.db`; a realtime event advanced it 10→11; the
relay was killed and restarted with Desktop up and an event sent after
recovery advanced it 11→12 within 2 polling ticks; Desktop was then
stopped, an event sent offline, and after relaunch with the appdir
preserved catch-up advanced it 12→13. `relay.log` shows 2 app WebSocket
connections, the restart-close signal, and fresh connections after each
recovery — the app-attributable socket evidence the previous round
lacked. The preserved appdir database is still on disk and `select
count(*) from observed_events` returns **13**. The ignored real-relay
archive wire test passed **1/1** at this head. This exercises the
`lib.rs` / `e2eBridge.ts` merge recombination under a real app and
blocker 2's failure-mode family (relay killed mid-session, app killed
with offline traffic in flight, sync did not go dead). It does **not**
directly force the precise overlapping B/C archive-ownership timing
window through the GUI; that invariant is pinned by the compiled
`drop(owner)` mutation proof below, and is deliberately not relabelled
as live race evidence. **Retraction retained for the record:** an
earlier revision of this description said live-local was GREEN at
`9a8128ba4` while citing a run executed against `f079d0914`, the pre-fix
head, roughly three hours before `9a8128ba4` existed. That claim was
false and was corrected before this run existed. The GREEN above rests
on the new receipts only.
- **Workspace-wide `cargo test --workspace` is red on `main`, not on
this PR.** The exact-head live run's workspace logs contain honest
`rc=101` results. All are in crates this PR does not touch: `git diff
origin/main...9a8128ba4` is **empty** for `crates/git-sign-nostr`,
`crates/buzz-relay`, and `crates/buzz-pair-relay`.
`git-sign-nostr::tests::test_parse_envelope_rejects_invalid_oa_pubkey`
fails **deterministically on clean `main`** — reproduced independently
in fresh worktrees at merge-base `a282e0643` and at current tip
`7f61cf431` (55 passed / 1 failed, same assertion at `lib.rs:2136`).
Root cause: `nostr` 0.36→0.44 changed `PublicKey::from_hex` from a
curve-point parse to a plain hex decode, so an all-zeros (off-curve) key
now parses; measured with a standalone probe, `from_hex("0"*64).is_ok()`
is `false` on 0.36.0 and `true` on 0.44.7. Filed as block/buzz#6175,
with the observation that nothing in CI runs this crate's tests (`just
test-unit` enumerates packages and omits it; `server-cross-compile`
compiles it and runs nothing). The mesh-demo and pair-relay failures in
the same logs are timing flakes in equally untouched crates. **None of
these gate this PR.**
- **Full smoke + integration: GREEN at `9a8128ba4`.** CI run 32072147445
is `success` with every job green — all 4 Desktop Smoke E2E shards, both
Desktop E2E Integration shards, Desktop Core, Desktop Build (macOS),
Desktop E2E Relay, Rust Lint, Windows Rust, DCO, Dead Token Reference
Guard. Attempt 1 was cancelled by an infrastructure hang, not by this
code: both integration shards stalled 19m37s inside `Install Playwright
system dependencies` (an apt install that runs before the e2e bundle is
built or the relay starts), so no test executed and all downstream steps
were skipped. The same step took 16s at `9128b9389` and 12s/18s on
attempt 2 of these same bytes. Attempt 2 was a job rerun, not a push;
the head is unchanged.
---
Opened by Eva (agent) on Tyler's behalf at his request.
---
## Review round 2 — head `9128b9389`
Carl's review
[4953970089](https://github.com/block/buzz/pull/6024#pullrequestreview-4953970089)
raised two blockers against `f079d0914`. Both were confirmed real at the
source by a second reader and both are fixed in `9128b9389`.
**Blocker 1 — recovery dropped the mutation whose ingest failed.** Every
captured mutation now gets one retry after `reopen()` refreshes
sequence/revision, which is safe because ingest is idempotent (events
upsert `DO NOTHING`, channel latest advances by `MAX`, membership is
`INSERT OR IGNORE`, and a replayed sequence returns a snapshot rather
than reapplying). `snapshotRequired` takes the same recovery path
instead of being treated as success. If reopen and retry both fail,
native is explicitly degraded and equivalent state is applied to the JS
fallback so `isNative()` stops reporting healthy. Regressions assert the
*originally rejected* marker, destructive clear, and membership delta
each survive; removing the retry takes 5 tests red including all three.
**Blocker 2 — a superseded archive start could tear down the newer
scope's relay session.** `begin` now returns an `ArchiveOwnership` token
that holds both ownership guards, and `archive_session` requires one, so
session acquisition happens inside the ownership critical section rather
than after it.
### Deviation from the requested regression test for blocker 2
Carl asked for "a production-wiring regression that pauses B after
`begin`, fully installs C, resumes B, and proves only C's session and
subscriptions remain live." **We did not write that test, deliberately,
and wrote a stronger one instead.**
Under this fix that scenario is unreachable by construction. The fix is
not a revalidation check that a test could observe passing or failing —
it is a type-level fence: `begin` hands the winner a token that holds
the ownership locks, and `archive_session` cannot be called without one.
A test that staged "B pauses after `begin`, C fully installs" would be
staging a state the type system now forbids, so it could only pass
vacuously.
Two things close the blocker, and only one of them is a test:
1. *A superseded start cannot reach `archive_session` at all.* Enforced
by the compiler, not by a test. `ArchiveOwnership` is un-constructible
outside `archive::sync`. Verified by attempting both bypasses: calling
`archive_session` without a token fails **E0061**, and forging the token
from a real non-archive caller fails **E0451** ("fields `_latest` and
`_running` are private"), with a second independent barrier because
`RunningSync` is itself a private type.
2. *While an owner holds the token, no newer start can claim.* This is
the property the token's usefulness rests on, and it is what the new
regression pins —
`a_newer_start_cannot_claim_while_the_owner_holds_its_token`.
That test is aimed at the one mutant that actually threatens this
design: keeping the token but releasing the guards inside `begin`, which
is what someone reaches for to avoid holding a lock across an await. It
**compiles**, it keeps every other lifecycle test green, and it restores
the exact race. Against it the new test goes **single-red** — the other
26 tests in the module stay green.
Proving mutual exclusion over the whole claim → acquire → install unit
is strictly stronger than staging a pause the type system forbids, which
is why we chose it.
Also recorded in the code, in `ArchiveOwnership`'s doc comment: what
this token does **not** cover. It serializes archive lifecycle against
archive lifecycle, not against a plain `NativeRelayClient::session()`
call from a non-archive feature. That window predates this PR and is
theoretical today given renderer gating, so it is documented rather than
fixed here.
### Round-2 receipts at `9128b9389`
- `cargo test --lib`: **2513 passed / 0 failed**
- `pnpm test`: **4926 / 4926**
- `cargo clippy --all-targets -D warnings`, `cargo fmt --check`, `pnpm
check`: rc 0
- Merged against `origin/main` in a scratch worktree (clean, zero
conflicts) and re-gated on that merged tree: **2544** Rust / **4956**
frontend, both 0 failed
---
## Round 3 — final head `9a8128ba4`
`9a8128ba4d2d695113505162be388fcec62cda0a` is a merge of `origin/main`
(`a282e0643`) into the pack branch, taken **before** final verification
so that verification would run on the bytes CI builds rather than on a
head that was about to move.
The merge is textually clean but touches two files the pack also
touches, `desktop/src-tauri/src/lib.rs` and
`desktop/src/testing/e2eBridge.ts`, so recombination was checked
explicitly rather than inferred from the absence of conflicts: **all 342
pack-added lines in those files survive (0 missing), and all 17
main-added lines survive (0 missing)** — no silent revert in either
direction. Main's contribution is a `#[doc(hidden)] pub use
print_agent_access_owner_only_probe_if_requested` in `lib.rs`, plus a
Bumble→Pollen rename and workflow revision / `workflowUpdateError` mocks
in `e2eBridge.ts`.
### Receipts at `9a8128ba4`
Implementer, in a shell with `git rev-parse HEAD` confirmed at this SHA:
- `cargo test --lib` (full package): **2544 passed / 0 failed** / 18
ignored
- `pnpm test`: **4956 / 4956** across 74 suites
- `cargo clippy --all-targets -D warnings`, `cargo fmt --check`, `pnpm
check`: rc 0
- The 4 remaining `pnpm check` findings (`channelMutesStorage.test.mjs`,
`channelStarsStorage.test.mjs`, `terminal.css`,
`empty-edit-delete.spec.ts`) are all in files this PR never touches
- All 7 pre-push hooks green, including `branch-skew`, which now passes
on its own because the branch contains `origin/main`
Independent reviewer, same exact head, own instruments:
- `cargo test --workspace`: green; `cargo clippy --workspace
--all-targets -- -D warnings`: green *(a later workspace run at
`9a8128ba4` hit pre-existing `main`-side failures in untouched crates —
see the workspace-gate entry under Open gates above; the difference is
flake timing plus which run happened to reach `git-sign-nostr`, not a
change in this PR)*
- `pnpm test`: **4956 / 4956**; `pnpm check`: rc 0, same known findings
outside touched files
- **Mutation proof of the blocker-2 fix:** dropping the ownership
token's hold (`drop(owner)`) turns
`a_newer_start_cannot_claim_while_the_owner_holds_its_token` red;
restoring the shipped bytes returns the suite green. The invariant that
a superseded start cannot claim or disturb a newer archive session is
pinned by a test, not just by the type system.
- **Live-local at this exact head: attested and verified.** The
mis-cited `f079d0914` run that originally appeared here is retracted; a
real run at `9a8128ba4` replaced it, and its receipts were opened
file-by-file (not relayed) by a second agent. See the live-local entry
under Open gates above for the receipt path, wall-clock bounds,
store-count chain, and socket evidence.
- Verdict: minimalness **9/10**, elegance **9/10**, correctness
**9.5/10**. **Score restored** now that the exact-head live-local run
exists and its provenance has been checked from the files (SHA-named
receipt dir, self-dating manifest, receipt mtimes after the commit time,
sealed digests spot-checked). Gatekeeper re-bless at `9a8128ba4`:
**9/10, merge recommended.**
The paired CPU benchmark remains the one gate not run. It is a
measurement of the pack's *benefit*, not of its correctness, and it is
stated as unrun rather than estimated.
---------
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Perci <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
|
||
|
|
87f8ff82ae |
feat(desktop): refine repository-aware project workspaces (#6003)
## Summary - Makes Projects repository-aware across browsing, branch and tag selection, local and remote source management, work items, commits, and contextual repository actions. - Refines project details into consistent single-column reading surfaces with a resizable, section-aware context pod, while keeping project conversations available in the attached chat panel. - Adds persistent sidebar project navigation, direct entity links, repository discussion channels, contributor identity matching, and consistent loading and activity presentation. - Keeps branch-specific controls on code-oriented sections while Tasks, Reviews, and Channels remain repository-scoped, reducing misleading context and actions. Replacement for #5981 with an identical final tree flattened into one signed-off commit because the required DCO check suite remained stalled. ### Related issue N/A ### Testing - [x] Desktop pre-push checks, TypeScript typecheck, and unit tests - [x] E2E production build with `pnpm build:e2e` - [x] Focused Playwright smoke coverage for project workspace, task, review, commit, sidebar, and contextual-panel behavior - [ ] Attach final before/after screenshots after design review --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz> Co-authored-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz> |
||
|
|
7e2651791d |
Add font size and conversation density preferences (#5644)
## Summary - Add separate **Font size** and **Conversation density** controls in Appearance. - Use a 13 / 14 / 15px conversation text ramp for Smaller / Default / Larger while scaling interface typography through a shared virtual-rem system. - Keep layout geometry fixed while typography changes, and compose Cmd +/- text zoom on top of the selected preference. - Let Compact, Comfy, and Spacious control conversation row spacing plus paragraph and list rhythm in Markdown content across Buzz. - Preview font size and density together with click-and-drag comparison; interrupted scrubbing restores the saved preference. - Synchronize preferences across open Buzz windows, including full local-storage clears during sign-out or dev reset. - Promote the segmented control to shared UI and make Settings rows adapt to their card width. ## Validation - `just ci` - 4,965+ desktop unit tests across the final merged branch - 1,465 mobile tests through the full repository gate - Focused Playwright coverage for font-size/density independence, drag preview cancellation, Inbox geometry, thread rendering, keyboard zoom, and cross-surface typography - Fresh E2E build against the final merged branch --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Co-authored-by: morgmart <98432065+morgmart@users.noreply.github.com> |
||
|
|
c442a90a17 |
fix(desktop-chrome): preserve balanced layout when sidebar collapses (#6000)
**Category:** fix **User Impact:** The desktop content surface now keeps balanced chrome spacing when the sidebar is collapsed or multiple communities are visible, with a cleaner sidebar transition. **Problem:** Collapsing the left sidebar removed the content surface's left inset, while multi-community layouts also exposed uneven visible gutters and sidebar content during the exit transition. **Solution:** Preserve an 8px visible gutter around the content surface, clip and fade only the sidebar's inner content while it trails off canvas, and leave the opaque shell in place until the existing visibility transition completes. <details> <summary>File changes</summary> **desktop/src/app/BuzzThemeSurfaces.tsx** Preserves a balanced visible left gutter after accounting for the content-edge shadow. **desktop/src/shared/ui/sidebar.tsx** Clips the collapsing sidebar, disables interaction off canvas, and fades/translates its inner content without exposing a different background. **desktop/tests/e2e/community-rail.spec.ts** Adds gutter assertions for single-community collapsed-sidebar and multi-community layouts. **desktop/tests/e2e/sidebar.spec.ts** Covers the collapse opacity, translation, clipping, pointer-event behavior, and restoration on reopen. </details> ## Reproduction steps 1. Launch the desktop app with the Buzz theme and at least two communities. 2. Collapse the channel sidebar. 3. Confirm the main content surface keeps equal visible left and right gutters and does not overlap the community rail. 4. Reopen and collapse the sidebar again; confirm its contents fade and trail right while the chrome background stays opaque, then restore fully when reopened. ## Screenshots | Single community | Multiple communities | | --- | --- | |  |  | ## Validation - `pnpm build:e2e` - focused sidebar integration tests: 2 passed - collapsed multi-community smoke regression: 1 passed - Biome check on all four changed files - pre-push desktop check, typecheck, and test hooks - `git diff --check` Related issue/PR: none found. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> |
||
|
|
122a8b8988 |
Projects v3: unify sharing, discussions, and issue ownership (#5792)
## Summary Projects v3 makes repository work shareable, discussion-aware, and easier to scan in one coherent workspace. People can copy canonical links, reopen the exact workspace tab, understand issue and pull-request context at a glance, find related channel conversations, and assign or unassign issues across Desktop and CLI. - **Unified workspace** — top-level sections sit above repository controls in one rounded workspace, with navigation positioned close to the page heading. README and Files retain branch selection; every section has a labeled icon header, and Issues and Pull Requests expose creation from a consistent right-aligned action. - **Repository management** — the repository selector is always available, including single-repository projects. Its integrated add flow lets project owners create a repository manually or select an existing repository without a separate toolbar button. - **Readable work-item lists** — issue and pull-request rows use plain-language context instead of opaque metadata. Files, commits, issues, pull requests, channels, and contributors share consistent row density and right-aligned timestamps, while deterministic fallback-avatar colors keep participants distinct on light backgrounds. Inbox pull-request metadata wraps between complete phrases and truncates long channel names instead of compressing copy into narrow columns. - **Reliable entity links** — projects, repositories, issues, pull requests, and commits have canonical `buzz://` links, preview cards, OS deep-link routing, and tab-aware navigation. Reopening the same link re-applies its destination instead of leaving the user on a locally selected tab. - **Related conversations** — repository and work-item views surface channels discussing the current entity, including participants, channel navigation, message context, and an explicit notice when discovery reaches its 500-result cap. - **Reversible issue ownership** — trusted assignment and unassignment events work across Desktop, Tauri, `buzz-sdk`, and `buzz issues`. Assignees appear in project views and the assigned inbox, while authorized users can remove assignments directly from the assignee row. Assignment state is derived chronologically from labeled Nostr notes. Issue authors and repository owners may change any assignee; other users may only assign or unassign themselves. Shared golden fixtures keep entity-link grammar and validation aligned across TypeScript and Rust. The branch also updates `webbrowser` to the patched release for RUSTSEC-2026-0257. ### Related issue N/A. ### Testing - [x] `just ci` — formatting, lint, typechecking, unit tests, and builds passed - [x] Full pre-push suite — organization, branch-skew, Desktop checks, typechecking, and tests passed on the latest push - [x] `cargo test -p buzz-cli` and focused `buzz-sdk` assignment tests passed - [x] Focused Tauri recipient-note and 500-result search-limit tests passed - [x] Desktop entity-link and issue-assignment unit tests passed - [x] Playwright smoke coverage passed for assignment, repeated entity-link navigation, repository create/select flows, section headers and actions, timestamp alignment, timeline icons, sentence-style issue/PR metadata, header spacing, avatar contrast, and Inbox metadata at stacked and side-rail breakpoints - [ ] Manual staging pass: link round-trips, Channels tab, assignment flows, and inbox routing ### Screenshots Pull requests explain who opened the request, where it lives, and which branch it comes from; fallback avatars remain visually distinct.  Issues use the same sentence-style hierarchy while keeping status and recency easy to scan.  The wide Inbox detail keeps author, timestamp, and origin context readable beside its metadata rail.  [View the complete six-state Projects v3 screenshot set](https://github.com/block/buzz/pull/5624#issuecomment-5268039672) and [the compact/wide Inbox comparison](https://github.com/block/buzz/pull/5624#issuecomment-5268614585). --- > Supersedes #5624, whose head commit accumulated permanently-queued required check suites (block-dco-check et al.) that GitHub never dispatched. History flattened into a single signed-off commit on latest main; tree verified byte-identical (`git merge-tree`) to merging the original branch into main. --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Co-authored-by: Wintermute <3f1797424fd9ad6653a83665c660517777cd7f8c228c0d5907f49e01537f3ca5@buzz.block.builderlab.xyz> |
||
|
|
068a83b097 |
feat(huddle): cut voice-turn time-to-first-audio from ~1.0 s to ~0.35 s (env-gated latency levers) (#5671)
## Overview
**Category:** feat (env-gated experiment + one exact always-on
optimization)
**Problem:** Speech-end -> first TTS audio through the desktop huddle
pipeline measures **924–1087 ms** on an M4 Max with a 0 ms LLM leg.
Voice turns feel sluggish no matter how fast the agent replies. Baseline
breakdown: ~300 ms hardcoded VAD silence flush + ~150–250 ms Parakeet
decode + ~380–550 ms TTS synthesis before the first player append.
**Outcome:** With all levers enabled, e2e time-to-first-audio measures
**347–384 ms** (307–357 ms on a longer utterance) on the same hardware,
harness, and production pipelines. Defaults preserve production behavior
everywhere except one deterministic, bit-exact cache win.
## What's in here
### Levers (all default-off, env-gated)
| Lever | Env | Effect (measured medians, short utterance) |
|---|---|---|
| Speculative Parakeet decode | `BUZZ_STT_SPECULATIVE=1` | STT leg ->
~max(flush, decode) |
| Streaming TTS synthesis | `BUZZ_TTS_STREAMING=1`,
`BUZZ_TTS_EMIT_FRAMES` | first audio 380–550 -> 211–320 ms (emit=12,
bit-exact) |
| ONNX intra-op threads | `BUZZ_STT_THREADS`, `BUZZ_TTS_THREADS` | TTS
first audio 211–320 -> 129–180 ms (4 threads) |
- **Speculative decode** starts the Parakeet decode at the *first*
silent VAD frame, overlapping it with the flush window. Resumed speech
invalidates the result (voiced-frame-count check); held silence emits it
instantly at the flush boundary.
- **Streaming TTS**: new `synth_chunk_streaming` (buzz-voice)
interleaves the Flow LM frame loop with incremental *stateful* Mimi
decoding, emitting PCM deltas to the player via the existing
`PlaybackChunkAudio` decoration. At `emit_frames=12` (the decoder's
native chunk) streamed audio is **bit-identical** to the batch path —
verified by the ignored test
`incremental_stateful_decode_matches_batch_decode` (max|diff|=0).
Smaller deltas are faster but diverge (~23 dB SNR; decoder intra-chunk
lookahead), hence the default of 12.
> **Removed after live testing:** the `BUZZ_STT_FLUSH_MS` flush-window
override. Lowering the silence window below natural mid-sentence pauses
(the fast-path recipe said 150 ms) split single spoken sentences into
multiple messages and confused the listening agents. The window is a
turn-taking quality knob, not a latency lever — it is now fixed at the
production 300 ms value.
### Push-to-talk grouping fix (always-on)
A held push-to-talk shortcut is an explicit "I am not done talking"
signal, so silence never ends the utterance while it is held — even when
the microphone is also manually open. The utterance flushes on shortcut
release (existing transmit-edge flush); a manually open mic with the
shortcut up keeps normal VAD pause flushing. Gate is the pure
`vad_flush_allowed` function with a unit-test truth table.
### Always-on (exact): voice-conditioning cache
Phase profiling (`BUZZ_TTS_PHASE_LOG=1`) showed a fixed ~160 ms
`condition_voice` Flow-LM pass on *every* chunk, re-deriving the same
post-conditioning state for the same reference voice. The state is now
snapshotted after first computation and restored per chunk (dtype-tagged
tensor copies, keyed identically to the existing `cached_voice`).
Deterministic — same tensors in, same tensors out. The default path's
TTS leg drops from 380–550 ms to 225–355 ms with no configuration.
### Bench harness
`huddle::latency_bench` (`#[cfg(test)]` + `#[ignore]`) drives the real
`SttPipeline` and `TtsPipeline`, feeding a 48 kHz WAV in real-time 100
ms batches (AudioWorklet cadence) with a configurable fake LLM in place
of the relay leg, timing speech-end -> transcript -> speak() -> first
accepted player append.
```
BUZZ_STT_SPECULATIVE=1 BUZZ_TTS_STREAMING=1 \
BUZZ_TTS_THREADS=4 BUZZ_STT_THREADS=2 \
BUZZ_BENCH_WAV=<48k f32 mono wav> \
cargo test --release -p buzz-desktop --lib huddle::latency_bench -- --ignored --nocapture
```
## Tradeoffs to weigh before promoting any lever to a default
- **Speculative decode**: the speculative buffer has ~1 silent tail
frame vs ~19; observed one CTC wobble ("fail" vs "failed") in 24 turns.
Mitigation if productionized: zero-pad the speculative buffer to match
the flush-path shape.
- **Threads**: defaults stay 1 pending the min-spec (4-core Intel) A/B
flagged in the existing `STT_NUM_THREADS` comment.
- **Streaming at emit<12** is NOT the same waveform — don't ship below
12 without an ear pass.
## Validation
- Full desktop lib suite: **2408 passed / 0 failed** at this head
(`18fab2e1c`).
- buzz-voice suite green; bit-exactness test passes against the
production batch decode.
- Defaults-only bench rerun stays in the baseline family everywhere
except the exact conditioning-cache win (stt 525–532, tts 225–355).
- `cargo clippy --workspace --all-targets -- -D warnings` + fmt clean
(pre-push hook battery green).
Measurement notes with per-lever logs: Eva's workspace,
`RESEARCH/HUDDLE_E2E_LATENCY_OPTIMIZATION_2026_08_12.md` +
`RESEARCH/HUDDLE_E2E_STT_FAKELLM_TTS_BASELINE_2026_08_12.md`.
## Suggested promotion order
1. Conditioning cache (in this PR, always-on, exact).
2. Streaming TTS at emit=12: bit-exact audio, biggest UX win — needs the
env-gate removed + barge-in soak + an ear pass on a real huddle.
3. Speculative decode with silence padding: near-free ~100–150 ms.
4. Threads: after min-spec A/B.
---------
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Signed-off-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Signed-off-by: Mari <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Signed-off-by: tlongwell-block <tlongwell@block.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz>
Co-authored-by: Max <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Co-authored-by: Mari <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Co-authored-by: tlongwell-block <tlongwell@block.xyz>
|
||
|
|
0571f5455b |
Polish glass Huddle tray behavior (#5590)
## Summary - inset the in-app Huddle tray with four rounded corners and even 8px spacing when Glass background is enabled - keep the popped-out Huddle dock full-width - hide and suppress Glass background on Linux ## Why The in-app tray reused the opaque backing needed by non-glass windows, which covered the native vibrancy around it. Linux does not support this window treatment. ## Testing - `pnpm -C desktop build:e2e` - focused Appearance and Huddle Playwright smoke tests - pre-push desktop checks, typecheck, and 4,666 unit 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> |
||
|
|
59f613c404 |
fix(desktop): defer foreground resume work (#5696)
## Summary - defer foreground resume work until the activation task has returned, a frame has painted, and a trailing task gets a turn - centralize app-focus subscribers and remove the broad TanStack `refetchOnWindowFocus` fan-out - coalesce relay recovery and preserve an explicit deferred refresh only for workflow data without a polling/push freshness path - defer the notification permission native check while keeping blur and cheap correctness signals immediate ## Why Buzz Desktop 0.5.10 can spend roughly 1.5 seconds in the WebKit window-focus listener/microtask checkpoint before returning to the run loop. Focus currently fans out into query refetches, React polling updates, relay reconnect/replay, and native work in one activation turn. This patch establishes an interaction-first foreground boundary rather than letting those consumers compete with the activating input and first paint. ## Validation - focused foreground/workflow/relay tests: 18/18 passed before commit - `pnpm --dir desktop typecheck`: passed before commit - pre-commit desktop check and file-size gate: passed - pre-push desktop check, typecheck, and full desktop unit suite: 4,743/4,743 passed at `704e7b4b6618fafce655bb2b07c7a9fe0fc8c643` - Princess Donut independent adversarial review: PASS after two lifecycle/freshness blockers were resolved ## Manual test 1. Install the PR build and use Buzz long enough to populate channels, home, workflows, agents, and other polling surfaces. 2. Switch to another app for 30-60 seconds. 3. Return by clicking Buzz and immediately click a channel or scroll. 4. Confirm the first interaction and paint are prompt, then confirm channels/home/workflows refresh and a degraded relay reconnects after the activation boundary. 5. Repeat while rapidly switching away again to verify no resume work starts after focus has been lost. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
63f961c7e4 |
Refine channel settings and profile panels (#5574)
## Summary - simplify channel settings into concise detail, member, canvas, and action sections - align human and agent profiles around shared rows, segmented tabs, and top-level actions - add agent runtime presentation, sticky glass behavior, and scroll-linked action transitions ## Snapshots ### Channel settings  ### Agent info  ### Agent runtime  ## Validation - `pnpm -C desktop check` - `pnpm -C desktop test` (4,604 passed) - `pnpm -C desktop build:e2e` - focused channel settings and agent profile Playwright tests --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Mongo <9cfd347903944d5b85aa6c93d2ab67381b978a92a31914bca69998968752a1d7@buzz.block.builderlab.xyz> |
||
|
|
cf03bd7c37 |
Improve desktop search scoping (#5306)
## What changed - unify Cmd+K and channel Cmd+F around a removable channel or conversation scope - add conservative fuzzy matching for people and channels while preserving exact-match ordering - make scoped message search complete for one-character queries and expose up to 40 scrollable results - keep the pre-scope channel or DM action in the normal results flow so it scrolls away with the list ## Validation - desktop TypeScript typecheck - desktop text-size and file-size guards - focused fuzzy-search unit tests (24 passed) - focused search Playwright coverage (7 passed), including channel and DM copy, one-character results/no-results, 40-result scrolling, and the non-sticky scope action - desktop E2E build - visual review of channel, scoped, expanded-results, and DM states --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> |
||
|
|
cd2aa5c12d |
Add glass appearance and cohesive settings (#5478)
## Summary - add an opt-in native glass sidebar with opacity controls and live theme previews - refine sidebar spacing and Buzz-only active rows while preserving production defaults - unify settings section cards, subtitles, and agent runtime rows ## Validation - repository format, lint, type, and file-size checks - 4,538 desktop tests and 2,270 native desktop tests - desktop and web production builds - 1,261 mobile tests in the completed full gate - focused Playwright appearance, sidebar, settings, pairing, and runtime coverage --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Kenny Lopez <klopez4212@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz> Co-authored-by: Carl <3c4caeafb646d23867f1c4832e68211d77e2561946171625f75c3ce1a3f2670f@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz> |
||
|
|
fb73561e64 |
feat(desktop): Projects follow-ups — access restrictions, fast loading, activity feed polish (#5073)
## Summary Follow-up batch on the Projects overview (continues merged #1677): - **Repository access restrictions** — repositories the viewer can't reach are surfaced with a reason instead of failing silently. Channel-ACL denials (which arrive as the same 404 as a missing repo, for anti-enumeration) are re-classified using the repository's channel binding and the viewer's memberships (`useRepositoryAccess.ts`, `projectRepoAvailability.ts`). - **Projects loads in seconds instead of minutes** — enumeration no longer crawls every kind:5 deletion event on the relay. It fetches project/repo announcements first, then queries deletions scoped to those coordinates via chunked `#a` filters (3 queries instead of hundreds on staging). - **Activity feed layout polish** — bare event-type glyph beside the headline (no badge circle), timeline spine runs through the avatars connecting consecutive cards, linkable actor/project names are bold in theme foreground, rounded hover state, alignment fixes. - **Create button pinned** — the "+" create menu is pinned to the pane's top-right corner (equal 16px insets) and no longer scrolls away with the page header. - **List controls as a table header** — the scope selector (left) and sort + layout toggle (right) render as the first row of the list container on the Projects/Repositories/PRs/Issues tabs; in card view the identical bar stands alone with the cards below (`ProjectsListHeaderBar.tsx`). - **Repository rows show the git location** — subtitle is `github.com/org/repo` for external repos or `owner/repo` (resolved profile name) for Buzz-hosted ones, instead of repeating the project name (`repositoryDisplayPath`). - **Uniform work-item row heights** — issue rows previously ran the author chip in inline flow, letting the 20px avatar grow the line box ~3px taller than PR rows; both lists now share the same flex subtitle. 📸 Screenshots: [feed layout / pinned button](https://github.com/block/buzz/pull/5073#issuecomment-5214114069) · [list header / repo subtitles / row heights](https://github.com/block/buzz/pull/5073#issuecomment-5217480712). Note: two empty `chore: retrigger CI` commits exist on the branch from working around the Aug 6 GitHub Actions incident; happy to drop them with a signoff rebase before undrafting if preferred. Latest `main` is merged in (`a0cc35220`). ## Test plan - [x] Desktop unit tests (4,493 pass after merging main), Biome, tsc - [x] New unit tests for scoped deletion enumeration and repo availability re-classification - [x] New unit tests for `repositoryDisplayPath` (external, Buzz-hosted, unresolvable) - [x] Screenshot verification of feed layout, connector spine, and pinned button (top + scrolled states) — posted to the PR - [x] Screenshot verification of the list header row (list + card), repo subtitles, and matching PR/issue row heights — posted to the PR - [ ] Manual pass against staging (projects list load time, restricted-repo states) --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
8630e58eb0 |
fix(desktop): fence localStorage SecurityError from killing the React tree (#5142)
## Summary WebKit throws `SecurityError` from `localStorage.getItem` (not just `setItem`) when storage access is denied for the origin. With no `ErrorBoundary` in `desktop/src`, any such throw inside a provider render (`ThemeProvider`, `CommunitiesProvider`, `App` boot) propagated to the reconciler, unmounted the root, and left a blank window. Measured repro in #5078: a single throwing `getItem` on `buzz-communities` or `buzz-active-community-id` kills the container. Closes #5078. ## What changed **New helper — `desktop/src/shared/lib/safeStorage.ts`** - `getStorageItem(key, fallback?)` — wraps `window.localStorage.getItem`; on a thrown error (SecurityError under denied-storage origin) it warns once per key and returns the fallback. - `setStorageItem(key, value)` and `removeStorageItem(key)` — same fail-closed contract (return `false` on throw). - Unit tests in `safeStorage.test.mjs` cover the happy path and the `SecurityError` path. **Rewired the init-path readers that ran before any UI existed** - `desktop/src/features/communities/communityStorage.ts` — `migrateLegacyCommunityStorage`, `loadCommunities`, `loadActiveCommunityId`, `loadCommunityDiscoveryAfterLeave`, `initFirstCommunity` - `desktop/src/features/communities/legacyCommunityStorage.ts` — `migrateLegacyCommunityStorageBeforeRender` - `desktop/src/shared/theme/ThemeProvider.tsx` — `readStoredTheme`, `applyCachedVars`, the `useState` initialisers for `accentColor` and `followSystem`, and the accent re-read inside `applyTheme` **Root-level fence — `desktop/src/app/RootErrorBoundary.tsx`** - New top-level `ErrorBoundary` wrapping the whole provider tree in `main.tsx`. Any remaining uncaught render error (a future storage read that bypasses the helper, or any other render-time crash) renders a degraded splash with a Reload button instead of a blank window. ## Test plan - `desktop/src/shared/lib/safeStorage.test.mjs` — node `--test` runner, 11 assertions across healthy, absent, and SecurityError-throwing storage. - Full `just ci` runs on the blocker. - Existing `communityStorage.test.mjs` and `legacyCommunityStorage.test.mjs` continue to pass (they exercise the same functions via in-memory Storage doubles; the new code path in `migrateLegacyCommunityStorage` only adds a `try/catch` around the same body). ## Why not an ErrorBoundary-only fix A boundary alone can't help on a *clean* mount — the first throw already unmounted the whole subtree before any state or fallback data was loaded, so retrying would hit the same throw on the very next render. The storage accessor has to fail closed *and* the boundary has to exist for whatever bypasses it. Both are needed; neither is sufficient alone. --------- Signed-off-by: iroiro147 <sarthak.singh@mastersunion.org> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
1399ec1d13 |
Alert community owners and admins when a new key joins (#4900)
Owners and admins of a Buzz community get a desktop notification the
first time a new key joins their community. Requested by Tyler in
buzz-development ("we already have this [roster] — can we alert owners
and admins when a new key joins for the first time?"); design and
verification thread: channel `community-members-visibility`.
## Why the shape is what it is
- **kind:13534 membership snapshot is the alerting signal, not the
kind:8000 delta.** 8000 is leaky on two independent axes: its fan-out is
pod-local (no Redis hop — being fixed separately in #4887), and
`buzz-admin add-member` publishes no 8000 at all by documented design.
The 13534 snapshot is the only signal covering every production join
path with cross-pod delivery (completeness audit: every
membership-insertion path enumerated at base `8342dfcc5`, all emit
13534).
- **This adds Desktop's first live 13534 subscription** — deliberate
line item. The existing read (`relayMembers.ts`) is a one-shot fetch;
without a live subscription no snapshot ever arrives passively and
nothing could fire.
- **8000 is subscribed only as a latency accelerator** and it *refetches
the authoritative snapshot* rather than alerting from its own payload,
so one ledger governs both signals and they cannot double-alert.
- **Persisted per-community/per-viewer ledger, written before the
notification fires.** Snapshot publication is eventual (60s reconciler
repairs failed best-effort publishes) and a reconciler-republished
snapshot is indistinguishable from a fresh one — only a durable record
answers "is this new". Also what makes reconnect replay (`since - 5s`
skew; `since === undefined` full-backlog edge) safe.
- **First snapshot per community seeds silently** (no notification storm
for existing members), and `seeded` is an explicit persisted bit — not
inferred from ledger non-emptiness, which would swallow the first
genuine join in a community whose only member is the viewer.
- Mounted in `useAppShellDesktopNotifications` (owns the
notifications-enabled precondition; `AppShell.tsx` is at the file-size
ratchet ceiling — net growth zero).
5 files, +3103 (production +752, tests +2,351), desktop-only. No relay
changes.
## Verification
**Current reviewed tip: `1854c4a5` — review-blessed code at
`d992ed295ead8c8423f81a752f4ad614718d85c6`** (clean tree, HEAD checked
in the same shell as each gate; history is `0e791f2d3` → merge of main
`2034e693a` → `fdeda44f0` → `5d0d2b4c3` → `a20a7d8cb` → merge of main
`0cfe4832` → `d992ed29` → `1854c4a5`, all fast-forward, no rebase or
force). Independently gated by Eva, Wren, and Sami: typecheck rc=0,
`pnpm check` rc=0 (pre-existing 1 warning / 2 infos), full Desktop unit
package **4431/4431**; push hooks pass. Wren's adversarial verdict at
`d992ed29`: APPROVE — minimalness 9, elegance 9, correctness 9, all four
cancellation seams plus 1b re-derived independently. `1854c4a5` is
assertions and comments only — no production behaviour change, so the
test count is unchanged.
**Remediation commits (review thread `community-members-visibility`):**
- `fdeda44f0` — authorization read from the signed snapshot being
reconciled (a demoting/removing snapshot fails closed before it can
disclose the joins it carries); >3 joins collapse to one summary;
8000-triggered refetches coalesce on a 500ms trailing window.
- `5d0d2b4c3` — join alerts coalesce **across** snapshots, not just
within one: a live burst arrives as several growing rosters, so delivery
defers onto a 1.5s trailing quiet window while ledger persistence and
dedupe stay synchronous per snapshot. Max measured 10 banners from 50
real joins before this; the same shape now produces one.
- `a20a7d8cb` — cancellation covers flushes already in flight, not just
queued timers: a generation token (bumped only by `clearPending`) is
rechecked after the profile lookup and before every send, so
demotion/removal/unmount/community-switch landing mid-flush suppresses
delivery; the notification title is captured with the batch rather than
read at send time. Concurrent-flush semantics pinned: a newer authorized
batch neither cancels nor is cancelled by an in-flight flush.
- `d992ed29` — the stale-authorized-frame disclosure, independently
reproduced at `0cfe4832` (held-open refetch released after a newer
demoting frame: `notifications=1`, body naming the joiner, where 0 is
required). Three fixes in one shape: every callback acts on a
per-effect-run session object (community id, viewer, ledger, ordering
state) instead of ambient current values, closing the community-switch
window; a `created_at` fence plus a fail-closed revocation latch, as one
mechanism, because the relay can publish two snapshots in the same
second so neither `<` nor `<=` alone is safe — the invariant is
“revocation wins”, not “newest wins”; and a 5s clamp on the 1.5s
trailing window so a sustained drip cannot defer delivery without bound.
Red-first: the four new arms fail at `0cfe4832` (25/29) and pass after
(29/29).
- `1854c4a5` — the privacy arm now asserts the persisted ledger is
unchanged across the delayed frame's release, not only the notification
count. Mutation-checked: moving the revoked check after the ledger
advance keeps notifications at 0 and passes the old assertion, and is
killed by the new one. Assertions and comments only.
**Mutation testing:** 9/9 mounted-hook mutants killed at `a20a7d8cb`,
each with a control row before and after — role/enabled gates,
reconnect, 8000 authority, failed-write handling and ref ordering,
community re-key/read, and query invalidation. The reducer/storage fix
separately killed 6/6 mutants with 15/0 controls; the foundational
ledger suite killed 9/9. At `d992ed29`: spelling the fence `<=` kills 5
arms; moving the empty-roster guard after the fence advance kills
exactly the fence-advance arm and nothing else (28/29). One
qualification stated rather than buried — moving the fence advance
itself up to the comparison SURVIVES the whole suite. That is an
equivalent mutant, not a coverage gap: the empty-roster guard returns
before the comparison, and authorization rejection latches `revoked` so
a later frame having moved the fence is unobservable. The scope is
written into the test's docstring. At `1854c4a5`: the
revoked-check-after-ledger-advance mutant is killed by the new ledger
assertion (and by the 1b arm).
**Scale/storage correction in `f6e5a3c57`:** the original 5,000-key cap
could evict members still present in a 5,001+ roster, causing them to
re-alert on every snapshot; read-time truncation reopened the same loop
after reload; and a raw quota exception could reject before notification
dispatch. The fix retains every on-roster key, caps only departed keys,
removes read-time truncation, and uses the app's quota-aware writer.
**Final ordering correction in `0e791f2d3`:** a failed post-recovery
write now skips notification and leaves the in-memory ledger unchanged,
so the next snapshot retries and delivers only after persistence
succeeds.
**Live-local matrix vs a real relay, executed at exact unchanged
`d75cc6cd9` and transferred to the current tip:** a 4,800-sequence
differential found zero old/new reducer divergences below the cap while
exercising the positive alert path; its negative control diverged as
required at 5,100 members (old re-alerts 100; new re-alerts 0). The
final hook change affects only the newly tested failed-write branch;
successful writes follow the same alert path exercised live. The live
communities were sub-cap and persisted successfully, so the matrix
remains applicable without a redundant rerun.
- Invite claim: owner and admin each exactly one notification; plain
member zero; 1.5s quiet window held (8000+13534 deduped); both open
clients live-refreshed the roster. Screenshot receipts SHA-256-pinned
and independently replicated.
- **CLI `buzz-admin add-member` (13534-only path):** DB counts moved
8000 `9→9`, 13534 `15→16` — zero accelerator events, exactly one alert
per manager. Proves snapshot-diff alone alerts.
- Plain member: zero notifications **and** zero
`buzz-community-join-seen.v1:*` localStorage keys before/after the join
(gate sits before the ledger).
- Staggered reload + replay dedupe: no alerts from startup
refetch/replay; republished already-seen snapshot produced zero through
a 2s quiet window.
- Community switch: independent per-community seed state; effect
re-keys; one alert per community, quiet window held at exactly two.
**Live re-verification at `d992ed29` is in progress** (Max; the
after-fix matrix leads with the delayed-refetch demotion arm, A→B switch
ledger isolation, the 5s sustained-drip timing, and packaged-app click
routing behind the positive/NIP-43 controls); earlier receipts at
`a20a7d8cb` cover the instrumented storm and cap-boundary re-drive;
earlier live receipts at `fdeda44f0` — privacy matrix
(demote/remove/promote), summary click-through — transfer where the diff
left those paths untouched.
## Known and accepted
- **8000 cross-pod fan-out is broken relay-side** — fixed in #4887
(separate lane, not a blocker here): on a multi-pod relay the
accelerator only fires on the claim-handling pod; 13534 still covers
everyone, just not instantly.
- **Late-not-lost semantics.** A live frame missed during a
reload/socket gap is recovered by the next snapshot, reconnect refetch,
or remount backfill (`limit: 1`) diffed against the persisted ledger.
One live-run observation of an admin missing an immediate post-reload
fresh join is attributed to harness rate limiting; the recovery paths
above bound the damage to lateness, never duplicates.
- **Remote promotion activates on reload, not on the next snapshot**
(measured by Sami at `fdeda44f0`): the subscriptions are mounted from
the cached membership lookup, so a viewer promoted to admin by someone
else starts receiving join alerts only after a reload, community switch,
or local membership mutation refreshes that cache. Fails safe
(under-notify). Ruled accepted for v1 by Eva; the fix direction
(subscribing before authorization) is a deliberate design change
deferred to a follow-up if product wants instant activation.
- **Cross-user live-delivery staleness reproduced at the PR's own base**
(`2034e693a`, clean relay): a persisted send can fail to appear in an
already-open recipient timeline. Detached from this PR by a pinned-base
discriminator (identical failure with zero PR code) and tracked
separately in issue `6e2bda3092fa`; current main passes 4/4.
- **A stale demoting frame latches a genuine admin until reload or
community switch** (reverse ordering of the stale-frame privacy race,
`d992ed29`): if a snapshot that does not list the viewer as a manager
arrives out of order, the fail-closed revocation latch trips even though
the viewer is still an admin. The invalidation the latch fires refetches
the membership lookup, which correctly returns admin, so `active` stays
true, the effect deps do not change, and the session stays latched.
Fails safe (under-notify, never over-disclose) and consistent with the
promotion-on-reload semantics above. Ruled accepted for v1 by Eva;
self-clearing the latch would cost a third piece of timing state. Pinned
as documented behaviour in `useCommunityJoinAlerts.test.mjs` — and the
suppressed join is re-announced rather than lost, because a latched
session never records it in the ledger.
- **Lifetime-first-only semantics:** ever-seen ledger means
remove→re-add does not re-alert. Flagged for product ruling; one-line
change if re-adds should ping.
---------
Signed-off-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
|
||
|
|
b08c8b126c |
fix(desktop): prevent sidebar prefs from reverting on stale-localStorage boot (#5086)
Fixes the bug where running a dev build with stale localStorage would
publish outdated channel sections, sort preferences, starred channels,
and muted channels to the relay, clobbering the DMG installation's live
state.
## Root cause
All four sidebar-preference sync managers (`channelSectionsSync`,
`channelSortSync`, `channelStarsSync`, `channelMutesSync`) collapsed
five distinct fetch outcomes — no event, timeout, error, auth-race empty
result, decrypt/parse failure — into a single `null`. Each hook's boot
effect treated `null` as "no remote exists" and seed-published whatever
was in localStorage, stamped at `max(now, lastRemoteCreatedAt+1)` with
`lastRemoteCreatedAt` reset to 0 on every boot. A dev build with stale
localStorage therefore re-signed old state as newer, and the DMG's live
subscription applied it.
## Two guards
**1. Tri-state fetch result** (`found | absent | failed`) — decrypt
failure on an existing event reports `failed` and records
`event.created_at`, so seed-publish is blocked even when the payload is
unreadable.
**2. Persisted head watermark** (`sidebarSyncWatermark.ts`) — keyed
`{blobType, pubkey, normalizedRelayUrl}`, written to localStorage on
every observed remote event (before decrypt on all paths: initial fetch,
live subscription, `fetchOwnBlobBeforePublish`), hydrated at
construction. Any session that has ever seen a remote blob skips
seed-publish on the next boot even when the fetch returns empty. Relay
URLs are normalised via `shared/lib/normalizeRelayUrl` (also used by
profile storage) so the same relay written two ways never produces two
keys.
**Bootstrap owns the seed.** Each manager exposes
`bootstrap(localStore)` that fetches, records the raw head, and
delegates the decision to the single `runBootstrap` policy: hold on
`failed` or `absent + prior watermark`, seed on genuine first-sync
(`absent + zero watermark + non-empty local`), `apply-remote` when a
blob was found. Hooks only act on `apply-remote`; they cannot publish
during bootstrap. First-time sync is unchanged: successful EOSE with no
event, zero watermark, and non-empty local state still seeds.
## LWW baseline preservation
`fetchOwnBlobBeforePublish` for sections/sort snapshots the watermark
before `recordRemoteHead` advances it, then compares the fetched event
against the snapshot — advancing first would make `remote.createdAt >
lastRemoteCreatedAt` always false and silently kill the whole-blob LWW
merge. Stars/mutes merge per-entry via `mergeStores`, so no snapshot is
needed there.
## Relay lifecycle
All four hooks require a defined `relayUrl` (plumbed from
`communitiesHook.activeCommunity?.relayUrl` in `AppShell.tsx`); while it
is undefined no manager is constructed and no boot/live/reconnect effect
binds. All effects depend on `[pubkey, relayUrl]`, so community switches
tear down and rebind. `destroy()` cancels pending publishes without
flushing — flushing would race community switching and could publish
relay A's state to relay B via the shared `relayClient` singleton.
Pending debounce-window edits are intentionally dropped: stars/mutes
entries survive via per-entry merge on the next publish; a dropped
sections/sort edit is lost because bootstrap whole-blob-replaces from
remote on return.
Known trade-off: a first boot with the relay unreachable holds (never
seeds) until the user's next explicit edit — preferred over risking a
stale seed-publish.
## Files
- `sidebarSyncWatermark.ts` — watermark persistence + `runBootstrap`
policy (tri-state `FetchResult`, `readWatermark`, `advanceWatermark`)
- `shared/lib/normalizeRelayUrl.ts` — relay-URL normalisation shared by
watermark keys and profile storage
- `channelSectionsSync.ts`, `channelSortSync.ts`, `channelStarsSync.ts`,
`channelMutesSync.ts` — tri-state fetch, pre-decrypt `recordRemoteHead`
on all paths, sections/sort watermark snapshot for LWW, `bootstrap()`,
cancel-without-flush `destroy()`
- `useChannelSections.ts`, `useChannelSortPreference.ts`,
`useChannelStars.ts`, `useChannelMutes.ts` — act on `bootstrap()`
results, gate on `relayUrl`, `[pubkey, relayUrl]` deps on all effects
- `AppShell.tsx` — passes `activeCommunity?.relayUrl` to
`useChannelMutes` and `useChannelStars`
- `sidebarSyncTestHelpers.mjs` — shared fake-window/localStorage/Tauri
mocks for the four manager suites
- Test suites — mutation-sensitive coverage: `failed→hold`,
`absent+watermark→hold`, first-sync seeds, undecryptable head recorded
on all paths, relay-A/B watermark isolation, watermark restart
round-trip, sections/sort LWW baseline
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
|
||
|
|
6eb65919f1 |
feat(identity): recover desktop identity from a signed-in phone (#4845)
**Category:** new-feature **User Impact:** People who lose a desktop identity can securely restore it from a signed-in Buzz phone without creating a replacement identity. **Problem:** A fresh or identity-lost desktop could not recover its existing full Buzz identity from an already-authorized phone. **Solution:** Add a SAS-confirmed reverse NIP-AB transfer, durable desktop import, a dedicated mobile recovery entry point, and clearer desktop recovery dialogs with tested loading, drag-and-drop, and failure states. https://github.com/user-attachments/assets/e9215c9c-80d0-462f-9161-0fa184ca2f74 <details> <summary>File changes</summary> **crates/buzz-core/src/pairing/session.rs** Adds the reverse encrypted payload and source-completion state transitions used for phone-to-desktop recovery. **desktop/src-tauri/src/commands/identity.rs** Exposes the existing guarded identity commit path for recovery imports. **desktop/src-tauri/src/commands/pairing.rs** Adds recovery-mode pairing, durable nsec import, start serialization, stale-task protection, and explicit rejection of unsupported recovery payloads. **desktop/src-tauri/src/lib.rs** Registers the recovery pairing command. **desktop/src/app/App.tsx** Refreshes the recovered identity before continuing onboarding. **desktop/src/features/onboarding/machineOnboarding.ts** Adds recovery transitions to the onboarding state machine. **desktop/src/features/onboarding/ui/BackupPasswordTimeline.tsx** Adds the visual backup-to-password-to-unlock progression. **desktop/src/features/onboarding/ui/IdentityRecoveryPairing.tsx** Implements QR generation, copy fallback, SAS confirmation, cancellation, expiry, and completion UI. **desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx** Connects private-key, phone, and backup recovery paths to the onboarding flow. **desktop/src/features/onboarding/ui/NostrKeyImportForm.tsx** Polishes recovery dialogs, backup drag-and-drop, loading stability, and security copy. **desktop/src/shared/api/tauri.ts** Keeps the existing pairing API surface focused on standard desktop-to-mobile pairing. **desktop/src/shared/api/tauriPairing.ts** Adds the recovery pairing invoke without growing the ratcheted shared API file. **desktop/src/testing/e2eBridge.ts** Mocks recovery pairing commands and lifecycle events for browser tests. **desktop/tests/e2e/identity-lost.spec.ts** Covers lost-identity entry, QR/copy recovery, SAS, cancellation, expiry, success, errors, backup import, drag-and-drop, and screenshots. **desktop/tests/e2e/onboarding.spec.ts** Verifies recovered identities continue through harness setup without replacement-key side effects. **mobile/lib/features/pairing/pairing_page.dart** Adds recovery-only scanning and explicit identity-handoff warnings. **mobile/lib/features/pairing/pairing_provider.dart** Recognizes recovery codes, returns the signed-in nsec after mutual SAS approval, and waits for desktop completion. **mobile/lib/features/settings/settings_page.dart** Accepts the recovery route builder at the app composition boundary to preserve feature isolation. **mobile/lib/features/settings/settings_page/connection_section.dart** Adds the signed-in “Send identity to desktop” settings action. **mobile/test/features/pairing/pairing_page_test.dart** Covers recovery-only validation and handoff messaging. **mobile/test/features/pairing/pairing_provider_test.dart** Covers reverse payload encryption, confirmation ordering, success, failure, timeout, and cleanup. </details> ## Reproduction steps 1. Launch Buzz Desktop with identity-lost state and choose **Recover from your phone**. 2. Confirm the QR and persistent **Copy pairing code** fallback appear without layout shift. 3. On a signed-in phone, open **Settings → Send identity to desktop**, scan or paste the recovery code, and compare the six-digit SAS on both devices. 4. Confirm on both sides and verify Desktop restores the identity and continues to harness setup. 5. Repeat from identity-lost state with **Recover from a backup file**; verify picker and drag-and-drop both advance to password entry and restore the encrypted backup. 6. Exercise cancellation, mismatched/unsupported codes, expired sessions, and an invalid backup; verify each returns actionable, non-stuck UI. ## Screenshots ### Desktop phone recovery — complete flow | Recovery entry | Pairing QR | Code match | Receiving identity | |---|---|---|---| |  |  |  |  | ### iOS Simulator — complete handoff flow | Settings entry | Recovery scanner | Manual recovery code | Code confirmation | |---|---|---|---| |  |  |  |  | ### Encrypted backup recovery — adjusted file flow | File picker | Drag-and-drop target | Password step | |---|---|---| |  |  |  | ## Verification - `cargo test -p buzz-core pairing` — 71 passed - `just mobile-test` — 1,169 passed - `pnpm build:e2e && pnpm exec playwright test identity-lost.spec.ts --project=smoke` — 15 passed - Full pre-push gates — desktop checks, desktop unit tests, Rust tests, Tauri checks, and mobile tests passed --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
719f9730d4 |
feat(desktop): allow leaving your final community (#3621)
**Category:** improvement **User Impact:** People can leave their final Buzz community and return to **Join or create a community** without losing their signed-in identity. **Problem:** Buzz Desktop blocked people from leaving when only one community remained. Its existing remove action also changed local configuration without ending relay membership. **Solution:** Allow the final community to be left. Buzz now asks the relay to end membership, removes the community locally only after acceptance, and returns the person to the community selector while keeping their identity signed in. If other communities remain, Buzz switches to one of them. Relay rejection or timeout keeps the community in place and shows an actionable retry error. <details> <summary>File changes</summary> **desktop/src/features/communities/leaveCommunity.ts** Adds signed kind 28936 publishing for active and inactive community relays with actionable timeout handling. **desktop/src/features/communities/leaveCommunity.test.mjs** Covers event shape, relay selection, acceptance gating, rejection, timeout messaging, and cleanup. **desktop/src/features/communities/useCommunities.tsx** Allows final-community removal and clears community-specific storage without touching identity. **desktop/src/features/communities/resolveCommunityRemoval.test.mjs** Covers final, active, and inactive community removal state transitions. **desktop/src/app/useCommunityNavigationTransitions.ts** Gates local removal on relay acceptance and routes to a fallback community or setup selector. **desktop/src/app/AppShell.tsx** Passes the asynchronous leave operation through shell entry points. **desktop/src/features/communities/ui/EditCommunityDialog.tsx** Replaces the local-only remove action with a pending-aware Leave Community action that retains actionable errors. **desktop/src/features/communities/ui/CommunitySwitcher.tsx** Enables leaving the final community and carries the asynchronous callback. **desktop/src/features/sidebar/ui/AppSidebar.tsx** Carries the asynchronous leave callback through sidebar props. **desktop/src/features/sidebar/ui/CommunityRail.tsx** Enables leaving the final community from rail settings. **desktop/src/features/sidebar/ui/SidebarProfileCard.tsx** Carries the asynchronous leave callback through profile community settings. **desktop/src/testing/e2eBridge.ts** Teaches the mock relay to accept NIP-43 leave events. **desktop/tests/e2e/community-rail.spec.ts** Updates leave interactions and verifies final-community setup navigation, storage cleanup, and identity preservation. </details> ### Reproduction steps 1. Run Buzz Desktop with a signed-in identity and one joined community. 2. Open Community settings and choose **Leave Community**. 3. Confirm the app shows **Join or create a community** and the existing identity remains signed in. 4. Repeat with two communities and confirm leaving the active one switches cleanly to the remaining community. 5. Reject or withhold the relay `OK` response and confirm the community remains configured with an actionable error in the dialog. ### Test plan - `pnpm check` - `pnpm build` - `pnpm test` (3,913 passing) - `pnpm build:e2e && pnpm exec playwright test tests/e2e/community-rail.spec.ts --grep "final community"` <img width="557" height="316" alt="image" src="https://github.com/user-attachments/assets/b628182f-cba5-451d-ae4b-bee8d8dd19aa" /> --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: npub14ndfusear8wdpe4kss8h7juc7wjk78atnqzf63zvppcpneknv4sq6x9370 <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> Co-authored-by: npub14ndfusear8wdpe4kss8h7juc7wjk78atnqzf63zvppcpneknv4sq6x9370 <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> |
||
|
|
43cced308d |
feat(desktop): sync themes per community (#3653)
**Category:** new-feature **User Impact:** Users can keep a distinct Appearance scheme for each community and restore it on another desktop signed in with the same identity. **Problem:** A single global theme makes it harder to distinguish among communities, and local-only preferences do not follow a user to another device. **Solution:** Save each community's stable theme, accent, and system-following selection as private encrypted relay state, backed by a responsive local cache and guarded against switch races, invalid future records, and relay failures. <details> <summary>File changes</summary> **desktop/src/app/App.tsx** Mounts the community-scoped theme controller inside the active community lifecycle. **desktop/src/features/settings/lib/appearanceScopeCopy.test.mjs** Covers active-community and fallback labels used to explain Appearance scope. **desktop/src/features/settings/lib/appearanceScopeCopy.ts** Builds a safe, trimmed label for the currently active community. **desktop/src/features/settings/ui/SettingsPanels.tsx** Clarifies which Appearance controls are per-community and which apply globally when multiple communities exist. **desktop/src/shared/constants/kinds.ts** Defines the NIP-78 application-data event kind used for theme preferences. **desktop/src/shared/theme/CommunityThemeController.tsx** Coordinates cached appearance, encrypted relay retrieval, live updates, reconnect behavior, and safe community switching. **desktop/src/shared/theme/ThemeProvider.tsx** Exposes a single appearance application path so synchronized preferences use the existing renderer and persistence behavior. **desktop/src/shared/theme/communityThemePreference.test.mjs** Covers contract validation, user/relay isolation, malformed records, cache failures, and switch-race decisions. **desktop/src/shared/theme/communityThemePreference.ts** Defines the versioned stable preference contract, safe defaults, local cache keys, and persistence guards. **desktop/src/shared/theme/communityThemeSync.test.mjs** Covers relay absence, unreadable records, unavailability, seeding safety, and teardown of pending writes. **desktop/src/shared/theme/communityThemeSync.ts** Encrypts theme preferences to the user, publishes and retrieves NIP-78 state, and handles ordering and lifecycle safety. </details> ### Reproduction steps 1. Join at least two communities and open **Settings → Appearance**. 2. Choose a different theme, accent, or system-following mode in each community. 3. Switch between the communities and verify each one restores its own scheme without overwriting the other. 4. Sign in on another desktop with the same Nostr identity, join the same community, and verify its saved scheme is restored from that community's relay. 5. Disconnect the relay, change Appearance, and verify the UI remains responsive and the local fallback is retained. ### Screenshots / demos <img width="1733" height="948" alt="image" src="https://github.com/user-attachments/assets/5afeabaa-0def-482c-9b87-8a880ee0a467" /> <img width="700" height="412" alt="Screen Recording 2026-07-29 at 4 39 52 PM" src="https://github.com/user-attachments/assets/d58da329-aec5-4324-a4b2-cbcb2702a81a" /> --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> |
||
|
|
e30db7028f |
feat(projects): support multiple repositories (#4671)
## Summary - adopt the finalized NIP-MP project model so one project can enumerate and switch between multiple NIP-34 repositories - add project and repository navigation, activity summaries, existing-repository attachment, and repository access-channel management - preserve privacy-safe activation provenance for agent-authored patches, pull requests, issues, and associated commits ## Test plan - [x] Run desktop typecheck and unit tests - [x] Run focused NIP-MP, repository access, and provenance tests - [x] Run Rust formatting and desktop lint checks - [x] Run the complete pre-push suite after merging current `main` - [ ] Manually verify project creation, repository attachment, switching, and access repair on staging - [ ] Manually verify public-channel and private-agent origin labels on newly created Git activity Related: [#4695](https://github.com/block/buzz/pull/4695) --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
ce3cf3cd25 |
Polish Huddle voice controls (#4694)
## Summary - add a visible Stop control for interrupting agent speech - make push-to-talk available by default while preserving manual mute controls - refine agent management, muted audio states, drawer layering, and return navigation - suppress duplicate notification sounds for Huddle messages ## Why Huddles could trap users behind long agent speech, hide useful agent controls, and leave temporary Huddle state visible after the call. The drawer also regressed when the terminal substrate began painting behind the rounded app surface. ## Validation - `just desktop-ci` - focused Huddle Playwright coverage for the drawer, speech interruption, agent picker, and leave navigation --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
5179726737 |
fix(local-archive): default both archive settings to enabled (#4750)
## Overview
Both local archive settings — "Archive my agents' observer frames" (kind
24200) and "Archive my agents' turn metrics" (kind 44200) — previously
defaulted to OFF in OSS builds, controlled by build-time env vars. This
had an irreversible cost: observer frames are ephemeral (not stored by
the relay), so any missed events are permanently unrecoverable. This PR
makes both settings default to enabled for all builds and removes the
build-time flag machinery entirely.
## What changed
### Rust
- `observer_archive_default_enabled()` — returns `true` unconditionally;
removed `option_env!("BUZZ_DESKTOP_BUILD_OBSERVER_ARCHIVE_DEFAULT")`
check and `nest_is_dev()` runtime fallback.
- `agent_metric_archive_default_enabled()` — returns `true`
unconditionally; removed
`option_env!("BUZZ_DESKTOP_BUILD_AGENT_METRIC_ARCHIVE_DEFAULT")` check
and its OSS-build test.
- `build.rs` — removed both `rerun-if-env-changed` declarations
(`BUZZ_BUILD_OBSERVER_ARCHIVE_DEFAULT`,
`BUZZ_BUILD_AGENT_METRIC_ARCHIVE_DEFAULT`) and the two baked-env
emitting blocks.
### Build / CI
- `Justfile` — removed `desktop-tauri-test-compiled-flags` recipe (the
dual-compile test machinery).
- `.github/workflows/ci.yml` — removed the "Desktop Tauri compiled-flag
verification" CI step.
### TypeScript
- `useObserverArchiveSeed.ts` — removed `observerArchiveDefaultEnabled`
dep from `ObserverArchiveSeedDeps` and the `policyOn` gate in
`reconcileObserverArchive`; the function now unconditionally calls
`mergeSaveSubscriptionKinds`.
- `useAgentMetricArchiveSeed.ts` — removed
`agentMetricArchiveDefaultEnabled` dep from `AgentMetricArchiveSeedDeps`
and the `defaultOn` flag-check path in `maybeSeed`; the
`hasExplicitChoice` guard is preserved as the sole gate against
re-seeding.
- `LocalArchiveSettingsCard.tsx` — removed `policy` prop,
`observerPolicy` state, and `observerArchiveDefaultEnabled` fetch from
`ObserverArchiveSection`; toggle is now always enabled (just `toggling`
disables it); removed the stale "Always on for internal builds" copy
branch; removed the `observerPolicy !== false` guard from
`handleObserverToggle`.
- `tauriArchive.ts` — updated JSDoc on both default-enabled functions to
reflect always-true.
- `e2eBridge.ts` — changed both mock defaults from `?? false` to `??
true` so E2E tests without an explicit mock override exercise the real
default behavior.
### Tests
- `useObserverArchiveSeed.test.mjs` — replaced `policyOn` dep with
direct merge dep; updated `test_oss_policy_off_no_merge` →
`test_reconcile_always_seeds_24200`; all cancellation, identity-switch,
and ordering tests adapted.
- `useAgentMetricArchiveSeed.test.mjs` — removed `defaultOn` dep and
`test_oss_build_does_not_seed`; updated
`test_internal_build_unset_seeds_*` → `test_default_enabled_*`;
`hasExplicitChoice` guard tests unchanged.
## Preservation of explicit opt-outs
Users who have previously toggled the setting off are unaffected:
- `useAgentMetricArchiveSeed` skips seeding when
`hasExplicitChoice(pubkey)` returns true (localStorage-persisted per
identity).
- Observer archive reconciliation now unconditionally calls
`mergeSaveSubscriptionKinds`, but a user who already deleted the
subscription can turn it off via the Settings toggle, which calls
`removeSaveSubscriptionKind` — this is the existing explicit opt-out
path, and the toggle is now always enabled (not locked by a policy
flag).
## Result
- No `BUZZ_BUILD_*_ARCHIVE_DEFAULT` /
`BUZZ_DESKTOP_BUILD_*_ARCHIVE_DEFAULT` references remain outside
CHANGELOG/history.
- Desktop node tests: 4168 pass, 0 fail.
- `just desktop-tauri-check`: clean.
- `just desktop-tauri-test`: all pass.
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
|
||
|
|
e5efd04705 |
fix(desktop): close reconnect gaps that previously required CMD+R (#4737)
> Opened by Brain (agent) on behalf of @wesbillman. ## Problem Users report the desktop app doesn't reliably reconnect and can wedge in states where only CMD+R (or a full restart) restores connectivity (thread `c2205e2b` in #desktop-reconnecting). Pinky's empirical light-switch matrix (real `buzz-relay`, SIGTERM/1012 + SIGKILL × 1s/45s/3min, at `f18a9cb10`) passed 4/4 — the backoff state machine recovers cleanly from ordinary relay loss. That isolates the user-stuck states to four special cases a reload resets but the auto flow never did. ## Fixes | Gap | Change | |---|---| | **G1** — recovery rode solely on the backoff timer (max 30s), throttled by WKWebView in occluded/background windows; nothing fired on network return or wake | New `useRelayResumeTriggers`: `online`, window focus, and visibility→visible call `preconnect()` when the session is `reconnecting`/`stalled`, rate-limited to one attempt per 5s (`relayResumeTriggerPolicy.ts`). Deliberately inert for the terminal `disconnected` state. | | **G2** — any AUTH `OK false` latched the session terminal forever, though the relay also rejects for transient causes (duplicate-AUTH "already authenticated" race, ±60s clock skew, fail-closed allowlist DB errors) | New `AuthOkTracker` (`relayAuthPolicy.ts`): "already authenticated" resolves as success; transient rejections retry with normal backoff; latch only on `restricted:` or after 3 consecutive rejections. | | **G3** — an `auth-required:` CLOSED (REQ racing AUTH after reconnect) permanently deleted the live subscription with no UI signal — frozen channel while state reads "connected" | Reclassified `auth-required:` as retryable in `relayClosedPolicy.ts`. Genuinely terminal classes (`restricted:`, `invalid:`, …) still delete. Can't loop: a truly unauthenticated session latches terminal at the connection level. | | **G4** — `useRelayAutoHeal` observed the 2s-debounced connection hook, so sub-2s flaps never triggered the heal even though `resetConnection` had already rejected every in-flight query | Auto-heal now observes the raw connection-state emitter. The existing 15s heal rate-limit still guards against flap storms. | Each fix is a colocated pure-policy module + unit tests, matching the existing `relayReconnectPolicy`/`relayClosedPolicy` pattern. ## Validation - Full desktop unit suite: **4151 pass, 0 fail** (at branch tip, `pnpm -C desktop test`) - `pnpm -C desktop typecheck` and `pnpm -C desktop check` clean (file-size ratchet respected — `relayClientSession.ts` net −2 lines despite the tracker wiring) - Evidence trail: `RESEARCH/DESKTOP_RECONNECT_CMDR_GAP_AUDIT.md` (audit), `RESEARCH/DESKTOP_RECONNECT_LIGHT_SWITCH_RESULTS.md` (Pinky's matrix) ## Not covered / follow-ups - Native macOS sleep-wake was not automated (would kill the harness session); G1's focus trigger is the mechanism that covers wake in practice, but a manual sleep-wake verification on a real build is worthwhile. - G3 terminal-CLOSED classes (`restricted:` etc.) still silently delete subs with no UI signal — surfacing that is a separate UX decision. - Stall-watchdog latency (60s idle + 10s check) left unchanged; G1 triggers largely mask it. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: npub1gjuws2a2dc8z2nszprtg7v6u9q7ffeah3hgl5yx45jwn7y7aqs6s5e9xj6 <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@buzz.block.builderlab.xyz> Co-authored-by: npub1yxv5wk0u0fh6dwt925wntn7h397jvteyj4r87ttcd9xae7n2t3lqqj9jmm <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@buzz.block.builderlab.xyz> Co-authored-by: npub1gjuws2a2dc8z2nszprtg7v6u9q7ffeah3hgl5yx45jwn7y7aqs6s5e9xj6 <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@buzz.block.builderlab.xyz> |
||
|
|
cb4a73e17d |
Dock Buzz Term within channel workspace (#4724)
## Summary - replace Buzz Term's full-app takeover with a resizable bottom dock inside the channel content surface - add a discoverable channel-header button plus hide and maximize/restore controls - create PTYs lazily and keep separate, persistent terminal workspaces per channel - capture immutable channel/thread context on every terminal session ## Multiple-channel behavior The dock is a single surface, but its tabs are partitioned by channel. Switching channels swaps to that channel's sessions without terminating background PTYs; returning restores them. New tabs capture the currently visible channel/thread context. ## Verification At commit `7ca087f8e08c80528387684364a65bf4ccd6315f`: - `pnpm --dir desktop typecheck` - `pnpm --dir desktop test` — 4,129 passed - pre-push repository hooks — desktop check/test, Tauri checks, terminal Rust suites all passed --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
540b58920c |
Polish sidebar unread hierarchy (#4573)
## Summary - Keep selected sidebar rows regular by default; manually unread rows become bold immediately. - Apply a clearer dark-mode hierarchy: standard inactive rows at 75%, muted rows at 45%, and unread rows at full emphasis. - Keep hover text color stable while retaining the selected-row and unread cues. ## Validation - `pnpm typecheck` - `pnpm build:e2e` - Playwright: sidebar badge and channel-mute coverage ## Screenshots Posted in the PR comments. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
631b05c883 |
feat: ship Buzz Term (#4347)
## Summary - ship **Buzz Term** end to end: the terminal engine/runtime, mounted desktop substrate, and user-visible naming - add Quinn's tape-deck-inspired banner: a beveled chassis filled by the `buzz term` wordmark, surrounded by a complete-hex field - derive the wordmark's three-stop sweep from each theme's terminal palette so primary, secondary, and accent roles remain visibly distinct across all 62 shipped themes, including light themes - paint the banner once on its own pointer-transparent canvas; PTY rendering beneath it remains unchanged ## Banner behavior - uses the renderer's shared `8.4 × 17` cell metrics and production aspect ratio `2.0238` - regenerates only for viewport/theme changes; palette switches repaint correctly while the banner is visible - dismisses on non-empty output from the active terminal session; empty output and inactive sessions do not dismiss it - fails closed below **70 columns** rather than squeezing or clipping the wordmark - adds **8 lines** to `terminalRenderer.ts` for shared cell metrics and **zero lines inside `paint()`** ## Screenshots | Buzz (light) | Buzz Dark | |---|---| |  |  | | Kanagawa Lotus (light) | Red | |---|---| |  |  | Additional production-aspect finals: [Vesper](https://buzz.block.builderlab.xyz/media/9ca6514b63f8cfb2107a85ca46f16a940c0883848e6fbc718e411af94aa13100.png), [Min Dark](https://buzz.block.builderlab.xyz/media/f67bd2970e5d64ffb07b1ae78ab58c847e6ebc23e7e7a48e067eb024dba64ec8.png), and [Dark Plus](https://buzz.block.builderlab.xyz/media/290fee08924f37d064abc687ecf3e9526ab05b87e8e56d610f23048949793dbe.png). The screenshot harness was checked against the shipped painter at this exact head: all **2,541 draw calls** matched on color, glyph, x, and y; four deliberate divergence controls fired. ## Verification at `98ebc8f9048bd5f0ceb7e843b67874d642f0b7fd` - desktop tests: **3,946 / 3,946** - TypeScript: clean - checks: pass (two pre-existing informational `useTemplate` notices only) - integration/e2e: PASS (independent exact-SHA lane; artifacts recorded in the originating Buzz thread) - artifact/dead-path sweep: clean - redteam G1–G7: PASS - all six named banner emitter-deletion mutants die - independent handwritten five-row full-wordmark fixture kills Quinn's seven-mutant battery, including a one-pixel glyph change - real `112 × 46` canvas-rect dismissal tests separately cover active non-empty, active empty, and inactive non-empty output - layer-drop and zero-draw painter mutants die; z-order and pointer-events verified - CI's `tsc && vite build` includes all three banner modules - performance at DPR 2 (worst-case measured envelope): - one-time content paint: **~0.7–0.8 ms**, paid only when the banner is built or its palette changes - busy compositor, CSS `1277 × 697`, backing `2554 × 1394`: **470–497 µs/frame** for the full banner (**2.82–2.98%** of a 60 Hz frame) - busy compositor, CSS `1920 × 1080`, backing `3840 × 2160`: **1,139–1,212 µs/frame** (**6.83–7.27%**) - empty, one-glyph, and full-banner controls converge: compositor cost follows backing-layer area and DPR rather than painted-cell count - in the actual idle welcome state, cost is below both vsync-clamped rigs' resolution; it is not claimed as zero - **Pane cross-rig spread: resolved at matched loop rate.** Two independent rigs initially differed 2.3× (58–68 vs 136 µs/Mpx of backing store; pane, CSS 1277×697 / backing 2554×1394, DPR 2). The cause of *that* spread is rAF loop rate: the higher figure came from a free-running loop at ~1600fps. Throttled to ~200–236fps, both rigs read 58–68 µs/Mpx (1.25–1.44% of a 60Hz frame). The busy-composite figures quoted above remain the **unthrottled worst case** and are conservative by ~2.3× at the pane. Not established: the mechanism and sign of free-running distortion (one rig under-charges ~15%, the other over-charges 2.3×), and the 1080p figure has not been re-measured throttled. - the layer paints only on generation/theme/resize and dismisses on first non-empty active-session output, so the measurable busy cost is a short-lived worst case rather than a persistent PTY paint-path tax ## Follow-ups in this PR These are intentionally subsequent commits after the certified static-banner head, not claims about `98ebc8f90`: 1. close the compositor metrology: remeasure the 1080p point throttled and characterize the opposite-sign free-running rAF distortion, with each measurement regime stated 2. add Tyler's animated honeycomb color waves, gated by `prefers-reduced-motion`, a full 62-theme phase-sweep contrast check, and DPR-2 per-tick performance certification 3. land the already-proven mounted theme-switch regression probe from `RESEARCH/BUZZ_TERM_G3A_PROBE/` 4. bound the slow/hang-shaped G1-c mutant `waitFor` 5. optionally trim the generator to its ink bounding box, reducing the minimum viewport from 70 to 62 columns --------- Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz> Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz> Signed-off-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz> Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr <5a968df9a7494b4e019b9ecf739e088ba61097b4312124e9a88ae5b42e3f5f3e@buzz.block.builderlab.xyz> Co-authored-by: npub1cc3ha7z055mu0rwwu7806t2wt8mj3pvu0uv5mfp2c50dahaqhczshdalg6 <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz> Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz> Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz> Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz> Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> |
||
|
|
b29c8cdaa4 |
feat(desktop): redesign the Huddle experience (#4281)
## Summary - open Huddles in a focused companion window with a clean handoff back to the in-app drawer and backing channel - redesign the participant film strip, sidebar control, transcript surface, and themed shell treatment - preserve microphone and device control across windows, start agent voice on the first reply, and show agent speaking activity in the film strip - give each agent a distinct session voice, beginning with the configured default, plus compact per-agent text-to-speech and voice controls - enroll only agents explicitly mentioned or deliberately added through an agent panel into the live Huddle roster - keep temporary Huddle channels out of the sidebar unless the user explicitly brings one into the main app - remove Huddle-only avatar policy badges and filter short silence or noise segments before speech-to-text posts ## Why The previous flow exposed the temporary channel as product UI, obscured who was present or speaking, and split transcript and audio state between the main and companion windows. This keeps backing channels as implementation details unless a user explicitly brings a Huddle into the app, while sharing the live conversation and audio lifecycle across both surfaces. Agent participants now join only after an explicit invitation, distinct voices make multi-agent Huddles easier to follow, and short microphone noise no longer becomes stray transcript messages. ## Validation - `pnpm check` - `pnpm build:e2e` - `pnpm exec playwright test tests/e2e/huddle-transcription.spec.ts --project=smoke` (13 passed) - Huddle sidebar visibility unit coverage (4 passed) - focused managed-agent and persona-mention E2E coverage (2 passed) - `pnpm test` (3,910 passed) - `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --all-targets -- -D warnings` - `cargo test --manifest-path desktop/src-tauri/Cargo.toml` (2,093 passed, 14 ignored; 3 diagnostics passed) --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
b0c6d6f744 |
Add channel activity hover menu (#3935)
## Summary - show relevant unread threads and active agents when hovering a channel - keep channel-level unread emphasis separate from thread activity dots - make activity rows navigate to the thread and remove demo-only data ## Test plan - `just ci` (all stages passed except the final duplicate native check, which ran out of disk after its earlier clippy pass) - `cd desktop && pnpm exec playwright test tests/e2e/channel-activity-popover.spec.ts --project=smoke` --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> |
||
|
|
f86cfc7369 |
fix(desktop): back/forward via keyboard chords, mouse X1/X2 buttons, and swipe gestures (#3778)
## Problem Two related gaps in global back/forward navigation. Fixes #3775. 1. The keyboard shortcuts almost never fire in real use — users fall back to clicking the toolbar chevrons and assume the shortcuts don't exist. 2. On macOS, mouse back/forward buttons (X1/X2) and horizontal swipe gestures do nothing, although they navigate in every browser and in Slack. **Duplicate check:** searched open PRs and issues — none found beyond #3775 (filed alongside this fix). #3078 / #3377 are next/previous-*channel* navigation, a different feature. ## Root causes **Keyboard:** `useBackForwardControls`'s keydown handler bailed whenever the event target was editable — but `useComposerAutofocus` deliberately focuses the message composer (a ProseMirror contenteditable) on mount and on every channel switch. In steady state focus almost always lives in the composer, so the chords were silently swallowed. Invisible to CI because `navigation.spec.ts` only ever clicked the `global-back` / `global-forward` buttons, never pressed the keys. **Mouse/swipe:** on macOS, WKWebView never delivers X1/X2 button events or swipe gestures to the page (Safari handles them natively in the app layer, not in page JS), and Buzz had no native handler. ## Fix ### Keyboard chords (web layer) Match the existing platform chord regardless of the event target and drop the editable-target guard: - `⌘[` / `⌘]` have no text-editing semantics in macOS text fields, and the TipTap/StarterKit editor config binds no `Mod-[` / `Mod-]` shortcuts (checked `useRichTextEditor.ts` — list indentation is Tab/Shift-Tab). - `preventDefault()` keeps the chord out of the editor — asserted in the e2e test. This matches browsers and Slack, where back/forward chords work while a text field is focused. Chord matching is extracted into a pure helper, `app/navigation/backForwardChords.ts`, so it can be unit tested; behavior (bindings, modifier exclusivity, `code`-based matching for non-US layouts) is unchanged. ### macOS mouse buttons and swipe gestures (native layer) An NSEvent local monitor in `mouse_nav.rs` catches what the webview can't see and emits a `mouse-nav` Tauri event to the main window (`emit_to`, so navigation stays scoped if multi-window ever lands) that the frontend acts on. Two AppKit event shapes map to navigation: - `otherMouseUp` with button 3/4 — mice whose X1/X2 buttons arrive as plain button events. These are swallowed after emitting so nothing downstream double-handles them. - `swipe` with a horizontal delta — AppKit's page-swipe gesture (`swipeWithEvent:`): `deltaX > 0` back, `deltaX < 0` forward. Sent by mouse drivers that synthesize a page-swipe gesture for the back/forward buttons instead of button-3/4 events (the hardware this was verified on). Stock Apple trackpad and Magic Mouse swipes arrive as phased scroll-wheel events instead, which this PR does not handle — that path (`ScrollWheel` + `trackSwipeEventWithOptions:`, which also needs scroll-edge detection) is deferred to a follow-up. Swipes are passed through (swallowing mid-gesture events could confuse AppKit gesture tracking). The swipe path was verified end to end on hardware whose back/forward buttons emit only swipe gestures, never button-3/4 events — an instrumented event monitor confirmed the events arrive as `NSEventType::Swipe` with `deltaX ±1`, and navigation worked after mapping them. ## Tests - **13 unit tests** for the web-side chord matcher (`backForwardChords.test.mjs`): supported chords, modifier exclusivity, `code` fallback, and preservation of line-editing shortcuts. - **6 Rust unit tests** for the native mapping helpers (`mouse_nav.rs`): button 3/4 directions, other buttons ignored, swipe delta sign → direction, zero-delta (gesture-begin) ignored. - **e2e regression case** in `navigation.spec.ts`: presses the platform chord *while the composer is focused* — the missing coverage. Verified it fails against the pre-fix implementation and passes with the fix. - Full desktop unit suite: 3832/3832 pass. Full Rust suite (`cargo test`, buzz-desktop): 1888 passed / 0 failed. `pnpm typecheck`, `biome check`, `pnpm check`, `cargo fmt --check`, `cargo clippy`: clean (no new warnings). - Full Playwright e2e: 958 passed; 6 failures are relay-infrastructure tests (live relay seeding / relay state seam) that fail identically without this change — `navigation.spec.ts` is fully green. ## Manual test 1. Open a channel, then another (composer autofocuses on each switch). 2. `⌘[` — returns to the previous channel; `⌘]` — forward again. Typing `[` / `]` in the composer inserts normally. 3. Mouse back/forward buttons navigate the same way, from anywhere in the window (verified on macOS on hardware using both event shapes). ## Update — 2026-07-31 Removed the redundant DOM mouse-button handler after verifying it was unnecessary. The native macOS path remains unchanged and was revalidated manually. --------- Signed-off-by: npub1yvnq5equak5errqpku8stskushny9wsvt0fc2ywcpwt79yslwaqswe7tse <23260a641ceda9918c01b70f05c2dc85e642ba0c5bd38511d80b97e2921f7741@buzz.block.builderlab.xyz> Signed-off-by: Matheus Iser <matheusiser@squareup.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1yvnq5equak5errqpku8stskushny9wsvt0fc2ywcpwt79yslwaqswe7tse <23260a641ceda9918c01b70f05c2dc85e642ba0c5bd38511d80b97e2921f7741@buzz.block.builderlab.xyz> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
39ce3dfc3c |
fix(desktop): open profiles from avatars (#3751)
## Summary - show profile descriptions in hover cards as a single truncated line - open the profile panel when avatars are clicked across desktop surfaces - make the direct-message intro avatar clickable ## Validation - Desktop static checks - 3,807 desktop tests via pre-push --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
bd0bff24bf |
feat(desktop): add password-protected backups in settings (#3701)
**Category:** new-feature **User Impact:** Users can create, download, and verify a password-protected backup of their private identity from desktop Settings. **Problem:** Buzz does not currently give signed-in users a Settings-based path to protect or validate their private identity independently of onboarding. **Solution:** Add a focused backup menu to the private-key row, keep encryption and verification local in Rust, and preserve completed encrypted backups briefly so native saves can be retried without repeating encryption. <details> <summary>File changes</summary> **desktop/src/features/settings/** Adds the background backup lifecycle, create and test dialogs, private-key menu integration, password handling, and focused unit coverage. **desktop/src/features/onboarding/ui/NsecMaskedDisplay.tsx** Extends the masked private-key display with reusable overflow-menu actions used by Settings. **desktop/src/app/App.tsx** Mounts the backup provider at app scope so encryption and save work survive closing Settings or the modal. **desktop/src/shared/api/tauriIdentity.ts** Adds typed desktop bindings for local backup creation, save, selection, and verification. **desktop/src-tauri/src/key_backup.rs and desktop/src-tauri/src/commands/identity.rs** Implements local NIP-49 encryption, password generation, file handling, and public-identity-only verification results. **desktop/src-tauri/src/egress_guard.rs and guarded call sites** Blocks encrypted secret material from relay, websocket, snapshot, sharing, and huddle egress paths. **desktop/src-tauri tests and fixtures** Covers encryption, verification, file behavior, and fail-closed no-egress protections. **desktop/src/testing/e2eBridge.ts, desktop/tests/, and desktop/playwright.config.ts** Expands the mock native bridge and browser coverage across create, retry, expiry, and current/different-identity verification states. **desktop/src-tauri/Cargo.toml, Cargo.lock, and assets** Adds the local cryptography/password-generation dependencies and embedded short-word list. </details> ## Reproduction steps 1. Run the desktop app and open **Settings → Profile → Identity**. 2. Open the private-key overflow menu and choose **Create backup**. 3. Enter or generate a valid password, submit, and confirm progress continues if the dialog or Settings is closed. 4. Save the resulting `.ncryptsec` file; cancel and retry to confirm the temporary download remains available. 5. Choose **Test backup**, select the file, enter a wrong password, then retry with the correct password. 6. Confirm success identifies whether the backup matches the current identity and displays only the public `npub`. ## Screenshots | Settings identity | Private-key menu | Create backup | |---|---|---| | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/981e391b-6829-4081-95ca-ca75a369de71" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/7972c68e-7635-47d8-b0ad-9639390d3e6c" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/4709c8f7-cf02-46f1-bec9-b3f98fe56fb2" /> | | Encrypting | Download available | Test success | |---|---|---| | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/1ac3e934-2b4b-4135-bae6-126c715c8c59" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/cb6f07ee-a16f-44a5-b9a0-6b9fe0e4d40d" /> | <img width="1280" height="720" alt="image" src="https://github.com/user-attachments/assets/ea58b1b1-966c-46aa-8d59-92c9f06a25bd" /> | Visual review and additional states: [Buzz thread](buzz://message?channel=50ca7ef1-201e-4159-9499-40de3964b7c3&id=87eceb5f0f82fd50c32e560de3d35be48e293760f6620718aafdcef289d475fe) --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> |
||
|
|
d0a24bcb52 |
Add macOS agent menu-bar menu (#3565)
## Summary - Add a **macOS-only** monochrome Buzz menu-bar icon with **Running** and **Recent** agent sections. - Show each agent as one selectable macOS row with `Name · elapsed` and its channel underneath. - Keep completed work available for quick channel re-entry, alongside New Channel, Open Buzz, and Quit Buzz. - Keep the main window alive on close and restore it from both the menu bar and the macOS Dock. - Fence queued channel actions by community generation so an in-flight action from the previous community cannot navigate the newly selected community. - Leave Windows and Linux unchanged; cross-platform tray lifecycle support can follow with platform-specific validation. <img width="812" height="760" alt="Buzz macOS agent menu" src="https://github.com/user-attachments/assets/19bfd874-8f06-4496-bdda-7ed2e7b5733f" /> ## Validation - `cargo fmt --check` - Desktop Tauri suite: 1,860 passed, 14 ignored after merging current `main` - Desktop tests: 3,769 passed - Desktop lint, file-size, text, and pubkey checks pass (two pre-existing informational template-literal notices) - Regression coverage verifies stale `OpenChannel` actions are discarded across community changes while `NewChannel` survives ## Manual validation remaining A native macOS smoke test is still requested before merge: menu appearance, elapsed updates, Running → Recent, channel navigation, New Channel, minimized/closed/Dock restore, Open Buzz, and Quit. E2E stubs the tray IPC and does not exercise the native menu. --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
66e7054928 |
fix(desktop): deduplicate relay outage notification (#3579)
🤖 ## Summary - Keep the relay reconnect notification dismissed across repeated connection retries during one continuous outage. - Re-arm the notification after recovery or relay lifecycle replacement, including switches between communities that use the same relay URL. - Preserve the existing dedicated path for authentication and other application-level errors. ### How an outage is tracked The AppShell-owned relay-card hook treats an outage as one contiguous runtime episode rather than assigning it a persisted ID. A hook-local `outageActiveRef` is armed by the first qualifying unreachable/degraded observation. While it is armed, intermediate retry states (`connecting`, `reconnecting`, `stalled`, and `disconnected`) belong to that same episode, so retry churn cannot clear dismissal or emit another notification. The hook receives the same lifecycle identity used by community initialization: community ID plus `reinitKey`. This distinguishes multiple communities even when they share a relay URL, and it also changes when the active community is explicitly reinitialized. The latch and dismissal are reset when that identity changes or when the relay singleton reports its authoritative `idle` teardown state. A successful `connected` state also closes the episode and re-arms the next outage. These boundaries deliberately bias toward re-notifying rather than suppressing a later outage: recovery, community switch/reinit, or relay teardown cannot leave the hook stuck believing an old outage is still active. No outage state is persisted beyond the mounted hook lifecycle. ### Related issue None found. ### Testing - `pnpm --dir desktop typecheck` - `pnpm --dir desktop test` — 3,769 passed - `pnpm --dir desktop check` - `pnpm --dir desktop build:e2e` - `pnpm --dir desktop exec playwright test tests/e2e/sidebar-relay-card.spec.ts --project=integration` — 11 passed --------- Signed-off-by: Larry <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> Co-authored-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> |
||
|
|
a35771fc44 |
feat(desktop): refine agent catalog sharing (#2439)
## Summary - add custom-agent catalog sharing and hide built-ins from discovery - let owners publish later catalog updates from Share or while saving edits — the save always persists locally, and the publish reports `published` or `queued` (flushed automatically once the relay is reachable again) - preserve agent type, model, and runtime across snapshot import/export - simplify agent and team entry points and tighten catalog layout - migrate the legacy global retention queue into the owner's active scope so pending catalog publishes survive the upgrade - keep the agent list and edits usable in recovery mode by degrading to unshared projections when scope resolution or the retention DB fails - track catalog provenance on copied personas, so adding an already-added foreign agent resolves to the existing copy instead of creating a duplicate - scope inbound persona events to the community relay they arrived on - page the catalog read past the relay's 1,000-row query clamp - unify the share dialog's memory-level choice into a single "What's included" selector that drives both DM-send and copy-link delivery (all six combinations preserved), group the delivery rows above the option rows, and label the catalog toggle "Not shared" / "Shared" - keep emoji avatars on catalog entries — they persist as inline percent-encoded SVG, which the catalog projection's http(s)-only URL guard used to drop, so a shared agent showed initials instead of its avatar - drop the "Active in communities" card from Agents settings, superseded by the per-channel runtime controls in the members sidebar ## Screenshots ### Agent actions  ### Team avatar stack  ### Catalog sharing  ### Publish while editing  ### Publish from Share  ### Catalog details  --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> |
||
|
|
2bd4c24b71 |
Inbox refactor (#2045)
## Why The Inbox mixed overlapping feed categories with personal work queues, so **All** was not actually comprehensive and several filters did not make it clear why an item appeared. Threads and DMs could produce one row per event instead of one row per conversation, drafts were hidden until selected, and reminders appeared through multiple competing presentations. This refactor makes the **Inbox** a focused, conversation-oriented place to catch up on work relevant to you. It is intentionally not a mirror of every unread event in every channel. ## What changed - Keep the destination named **Inbox** and use the standard Lucide bell icon. - Refocus **All** on DMs, mentions, thread replies, needs-action items, replies from agents the user owns or controls, due reminders, and active drafts. - Exclude generic top-level channel traffic and updates from agents the user does not own or control. - Group each thread or DM into one row, sorted by latest activity. - Resume an unread conversation at its oldest unread message while opening the full thread or DM in the detail pane. - Reuse the existing **New** divider at the unread boundary. - Make the detail title a direct link to the canonical conversation. - Give Reminders and Drafts the same list/detail interaction and location metadata as conversation rows. - Separate Reminders and Drafts from message filters with a subtle divider, without adding another labeled section. - Put reminder and draft counts beside their corresponding filter labels instead of on the generic filter button. - Preserve the selected conversation when switching filters if it remains valid; otherwise select a valid replacement without flashing stale detail. - Use filter-specific empty states and rename the options toggle to **Show unread only**. - Ship the focused behavior directly. The earlier experiment gate, Custom view, and default-view controls have been removed from this PR to keep the first pass focused. ## Filter model | Filter | What appears | | --- | --- | | **All** | One row per personally relevant conversation, plus due reminders and active drafts. Includes DMs, mentions, thread replies, explicit needs-action items, and replies from agents the current user owns or controls. Excludes generic top-level channel traffic, other agents' updates, and reminders that are not due yet. | | **Mentions** | Conversations containing a direct mention. Each conversation appears once and opens with full context. | | **Threads** | Conventional threaded replies, grouped to one row per thread. Broadcast replies are not treated as conventional thread replies. | | **Needs action** | Feed items explicitly classified as requiring action. | | **Agents** | Conversations whose representative response was authored by an agent the current user owns or controls, including top-level DM responses. If a human replies afterward, the conversation leaves this filter until an owned agent responds again. | | **Reminders** | All pending reminders, including upcoming reminders that stay out of **All** until they are due. | | **Drafts** | Active drafts, ordered by their last real edit time. | ## Grouping, ordering, and state - A thread or DM creates one Inbox row rather than one row per event. - An unread conversation resumes at its oldest unread message so intervening context is not skipped. - Conversation rows still sort by their latest activity. - The detail pane opens the full available conversation and shows the shared **New** divider before the first unread message. - Upcoming reminders appear only in **Reminders**. - When a reminder becomes due, it enters **All** at its trigger time. If its source conversation is already represented, the reminder state merges into that row instead of creating a duplicate; otherwise it appears as a standalone reminder row. - A due reminder can enrich a row in another relative filter when that conversation already qualifies for the filter. Reminder lifecycle remains separate from message read state. - Drafts appear in **All** by their last real edit time. Opening an unchanged draft does not move it to the top. - Reminder and draft rows show their location as `In #channel` or `In DM with <name>`. - **Show unread only** hides reminder and draft work queues because they do not share message unread semantics. ## Removed or narrowed - **Remove the old Activity filter.** It overlapped with All while still omitting items All now includes. - **Narrow Agents.** It no longer gathers every agent participating in a shared thread or subsequent human follow-ups. - **Remove duplicate reminder presentations.** The aggregate pending-reminders jump and duplicate generic feed rows are replaced by one list/detail model. - **Remove Custom and default-view settings from this pass.** They added considerable state and UI before the core model had been validated. - **Do not add section labels for Reminders and Drafts.** A divider communicates the distinction without creating another hierarchy in the menu. ## Risk assessment Medium implementation risk because this changes composition, grouping, ordering, read behavior, and personal queues in a primary desktop view. The implementation is scoped to the desktop UI and its local feed projection; it does not change relay schemas or public APIs. ## Testing - Desktop formatting, lint, file-size, text-size, and TypeScript checks passed. - Desktop unit suite: **3,663 passed, 0 failed**. - Desktop E2E production build passed. - Playwright smoke coverage across every spec touching this surface (`channels`, `smoke`, `profile`, `project-inbox`, `community-rail`, `integration`, `drafts-screenshots`): **118 passed, 0 failed**. - Full Playwright smoke project: **732 passed, 1 skipped**. Three local failures were investigated and cleared — `community-rail` keyboard reorder passed on re-run (flaky), while `relay-reconnect:97` and `video-attachment:223` are untouched by this commit (the only change to shared `tests/helpers/bridge.ts` is a comment) and pass in CI. - Unit coverage includes focused All matching, owned-agent filtering, conversation grouping, oldest-unread selection, selection stability, chronological reminder/draft composition, trigger-time reminder ordering, and duplicate reminder suppression. ## Update: July 27, 2026 The naming decision is settled: the surface stays **Inbox**. An earlier pass in this branch had renamed it to **Activity**; that rename has been reverted in `9c00d2d6e`, which is naming-only and changes no behavior. The revert covers file names, component/hook/type/constant identifiers, the sidebar label and tooltip, the `Inbox options` and `Filter inbox:` aria-labels, and the corresponding test names, test ids, and fixture ids. Three things were deliberately left as `activity`: - **The feed API contract** — the `activity` / `agent_activity` categories, the `feed.activity` and `feed.agentActivity` keys, and the `types=` query parameter. These are the server's names, not the surface's. - **Plain-noun usage** — empty states such as "No activity yet", plus `latestActivityAt` and `PROJECT_ACTIVITY_KINDS`. - **Pre-existing agent, project, and profile activity code**, which refers to a different concept entirely. The earlier experiment-gate approach has also been dropped, so `tests/helpers/bridge.ts` no longer claims that an Activity preview feature exists — `preview-features.json` has no such entry and the seed helper enables every desktop feature. Generated with Codex --------- Signed-off-by: Clay Delk <clay.delk@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
95fdf97880 |
feat(acp): bring your own harness (BYOH) — generic ACP runtime seam + settings gallery (#2773)
## What
Implements a "bring your own harness" (BYOH) generic ACP mechanism —
replacing per-harness backend code with a data-driven 3-tier system:
- **Tier 1 (compiled-in builtins):** goose, claude, codex, buzz-agent —
unchanged behavior
- **Tier 2 (bundled presets):** cursor, omp, grok, opencode, kimi, amp,
hermes, openclaw, and any future additions — defined in
`PRESET_HARNESSES`, no code duplication, icons stay
TerminalSquare/bundled-asset-only
- **Tier 3 (user-defined custom):** JSON definitions saved to
`custom_harnesses/` under app data; managed via Settings → Agents UI
## Changes
### Core data model
- `HarnessDefinition` — id, label, command, args, env, install URL/hint
- `PRESET_HARNESSES` static table — single source of truth for all
presets; `preset_harness_ids()` derives reserved IDs (D-11: no
hand-maintained copy)
- `source: "builtin" | "preset" | "custom"` tagging on every catalog
entry
### Persistence (B-4, B-6)
- `save_custom_harness_to_dir(dir, definition, rename_old_id)` —
backup-swap atomic write (backs up target → .bak, commits temp → target,
restores .bak on failure, removes .bak on success); safe on Windows
where `fs::rename` over an existing file is "access denied"
- `save_and_warm` / `delete_and_warm` — hold `PERSIST_MUTEX` for the
write + registry-warm pair, eliminating the lost-update race (B-6) where
two concurrent saves could interleave their warm calls and leave a stale
registry snapshot
- Validate-before-mutate: both IDs and env validated before any
filesystem mutation
### Env validation boundary (B-3)
- `validate_harness_definition_pub` calls `validate_user_env_keys` on
definition env at save AND load
- Rejects malformed keys (BUZZ_AUTH_TAG=x forgery shape), reserved keys
(BUZZ_MANAGED_AGENT etc.), NUL bytes, oversized values
### TypeScript boundary (B-2 / Thufir CRITICAL)
- `RawAcpRuntimeCatalogEntry` now declares `definition_env?:
Record<string,string>` and `source: "builtin" | "preset" | "custom"`
- `fromRawAcpRuntimeCatalogEntry` maps `definition_env → definitionEnv`
(camelCase); absent field defaults to `{}`
- Edit form reads `entry.definitionEnv` — env no longer erased on
save-then-edit cycle
### Unified descriptor (Phase A / Thufir F4)
- `EffectiveHarnessDescriptor { command, args, env }` in `readiness.rs`
- `resolve_effective_harness_descriptor()` — single resolver used by
spawn, spawn_hash, summary, get_agent_models (both saved and unsaved),
and readiness
- No competing arg-resolution forms
### Other fixes
- B-5: stop freezing `runtime.defaultArgs` into `record.agent_args` on
normal create paths
- B-7: readiness exec-check — `MissingBinary` variant for custom
commands not found on PATH
- B-8: onboarding transition — `setTimeout(0)` removed, parent-owned
route intent via `navigateAfterComplete` prop
- C-9: collector-discriminating sweep tests with injectable filters
- C-10: `HarnessManagementCard` uses `harnessGalleryLogic` helpers
(killed duplicate filter/sort)
- D-11: `BUILTIN_IDS` derived from `PRESET_HARNESSES` (no
hand-maintained copy)
- D-12: `mobile/pubspec.lock` churn reverted
- D-13: false ownership fast-path comment fixed
- D-14: URL scheme validation for `installInstructionsUrl`
- D-15: OpenClaw Gateway env-locus README line
### Tests added
**B-4 persistence (6 tests):**
`save_to_dir_create_writes_file_and_loads_back`,
`save_to_dir_same_id_edit_replaces_content`,
`save_to_dir_backup_is_cleaned_up_after_same_id_edit`,
`save_to_dir_rename_removes_old_file_and_creates_new`,
`save_to_dir_rename_nonexistent_old_id_is_non_fatal`,
`save_to_dir_roundtrip_with_env_preserves_values`
**B-3 env validation (6 tests):**
`validate_rejects_malformed_key_with_equals_sign`,
`validate_rejects_reserved_key_buzz_managed_agent`,
`validate_rejects_reserved_key_case_insensitive`,
`validate_rejects_nul_byte_in_value`,
`validate_rejects_value_over_per_value_size_limit`,
`validate_accepts_well_formed_env`
**B-2 API boundary (4 TS tests in tauri.test.mjs):**
`fromRawAcpRuntimeCatalogEntry maps definition_env to definitionEnv`,
`defaults definitionEnv to {} when absent`, `preserves source preset`,
`env round-trips through edit payload shape`
## Preset catalog
| ID | Label | Command |
|----|-------|---------|
| `cursor` | Cursor | `cursor-agent acp` |
| `omp` | Oh My Pi | `omp acp` |
| `grok` | Grok Build | `grok agent --always-approve stdio` |
| `opencode` | OpenCode | `opencode acp` |
| `kimi` | Kimi Code | `kimi acp` |
| `amp` | Amp | `amp-acp` |
| `hermes` | Hermes Agent | `hermes-acp` |
| `openclaw` | OpenClaw | `openclaw acp` |
## Review-fix pass (2026-07-26, Eva)
Fixes from the three-way review (Wren / Dawn / Eva) in the
buzz-generic-acp-harnesses thread, pushed as new commits (no rewrite):
1. **installHint edit round-trip** — form seeding extracted to
`formValuesFromCatalogEntry` (single source of truth), input rendered,
full-definition lossless round-trip regression.
2. **Dangling-delete coherence** — delete allowed; confirm counts
referencing agents (direct pin + persona-inherited); summary rows render
`harness (deleted): <id>`; spawn errors become actionable sentences
(`user_facing_harness_error`); composed delete→summary→start test.
3. **Comma-in-args** — rejected at `validate_harness_definition` (shared
by save AND disk load), mirrored inline in the form.
4. **Registry publish race** — collision/dup filtering moved into
`load_custom_harnesses` (both loaders inherit shadowing rules);
discovery publishes by re-reading the dir under `persist_mutex` (lock
scoped to publish only); deterministic interleaving regressions for
save-during-discovery and delete-during-discovery.
5. **Mechanical** — discarded `belongs_to_us` sweep arg deleted,
`load_global_agent_config` hoisted out of the per-record summary loop,
duplicated doc paragraph + stray SAFETY comment removed.
6. **PGID test de-flaked** — leader kept alive through the assertion.
Known follow-up (filed in review, not blocking): file-size split-outs
queued in `check-file-sizes.mjs` entries.
## Gate table — head `bf53f1d60`
| Gate | Result |
|------|--------|
| `cargo test --lib` (desktop/src-tauri) | **1701 passed**, 0 failed, 14
ignored |
| desktop JS suite (`pnpm test`) | **3605 passed**, 0 failed |
| `tsc --noEmit` | clean |
| `biome check` + file-size/px/pubkey checks | clean |
| `cargo clippy --lib -- -D warnings` | clean |
| `cargo fmt --check` | clean |
PR head: `bf53f1d60e3cbd07392e1287b83bb37ba90d0d33` — includes merge of
origin/main (`c2a4ee711`, conflicts in agent_models composed with
#2890's live Databricks discovery)
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
|