mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
2ce8df8533d8c8598ab3d7a2faa797f8b5ee2eea
1599 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2ce8df8533 |
fix(models): curate Databricks alias-aware labels for 5 missing endpoints (#6360)
Curates Databricks model labels through prefix-stripping aliasing and materializes exact records for five endpoints missing from the Databricks catalog. **Alias-aware label resolution** (Databricks v2) - Strip workspace-specific prefixes (`goose-`, `team-x-`, etc.) before exact-record lookup so any prefixed alias resolves to its canonical label without enumerating every alias variant. - Return `None` for uncurated ids instead of falling back to a raw string; callers control the fallback display. **New exact records — label-only (Anthropic Messages route, axes from family rules)** - `databricks-claude-fable-5` → "Claude Fable 5" (materialized from `anthropic-adaptive-xhigh-fable-5`) - `databricks-claude-opus-4-8` → "Claude Opus 4.8" (materialized from `anthropic-adaptive-xhigh-opus-4-8`) - `databricks-claude-opus-5` → "Claude Opus 5" (materialized from `anthropic-adaptive-xhigh-opus-5`) - `databricks-claude-sonnet-5` → "Claude Sonnet 5" (materialized from `anthropic-adaptive-xhigh-sonnet-5`) **New capability-bearing record — Kimi K3 (MLflow Chat route)** - `databricks-kimi-k3` → "Kimi K3" with axes from models.dev Moonshot catalog: reasoning toggle + effort `[low, high, max]`, no default; `_reconciliation_doc` records the source and reconciliation policy. **Corpus and tests** - 6 new normative-corpus vectors (canonical + one `goose-*` alias per new id); executable vector count updated to 113 in both Rust and TS gates. - Rust and TS label tests extended to cover all 3 new canonical ids and their aliases via the prefix stripper. - Sentinel variable fix in discovery-provider test to prevent false failure in Databricks dev environments where `BUZZ_AGENT_PROVIDER` is set. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
3c228b1082 |
feat(desktop): refine context-aware Projects collaboration (#6396)
## Summary - give inline project agents bounded visible-page and selection context while reusing the shared message-thread presentation - add contextual collaboration actions for discussing project entities in related channels - align project list metadata, context rails, and work-item communication actions with the active workspace This is Part 3 of the Projects v6 stack, following #6368. Part 4 contains the remaining navigation and detail-page polish. ## Testing - Desktop unit suite: 5,125/5,125 passed - Projects smoke specs: 62/62 passed - TypeScript, Biome, typography, pubkey, and differential file-size checks passed - full pre-push gate passed ## Post-Deploy Monitoring & Validation - validate Projects overview/detail agent chat and discuss-in-channel journeys in the first staging Desktop session - healthy signals: context matches the active project/repository/work item, messages remain in the chosen channel, and restored conversations exclude unrelated DM history - failure signals: stale or cross-project context, duplicate/missing thread rows, or collaboration actions targeting the wrong channel; mitigate by reverting this PR Related: #6335 --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
2e7583bf5a |
fix(desktop): distinguish duplicate agent devices (#6337)
## Summary - distinguish same-name owned agents by management provenance: `managed here` for Desktop-managed identities and `managed elsewhere` for same-owner relay identities - show provenance only when same-name suggestions collide, alongside each identity's short npub - preserve exact-pubkey selection and keep unique-agent autocomplete unchanged - add a composed mock-bridge regression covering relay owner propagation, rendered labels, keyboard/pointer selection, and outbound mention pubkeys ## Testing - focused mention suggestion mapping and label tests - composed Desktop E2E passes for both same-name identities and exact-pubkey routing - causal mutation verified: replacing the relay candidate's `ownerPubkey` with `null` makes the composed E2E fail on the `managed elsewhere` assertion - pre-push Desktop checks: Biome, TypeScript, file-size ratchet, and 5,103 Desktop tests ## Manual test With two same-name owned agents visible in a channel, type `@<name>`. Duplicate rows identify the identities as `agent · managed here` and `agent · managed elsewhere`, include distinct short npubs, and selecting either routes the mention to that row's exact pubkey. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
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> |
||
|
|
f88cda9eb8 |
feat(desktop): make the Projects overview follow the selected section (#6335)
## Summary Part 1 of 4 stacked PRs continuing the Projects work from #6003. - The Projects overview now follows the selected section: the right-hand context pod stays visible across the Projects / Repositories / Reviews / Tasks / Channels tabs and shows section-specific people, stats, and contribution graphs, so it reads as live context rather than a detached summary. - Entity list rows are consolidated onto one compact line (title first, icons after, aligned dates/counts), shared across projects, repositories, PRs, issues, and channel lists via `ProjectEntityListRow`. - New `projectRelatedChannels` helper resolves the channels a project is discussed in for the overview. Follow-ups in this stack: part 2 (selectable workspaces), part 3 (context-aware collaboration), part 4 (navigation & detail-page polish). ## Test plan - [x] Desktop unit tests (`pnpm test`) — pass - [x] TypeScript (`tsc --noEmit`), Biome, clippy — clean via pre-push gate - [x] Playwright specs updated for the new overview behavior (`projects-v3-screenshots.spec.ts`, `sidebar.spec.ts`) — pass locally in the smoke project --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
09718fbb60 |
refactor(desktop): coordinate TTS playback (#6341)
## Summary - introduce a `PlaybackCoordinator` as the exclusive owner of the rodio `Player` - serialize append, cancellation, synthesis-flight, and drain-release state - cancel without blocking the coordinator by replacing the player queue - preserve first-append/onset bookkeeping across cancellation - increase the existing playback lead-in from 20 ms (480 samples) to a derived 22 ms (528 samples), covering rodio 0.22.2's 512-source-sample add-time bootstrap span This is the behavior-preserving coordinator prerequisite for the follow-up floor-authorization policy. The only intentional playout difference is +2 ms silence at playback-chunk starts to prevent the measured fresh-player bootstrap chirp. ## Verification (all at this exact head, `8bfa4c0531834dce39f5ad0266f2674505e659bf`) - `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --workspace --all-targets -- -D warnings` — clean - `cargo test --manifest-path desktop/src-tauri/Cargo.toml --package buzz-desktop` — 2,596 passed / 17 ignored / 0 failed - coordinator concurrency tests: stalled cancellation, append-vs-cancel serialization, cancel election, first-append rearming, and the post-swap `starts_playback_chunk` invariant - headless known-answer sweep: 528-sample lead-in clean across 7/7 device configurations (incl. falsifier arm at 192k/96k/32k/16k/8k and 1/2/4/6 ch); current 480 dirty across 5/7 - live CoreAudio gate at production buffer shape (24 kHz mono, real `build_sentence_append_buffer` path): bootstrap chirp eliminated (660.1 Hz head vs 660 Hz control), no false drain, cancel-to-silence ~1 ms vs 12 ms baseline - independent line review + mutation testing on the shipping coordinator bytes: 4/6 mutants killed, 2 shown equivalent under rodio 0.22.2 `Player::drop` semantics ## Playout cost - +2 ms lead-in at each playback-chunk start versus main - first post-cancel utterance receives 22 ms total intentional silence (not stacked cushions) ## Provenance Authored and reviewed by Buzz agents (Wren: implementation; Dawn: line review, known-answer control, mutation testing; Max: live CoreAudio gates; Eva: coordination and final verification), with review records on the Buzz relay. This PR was briefly merged to main directly by mistake and reverted via ref reset; this is the same commit going through the front door. --------- Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Signed-off-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz> Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz> |
||
|
|
9c2f05346f |
fix(desktop): show complete repository trees (#5102)
## Summary Large repositories were silently limited to the first 250 lexicographically sorted paths, which could leave the Files tab showing only dot-directories. Retain all tracked paths while limiting eager content previews, and replace the Files tab hard cutoff with incremental 200-entry pagination. ### Related issue Fixes #4960. No duplicate PR found. ### Testing - Targeted Rust regression: `cargo test --manifest-path desktop/src-tauri/Cargo.toml parse_ls_tree_keeps_paths_after_eager_preview_limit --lib` - Pagination helper regression: `node --import ./test-loader.mjs --experimental-strip-types --test src/features/projects/lib/projectsViewHelpers.test.mjs` - Desktop Biome, type checking, and file-size checks pass - All 4,393 desktop tests pass - Repository pre-push hooks pass - `just ci` currently stops on pre-existing Clippy warnings in `crates/buzz-acp/src/queue.rs` and `crates/buzz-agent/src/hints.rs`, outside this PR diff Generated with Codex --------- Signed-off-by: Evan Chen <evanc@squareup.com> Co-authored-by: Codex <noreply@openai.com> |
||
|
|
9a1e861ab8 |
Add appearance preference previews (#6193)
## Summary - Replace the Link previews and Thread layout dropdowns with the shared segmented control from #5644. - Pair each setting with a real, inert preview: production Compact/Rich link cards and a simplified Focus/Split thread diagram. - Keep scrub previews local to Settings so only a completed selection updates the saved preference and real app behavior. - Keep each setting and preview responsive as one grouped unit, with focused light/dark and narrow/wide coverage. ## Dependency - Stacked on #5644, which provides the typography/density foundation, shared `SegmentedControl`, and responsive `SettingsOptionRow`. - After #5644 merges, this PR should be retargeted/rebased onto `main`. ## Validation - `pnpm typecheck` - `pnpm check:px-text` - `pnpm check:file-sizes` - `pnpm exec playwright test tests/e2e/appearance-previews.spec.ts --project=smoke` (3 passed) - Focused existing Appearance coverage (2 passed) - Pre-push desktop checks, typecheck, tests, and Tauri checks Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> |
||
|
|
f96f1883c5 |
fix(desktop): restore emoji recents (#6263)
## Summary
- normalize emoji-mart's poisoned literal `{}` recents state before
initialization
- preserve the established idle emoji-index prewarm so first picker open
stays responsive
- keep the mounted quick-reaction tray behavior unchanged from main
- cover full-picker same-session recents, prewarm ordering/callback
behavior, and the production picker-to-prewarm import boundary
## Validation
- focused prewarm tests passed 2/2 at
`98188797e219db20670b46379a240493ff8f4cbe`
- mutation check passed: changing `EmojiPicker` back to direct
`@emoji-mart/data` consumption fails the new production-boundary
assertion
- pre-push branch-skew, file-size, Desktop check, typecheck, and full
Desktop unit suite passed (5,024/5,024) at exact pushed head
`98188797e219db20670b46379a240493ff8f4cbe`
- focused full-picker lifecycle plus existing custom-emoji/tray smoke
passed 2/2 on the preceding runtime-equivalent head
- independent Royal Court scope review and headless field test passed
the full-picker journey on the preceding runtime-equivalent head
---------
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
|
||
|
|
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>
|
||
|
|
fe7c6808e7 |
fix(desktop): hide archived channels from #/Tab autocomplete (#6156)
🤖 ## Summary Desktop's `#`/Tab channel autocomplete offered archived channels as if they were valid destinations. Stale channels competed with active ones in the picker, including a confusing uppercase `STREAM` entry — the badge of an archived stream channel leaking into the suggestion list. After this change, autocomplete suggests only active, non-DM channels. Links to archived channels in old messages still resolve normally. ## Demo Before/after, real archive flow:  ## What changed `useChannelLinks.ts` builds the autocomplete suggestion list from the raw relay channel list and previously excluded only DMs, so archived channels slipped through. The fix filters `archivedAt === null` while generating suggestions only — the shared navigation list stays unfiltered, so resolving historical links to archived channels is unchanged. - `desktop/src/features/messages/lib/useChannelLinks.ts` — suggestion selection centralized in `selectChannelSuggestions`; excludes archived channels and DMs; keeps the existing eight-result limit. - `desktop/src/features/messages/lib/useChannelLinks.test.mjs` — new tests: archived stream/forum excluded, active ones included, DMs excluded, mixed lists. ## Validation At exact head `5a963b8407bd48f40d992e026d0a841d2445647f`: `pnpm check`, `pnpm typecheck`, and the full Desktop unit suite (4,960 passed). Smoke E2E run compared against exact base `f956e6fe`: the only failures are two pre-existing failures that reproduce on base and four documented pass-on-retry flakes — none touches the autocomplete surface. Signed-off-by: loganj <loganj@squareup.com> Co-authored-by: Claude Code <noreply@anthropic.com> |
||
|
|
e5a6e2022f |
fix(desktop): morph the drawer panel icon instead of sliding it (#6306)
## Summary - Replaces the drawer panel icon's CSS `translateX` slide with a `motion/react` width + corner-radius morph, so the icon reads as the panel opening rather than the glyph drifting sideways. - Honors `prefers-reduced-motion` via `useReducedMotion` (no animation for users who opt out). Follow-up polish to the projects workspaces work that just landed in #6003 — the slide animation shipped there was the wrong visual. ## Test plan - [x] Desktop unit tests and typecheck pass with this file at this content (validated as part of the projects-v6 branch validation) - [ ] Visual check: open/close the right drawer and confirm the icon morphs in place Signed-off-by: Thomas Petersen <thomasp@squareup.com> |
||
|
|
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> |
||
|
|
a362fecc23 |
perf(desktop): parallelize relay agent directory rebuild (#6258)
The shared-agent directory rebuild resolves runtime directories, owner profiles, and managed policies for every candidate agent via dozens of exact-author query batches. Issuing those batches serially made a ~100-agent rebuild take 6.5–8.4s, which dominated @mention autocomplete latency. #6224 already scoped the *send-path* revalidation (`revalidate_relay_agents`) to just the mentioned pubkeys, so the send stall is fixed. But the autocomplete directory (`list_relay_agents`) still rebuilds the full membership set serially — this PR removes that remaining cost. ## Change Run the query batches with bounded concurrency via a shared `query_filter_batches` helper. Each directory rebuild constructs one `tokio::sync::Semaphore` (8 permits) and shares it across all of that rebuild's phases, so the runtime-directory and owner-profile phases that run concurrently under one `try_join!` never exceed 8 `/query` requests in flight together — the bound is per-rebuild. The policy phase reuses the same budget. Same events, keyed by pubkey downstream so ordering is irrelevant. Both `list_relay_agents` (autocomplete) and `revalidate_relay_agents` (scoped send-path check from #6224) funnel through `list_relay_agents_for_selection`, so the helper is a no-op for the tiny 1–3-mention revalidation set and only the full autocomplete rebuild sees the win — the scoped send path is untouched. ## Measurement Live on the production relay, warm connection, full `list_relay_agents`: | | Full-directory rebuild | |---|---| | Serial (before) | 6.5–8.4s | | Bounded-concurrency (after) | 2.0–3.4s | Returned pubkey set is byte-identical pre/post. The 8-permit ceiling holds under saturation (24 batched requests → peak exactly 8, zero failures, zero requests left in flight). Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
121e4b3ce7 |
fix(desktop): exclude archived agents from nest, order regeneration (#5905)
The managed "Active Agents" table in `~/.buzz/AGENTS.md` was rendered from every managed-agent record with no filtering, so archived duplicate instances kept appearing under the active relay's header. This scopes the roster to identity-active agents and makes regeneration safe under concurrency. ## Roster filter: identity-archive only `render_dynamic_section` now drops only records whose pubkey is present in the relay's `kind:13535` archive snapshot. Local records can't tell they're archived — they all carry `is_active: true` (that flag is a *definition*-archive, not an identity-archive), so archive truth lives only relay-side. The read **fails open**: an unreachable relay yields an empty set and hides no one. There is deliberately **no** relay-scope filter. `relay_url` is a legacy creation-era field that `effective_agent_relay_url()` ignores — every agent is eligible on every community, and snapshot-imported records store `relay_url: ""` by design. Filtering on it would hide valid, runnable agents after a workspace switch or import. Foreign-relay relic records leave the table via record deletion, not code. ## Regeneration on archive / unarchive `archive_identity` and `unarchive_identity` submitted the relay event and returned without refreshing `AGENTS.md`, unlike the ~20 other mutation sites that call `try_regenerate_nest`. A just-archived agent therefore lingered on the roster until an unrelated edit or the next launch. Both commands now trigger a regeneration after a successful submit. Regeneration is bound through a `NestRegenTrigger` trait rather than constructing the `try_regenerate_nest` callback at the Tauri-command delegation site. The command cores take `regen: &impl NestRegenTrigger` and own the `|| regen.trigger()` binding; the thin wrappers only pass `&app` (whose `impl` calls `try_regenerate_nest`). This puts the regen wiring inside the unit-tested core — a `CountingRegen` double proves each core fires exactly one regeneration — instead of an untestable seam where a wrapper could silently lose the refresh while the suite stayed green. The regen races the relay's `kind:13535` snapshot update, so it's best-effort and fail-open — a stale render self-heals on the next cycle. ## Ordered regeneration `try_regenerate_nest` previously spawned unconstrained tasks that each snapshotted state, awaited two relay requests, then wrote — so a slow pre-edit generation could overwrite a newer one. Boot made this deterministic: the boot regen races the `apply_workspace` regen, and the fallback-relay render could finish last. `NestRegenGate` fixes this with a single `highest_requested` watermark. A monotonic generation is claimed *synchronously* at request time (encoding call order) and advances the watermark under one lock; the spawned task carries its generation and, at commit, reads the watermark under that same lock — the compare-and-write is atomic with no await held across it. A generation whose number is below the current watermark drops its result instead of rolling the file back. Gating on highest-*requested* rather than highest-*written* is the load-bearing choice: if a newer generation is requested but then fails its relay reads, an older in-flight generation must **not** publish its now-obsolete roster. Behavior delta: once a newer regeneration has been requested, no older generation will ever write; if that newer generation fails, the file is left as-is and self-heals on the next trigger rather than regressing to a stale snapshot. This is an ordered, latest-request-wins gate — not a work coalescer: superseded generations still perform their relay reads and drop the result at commit time. The gate's commit lock is acquired with a poison-to-`io::Error` mapping rather than `expect()`, so a poisoned lock degrades to the same warn-and-continue path as any other commit failure instead of panicking the desktop process (a best-effort housekeeping write must never take down the app). ## One relay target per regeneration A regeneration read the workspace relay override three times — the NIP-11 signer in `fetch_relay_self`, the snapshot query in `query_relay`, and the rendered footer — so a workspace switch mid-flight could pair one relay's advertised signer with another relay's snapshot, fail open, and render archived agents as active. `capture_relay_target` now resolves the effective relay (WebSocket + HTTP API base) once, before any network work, and `fetch_archived_pubkeys_at` threads that single target through both the NIP-11 fetch and the `/query`; the footer renders the same target. Signer, snapshot, and footer always belong to one relay. ## Monotonic archival snapshot publishing `publish_nipia_archival_list` stamped its `kind:13535` snapshot with a whole-second `created_at`. A rapid archive→unarchive within the same second produced two events whose NIP-16 replaceable-event tie-break (higher event id wins) could strand the older, stale archive state as canonical. Publishing now uses a bounded retry that re-reads current archive state and rebuilds the snapshot each attempt, so the published list reflects the latest intended state rather than a fixed same-second race loser. After the attempt budget (8) is exhausted the publisher `bail!`s; the sole caller treats that as a `warn!` side effect and continues, matching the surrounding best-effort submit path. ## Test-file split The renderer, `upsert_managed_section`, and the regeneration-gate tests moved to `nest/render_tests.rs` so each test file stays under the repository's 1000-line ratchet. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Mongo <5c25403eab7271f9f94ddd4f2b270e8cac2c92e2c830c51877cca6ec974ffb3f@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@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> |
||
|
|
6e8d078ffe |
fix(desktop): emit camelCase config-write payload fields (#6062)
Fixes #6015. `ConfigWriteMechanism` is internally tagged and carried only `rename_all = "camelCase"`. On an internally tagged enum that renames the **variants**, never the variants' fields, so the payload went out snake_case: ``` {"type":"respawnWithEnvVar","env_key":"K"} {"type":"acpSetConfigOption","config_id":"c"} {"type":"acpSetSessionModel"} {"type":"gooseNativeConfigWrite","config_key":"g"} {"type":"readOnly"} ``` against `envKey` / `configId` / `configKey` in `desktop/src/shared/api/types.ts:615-620`. That output is a probe run of the real module before the fix, not a reading of the code. What makes it read as correct is the asymmetry: the variant *names* rename fine, so the `type` discriminant and every `switch (writeVia.type)` behave; and the enclosing `NormalizedField`'s own fields (`writeVia`, `overriddenValue`, `isRequired`) rename fine too, because `rename_all` does apply to struct fields. Only the variant's field is wrong. Adding `rename_all_fields = "camelCase"` fixes it. `rename_all_fields` appeared zero times in `desktop/src-tauri` before this. **Severity, stated plainly: latent, not currently user-visible.** Nothing in `desktop/src/**` reads `.envKey`/`.configId`/`.configKey` off a `writeVia` — `AgentConfigPanel.tsx` is the only `RuntimeConfigSurface` consumer and never touches the field, and no Rust code deserializes the type either. The write-back path these fields exist for is not wired yet. The hazard is for whoever wires it: `invokeTauri<T>` is an unchecked cast, so they get `undefined` with a green `tsc`. I also carried the attribute onto `ConfigFieldType`. Its only payload field is `options`, single-word, so that half is not a fix — it is the attribute the next multi-word field would silently need. One divergence from the issue's suggested step 3: the 20 `e2eBridge.ts` sites already emit camelCase, and camelCase is the contract, so they are correct as written — changing them would have been wrong. What they lacked was provenance, since agreeing with `api/types.ts` while the backend emitted something else is exactly what let this sit. They now name the Rust test that pins the bytes. **Tests** (`wire_format_tests`, 4 cases, whole-value not key-set — a key-set assertion still passes when a variant name regresses): - every variant against the TypeScript spelling; - the nested `NormalizedField`, which is the shape the renderer actually receives; - a camelCase round-trip **plus** an assertion that the old `env_key` spelling is now rejected, so a revert cannot quietly keep deserializing; - `ConfigFieldType::Enum`. Removing `rename_all_fields` again turns three of the four red. Verified locally: full Tauri library suite 2444 passed / 15 ignored / 0 failed; `cargo clippy --manifest-path desktop/src-tauri/Cargo.toml --workspace --all-targets -- -D warnings` clean; `cargo fmt --manifest-path desktop/src-tauri/Cargo.toml --all -- --check`; in `desktop/`: `pnpm typecheck`, `pnpm check`, `pnpm test` 4954 passed; `git diff --check`. Not run: the app itself — there is no UI path to this field yet, which is the same reason the bug is latent. --------- Signed-off-by: Taksh <takshkothari09@gmail.com> |
||
|
|
e2ade93f02 |
fix(desktop): downscale large avatars for agent-share PNG body (#6260)
Sharing an agent encodes a `.agent.png` snapshot with the avatar as the PNG image body. Non-PNG avatars were transcoded at full resolution and already-PNG avatars were carried through unchanged, so a large source avatar could produce a PNG that exceeds the 10 MiB `MAX_SNAPSHOT_PNG_BYTES` cap and fail the send. A 2764×4096 webp avatar encoded to ~26 MB — over 2.5× the ceiling — which is why sharing that agent hung for a few seconds (fetching and transcoding the image) and then failed. The share dialog compounded the problem: on failure it discarded the pipeline's real error (`Snapshot exceeds the 10 MiB size limit…`) and toasted a generic `Couldn't send … Try again.`, hiding the actual cause. ## Changes - **Downscale the avatar for the snapshot PNG body.** The body is only a card thumbnail, so raster avatars are now downscaled to a 512px longest edge (`MAX_PNG_BODY_EDGE`) before PNG re-encoding, mirroring the frontend SVG rasterizer's 512×512 cap in `snapshotAvatarPng.ts`. Already-PNG avatars over the dimension cap route through the same downscaling transcode path instead of a straight tEXt-chunk injection. The manifest's `avatar_url`/`avatar_data_url` source reference is untouched — only the PNG image body is downscaled. - **Surface the real error in the share dialog.** `PersonaShareDialog` (used by `SnapshotShareDialog`/`TeamShareDialog`) and `AgentCardViewerDialog` now toast the send controller's actual error message, falling back to the generic string only when it is empty. A `getCurrentError()` accessor reads the error through a ref because the render-captured `state.error` is stale in the closure immediately after `beginSend` resolves. A unit test pins the invariant: an oversize (2764×4096) avatar produces a snapshot that stays far under the cap with body dimensions clamped to 512px. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
6ea7a2b221 |
fix(desktop): preserve early relay auth challenges (#3320)
## Summary - buffer native WebSocket frames until `plugin:websocket|connect` returns the socket ID and the NIP-42 auth waiter is installed - drain those frames in order before normal inbound handling begins - add a deterministic E2E seam and regression for an AUTH challenge delivered before connect resolves ## Root cause The native WebSocket task starts forwarding relay frames before the connect command returns its socket ID. Buzz relay sends the NIP-42 AUTH challenge immediately on connection, so Desktop could process that challenge while `wsId` and `authRequest` were still unset. The challenge was discarded, the cold connection failed authentication, and the first plain-text send failed. Channel history still appeared because it loads through the Tauri channel-window command rather than this frontend WebSocket. ## Verification - deterministic regression is red before the fix (`connecting` after 5s) and green after it - `pnpm test`: 3,721 passed - `pnpm build:e2e`: passed - focused early-AUTH E2E: passed - existing failed-initial-dial E2E: passed - `pnpm typecheck`: passed - Biome + file-size pre-commit checks: passed The complete 7-test relay-reconnect file passed once before the final line-count-only compaction. After that compaction, two full-file reruns each had the same unrelated startup-seam flake in the existing initial-dial test; both the new test and that existing test pass independently at the committed HEAD. --------- Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz> Signed-off-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz> Co-authored-by: Wren <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@buzz.block.builderlab.xyz> |
||
|
|
50a71137e6 |
feat(managed-agents): close five Claude Code agent-config gaps (#4557)
Closes five Claude Code agent-config gaps in Buzz Desktop, split into three commits that share the spawn-time and live-switch surfaces. ## Config isolation, model authority, and Auto mode (#2692, #2884, #3493) - **`CLAUDE_CONFIG_DIR` isolation (#3493).** `config_bridge` resolves both `settings.json` and `.claude.json` panel paths from the agent's effective env (`resolve_effective_agent_env` — baked floor → definition → global → persona → record), so the panel reads the same directory the agent runs against. `mcp_config_file_path_for_runtime` honors a custom dir; empty/blank is treated as unset, matching Claude's `CLAUDE_CONFIG_DIR || homedir()` semantics. `AgentConfigPanel` shows a Keychain caveat when a custom dir is active (a custom dir maps to a fresh Keychain namespace unless `CLAUDE_SECURESTORAGE_CONFIG_DIR` is also set). - **Single startup model authority (#2692).** `ANTHROPIC_MODEL` is the sole startup model authority for Claude. Local spawns write `ANTHROPIC_MODEL` and strip `BUZZ_ACP_MODEL` so the harness never sees two authorities; remote deploys send `ANTHROPIC_MODEL` in `policy_env` instead of `BUZZ_ACP_MODEL`. Non-Claude runtimes are unchanged. - **`PermissionMode::Auto` (#2884).** Wire string `"auto"`, model-gated, degrades to the agent default when the active model doesn't advertise it. ## Thinking effort end-to-end for local Claude agents Effort flows from the running session's discovered `thought_level` config option through the config surface to a local-only write control and a read-only two-facts display. - **Reader.** Discovers the `thought_level` config option from the session cache (never hardcoded) and populates `effort_config_id` / `effort_options` on `RuntimeConfigSurface`. The canonical effort tier orders record env > `record.effort_level` (`BuzzExplicit`) > ACP > persona > global > definition > file, so the panel shows the effort the next spawn will launch with while `resolve_with_override` surfaces the running ACP value as the struck-through override — neither masks the other silently. - **Write control.** `persist_agent_effort_level` is a direct-write Tauri setter (writes `record.effort_level` + `updated_at`, `save_managed_agents`) that rejects non-local backends — remote effort is set at deploy time via `policy_env`. `EffortPickerField` mounts in `AgentInstanceEditDialog` beside the Model block, gated on a local backend **AND** a discovered `effortConfigId`. It persists directly and invalidates the config surface, mirroring the `setManagedAgentAutoRestart` standalone-setter precedent, so the frozen `UpdateManagedAgentInput` shape stays frozen. - **Display.** The read-only configured-vs-current two-facts display is the `thinkingEffort` normalized field in `AgentConfigPanel`, fed by the reader's canonical tier ordering. - `buzz-acp` applies the startup effort env at session start. ## Distinguish a rejected model switch from silent success A live model switch collapsed adapter rejection into success: `apply_model_switch` returned `Ok` on both an accepted switch and an application-level refusal, so the caller cached pre-switch capabilities as if they described the target model and Desktop reported the pick as landed. - `ModelSwitchOutcome::{Applied(Value),Rejected}`. Transport-class errors still propagate as `Err` (respawn the poisoned stdio); an application-level refusal is now `Rejected`. - The caller drives everything off `post_switch_snapshot`: `Applied` refreshes `model_capabilities` from the target model's echoed `configOptions` (or drops to `None` when none are echoed, so a pre-switch snapshot is never mistaken for the target's); `Rejected` preserves pre-switch caps and emits a `failure` `control_result`. Effort resolution and the session-config capture read the post-switch snapshot so they converge on the model the session actually runs; `modelOverridden` is gated on `switch_succeeded`. - `liveSwitchOutcome.ts` gains a distinct `"failed"` outcome for the adapter `failure` frame and treats the busy-path `"sent"` ack as provisional — it never counts toward success. Success is confirmed only by a real positive terminal frame (the busy-path deferred apply emits a correlated `switched` `control_result` when it lands), and the fallback timeout resolves an honest `"pending"` (accepted, apply deferred), never a false `"ok"`. `ModelPicker` surfaces a distinct toast per outcome — failed, unsupported, and pending. ## Scope explicitly excluded Per-agent config dir provisioning, `CLAUDE_SECURESTORAGE_CONFIG_DIR` sentinel injection, `settings.json` projection, protected-key stripping, MCP inheritance, spawn serialization, and the `last_spawn_warnings` surface are absent from this diff. Silent-fallback machinery for non-Claude runtimes (#2265/#4004) is a tracked follow-up. ## Sanctioned follow-ups - **Live mid-conversation effort switching.** Effort is spawn-scoped only in this PR: the worker reads `BUZZ_ACP_EFFORT_LEVEL` once and applies it at session creation. The live effort-switch machinery (mid-conversation effort RPC + ack frame) was deliberately removed and is archived on `archive/claude-config-gaps-live-effort` for a future plan-gated revival. - **Idle-path late model-switch rejection is unobservable.** An idle switch acks `switched` immediately after catalog validation, but the real `set_config_option` runs at the next session creation — potentially much later — so a rejection there is not surfaced back to the picker (holding a subscription that long is not sensible). Pre-existing, catalog-gated behavior; a durable fix is a tracked follow-up. Closes #2692, #2884, #3493 --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
d7e8fdb10c |
fix(shared-ui): delay hover disclosures by default (#5821)
**Category:** fix **User Impact:** Hover tooltips and informational popovers now wait for deliberate pointer dwell instead of appearing while users move around the app. **Problem:** Tooltips and hover-controlled popovers appeared after inconsistent, often very short delays, so moving across composer and navigation controls could obstruct the next interaction. **Solution:** Establish a 500 ms shared dwell default with no tooltip skip-delay cascade, apply it to informational hover-controlled Popovers, and preserve immediate click and keyboard behavior. The responsive Community actions navigation submenu retains its documented 80 ms open / 160 ms close timing. ## Before / after | Before | After | |---|---| |  |  | <details> <summary>File changes</summary> **desktop/src/shared/ui/tooltip.tsx** Wraps the Radix provider with documented 500 ms and zero skip-delay defaults. The provider API still permits a future proven exception, but no current `desktop/src` caller overrides either timing. **desktop/src/shared/ui/popover.tsx** Exports the documented shared hover-open timing for controlled popovers; ordinary click/focus Popovers remain immediate. **desktop/src/main.tsx** Uses the shared Tooltip provider defaults at the application root. **desktop/src/shared/ui/sidebar.tsx** Removes the sidebar's instant Tooltip timing override. **desktop/src/features/channels/ui/AddChannelBotTeamsSection.tsx** Removes the local short Tooltip timing override. **desktop/src/features/home/ui/InboxDetailPane.tsx** Removes the local short Tooltip timing override. **desktop/src/features/messages/ui/MessageTimeline.tsx** Removes the timeline's local short Tooltip timing override. **desktop/src/features/messages/ui/MessageTimestamp.tsx** Drops the timestamp-only provider now that its 500 ms, zero-skip behavior is shared globally. **desktop/src/features/channels/ui/BotActivityBar.tsx** Raises composer agent-activity hover dwell from 150 ms to the shared default while preserving immediate click/focus opening. **desktop/src/features/sidebar/ui/ChannelActivityPopover.tsx** Raises channel activity hover dwell from 250 ms to the shared default while preserving immediate focus opening. **desktop/src/features/profile/ui/UserProfilePopover.tsx** Reuses the shared hover timing in place of its equivalent local constant. **desktop/src/shared/ui/PubKey.tsx** Reuses the shared hover timing in place of its equivalent local constant. **desktop/src/shared/ui/markdown/InlineEmojiPopover.tsx** Raises emoji inspection hover dwell from 200 ms to the shared default while preserving immediate focus opening. **desktop/src/features/messages/ui/MessageReactions.tsx** Raises reaction inspection hover dwell from 200 ms to the shared default while preserving immediate focus and reaction clicks. **desktop/src/features/communities/ui/CommunitySwitcher.tsx** Documents the Community actions navigation submenu as an intentional timing exception: 80 ms to open responsively and 160 ms to preserve the pointer bridge into its portalled panel. </details> ## Reproduction steps Move the pointer rapidly across each surface first, then hold it still over a labeled control. Hover-only disclosures should stay closed during transit and open after about **500 ms** of deliberate dwell. Moving directly between adjacent Tooltip triggers should start a fresh 500 ms dwell rather than cascading the next Tooltip open immediately. | Surface | Where to test | What to expect | |---|---|---| | Composer controls | Attachment, emoji, image editor, formatting, and composer toolbar buttons | No Tooltip while sweeping across controls; the hovered control's Tooltip opens after ~500 ms. Clicking remains immediate. | | Message actions | Hover a message, then test reply, react, more-actions, edit, and related action-bar controls | Each Tooltip waits ~500 ms, including when moving between adjacent actions. The action itself still runs immediately on click or keyboard activation. | | Message metadata and content tools | Message timestamps, code-block copy controls, diff controls, system-message controls, and video-player controls | Tooltip appears after ~500 ms. Timestamp behavior should look unchanged; it was already 500 ms with no skip cascade. | | Reaction pills | Hover a reaction with one or more reactors; also click the pill | Reactor Popover waits ~500 ms instead of 200 ms. Clicking still toggles the reaction immediately. | | Inline custom emoji | Hover a rendered custom emoji in message Markdown, then focus it with the keyboard | Emoji inspector waits ~500 ms on hover instead of 200 ms. Keyboard focus opens it immediately. | | Masked links | Hover a masked Markdown link, including one revealed inside a spoiler | Destination Tooltip waits ~500 ms instead of the former app-level 300 ms. Hidden spoilers still reveal no destination; keyboard focus remains immediate. | | Main and collapsed sidebar controls | Collapse the sidebar and hover icon-only navigation/menu buttons; also test community-rail controls | Sidebar Tooltips wait ~500 ms instead of opening instantly. Rapid movement across icons should not produce a tooltip cascade. | | Channel header and management controls | Channel members, huddle, settings, thread-view mode, management rows, and quick-agent controls | Each Tooltip waits ~500 ms; click/keyboard behavior remains immediate. | | Add-channel team chips | Open Add channel where saved teams are available and hover a team chip | Team details Tooltip waits ~500 ms instead of 150 ms; clicking the chip still toggles the team immediately. | | Channel activity preview | Hover a sidebar channel that has activity, then keyboard-focus its trigger | Activity Popover waits ~500 ms instead of 250 ms. Focus opens it immediately, and quickly crossing channel rows should not leave previews in the way. | | Composer agent activity | Run an agent so the composer activity control is present; hover, click, and focus it | Hover Popover waits ~500 ms instead of 150 ms. Click and keyboard focus still open it immediately. | | Inbox and draft controls | Home inbox open-context/more-actions controls, inbox-list controls, draft detail, and drafts panel | Tooltips wait ~500 ms instead of the inbox detail's former 200 ms/local defaults. Clicks remain immediate. | | Profile and public-key previews | Hover avatars, names/mentions, project author identities, and displayed public keys | Profile/pubkey Popovers open after ~500 ms, matching their prior behavior; the change centralizes that timing. Click actions and focus behavior remain immediate. | | Agent, team, memory, and update controls | Managed-agent rows, team identity cards, restart-diff badges, memory actions, setup steps, and update indicator | Tooltips wait ~500 ms and do not cascade when traversing adjacent controls. Actions remain immediate. | | Huddle controls | Huddle bar/indicator, mic controls, and participant-list actions | Tooltips wait ~500 ms; mute, join, participant, and keyboard actions remain immediate. | | Projects and activity surfaces | Project/repository cards, overview rail, contribution graph, activity feed, reviewers, and Pulse note controls | Tooltips wait ~500 ms with a fresh dwell between adjacent targets. Clicking/focusing interactive controls remains immediate. | | Intentional interaction-mode exceptions | Focus a Tooltip trigger; click/focus an ordinary Popover; move the pointer from an open hover Popover into its panel | Tooltip focus and Popover click/focus open immediately because they are explicit user intent, not incidental hover. Hover Popovers retain their 180–200 ms close grace so the pointer can cross into the panel. The Community actions navigation submenu is the intentional exception: it opens after 80 ms and keeps its 160 ms pointer bridge; informational hover Popovers use the shared 500 ms delay. | ## Verification - `pnpm typecheck` - `pnpm check` (passes with three existing diagnostics outside this diff) - `pnpm test` — 4,775 passed - `pnpm build` --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.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> |
||
|
|
8232299cbe |
chore(release): release Buzz Desktop version 0.5.17 (#6234)
## Buzz Desktop release v0.5.17 - **Frozen main:** `3fdf289b78c40f80abce86575c25b5ed6361d82c` - **Reviewed candidate:** `c3bfd66947978fae93f4cfb46bea98ba20e32ccf` - **Previous desktop release:** `desktop-v0.5.16` - **Proposed immutable tag:** `desktop-v0.5.17` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com> |
||
|
|
3fdf289b78 |
fix(desktop): bound remote agent mention authorization (#6224)
## Summary - add a targeted native `revalidate_relay_agents(pubkeys, channel_id)` command for send-time authorization - scope membership discovery to the destination channel and selected pubkeys before runtime/profile/policy queries - replace full relay-directory rebuilds with targeted checks before agent side effects and again at publication - preserve managed-agent evidence independently and retain internal owner-only filtering ## Security and trust boundary The targeted command reuses the existing authoritative chain: 1. relay-signed kind:39002 membership scoped by viewer and destination `d` tag 2. agent runtime directory event 3. agent-signed owner profile verification 4. owner-signed managed policy 5. internal-build `owner_only` filtering before policy lookup and on final results Relay-only agents are dropped on any targeted directory failure. Fresh managed-agent evidence remains valid when the unrelated relay directory fails. ## Validation - pre-push gate passed: branch skew, Desktop check/typecheck/tests, Tauri checks, Rust tests, and mobile tests - Desktop unit suite: 4,992 passed - targeted Rust relay-directory tests passed - focused Playwright mention-send acceptance passed - relay-only send emits the expected `p` tag - exactly two targeted `revalidate_relay_agents` calls on the already-member send path: pre-side-effect and pre-publication - no `list_relay_agents` call on that send path - relay failure and revocation remain fail-closed - internal owner-only mode hides other-owned agents - manual local Desktop testing by Wes: mention sends felt materially improved ## Notes This intentionally does not clear the composer early or add an optimistic timeline row. Publication still waits for fresh authorization. --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Princess Donut <68157ebd23b3897c1991015c3038658ea916200c67d3a54620b0754d1b92f6e0@buzz.block.builderlab.xyz> |
||
|
|
081910424a |
fix(desktop): bind presence retry timers (#6213)
## Summary - preserve the WebKit-required `Window` receiver when scheduling and clearing presence subscription retries - add a receiver-sensitive regression test covering both retry creation and disposal ## Impact When subscription opening failed, WebKit rejected the detached timer call before `retryTimer` could be set. The reconciler's `finally` block then immediately started another reconciliation because demand was still unsatisfied and no retry appeared pending. This bypassed the intended exponential backoff and could repeatedly reopen subscription work during startup, so the impact was more than console noise. With the timer receiver fixed, a failed open schedules one bounded retry at a time (1s exponential backoff, capped at 30s), and disposal cancels it correctly. ## Validation - `cd desktop && node --import ./test-loader.mjs --experimental-strip-types --test src/features/presence/lib/presenceSubscriptionReconciler.test.mjs` (11 passed) - `cd desktop && pnpm test` (4,993 passed) - `cd desktop && pnpm typecheck` - `cd desktop && pnpm check` (passes with existing unrelated warnings) - pre-push hook on `578b9a0b5c851b21de0b8746238b04dc69032e78` (desktop check/typecheck/tests, Rust tests, Tauri checks, mobile tests all passed) Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
6d45f98665 |
ci: make file-size policy a first-class gate (#6187)
## Summary - make the differential file-size ratchet a first-class repository gate - run the same unfiltered gate from pre-push, `just check`, and CI - remove hidden file-size coupling from Desktop, Web, and Mobile lint commands - isolate ratchet Git subprocesses from hook-exported repository state ## Why The Desktop ratchet grew to govern `desktop/src-tauri/crates/**`, but the pre-push `desktop-check` command remained path-filtered to non-Tauri files. That contract drift allowed a Tauri Rust file-size regression through local validation. The ratchet already computes its own merge-base diff, so duplicating governed paths in Lefthook and CI adds drift risk without meaningful runtime savings. One root gate owns the policy now. ## Testing - `just file-size-check` - oversized untracked probe under `desktop/src-tauri/crates/**` fails with the 1,000-line ceiling - file-size core tests with hook-style `GIT_DIR` / `GIT_WORK_TREE` environment - `lefthook dump` confirms the unfiltered pre-push command - mandatory pre-push suite passed on `3217003db10c84d8a0c5f636ec4c92936777a002` - `git diff --check` Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
f0234f1449 |
fix(desktop): eliminate mounted-view CPU burn — compositor-safe shimmer, observer append fast path, poll-tick disk reads (#6198)
## Summary Live 0.5.16 review (Royal Court thread) traced the sustained 20–25% WebContent CPU burn in any large mounted channel with a working agent to three defects, in descending impact: 1. **Shimmer animation forces per-frame style + compositing walks.** `.buzz-shimmer` animated `background-position` under `-webkit-background-clip: text`, which WebKit cannot run on the compositor: every frame did a full document style resolve plus a recursive compositing-hierarchy update over the timeline's layer tree. The highlight now lives on an `aria-hidden` overlay child duplicating the label text and animates **opacity only** (compositor-accelerated); a real element is used instead of `::after` generated content so screen readers never see the duplicate text. Visual: the moving sweep becomes a gentle pulse. `prefers-reduced-motion` removes the overlay entirely — static muted label, exactly as before. 2. **Observer journal whole-journal dedup + re-sort per append.** Every one-per-second observer frame rebuilt a dedup Set over up to 3000 retained events and re-sorted the whole journal with a `Date.parse`-per-comparison comparator. In-order batches (the ordinary live path, same condition as the existing incremental transcript fold) now dedup within the batch and concat; out-of-order/replayed arrivals keep the full path. 3. **Redundant disk reads in the 5s agent-list poll.** `build_managed_agent_summary` re-read the global config from disk per call despite receiving it as a parameter, and re-read the teams store per tracked pair — 2N redundant reads per poll tick for N agents. Both are now caller-supplied; one-shot command paths use a `summarize_from_disk` helper. Same stores, same `unwrap_or_default` failure posture, read once. ## Evidence (mechanism attribution, live 0.5.16-block, mounted ~1500-event channel) - True-idle mounted view (working-state UI live): **25.08% mean / 24.15% median** WebContent CPU; post working-state decay: **6.33% / 3.50%**. - Reduce Motion A/B during a live agent turn (isolates the shimmer, working UI still mounted): **19.50% mean → 5.39% mean** (72% collapse). Native 10s samples: `Document::resolveStyle` 379 samples → 1; `updateCompositingLayersAfterStyleChange` 378 → 0; recursive `updateBackingAndHierarchy` 363 → 6. - The shimmer mechanism predates 0.5.16 (CSS unchanged since #3151); current multi-agent workloads exposed and amplified it. The mention regression itself was fixed separately in #6182. ## Testing - `desktop` observer store suites: 55/55 pass, including 5 new tests pinning the fast-path invariants (equal-timestamp seq ordering, duplicate-batch redelivery, intra-batch duplicates, overlapping late arrival takes the slow path, transcript-equals-replay on both paths). - `cargo test --lib managed_agents` (1016 passed) and `--lib commands` (727 passed); `cargo clippy` clean; `pnpm typecheck` + biome clean. - Release gate for the patched build (per Mongo): active-turn mounted CPU must collapse from the ~21–25% baseline with no per-frame style/compositing walk in a native sample — measured on Wes's workspace once a build with this branch is running. Findings and raw samples: `RESEARCH/LIVE_0_5_16_WEBKIT_ATTRIBUTION_2026_08_18.md`, `RESEARCH/RENDERER_STEADY_STATE_LOOP_AUDIT_2026_08_18.md`, `.scratch/live-app-review/` (Carl/Donut/Mongo/Brain, Royal Court thread 01a7fe75). --- *Opened by Brain (agent) via @wesbillman's account on his behalf — coordinated in Buzz channel agent-mention-policy-royal-court, thread 01a7fe75.* --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@buzz.block.builderlab.xyz> |
||
|
|
978e585e8d |
chore(release): release Buzz Desktop version 0.5.16 (#6191)
## Buzz Desktop release v0.5.16 - **Frozen main:** `ee992ff0822f44d1c308822f116cb9d26f9a3386` - **Reviewed candidate:** `a6211b0e285600a6f08d6592261e44ecc4a6917b` - **Previous desktop release:** `desktop-v0.5.15` - **Proposed immutable tag:** `desktop-v0.5.16` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com> |
||
|
|
ee992ff082 |
fix(desktop): restore release agent mentions (#6182)
## Summary - preserve OSS relay-agent mentions under shared channel and agent policy - restrict owner-only release builds to relay agents with cryptographically verified ownership matching the current user - remove the remote policy replay loop that repeatedly rebuilt the relay directory, while retaining focused polling and send-time revalidation - query relay profiles and managed policies by exact author coordinates to prevent noisy events from crowding out valid agents ## Diagnosis The packaged Block release compiles `BUZZ_DESKTOP_BUILD_AGENT_ACCESS_OWNER_ONLY`, while ordinary OSS/dev builds do not. Relay-discovered agents were filtered as if all remote agents were outside that owner-only boundary, so a same-owner agent running on another machine disappeared in the release even though the OSS path could look healthy. The fix uses the NIP-OA-authenticated owner from the relay directory as the cross-machine proof. Internal builds admit only verified same-owner agents and fail closed for missing, mismatched, stale/revoked, or unavailable ownership evidence. OSS builds retain shared channel/policy behavior. ## Validation - desktop focused unit coverage: 39 tests passed - desktop typecheck and focused static checks passed - focused Tauri Rust policy/directory tests passed - production-style E2E build succeeded - targeted Playwright mention scenarios passed: - owner-only release hides other-owned relay agent - owner-only release shows verified same-owner relay agent - OSS build shows shared `anyone` agent - repository pre-push hook passed on `4d40b6e5bb032f2c0755127172c50dee213f65a3`: - branch skew - desktop check and typecheck - desktop tests - Rust tests - Tauri checks - mobile tests --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
f8692fa9b5 |
test(desktop): cover exact workflow batch limit (#6168)
## Summary - retain explicit regression coverage for the exact 128-channel relay request limit - cover the 129-channel split into 128 + 1 filters The workflow-listing implementation originally carried by this PR landed through #6009. This branch is now rebased onto current `main`, so the remaining diff is only the boundary test that #6009 did not include. Fixes #6116 ## Test plan - `cargo test --manifest-path desktop/src-tauri/Cargo.toml workflow_queries_respect_relay_explicit_channel_limit` - pre-push hook: Desktop checks, Desktop tests, Desktop Tauri checks, and path-scoped Rust tests Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
c8c8eb58ad |
chore(release): release Buzz Desktop version 0.5.15 (#6173)
## Buzz Desktop release v0.5.15 - **Frozen main:** `7f61cf431af1d8f0480a0baf525881a12f2be7f2` - **Reviewed candidate:** `7ad30276d05c39ccd8699ca2521e761fd285ea49` - **Previous desktop release:** `desktop-v0.5.14` - **Proposed immutable tag:** `desktop-v0.5.15` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com> |
||
|
|
7f61cf431a |
Preserve managed agent mentions during relay errors (#6167)
## Summary - preserve selected managed-agent `p` tags when fresh managed-directory evidence succeeds but relay discovery or owner-profile lookup fails - keep relay-only agents fail-closed unless fresh relay evidence and any required owner proof are available - cover selective admission with focused unit tests and a signed-event Playwright regression ## Testing - `node --import ./desktop/test-loader.mjs --experimental-strip-types --test desktop/src/features/messages/lib/agentMentionRevalidation.test.mjs` (7 passed) - focused Playwright regression plus adjacent relay-revocation case (2 passed) - pre-commit desktop Biome/file-size hook - pre-push desktop check, TypeScript typecheck, and full desktop unit suite (4,987 passed) Fixes #6147 Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
f7a01bda7b |
fix(workflows): preserve multi-channel listing semantics (#6009)
**Category:** fix **User Impact:** Workflow listings reliably include every accessible channel, including for users with more than 128 memberships and when connected to older relays. **Problem:** Multi-value `#h` filters could lose live delivery, apply channel scoping after SQL limits, mishandle partial authorization or revocation, and permit unbounded membership work. Desktop also submitted every channel in one request, exceeding the relay's new 128-value safety bound. **Solution:** Preserve NIP-01 OR semantics across relay query, count, and live-subscription paths while enforcing authorization and bounded explicit-channel work before database or Redis operations. Desktop keeps the older-relay-compatible one-channel-per-filter shape, sends filters in bounded batches, combines responses, and deduplicates signed events by event ID. <details> <summary>File changes</summary> **crates/buzz-db/src/event.rs** Distinguishes authorization channel scopes from explicit `#h` scopes in list and count SQL so requested channels are applied before limits without implicitly including global rows. **crates/buzz-relay/src/handlers/req.rs** Shares explicit-channel scope extraction and limits, preserves valid OR siblings when malformed branches cannot match, repairs request-local membership misses, and registers authorized live subscriptions per channel. **crates/buzz-relay/src/handlers/count.rs** Applies the same bounded explicit-channel authorization to COUNT and preserves channel scope when a multi-channel request narrows to one authorized channel. **crates/buzz-relay/src/api/bridge.rs** Brings HTTP query and count behavior in line with WebSocket semantics before SQL execution and rejects over-limit explicit-channel requests before membership I/O. **crates/buzz-relay/src/subscription.rs** Indexes multi-channel subscriptions by every authorized channel and shrinks, rather than destroys, their scope when one channel is revoked. **crates/buzz-relay/src/handlers/side_effects.rs** Releases only revoked channel topics and sends terminal closure only when no authorized channel remains. **crates/buzz-test-client/tests/e2e_relay.rs** Adds ignored relay integration coverage for multi-channel delivery and valid historical/live behavior with malformed or empty OR siblings. **desktop/src-tauri/src/commands/workflows.rs** Builds one single-channel filter per membership, submits at most 128 per relay request, combines batches, and deduplicates by immutable signed event ID. **desktop/src-tauri/src/commands/workflows_tests.rs** Covers filter compatibility, malformed input, 129-channel batching, and cross-batch event-ID deduplication. </details> ## Reproduction steps 1. Join multiple channels containing workflows, open **Workflows**, and confirm workflows from every accessible channel appear. 2. Repeat with more than 128 memberships and confirm the listing remains complete rather than failing the relay request. 3. Send a multi-value `#h` query/count and confirm only requested authorized channels affect SQL limits and counts. 4. Subscribe to channels A and B, revoke A, and confirm B continues delivering live events. 5. Subscribe with a valid channel branch plus a malformed or empty `#h` sibling and confirm valid history, EOSE, and post-EOSE live delivery still occur. ## Validation At pushed head `c419a923f05e483ab26c006a0b3a80cfb3c73844`: - Relay request tests: 53 passed. - Desktop full Rust unit suite: 2,468 passed, 17 ignored. - Relay E2E target compiled with `--no-run`. - Strict relay clippy passed. - Desktop Tauri clippy/check passed. - Pre-push Rust tests and Desktop Tauri checks passed. - Rust formatting and `git diff --check` passed. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> |
||
|
|
439c037491 |
fix(desktop): align preview sidebar row styling (#6163)
## Summary - apply the inactive primary-navigation opacity treatment to every sidebar destination, including Pulse, Projects, and Workflows - remove the duplicated Inbox and Agents conditionals so future gated rows inherit the same hierarchy - add E2E coverage for all inactive rows and restoration to full opacity when selected ## Validation - `pnpm --dir desktop build:e2e` - `pnpm --dir desktop exec playwright test badge.spec.ts --grep "primary navigation rows share the same inactive emphasis" --project=smoke` - pre-push hook: desktop check, typecheck, and 4,984 unit tests Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
57feca2f20 |
fix(desktop): repair dropped team membership links at boot and on edit (#5904)
Two membership-propagation defects let an agent team silently lose members — both observed live on Will's store (Sietch Tabr), not hypothetical. **Stale `persona_ids` dropped on save.** Team records written before persona ids were namespaced hold bare slugs (`thufir`) instead of the namespaced id (`sietch-tabr:thufir`). Nothing rewrites them, and the interactive save path (`ensure_persona_ids_are_active`) *drops* any id it cannot resolve — so the next in-app save shrinks the team. This nuked four of five Sietch Tabr members. **`team_id` drifts from team membership.** Team instructions are injected at spawn by matching `record.team_id` (`spawn_snapshot::effective_team_instructions`), so an instance's binding must track its persona's membership. It drifts two ways: adding a persona to a team leaves the persona's already-running instances at `team_id: null` (a member in the roster but not in behavior — seen twice, Gurney and Hayt), and removing a persona while keeping its agents leaves the kept instance bound to a team that no longer lists it (still drawing that team's instructions at spawn). ## Fix A boot migration (`migration/team_membership.rs`) heals existing stores in one pass over `teams.json` + `managed-agents.json`: - **Rewrite stale ids.** A stale id is one no definition slug resolves. Its target is the definition whose `source_team_persona_slug` equals the bare slug, scoped to the team's source team (via `source_dir` for a directory-backed team, or the unique `source_team` among resolvable members for a detached one). Rewrite only when exactly one candidate matches; zero or many leave the id in place — strictly safer than the save path, which drops it. - **Repair `team_id`.** Backfill an instance whose persona is a team member but whose own binding is unset, and heal a stale binding whose team no longer lists the persona (re-point when exactly one *other* team claims it, otherwise unbind). Both directions gate on single-team evidence — a persona spanning several teams has none (JSON team order is not ownership), so it is left as-is and logged. A binding whose team still lists the persona is authoritative and never touched. Runs BEFORE `detach_directory_backed_teams` (so a not-yet-detached team can still be scoped by its `source_dir`) and before any UI save can drop an id. Rewrite-or-leave converges to a fixed point, so a second boot is a no-op; the store is backed up once before either write. The edit path (`commands/teams.rs`) propagates a membership change to live instances immediately, without waiting for the next boot, scoped to the delta between the pre-edit and post-edit rosters: - **Added personas** (on the team now, not before) backfill `team_id` on their unbound instances. An explicit add is legitimate binding evidence even for a persona shared across teams — unlike the order-blind boot case. - **Removed personas** (on the team before, not now) clear `team_id` on instances bound to *this* team (bindings to other teams are untouched), so a "keep agents" removal stops feeding a kept instance the old team's instructions. - **Delta-scoping keeps a metadata-only edit inert:** with no roster change, no instance is re-pointed — a shared unbound persona is never silently bound to whichever team was edited last. Propagation is best-effort after the authoritative `save_teams` (mirroring `retain_team_pending`): the team already exists on disk, and boot repair is the designed retry for a stale/unset binding, so a secondary `managed-agents.json` write failure no longer fails a command whose team write succeeded — which would otherwise let a UI retry mint a duplicate team. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
076081bfc6 |
Rename Bumble agent to Pollen (#5864)
## Summary - Rename the built-in Bumble agent to Pollen across desktop, onboarding, docs, and test fixtures. - Migrate existing stock definitions and instances in place while preserving customized fields and the stable persona coordinate. - Reserve the Pollen name by removing it from Fizz's generated-name pool. ## Validation - Pre-push desktop checks, typecheck, 4,791 frontend tests, Tauri clippy, and 2,432 native tests - Desktop E2E build --------- 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> 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> |
||
|
|
d12d825778 |
fix(desktop): resolve agent profiles through one archive-aware selector (#5706)
Agent profiles resolve through one shared selector (`pickProfileAgent`) at every entry point — the persona card, the profile panel, and library grouping. That selector ranked instances only by active/name, with no archive awareness, so a relay-archived instance early in file order could hijack the persona card and the profile panel. The persona card also recorded a durable pubkey target, which could strand the panel on an archived identity when the click landed during the archive-snapshot fail-open window. The profile panel's Runtime → Instances roster had the same blind spot: it rendered every persona instance raw, so archived instances appeared mixed in with live ones as if active. This makes the shared resolution path archive-aware via the existing fail-open `useIsArchivedPredicate`: - `pickProfileAgent` filters archived instances before ranking and returns `undefined` when every instance is archived (persona-only mode). - `buildUnifiedGroups` drops archived agents from the standalone `Custom agents` and `Unknown agents` buckets; matched persona groups keep their full list and rely on the selector's persona-only fallback. - `useCanonicalManagedAgentProfile` resolves through a pure `resolveCanonicalManagedAgent` helper that applies the target-provenance rules: a deliberately requested archived pubkey stays exact (so its archive controller can unarchive it, even when a live sibling exists), `preserveRequestedInstance` still pins a Runtime → Instances selection, and non-archived historical navigation keeps its canonicalization. - The persona card's main click records a persona target that re-resolves every render, so it self-corrects to a live sibling after hydration. Deliberate instance navigation and the runtime-error affordance keep their explicit-pubkey path. - The Runtime → Instances roster (`ProfileInstancesSection`) buckets instances off the same predicate via `bucketPersonaInstances`: live rows render as before, and archived rows move under a labeled `Archived` subsection. The instance count reflects both buckets, and archived rows keep their explicit-pubkey click so unarchive stays UI-reachable (the deliberate-navigation path above). The predicate is fail-open (treats every identity as live while the relay archive snapshot loads) and self-exempt, so a cold start never hides an identity and a user is never folded from their own client. While the snapshot is loading, every instance renders in the live list — nothing hidden, nothing labeled. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
edc4a09aaa |
feat(workflows): add responsive library card actions (#6008)
**Category:** improvement **User Impact:** Users can scan what each workflow does and trigger, edit, duplicate, enable, disable, or delete it directly from the library. **Problem:** The workflow list buried common actions and did not expose each automation's trigger-to-action shape at a glance. **Solution:** Add a responsive workflow library with a persistent create tile, compact trigger/action diagrams, prominent workflow titles with supporting descriptions, and shared card actions while preserving existing detail, editor, and run-history entry points. Card toggles refresh both list and open-detail caches so status and definition stay consistent. <details> <summary>File changes</summary> **desktop/src/features/workflows/ui/WorkflowActionsMenu.tsx** Adds a shared card menu for trigger, edit, duplicate, enable/disable, and delete actions. **desktop/src/features/workflows/ui/WorkflowCard.tsx** Reworks cards around the prototype's visual hierarchy: color-coded trigger, action flow, sentence-case eyebrow, prominent title, supporting description, status, channel, and update date without a footer clock icon. **desktop/src/features/workflows/ui/WorkflowsView.tsx** Adds the responsive grid, create tile, mutation wiring, and list/detail cache invalidation. Container breakpoints keep cards two-across at medium widths and three-across in the 1280px desktop layout. **desktop/src/features/workflows/ui/workflowDefinition.ts** Adds immutable enabled-state updates plus narrow trigger and first-action readers used only to select card icons. **desktop/src/features/workflows/ui/workflowDefinition.test.mjs** Covers neutral icon selection, enabled-state immutability, and status presentation. **desktop/tests/e2e/workflows.spec.ts** Covers the create tile, title/description hierarchy, selected-card enable/disable consistency, and deterministic narrow/medium/wide captures while retaining existing action coverage. </details> ## Reproduction steps 1. Open **Workflows** and confirm the create tile stays first as cards flow from one to three columns with available width. 2. Confirm each card shows a sentence-case trigger eyebrow, prominent workflow title, supporting description when present, status, channel, and update date without a clock icon. 3. Open a card's overflow menu and trigger, edit, duplicate, enable/disable, or delete the workflow. 4. Leave the detail panel open while toggling and confirm its badge and JSON definition update with the card. ## Screenshots Real built E2E UI with representative workflow data at three viewport sizes. ### Narrow — 800 × 720  ### Medium — 1024 × 720  ### Wide — 1280 × 720  ### Card actions  --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> |
||
|
|
f716eef437 |
fix(desktop): enforce shared agent access across devices (#6086)
## Summary - discover shared managed agents from authenticated relay directory records instead of treating channel membership as sufficient proof - publish and refresh access-policy changes immediately so running clients converge across machines without a restart or five-minute poll - route profile edits through the exact managed instance and stop/restart runtimes around access changes so unrelated edits cannot silently widen access - keep mention send-time revalidation and Block owner-only build enforcement fail closed - explain invalid custom provider/model configuration instead of leaving Save silently disabled ### Related issue Fixes #3204 ### Known residuals - a brand-new remote agent's first policy record can wait for the bounded directory poll when no authenticated directory coordinate exists yet; send-time mention revalidation remains fail closed - a failed remote-provider policy redeploy is recorded but cannot undeploy the older provider instance until the provider protocol gains the destructor tracked by #5570 ### Testing - full Desktop unit suite: 4,961 tests passed - focused profile editor Playwright workflow passed, including Customize access edits and prompt-only edits after tightening an instance - Desktop TypeScript, Biome formatting, file-size ratchet, Tauri checks, and pre-push suites passed - independently reviewed for authenticated directory trust, live subscription teardown, runtime revocation ordering, fail-open edit paths, and per-agent provider deployment serialization --------- Signed-off-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@buzz.block.builderlab.xyz> Co-authored-by: diegorumo <diegorumo@gmail.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@buzz.block.builderlab.xyz> |
||
|
|
1b7e5ac1be |
feat(model-capabilities): drive model capabilities and labels from one manifest (#5597)
## Summary Centralizes model capability knowledge — thinking mode, supported effort levels, wire routes, and human-readable labels — into a single manifest, `scripts/model-capabilities.json`. Rust and TypeScript each get a small interpreter that reads the same manifest, replacing hand-maintained tables scattered across both languages that had already drifted apart. A capability change is now a data edit, not parallel edits to two code paths. Supersedes the codegen approach explored in #3603. A cross-language contract keeps the two interpreters honest: `scripts/normative-corpus.json` is a golden snapshot generated from the Rust resolver (103 vectors covering all six capability axes) and replayed natively in TS. CI fails if either language disagrees with the corpus or the corpus drifts from the resolver. Regenerate with `just regen-model-corpus`. ## Behavior changes - **Effort dropdown for `openai-compat` providers** no longer offers `max`. The request path always clamped `max` to `xhigh` on the wire, so the UI stops offering a value that was silently rewritten. UI-only, wire-identical. - **Databricks v2 routing (wire-visible):** uncurated endpoint names carrying a bare Claude code-name segment (e.g. `goose-opus-5`) now route to the MLflow chat wire instead of Anthropic Messages — they lose Anthropic prompt caching but still succeed on a valid OpenAI-compatible wire. Curated `databricks-claude-*` records and any name starting with `claude` are unchanged. A handful of other uncurated/adversarial name shapes similarly fall back to MLflow chat instead of pattern-matched routes; every curated model resolves identically to before, all axes. - **Curated model labels on the real discovery path.** The Databricks API returns no display name, so discovery emits the raw endpoint id as the model `name` (`{id, name: id}`) on every path. `ModelEntry.name` is now curated at all four construction seams in `buzz-agent` — v2 discovery, v1 parse, the auth-empty default catalog, and the configured-model fallback — via a read-only `databricks_registry_label` lookup over the manifest's `databricks_v2` exact records; `id` stays the raw wire/config value. A known id renders its curated label (`databricks-gpt-5-5` → `GPT-5.5`), an unknown id passes through unchanged, and the default-catalog row reads `GPT-5.5 (default catalog)`. As a defense against older `buzz-agent` binaries and any harness that echoes ids, `resolveModelLabel` treats a discovered name equal to the trimmed id as absent and falls through to the registry tier; a genuinely distinct name (including the suffixed default-catalog label) still wins. ## Cleanup Deletes the duplicated capability tables and their tests: the `config.rs` gpt5 matchers, effort tables, and clamp logic; the legacy segment-based Databricks v2 route classifier in `llm.rs`; and the TS hand tables plus `effortTable.fixture.json`. All are replaced by manifest lookups through the shared resolver — no line of capability data exists in two places. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> |
||
|
|
78cbffeb64 |
fix(desktop): hide the offcanvas-collapsed sidebar so it stops painting over the community rail (#5947)
## Summary Collapsing the sidebar left a phantom copy of it painted over the community/relay rail — opaquely on flat themes (vesper et al., which made the rail look *removed*), and as ghost fragments (muted search-box fill, truncated channel-name tails) on the Buzz themes whose chrome is intentionally transparent for the gradient. **Cause:** #4281 made the app-sidebar layer `overflow-visible` (the huddle drawer needs to escape it). That removed the ancestor clipping the offcanvas collapse relied on: the sidebar slides to `left: -sidebar-width` but kept painting, exactly over the `z-0` rail (`z-10` sidebar layer). **Fix:** the offcanvas-collapsed sidebar container is now `invisible` + `pointer-events-none`, with `visibility` added to the transition list so the 200 ms slide-out still animates and the flip happens only at the transition's end. Theme-independent; no per-theme CSS touched; the huddle drawer's `overflow-visible` is preserved. ## Before / after Left 420px of the app with the sidebar collapsed. Before = unpatched `origin/main` @ 69107dc3b; after = this branch. Same seeded state, same build pipeline (`build:e2e` between checkouts). | theme | before (ghost sidebar over the rail) | after (rail clean: A / B / + visible) | |---|---|---| | vesper |  |  | | buzz |  |  | | buzz-dark |  |  | Before shots: ghost `⌘K` search chip + blue active-item pill painted over the rail column; on vesper the opaque panel hides the rail buttons entirely. After: the rail's community buttons (A, B) and `+` are visible and clickable in all three themes. Reported by Thomas P in #buzz-bugs: buzz://message?channel=e62570dd-33ad-42c5-b92b-75f2689f9694&id=9ea401ca1d009f555ca4324e136f8d8d8156db2f8afa3ff89fd038d2c16260f7 cc @klopez4212 — this touches the layout your #4281/#5478 work shaped; please confirm it doesn't defeat the huddle drawer or glass intentions. The change deliberately hides only the *offcanvas-collapsed* container, nothing in the expanded path. ## Test plan - [x] New Playwright regression spec `sidebar-offcanvas-rail.spec.ts` (buzz / buzz-dark / vesper): collapsed sidebar must be `visibility: hidden` + `pointer-events: none`, community rail stays visible and interactive. **Fails on unpatched build** (verified), passes with the fix. - [x] Full desktop unit suite: 4,954 pass / 0 fail - [x] `pnpm typecheck`, `pnpm check` (biome + file-size ratchet + px-text + pubkey-truncation) green - [x] Before/after screenshots above captured via the e2e harness on both builds Signed-off-by: Thomas Petersen <thomasp@squareup.com> Co-authored-by: Wintermute <165f0c871dd2586bb18b6aa109eeaf57bb2132ff4d27b10120f4368a0f627022@buzz.block.builderlab.xyz> |
||
|
|
82f7ed1532 |
chore(release): release Buzz Desktop version 0.5.14 (#5917)
## Buzz Desktop release v0.5.14 - **Frozen main:** `1b3dbcaaea882eeea90359c1db02e306d2f4f50a` - **Reviewed candidate:** `391495e7d347d20b67e39e3c240d17ef63c5c2c0` - **Previous desktop release:** `desktop-v0.5.13` - **Proposed immutable tag:** `desktop-v0.5.14` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com> |
||
|
|
51beba6038 |
chore(release): release Buzz Desktop version 0.5.13 (#5912)
## Buzz Desktop release v0.5.13 - **Frozen main:** `09768100ec3420f0aa7cd278bd00fe0baab5de8d` - **Reviewed candidate:** `a239e0f6793ac6e88ccf92cc231054090a9753cc` - **Previous desktop release:** `desktop-v0.5.12` - **Proposed immutable tag:** `desktop-v0.5.13` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com> |
||
|
|
263c9bf76c |
fix(desktop): restore the agent trading-card mint button (#5900)
## Problem PR #5574's profile-panel redesign dropped `ProfileSummaryView`'s `onCreateCard` prop — the only caller of `setCardMintTarget` — so the entire Agent Trading Cards feature (#3278) became unreachable from the GUI while staying fully wired underneath: mint dialog, background job store, viewer, gallery, composer chip, and the Rust `mint_agent_card`/`save_agent_card` commands all survive at main. `git log -S 'setCardMintTarget('` shows exactly two commits: the feature and the accidental removal. ## Outcome The mint trigger returns as a management row in the agent profile's Info tab, directly under **Export agent**, gated `isBot && canManagePersona` exactly like Duplicate/Export. Target resolution is byte-for-byte the original logic: prefer the live instance pubkey, fall back to the persona/definition id, allow locking only when an instance keypair exists. ## Shape - `UserProfileAgentManagementRows`: new optional `onCreateCard` row (Sparkles icon, `user-profile-create-card-row`), placed after Export. - Prop threaded `UserProfilePanel` → `ProfileSummaryView` → `ProfileInfoTabContent` → management rows, mirroring `onExportAgent` at every layer. - The mint-target state + open callback move into a `useCardMint` hook in `UserProfilePersonaDialogs` (beside the `CardMintTarget` type it manages). This keeps `UserProfilePanel.tsx` at 999 lines — the file sits at the size-ratchet cap and may not grow. ## Validation - `pnpm check` green (biome, file-size ratchet, px-text, pubkey-truncation). - `pnpm typecheck` green. - Full desktop unit suite: **4888 passed, 0 failed**. - Profile e2e spec: **32 passed**, including the updated management-row-order assertion and a new click → mint-dialog-visible → Escape → closed exercise of the restored row. Verified at `bff3110a0aeb3d63683eac9ed3e587829f9436da`, one commit atop main `01f76ec97`. Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> |
||
|
|
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> |
||
|
|
1f4c69eccf |
chore(release): release Buzz Desktop version 0.5.12 (#5903)
## Buzz Desktop release v0.5.12 - **Frozen main:** `757779bb1ef22cc4a1c233344baa0946d907e5a6` - **Reviewed candidate:** `bfc34904adc414efcd8e9c5548dff82c3545b677` - **Previous desktop release:** `desktop-v0.5.11` - **Proposed immutable tag:** `desktop-v0.5.12` This PR may be **squash merged** after the Desktop Release Candidate check and all protected-branch checks pass. Merging authorizes publication of the exact reviewed candidate; later or unrelated changes on `main` cannot alter it. The checked-in changelog accounts for every non-merge commit in the release range. The Desktop tag points to the reviewed candidate commit, not the later squash commit. Publication remains bound to that immutable candidate tag. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Release Automation <release-automation@users.noreply.github.com> |
||
|
|
757779bb1e |
perf(desktop): update active turns incrementally (#5897)
## Problem Every observer-store publication made the active-turn bridge scan every running/deployed agent and replay each agent's retained observer journal. Watermarks kept the replay idempotent, but did not remove the repeated work. Under an active fleet, one changed agent therefore caused work proportional to the whole fleet and its retained history. ## Change - observer publications now identify the changed agent and only the newly admitted, retained events - the active-turn bridge still performs one full hydration when its agent list mounts or changes - steady-state publications process only that changed active agent's delta - other observer-store subscribers keep their existing notification behavior - duplicate-only envelopes still do not publish ## Correctness Regression coverage pins: - retained/duplicate history is omitted from deltas - stopped-agent updates do not enter active-turn state - an incremental terminal clears a turn hydrated from retained history - batching still publishes once and preserves transcript/terminal outcomes - existing watermark, tombstone, pruning, community restore, clear, and eviction suites remain green ## Validation Exact pushed head: `a480ffd2531023ea32b2a5518b5d9d41f04577c8` - focused active-turn + observer-retention suites: 90 passed - full desktop suite: 4,891 passed - `pnpm --dir desktop typecheck`: passed - `pnpm --dir desktop check`: passed (pre-existing repository warnings only) - mandatory pre-push hook at the exact pushed head: passed `branch-skew`, desktop check/typecheck/test, mobile tests, Rust tests, and Desktop Tauri checks Packaged same-fleet CPU/RSS validation is follow-up evidence; this PR proves the algorithmic amplification is removed without claiming an installed-app percentage from unit tests. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
f086eb6544 |
fix(link-previews): send while previews finish in background (#5697)
**Category:** fix **User Impact:** Messages send immediately after submission while link previews finish in the background, with an option to skip delayed preview preparation. **Problem:** Waiting for link-preview metadata or snapshot uploads kept the composer occupied after users pressed Send, while races between completion, timeout, and cancellation risked inconsistent payloads. **Solution:** Freeze and promote speculative preview work into a bounded background send task, clear the composer immediately, and publish exactly once with prepared previews or gracefully without them when skipped, failed, or timed out. https://github.com/user-attachments/assets/987d2f2c-679f-473a-965f-dfb279951e52 <details> <summary>File changes</summary> **desktop/src/features/communities/useCommunityInit.ts** Resets pending link-preview preparation when community context changes so work cannot cross community boundaries. **desktop/src/features/messages/lib/linkPreviewPreparationStore.ts** Adds the coordinator-owned preparation state machine, bounded fallback, Skip behavior, and exactly-once terminal publication handling. **desktop/src/features/messages/ui/ComposerUploadProgressOverlay.tsx** Extends floating background progress UI to include link-preview preparation. **desktop/src/features/messages/ui/ComposerUploadProgressPill.tsx** Adds the preparing-link-preview label and Skip action to the progress pill. **desktop/src/features/messages/ui/MessageComposer.tsx** Hands submitted preview work to the background coordinator and clears the composer immediately. **desktop/src/features/messages/ui/messageComposerAutoSubmit.test.mjs** Updates auto-submit unit coverage for coordinator-owned preview preparation. **desktop/src/features/messages/ui/messageComposerAutoSubmit.ts** Allows submit to promote unfinished preview work instead of blocking composer submission. **desktop/src/features/messages/ui/useComposerLinkPreviews.tsx** Starts preview work speculatively and exposes frozen preparation jobs for adoption by the send flow. **desktop/src/features/messages/ui/useMentionSendFlow.helpers.ts** Carries prepared preview tags through the mention and media payload helpers. **desktop/src/features/messages/ui/useMentionSendFlow.ts** Integrates prepared preview tags into final message publication. **desktop/src/shared/lib/useResolvedLinkPreviews.ts** Exposes the in-flight metadata promise so promoted work can be adopted rather than restarted. **desktop/tests/e2e/messaging.spec.ts** Covers immediate submit, upload handoff, Skip/completion races, failure fallback, auto-send, and exactly-once publication. </details> ## Reproduction steps 1. Enter a supported link and press Send while preview metadata or snapshot upload is still pending. 2. Confirm the composer clears immediately and the floating progress UI shows **Preparing link preview · Skip**. 3. Let preparation finish and confirm one message is published with its preview. 4. Repeat and choose **Skip**; confirm one message is published without waiting for the preview. 5. Simulate preview failure or timeout and confirm the message still publishes once without preview tags. ## Validation - TypeScript, Biome/format, file-size, px-text, and pubkey checks - Full desktop unit suite: 4,734 passed - Focused Playwright messaging suite: 5 passed - Push hooks at `86c0aa7de2ff81b79286c99bf23db12345adc6ca`: desktop check, typecheck, and tests passed --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz> |
||
|
|
01f76ec971 |
fix(desktop): cut steady-state relay traffic from polls and read-state echo (#5879)
## Problem Desktop webview CPU stayed high after the presence-scope fix (#5830) and the shared useNow ticker (#5861). A per-kind byte tap hot-patched into `relayClientSession.ts` on a live desktop (~500 channels, large agent fleet; 850 s capture correlated with CPU sampling) showed the remaining steady-state relay traffic is mostly self-inflicted: | kind | what | share of inbound bytes | shape | |------|------|-----------------------|-------| | 30078 | read-state | **34%** | our own ~44 KB nip44 blob echoed back every ~10-30 s while reading | | 30030 | emoji union | **33%** | 2-min poll refetching every member's full set (~300 KB burst) | | 30175 | persona catalog | **13%** | same 2-min backstop pattern, ~150 KB per walk | CPU tracked the bursts directly: 3-5% in quiet 10 s buckets vs 44-54% in buckets containing a poll burst or read-state echo. (The kind-24200 observer-frame theory was tested and disproven by the same tap: 9.7% of bytes, steady trickle.) ## Outcome - **Read-state echo drop.** `ReadStateManager` remembers the ids of events it just published (FIFO set capped at 64) and drops their relay echoes before the nip44-decrypt + `JSON.parse` step. Ids are recorded *before* publishing so relay fan-out can't race the OK. The drop consumes the id, so a reconnect replay of the same event still parses normally. Events from other clients of the same pubkey are untouched. - **Poll backstops stretched 2 min → 20 min** for the emoji union and persona catalog queries. The live subscriptions (invalidate on any new 30030/30175) and the reconnect invalidations remain the freshness paths; the poll only exists to cover a silently dropped live event. Behavior on publish, focus, and reconnect is unchanged. - Mechanical: localStorage identity helpers moved to `readStateIdentity.ts` (no behavior change) to keep `readStateManager.ts` under the file-size ratchet. Expected effect on the measured profile: the poll stretch cuts the 30030/30175 bursts (46% of inbound bytes) by 10x; the echo drop removes the recurring ~44 KB nip44-decrypt + parse per publish cycle (the echo still arrives on the wire — nostr filters cannot exclude own-author events — so this is a CPU/IPC saving, not a bandwidth one). ## Acceptance - New tests: echo dropped **before** decrypt (mutation-checked: disabling the drop fails the test), replayed duplicate of the same id still parses, foreign-client events always parse, published-id set stays capped when publishes fail (never-echoed ids). - Full desktop suite **4794/4794**, `tsc --noEmit` clean, `pnpm check` (biome + ratchets) clean at head. ## Not addressed (follow-ups) - The 44 KB blob itself (one read-state event carries all ~500 channels; a delta or per-channel-shard format is a protocol change). - Duplicate delivery of the same events on concurrent `history-` subscriptions (relay/client dedupe). - Webview RSS of 12.5 GB observed on the same machine — retention hunt is separate work; shrinking the heap multiplies the value of this PR since the GC floor scales with live-heap size. Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> |