Commit Graph
1946 Commits
Author SHA1 Message Date
12d63c67be release(chart): publish 0.1.7 (#3393)
## Why
Publish chart 0.1.7 after the feature PR merged from a fork and
therefore intentionally skipped the internal-branch auto-tag job.

## What
- Trigger the `chart-release/0.1.7` release lane
- Update the quickstart example to reference chart 0.1.7

## Risk Assessment
Low — the chart implementation is already merged and tested; this PR
creates its immutable release tag and OCI artifact.

## References
- Chart implementation: https://github.com/block/buzz/pull/3322
- `helm unittest` 0.8.2: 43/43 tests passed
- Local pre-push checks passed

Generated with Amp

Signed-off-by: David Grochowski <dgrochowski@squareup.com>
Co-authored-by: Amp <amp@ampcode.com>
chart-v0.1.7
2026-07-28 14:44:53 -07:00
f25e6dd6aa feat(acp): steer claude-code and codex agents via _session/steering (#3007)
Mid-turn steering was reachable only through goose's
`_goose/unstable/session/steer`, which requires an `expectedRunId`
sourced from `_meta.goose.activeRunId`. claude-agent-acp and codex-acp
never emit a run id, so every mid-turn mention to those harnesses bailed
at the run-id guard before writing a byte and degraded to cancel +
merge, destroying in-flight tool calls.

Both adapters ship `_session/steering` (params `{sessionId, prompt}`,
result `{outcome}`) and advertise it as `_meta.steering.supported` on
the `initialize` response. This adds it as a second steer transport
selected at write time, reusing the existing withhold/release, ack
routing, and cancel+merge fallback machinery unchanged.

## Transport selection

| `active_run_id` | `steering_supported` | Transport |
|---|---|---|
| `Some(run_id)` | any | `_goose/unstable/session/steer` +
`expectedRunId` (unchanged) |
| `None` | `true` | `_session/steering` with `{sessionId, prompt}` |
| `None` | `false` | ack `ExpectedRunIdMissing`, write nothing
(unchanged) |

goose keeps priority when both are present — `expectedRunId` is strictly
more precise about *which* run is being steered.

## Two load-bearing safety properties

**The advertised capability is the only gate — never error-code
probing.** codex-acp's `extMethod` answers unrecognized extension
methods with a bare `{}`, which is a JSON-RPC *success* rather than
`-32601`. Buzz maps a steer success to `queue.remove_event`, so probing
an unknown method would silently delete the user's message with no
error, no fallback, and no log line.

**An `outcome` must be positively recognized.** Only `injected` and
`startedNewTurn` count as delivery. Anything else — codex's `failed`, an
unknown value, or a missing `outcome` entirely — is
`SteerError::OutcomeRejected`, which releases the withheld event and
fires the cancel+merge fallback. This makes the silent-loss path above
unreachable even if an adapter mis-advertises.

`startedNewTurn` acks `Success`, because the message really was
delivered and must not be redelivered, but deliberately does **not**
renew the read loop's hard deadline: the turn Buzz was awaiting had
already settled, and renewing would extend the clock on a finished turn.

## Notes for reviewers

- `SteerError::OutcomeRejected` needs no new arm in the
`PoolEvent::SteerAck` match — the existing catch-all
`Ok(SteerAck::Err(_)) => (true, false, true)` already gives release +
fallback, and the two `AgentError` arms above it match that variant
specifically, so they do not shadow it.
- Comments that described the old goose-only "try-and-tolerate" `-32601`
behavior are corrected; that assumption was never valid for codex-acp.
- No CI job runs `buzz-acp` tests. The full package suite was run
locally: **617 passing, 0 failing**.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
2026-07-28 17:21:05 -04:00
Will PflegerandGitHub 3ece4461df feat(desktop): apply WebKit rendering workarounds at startup on Linux (#3271)
On some Linux GPU/driver/compositor combinations, WebKitGTK's dmabuf
renderer aborts the web process during startup, so Buzz comes up with no
window at all and the user has no way to fix it. Setting
`WEBKIT_DISABLE_DMABUF_RENDERER=1` avoids the abort by falling back to
the shared-memory buffer path.

WebKit reads each of its rendering variables exactly once per process,
so the choice has to be made before anything initializes — there is no
runtime toggle and no second chance later in the same process. This
decides up front from two cheap preflight signals rather than reacting
to a crash:

- **NVIDIA GPU** — any DRM device under `/sys/class/drm` reporting PCI
vendor `0x10de`, the driver family behind most upstream reports.
- **AppImage** — the `APPIMAGE` environment variable. linuxdeploy's
AppRun hook pins `GDK_BACKEND=x11`, and the dmabuf renderer buys nothing
on that XWayland path.

Either signal disables the dmabuf renderer. Neither signal leaves the
environment untouched.

## Escape hatches

`--safe-rendering` forces the safest configuration for one launch —
`WEBKIT_DISABLE_DMABUF_RENDERER` plus `WEBKIT_DISABLE_COMPOSITING_MODE`
— for a machine neither signal recognises.

Any user assignment of a variable this module may set stands the
heuristic down **wholesale**. Presence is the test, not truthiness, so
`VAR=0` and `VAR=` both count: a user asking for the dmabuf renderer
*on* gets it, even on a machine the heuristic would have opted out.
`--safe-rendering` against such an assignment is refused with a
diagnostic naming both the assignment and the key to unset, and exits
non-zero — the flag and the environment are two incompatible answers to
one question, and neither is guessed.

## Placement

`webkit_rendering::apply()` runs at the top of `fn main()`, before
`buzz_lib::run()`. That is the only point where the process is still
single threaded with no GTK object alive, which is what makes
`std::env::set_var` sound; the module doc and the call site both say so.
The whole module is `#[cfg(target_os = "linux")]` — macOS and Windows
compile none of it.

The decision is a pure function of argv, an injected environment lookup,
and an injected DRM root, so all of it is unit-testable without mutating
the process environment.

Closes #2338. Upstream:
[tauri#9394](https://github.com/tauri-apps/tauri/issues/9394). Same
approach and same variable as
[clash-verge-rev](https://github.com/clash-verge-rev/clash-verge-rev/blob/main/src-tauri/src/utils/linux/workarounds.rs)
`workarounds.rs` and
[screenpipe](https://github.com/screenpipe/screenpipe/blob/main/apps/screenpipe-app-tauri/src-tauri/src/linux_webkit_env.rs)
`linux_webkit_env.rs`.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 17:20:29 -04:00
913d564ce0 fix(desktop): stabilize flaky DM expansion E2E ordering assertions (#2004)
## Summary

Fixes 4 flaky DM expansion E2E tests in Desktop Smoke shard 1 that were
failing non-deterministically on CI (also reproducing on `main` at run
`29526844596`).

**Failing tests:**
- `channels.spec.ts:652` — creates the DM before preparing a persona
mention
- `channels.spec.ts:760` — routes an agent mention from an existing DM
to the expanded conversation
- `channels.spec.ts:815` — routes a relay-agent mention from an existing
DM to the expanded conversation
- `channels.spec.ts:940` — drops an expanded DM after the first message
fails

## Root Cause

Race condition: under fast CI execution, mock command completions
(create_managed_agent, open_dm) can resolve in non-deterministic order,
causing assertions to observe stale or mid-transition state.

## Fix

- **:652** — Move the `new-message-recipient-popover` hidden assertion
after `chat-title` settles (both names present), so it runs
post-transition rather than mid-transition.
- **:760, :940** — Add `createManagedAgentDelayMs: 100` to ensure
persona provisioning doesn't collapse into the same tick as the
expanded-DM open/start sequence.
- **:815** — Add `openDmDelayMs: 100` so the two open_dm calls resolve
in deterministic order.

## Validation

All 4 tests pass with `--repeat-each=3` (12/12 green) locally. Biome
lint clean.

## Scope

Test-only change: 12 insertions, 1 deletion in
`desktop/tests/e2e/channels.spec.ts`.

---

Investigated by Ferret, reviewed by Grumplestiltzkin.

Signed-off-by: Cameron Hotchkies <chotchkies@block.xyz>
Co-authored-by: Goose <opensource@block.xyz>
2026-07-28 14:17:17 -07:00
kcao-gssandGitHub 4fcd55a999 docs(contributing): document the Linux system libraries just ci requires (#3396)
## Problem

The prerequisites table lists language toolchains (Rust, Node, pnpm,
Flutter, Docker, `just`) but no system libraries. Hermit pins the former
and not the latter, so following the setup section exactly on Linux
still leaves `just ci` unable to run: it fails partway through its first
dependency, `just check`, at `desktop-tauri-clippy`.

```
The system library `gdk-pixbuf-2.0` required by crate `gdk-pixbuf-sys` was not found.
The file `gdk-pixbuf-2.0.pc` needs to be installed and the PKG_CONFIG_PATH environment variable must contain its parent directory.
```

The desktop crates link against GTK and WebKitGTK. CI installs those
packages explicitly, so it never sees this — which is exactly why the
gap is invisible from the maintainer side. Since `check` runs first in
the `ci` chain, the failure also masks everything after it (`test-unit`,
`desktop-test`, `web-build`, `mobile-test` never run), which makes it
read as a broken repo rather than a missing dependency.

## Change

Adds a `#### Linux: Tauri system libraries` subsection under
Prerequisites with:

- The apt list copied from `.github/workflows/ci.yml`, so a local run
matches CI rather than drifting from it
- A pointer to [Tauri's
prerequisites](https://tauri.app/start/prerequisites/) for non-Debian
distributions
- A note that server-side contributors can skip it — `just fmt-check`,
`just clippy`, `just test-unit`, and `just test` need no GTK

Docs only. No TOC entry needed, since the TOC lists `##` headings and
this is a `####` subsection.

## How I hit it

Running `just ci` before pushing #3372, on Ubuntu under WSL2 with the
Hermit toolchain active and all Docker services healthy. Everything the
guide asks for was in place. The four `check` steps before
`desktop-tauri-clippy` (`fmt-check`, `clippy`, `desktop-check`,
`desktop-tauri-fmt-check`) passed, which is what makes the failure point
specific rather than a general build problem.

## Closest existing work

None found. I searched open and closed issues and PRs for `gdk-pixbuf`,
`libgtk`, `webkit2gtk`, `system dependencies`, `prerequisites`, `just
ci`, and `linux setup`. The Linux/GTK issues that exist (#2604, #2643,
#2982, #2811, #2562) are all runtime bugs in shipped builds, not
setup-path failures.

## Verification

The package list is transcribed from `.github/workflows/ci.yml:152-163`;
the same list appears in `release.yml` and `linux-canary.yml`. I have
not installed the packages on my machine, so I can confirm the failure
and the source of the fix but not that the list is exhaustive on a clean
box — worth a second pair of eyes from anyone who has done a fresh Linux
setup recently.

Signed-off-by: Kyler Cao <kcao@gssmail.com>
2026-07-28 21:13:47 +00:00
1d3b810ad7 fix(desktop): paint community rail full height (#3382)
## Summary

- paint the community rail across the full app height instead of
exposing the parent background through external margins
- preserve the existing community-button alignment and balanced
horizontal gutters by moving vertical spacing inside the rail
- update the rail geometry coverage to require full-height paint
ownership

## Root cause

PR #2972 aligned the rail box with the inset content by adding top and
bottom margins to the `bg-sidebar` element. Margins are outside the
painted box, so flat light and dark themes exposed a differently colored
app background above and below the rail.

## Validation

- pre-push `desktop-check`
- pre-push desktop unit suite: 3,751 passed
- `git diff --check`

Local Playwright/E2E was not run; CI owns the full browser matrix.

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-28 20:17:01 +00:00
Will PflegerandGitHub 1d4f97b959 fix(acp): disable goose cron scheduler in managed agent children (#3144)
A Buzz install with a scheduled goose recipe fires each cron entry once
per `goose acp` child instead of once, because every child
unconditionally starts its own cron scheduler over the shared
`~/.local/share/goose/schedule.json`. With a pool of N children per
harness and multiple harnesses, one scheduled recipe fans out to N ×
harness_count executions — each running under the managed agent's
identity rather than the operator's, and racing the operator's own
standalone goose over the same schedule file.

This injects `GOOSE_ACP_SCHEDULER_DISABLED=true` into every child
spawned by `AcpClient::spawn`, so a managed agent never owns the
operator's cron schedule.

## Placement

The `cmd.env` call is set last — after the `extra_env` operator-wins
loop and after the `CODEX_CONFIG` merge — deliberately with no escape
hatch. Managed children not running the operator's schedule is a
correctness invariant rather than an operator-tunable default, so the
injection must beat both a conflicting persona `extra_env` entry and any
value inherited from the parent process.

It is injected for all agents, not just goose. Agent builds that don't
recognize the variable ignore it.

## Sequencing

The goose-side flag that reads this variable and skips scheduler startup
lands separately (repo TBD). Until it does, this change is a
forward-compatible no-op: it sets an environment variable nothing
currently reads. Merging it first means no coordinated release is needed
— the fix takes effect as soon as the goose side ships.

Related: https://github.com/aaif-goose/goose/pull/10738

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 16:03:13 -04:00
Will PflegerandGitHub b0503d80c2 feat(desktop): add custom harness inline from agent dialogs (#3252)
Registering a custom ACP harness works today, but only from Settings →
Agents. Anyone whose first touchpoint is "New agent" has no way to
discover the custom path — the dropdown just lists the baked-in presets
plus whatever was registered earlier. This adds an inline "Add custom
harness…" entry to the harness dropdown in all three agent surfaces:
create, edit-definition (`AgentDefinitionDialog`), and instance edit
(`AgentInstanceEditDialog`).

The entry is a sentinel option (`ADD_CUSTOM_HARNESS_VALUE`, NUL-prefixed
so it can never collide with a real harness id — backend ids match
`[a-z0-9_][a-z0-9_-]*`), mirroring the `CUSTOM_ENTRY_ID` trick already
used in `HarnessCatalogDialog`. Picking it never writes into form state;
it opens `AddCustomHarnessDialog`, a thin modal wrapper hosting the
existing `CustomHarnessForm` in `chromeless` mode. `CustomHarnessForm`'s
`onSaved` now carries the saved `definition.id` (the form may rewrite
it); the two existing call sites ignore the argument, so their behavior
is unchanged.

Selection after save is deferred rather than immediate.
`usePendingHarnessSelection` holds the saved id until the runtime
catalog actually publishes it via discovery, then selects it exactly
once — so the dialog never selects an id it cannot render, and
back-to-back registrations resolve correctly. The wait is scoped to the
owning dialog's `open` state: both host dialogs stay mounted when
closed, so an unpublished id is dropped on close rather than selecting
into reset form state when discovery later catches up. Selection is
routed through each dialog's normal dropdown change handler, so
provider/model reset (and command pinning in the instance dialog) behave
identically to a hand-picked harness. Dismissing the modal leaves the
previous selection untouched. `AgentInstanceEditDialog`'s existing
"Custom command" option is a different feature (ad-hoc command override
vs. a registered reusable harness) and is untouched.

Coverage is 16 unit tests in `addCustomHarness.test.mjs` (real React
mount, following the existing `.test.mjs` pattern) plus 4 Playwright
specs in `inline-custom-harness.spec.ts` covering all three surfaces
end-to-end. Both suites were mutation-verified: treating the sentinel as
a real selection, selecting before the catalog publishes, never clearing
the pending id, ignoring the dialog's open state, and reversing
latest-save-wins each turn the unit tests red; reverting the two dialog
diffs turns all four e2e specs red. The `check-file-sizes.mjs` overrides
for the two dialogs are ratcheted to their exact new counts (1048 and
1229) — verified tight in both directions, N passes and N−1 fails, so no
headroom is introduced.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 16:01:41 -04:00
1e307e178a chore(compose): remove stale typesense env vars (#3332)
Search migrated to Postgres FTS (commit f8bbe6efc). 

The Typesense container was removed from compose.yml and the Helm chart,
but the cleanup missed two template/config files:

- `deploy/compose/.env.example`: `TYPESENSE_API_KEY` and
`TYPESENSE_PORT` are dead — no typesense service exists in compose.yml
and the relay binary no longer reads `TYPESENSE_API_KEY`. The
`CHANGE_ME_RANDOM_API_KEY` placeholder was never consumed, so removing
it also unbreaks the sed loop in the blog draft (one fewer no-op secret
to generate).
- `benchmarks/harbor-buzz-orchestra/scripts/benchmark.py`: generates a
typesense_api_key in state and writes `TYPESENSE_API_KEY` to the .env
file it creates.
- *Editing this file caused the
https://github.com/block/buzz/blob/main/.github/workflows/benchmark-harbor.yml
linter ci checks to run, which seemingly haven't run before, so I needed
fix the lint issues to pass this.*

---------

Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Co-authored-by: npub1c4alndp82zyt9veaklm5d965quss79vlhk9awv7qu5erwhmf42qqlvc25c <c57bf9b4275088b2b33db7f746975407210f159fbd8bd733c0e532375f69aa80@buzz.block.builderlab.xyz>
2026-07-28 12:31:24 -07:00
a35771fc44 feat(desktop): refine agent catalog sharing (#2439)
## Summary

- add custom-agent catalog sharing and hide built-ins from discovery
- let owners publish later catalog updates from Share or while saving
edits — the save always persists locally, and the publish reports
`published` or `queued` (flushed automatically once the relay is
reachable again)
- preserve agent type, model, and runtime across snapshot import/export
- simplify agent and team entry points and tighten catalog layout
- migrate the legacy global retention queue into the owner's active
scope so pending catalog publishes survive the upgrade
- keep the agent list and edits usable in recovery mode by degrading to
unshared projections when scope resolution or the retention DB fails
- track catalog provenance on copied personas, so adding an
already-added foreign agent resolves to the existing copy instead of
creating a duplicate
- scope inbound persona events to the community relay they arrived on
- page the catalog read past the relay's 1,000-row query clamp
- unify the share dialog's memory-level choice into a single "What's
included" selector that drives both DM-send and copy-link delivery (all
six combinations preserved), group the delivery rows above the option
rows, and label the catalog toggle "Not shared" / "Shared"
- keep emoji avatars on catalog entries — they persist as inline
percent-encoded SVG, which the catalog projection's http(s)-only URL
guard used to drop, so a shared agent showed initials instead of its
avatar
- drop the "Active in communities" card from Agents settings, superseded
by the per-channel runtime controls in the members sidebar

## Screenshots

### Agent actions

![Agent
actions](https://raw.githubusercontent.com/block/buzz/4643581cd0882d8b101b04e3d8be290ea7e39f08/pr-2439--01-agent-menu.png)

### Team avatar stack

![Team avatar
stack](https://raw.githubusercontent.com/block/buzz/4643581cd0882d8b101b04e3d8be290ea7e39f08/pr-2439--02-team-menu.png)

### Catalog sharing

![Catalog
sharing](https://raw.githubusercontent.com/block/buzz/648d6eaf6df7d4daa5b7375948ed221f723793d6/pr-2439--03-share-to-catalog.png)

### Publish while editing

![Publish while
editing](https://raw.githubusercontent.com/block/buzz/bd4eb473ff456f6e665173054dc5a0f764594d1e/pr-2439--01-edit-agent-publish-updates.png)

### Publish from Share

![Publish from
Share](https://raw.githubusercontent.com/block/buzz/648d6eaf6df7d4daa5b7375948ed221f723793d6/pr-2439--02-share-dialog-publish-updates.png)

### Catalog details

![Catalog
details](https://raw.githubusercontent.com/block/buzz/4643581cd0882d8b101b04e3d8be290ea7e39f08/pr-2439--04-agent-catalog.png)

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
mobile-v0.6.0-rc.1
2026-07-28 15:01:03 -04:00
thomaspblockandGitHub 3afa129ee7 fix(desktop): keep drafts out of the Inbox All view (#3217)
## Summary

Drafts were showing up in the Home Inbox **All** view, mixed in with
messages and reminders (reported in `#buzz-bugs`). Drafts are private
composer state, not inbox activity — they now appear only under the
dedicated **Drafts** filter.

## Changes

- **`inboxListRows.ts`** — drop the `draft` row variant from
`buildInboxListRows`; the mixed view builds only `inbox` + `reminder`
rows.
- **`InboxListPane.tsx`** — remove the draft branch of the All-view
render path; `PersonalItemRow` now renders reminders only.
- **`useHomePersonalInbox.ts`** — stop enabling draft selection (and its
root-status relay probing) for the mixed view; draft selection is scoped
to the Drafts filter.
- Drafts filter behavior is unchanged: the filter badge count,
`DraftsPanel` list, and `DraftDetailPane` all still work.

## Testing

- `pnpm test` (desktop unit suite): 3697 passed, 0 failed.
- `pnpm exec biome check src/features/home tests`: clean.
- Updated `inboxListRows.test.mjs` for the two-variant row model.
- Updated the e2e test (`channels.spec.ts`) to assert All never lists
drafts and that the draft is still reachable under the Drafts filter.
- Added `drafts-all-fix-screenshots.spec.ts` capturing both states
(screenshots below).

### All view — draft is gone, messages/reminders unaffected


![01-all-view-no-drafts](https://raw.githubusercontent.com/block/buzz/12c97624832cef40df951c403982994fea58dd80/pr-3217--01-all-view-no-drafts.png)

### Drafts filter — the draft is still listed and editable


![02-drafts-filter-still-lists](https://raw.githubusercontent.com/block/buzz/12c97624832cef40df951c403982994fea58dd80/pr-3217--02-drafts-filter-still-lists.png)

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
2026-07-28 19:37:12 +01:00
35305bfc8f docs: restructure DCO guidance into scannable subsection (#3337)
Extracts the dense inline DCO paragraph from the "Before You Open a PR"
section into a dedicated `### Sign Your Commits` subsection.

## What changed

- Adds a `### Sign Your Commits` heading directly below the Conventional
Commits paragraph
- Leads with the command (`git commit -s`) in a code block
- Follows with a plain-English explainer of what the sign-off does
- Adds linkable `#### Fix unsigned commits already pushed` and `####
Auto-setup for future commits` subheadings
- Removes the old inline paragraph (content preserved, structure only
changed)

## Why

The existing guidance was buried mid-paragraph; contributors may not
find it until CI blocks them. This makes the requirement and its fix
immediately visible and actionable.

## Notes

Docs-only change, no code modified.

Signed-off-by: Cameron Hotchkies <chotchkies@block.xyz>
Co-authored-by: npub1ep9tf72jk6xgwamqj5m2j0xvqvwm9vdu3zxlz7cesxg53x52tkkqf6pa42 <c84ab4f952b68c8777609536a93ccc031db2b1bc888df17b198191489a8a5dac@buzz.block.builderlab.xyz>
2026-07-28 18:27:22 +00:00
klopez4212andGitHub a77212875a Unify mobile loading spinners (#3314)
## What
- add the shared desktop-style arc spinner for mobile
- replace app loading indicators with the shared component
- preserve a static pose when reduced motion is enabled

## Stack
- follows #3313

## Validation
- `just mobile-check`
- focused spinner and pairing widget tests

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-07-28 19:12:54 +01:00
00ede2e7aa fix(desktop): restore the inbox icon in the sidebar (#3341)
## Why

The Inbox surface was briefly renamed to **Activity** during #2045 and
picked up a bell icon to match. The name was reverted to **Inbox**
before merge, but the icon was not.

A bell says "notification tray." Inbox is a destination — a focused,
conversation-oriented place to catch up on work relevant to you,
including drafts and reminders that have nothing to do with
notifications. The glyph should say that.

## What changed

- Swap the sidebar entry from Lucide `Bell` to Lucide `Inbox`.
- Assert the icon in `inbox-refactor-screenshots.spec.ts`. Nothing
pinned it before, which is exactly how it drifted through a rename.

This also brings desktop back in line with mobile, which already uses
`LucideIcons.inbox300` / `inbox500` for the same destination.

## Deliberately unchanged

The bell on **reminder** rows in the list pane (`InboxListPane.tsx`,
reminders → bell, drafts → file) stays. A bell is the right glyph for a
reminder; that one was never about the surface's identity.

## Verification

- The new assertion is a real guard, not a no-op: with `Bell` restored
the test fails with `Expected: 1, Received: 0` on `svg.lucide-inbox`.
Confirmed before committing.
- `biome` and `tsc` clean.
- Playwright smoke: `inbox-refactor-screenshots` 4 passed; `smoke`,
`navigation`, `channels`, `sidebar-more-unread-overlap`,
`home-collapsed-top-chrome`, `workspace-rail` — 107 passed, 1 skipped.
- Screenshot below is the regenerated `02-current-controls` shot from
the spec.

Signed-off-by: Clay Delk <clay.delk@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 14:01:18 -04:00
Will PflegerandGitHub 4e3998f36e fix(desktop): gate codex-acp on a minimum supported version (#3254)
The codex adapter version gate accepted any `major >= 1`, so a 1.x
`codex-acp` older than the version that fixes outbound relay access for
`buzz` CLI subprocesses classified as `Available` and was never offered
a reinstall. Only the 0.16.x `@zed-industries/codex-acp` adapter — which
fails `--version` outright — was caught.

`probe_codex_acp_version` now returns the full `(major, minor, patch)`
triple and `codex_adapter_availability` compares it against a new
`MIN_CODEX_ACP_VERSION` floor of `1.1.7`, the current npm latest. An
adapter below the floor classifies as `AdapterOutdated`, which routes it
through the existing uninstall-then-install reinstall plan.

The parse requires exactly three numeric dot-separated components.
Partial versions (`1.2`) and prerelease tags (`1.2.0-rc1`) return `None`
and therefore classify as `AdapterOutdated` — a version Buzz cannot
compare against the floor fails closed, offering a reinstall rather than
running an adapter of unknown vintage. Both the floor's bump policy and
the strict-parse behavior are stated in doc comments rather than left
implicit.

Supersedes [#3097](https://github.com/block/buzz/pull/3097) by
@Bharathchinneni, whose semver floor and behavior tests this carries.
That PR could not land as written: the two
`probe_codex_acp_major_version` compatibility wrappers it kept had no
non-test callers, which is a hard `clippy -D warnings` failure. The
wrappers are deleted here and their call sites collapsed onto
`probe_codex_acp_version`.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 13:56:59 -04:00
60158fce3e feat(cli): add users set-status command for NIP-38 profile status (#3253)
## Summary

The desktop client renders a persistent user status (NIP-38 kind:30315,
`d:general`) as the status line on profiles, but the CLI had no way to
set it — only ephemeral presence (`set-presence`, kind:20001).
Integrations that want a scriptable, durable status line (for example a
now-playing music bridge that shows the current TIDAL track on a
profile) had no entry point.

## Screenshots

<img width="1455" height="960" alt="1"
src="https://github.com/user-attachments/assets/f1669ec6-212b-4f6e-ad53-07df9aacffc9"
/>
<img width="1455" height="960" alt="2"
src="https://github.com/user-attachments/assets/5bf70f47-e5b5-4eb0-a426-b5f1ef90d2ec"
/>


This adds:

```bash
buzz users set-status --text "Working on the relay" --emoji "🔧"
buzz users set-status --text "" --emoji "🎶"   # intentional emoji-only status
buzz users set-status --clear                  # removes the status
```

- Signs and submits the replaceable kind:30315 event via the HTTP bridge
(no WS needed — unlike presence, user status is a stored event).
- Uses the `d:general` coordinate the desktop client already reads for
the profile status line, and the same `emoji` tag shape
`SetStatusDialog` publishes.
- Event construction lives in `buzz_sdk::build_user_status()`, keyed off
`buzz_core::kind::KIND_USER_STATUS`, so the CLI command is a thin
sign/submit wrapper. Text and emoji are trimmed; a blank emoji is
omitted rather than emitted as an empty tag.
- Clearing is the explicit `--clear` flag, mutually exclusive with
`--text`/`--emoji`. It publishes an empty-content event carrying only
`d:general`, which the desktop treats as no status. `--text ""` with an
`--emoji` is an emoji-only status, not a clear.

---------

Signed-off-by: Kagan Yaldizkaya <kagan@squareup.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-28 13:44:57 -04:00
5457c947a7 fix(composer): scope multiline block formatting (#3246)
**Category:** fix
**User Impact:** Composer block formatting now applies to the intended
line or selection without collapsing multiline content.

**Problem:** Block formatting from a Shift+Enter line could convert the
entire draft, selected visual lines could collapse into one list item,
and code conversion could lose line breaks. **Solution:** Scope caret
formatting to its hard-break-delimited line and normalize explicit
selections for the destination block type while preserving neighboring
content and visual line boundaries.

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

**desktop/src/features/messages/lib/selectionBlockFormatting.ts**
Scopes collapsed-caret block actions to the active visual line and
normalizes multiline selections for lists and code blocks.

**desktop/src/features/messages/lib/selectionBlockFormatting.test.mjs**
Adds unit coverage for caret-line isolation across line positions and
selection directions.

**desktop/src/features/messages/ui/FormattingToolbar.tsx**
Routes list, quote, and code-block actions through the selection-aware
formatting transaction.

**desktop/tests/e2e/composer-selection-formatting.spec.ts**
Covers caret-only formatting, multiline list conversion, list-to-code
conversion, preserved hard breaks, Markdown output, and backward
selections.

</details>

## Reproduction steps

1. In the desktop composer, enter several lines using Shift+Enter and
place the caret on one line.
2. Apply a bullet list, ordered list, quote, or code block; only the
caret line should change.
3. Select several Shift+Enter lines and apply a list; each visual line
should become its own item.
4. Select several list items and apply Code block; they should become
one multiline code block while unselected neighbors remain intact.
5. Select several Shift+Enter lines and apply Code block; each line
break should remain visible.

## Screenshots/Demos
<img width="508" height="222" alt="Screen Recording 2026-07-27 at 5 29
19 PM"
src="https://github.com/user-attachments/assets/35640dea-0cfb-44f1-9b0b-a993c69cb55f"
/>

Expected multiline code-block result:
https://buzz.block.builderlab.xyz/media/d2e2668093af3b67d896a32e9799daccd236da9fc9e24ec56ddb4ebf7d01dd96.png

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-07-28 13:31:27 -04:00
af4d861516 feat(chart): add relay pod extension points (#3322)
## Why
Allow operators to install wrapper binaries and override the relay
entrypoint without maintaining a duplicated Deployment outside the OSS
chart. `extraManifests` can create independent resources but cannot
extend the chart-managed relay Pod.

## What
- Add opt-in init-container, volume, volume-mount, command, and args
extension points
- Preserve image defaults when extensions are empty and compose generic
init containers with the MinIO readiness gate
- Document the distinction from `extraManifests`, add schema coverage,
and release chart 0.1.7

## Risk Assessment
Low — all new values are opt-in, and default rendered manifests are
unchanged apart from version-derived metadata. Merge publishes a new
chart version without modifying existing installations.

## References
- [OpenTelemetry Collector Pod
extensions](https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/templates/_pod.tpl)
alongside
[extraManifests](https://github.com/open-telemetry/opentelemetry-helm-charts/blob/main/charts/opentelemetry-collector/templates/extraManifests.yaml)
- [Argo CD
extraObjects](https://github.com/argoproj/argo-helm/blob/main/charts/argo-cd/templates/extra-manifests.yaml)
alongside component-scoped Pod extension hooks
- `helm unittest` 0.8.2: 43/43 tests passed
- Helm lint, schema validation, fixture renders, and chart packaging
passed
- Oracle review found no functional issues; its literal no-`tpl`
regression test recommendation is included

Generated with Amp

---------

Signed-off-by: David Grochowski <dgrochowski@squareup.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-07-28 13:07:04 -04:00
klopez4212andGitHub a3b097745a Refine mobile attachment picking (#3313)
## What
- morph the composer plus button into the attachment menu, camera, and
photo surfaces
- add ordered multi-select with inline recent photos and system picker
fallback
- add native iOS attachment/photo popovers and align the Android camera
treatment

## Stack
- follows #3312

## Validation
- `just mobile-check`
- `flutter test test/features/channels/compose_bar_test.dart`
- full mobile pre-push suite

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-07-28 18:05:43 +01:00
6da45ac5cf Polish mobile message and search layouts (#3121)
## Summary

- align message typography, avatars, metadata, and spacing across mobile
surfaces
- improve message follow behavior, touch feedback, and Activity popover
motion
- refine Search motion, gutters, and explicit recent-search history

## Snapshots

### Home


![Home](https://raw.githubusercontent.com/block/buzz/99aaf9719f68a2813e14c484f503af10c4fca04a/pr-3121--01-home.png)

### Activity


![Activity](https://raw.githubusercontent.com/block/buzz/99aaf9719f68a2813e14c484f503af10c4fca04a/pr-3121--02-activity.png)

### Search


![Search](https://raw.githubusercontent.com/block/buzz/99aaf9719f68a2813e14c484f503af10c4fca04a/pr-3121--03-search.png)

## Testing

- `just mobile-check`
- `just mobile-test` (749 passed, 1 skipped)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Co-authored-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
2026-07-28 17:20:50 +01:00
klopez4212andGitHub 7dfea2634f Add mobile message image galleries (#3312)
## What
- group uploaded photos into full-width message carousels
- add a fullscreen viewer with pinch zoom, double-tap reset, swipe-down
dismissal, a centered filmstrip, and image actions
- preload nearby display-sized images for smoother swiping and keep each
upload as its own avatar-backed message

## Validation
- `just mobile-check`
- `flutter test test/features/channels/message_content_test.dart`
- iOS 26.5 simulator gesture pass

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-07-28 16:48:35 +01:00
WesandGitHub 4a977c588a chore(release): release Buzz Desktop version 0.5.0 (#3213)
## Buzz Desktop release v0.5.0

### Changes since v0.4.26:

- feat(invites): add use-limited invite links
([#3141](https://github.com/block/buzz/pull/3141))
([`d500c2d5c`](https://github.com/block/buzz/commit/d500c2d5cf5d9aabe0ca4ebebfcafdbe5f5b7fd3))
- fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0
floor ([#3218](https://github.com/block/buzz/pull/3218))
([`98a7b1334`](https://github.com/block/buzz/commit/98a7b1334823ee0be3e3fa5cab7a2e349e438dab))
- fix(desktop): preserve thread anchor through layout reflow
([#3212](https://github.com/block/buzz/pull/3212))
([`9810d8545`](https://github.com/block/buzz/commit/9810d8545937329f229ff40d8a19edc9e3e325c1))
- feat(search): parse from:/in:/after:/before: and pass them in the
filter ([#2871](https://github.com/block/buzz/pull/2871))
([`cb2a265b5`](https://github.com/block/buzz/commit/cb2a265b5399426e808461c1a16713754c593258))
- fix(desktop): fetch join policies through native networking
([#2862](https://github.com/block/buzz/pull/2862))
([`0019f8076`](https://github.com/block/buzz/commit/0019f80765e96f056e81b57789b8b5fb80936f72))
- fix(desktop): republish agent identity records when a persona rename
propagates ([#2607](https://github.com/block/buzz/pull/2607))
([`7ca0bbd94`](https://github.com/block/buzz/commit/7ca0bbd946fd82a7008132f94d069a97bb53f94b))
- fix(desktop): keep project Inbox previews compact
([#3193](https://github.com/block/buzz/pull/3193))
([`de1396050`](https://github.com/block/buzz/commit/de13960505fd798070e177cb33b1663100ac06bb))
- Inbox refactor ([#2045](https://github.com/block/buzz/pull/2045))
([`2bd4c24b7`](https://github.com/block/buzz/commit/2bd4c24b71335e7ce272ec6de6491f7f37f4b20d))
- Fix composer selection formatting and drop overlay
([#3172](https://github.com/block/buzz/pull/3172))
([`99da5b7eb`](https://github.com/block/buzz/commit/99da5b7ebb19e26453e075bfb949672122b31be3))
- Refine pending message status
([#3153](https://github.com/block/buzz/pull/3153))
([`75588eaff`](https://github.com/block/buzz/commit/75588eaff2354d620e554c055b80ec83735ddb0a))
- fix(desktop): recover full local storage on startup
([#3182](https://github.com/block/buzz/pull/3182))
([`174c38e4b`](https://github.com/block/buzz/commit/174c38e4bd1ed8498641546bc4fcb6d5a4c9cede))
- fix(desktop): keep collapsed table separators out of spoilers
([#3169](https://github.com/block/buzz/pull/3169))
([`4d8b676bb`](https://github.com/block/buzz/commit/4d8b676bb283a1917cec5850c3b7327fe122b0c1))
- feat(desktop): redesign agent runtime settings
([#3093](https://github.com/block/buzz/pull/3093))
([`d98da7389`](https://github.com/block/buzz/commit/d98da7389e60cfbd79b219aa411449fe2e53a18a))
- fix(desktop): use forward slashes for git credential.helper on Windows
([#3023](https://github.com/block/buzz/pull/3023))
([`899531684`](https://github.com/block/buzz/commit/8995316844f7ad50552fbae67fbd35119262796f))
- chore(desktop): add AgentCreationPreview file-size override to unblock
main CI ([#3154](https://github.com/block/buzz/pull/3154))
([`b92a1f4bf`](https://github.com/block/buzz/commit/b92a1f4bf400e7da5ab7a010cdd81a69497d8191))
- fix(desktop): make the test loader work on Windows
([#2758](https://github.com/block/buzz/pull/2758))
([`8bb43d519`](https://github.com/block/buzz/commit/8bb43d51912894553f2670b2d285a96cf09cd472))
- fix(desktop): make lint and unit-test gates work on Windows
([#2943](https://github.com/block/buzz/pull/2943))
([`545bb46b8`](https://github.com/block/buzz/commit/545bb46b824a3fbf4401062f03b72531d832ebb9))
- feat(desktop): add search to agent emoji picker
([#2630](https://github.com/block/buzz/pull/2630))
([`313f793c8`](https://github.com/block/buzz/commit/313f793c8753d413c22ff8edfe420d5ee78708bc))
- fix(desktop): keep identity key help dialog readable in dark mode
([#2854](https://github.com/block/buzz/pull/2854))
([`be275cfc6`](https://github.com/block/buzz/commit/be275cfc6c7b80fe43e9d66c6d14b6d2bbe58a10))
- feat(acp): title agent sessions from the agent and channel name
([#3028](https://github.com/block/buzz/pull/3028))
([`f2fe3b63c`](https://github.com/block/buzz/commit/f2fe3b63c21be55907175715c076cd3a9195b74d))
- feat(git): use agent display name as git author name
([#3040](https://github.com/block/buzz/pull/3040))
([`18eef633d`](https://github.com/block/buzz/commit/18eef633d88ac465c61d98f12655fbf51dc3ca44))
- fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote
DoS) ([#3135](https://github.com/block/buzz/pull/3135))
([`31e2de196`](https://github.com/block/buzz/commit/31e2de1966672e73e026af3c54f3a1a9a2f5e103))
- fix(desktop): read the newest pair-scoped harness log
([#3134](https://github.com/block/buzz/pull/3134))
([`654f38490`](https://github.com/block/buzz/commit/654f384906b5c720a60a199d85031a6f1cb6efc9))
- feat(desktop): handle project work from Inbox
([#3117](https://github.com/block/buzz/pull/3117))
([`c5c4f390b`](https://github.com/block/buzz/commit/c5c4f390b6713256e2efb8394c59823ebad73db6))
- fix(desktop): clarify identity key button when key exists
([#2357](https://github.com/block/buzz/pull/2357))
([`87b3fcd3c`](https://github.com/block/buzz/commit/87b3fcd3c0131683569dd4268b099d18b25dcd5e))
- Restore Goose and Buzz Agent to onboarding harness selection
([#2731](https://github.com/block/buzz/pull/2731))
([`7fc0cc82d`](https://github.com/block/buzz/commit/7fc0cc82db4d9dced9c258bbe8b530164a832a77))
- fix(desktop): render rich project work item content
([#3100](https://github.com/block/buzz/pull/3100))
([`afb272bb7`](https://github.com/block/buzz/commit/afb272bb7b8d7d45d7de676fa97dcd5a8eefacc7))
- feat(acp): bring your own harness (BYOH) — generic ACP runtime seam +
settings gallery ([#2773](https://github.com/block/buzz/pull/2773))
([`95fdf9788`](https://github.com/block/buzz/commit/95fdf978800982389b120c66ff5e766d785419c7))
- feat(desktop): use collective mesh routing for Auto
([#2825](https://github.com/block/buzz/pull/2825))
([`16d4ec335`](https://github.com/block/buzz/commit/16d4ec335e210295a9d9f77f36c1e85a18b6814a))
- fix(desktop): strip legacy baked team instructions from stored prompts
([#3035](https://github.com/block/buzz/pull/3035))
([`aee631448`](https://github.com/block/buzz/commit/aee63144843854ee32ed9d36a2e7511c82ddc6b0))
- feat(agents): lower default agent parallelism from 24 to 10
([#3038](https://github.com/block/buzz/pull/3038))
([`5d8ede446`](https://github.com/block/buzz/commit/5d8ede446f8fdc48146fe56d389cab6bf3500f92))
- Polish community rail and mobile pairing
([#2972](https://github.com/block/buzz/pull/2972))
([`e6c90bb7c`](https://github.com/block/buzz/commit/e6c90bb7c430d1b2af16508b634f9a5283b7fa3b))
- fix(desktop): remove bundled libsystemd from AppImage
([#2353](https://github.com/block/buzz/pull/2353))
([`a31fc4d2f`](https://github.com/block/buzz/commit/a31fc4d2f35d51cdf45ff8c61fc3a07f49c665e8))
- fix(desktop): make agent definition authoritative for
model/provider/prompt ([#1968](https://github.com/block/buzz/pull/1968))
([`8c0e8cb16`](https://github.com/block/buzz/commit/8c0e8cb1656b04ad269bce3c2deeda2a943ae78a))
- chore(desktop): delete dead persona catalog UI cluster
([#2886](https://github.com/block/buzz/pull/2886))
([`8e67cf399`](https://github.com/block/buzz/commit/8e67cf399d0291bcdbc69cd0402983ca030f05bb))
- fix(desktop): surface install failures hidden by curl-pipe exit codes
([#2892](https://github.com/block/buzz/pull/2892))
([`166c6655e`](https://github.com/block/buzz/commit/166c6655e8bca87d83ad60c087fb70a32a026baf))
- Refactor managed-agent runtime into cohesive modules
([#2974](https://github.com/block/buzz/pull/2974))
([`74b63e184`](https://github.com/block/buzz/commit/74b63e1846212af6e6751a62cfc631f74b1dfe07))
- fix(desktop): make Linux AppImage GStreamer work on non-Debian distros
([#2176](https://github.com/block/buzz/pull/2176))
([`cc6c4d347`](https://github.com/block/buzz/commit/cc6c4d3471629fad018bcf645f9471a01b9ffe2f))
- refactor(desktop): remove Agent directory section from Agents page
([#2290](https://github.com/block/buzz/pull/2290))
([`5d1233e84`](https://github.com/block/buzz/commit/5d1233e841b0efa91470bb45467b2c8e4284ebf6))
- fix(desktop): enable arboard Wayland backend so Linux copies reach the
Wayland clipboard ([#2904](https://github.com/block/buzz/pull/2904))
([`ab7aa8b12`](https://github.com/block/buzz/commit/ab7aa8b1200710dbc2d7a8661ed5aab95c4199c1))
- fix(desktop): supervise and re-arm relay-mesh runtime
([#2823](https://github.com/block/buzz/pull/2823))
([`aa51dab9d`](https://github.com/block/buzz/commit/aa51dab9da5fef7054d03cf1a1207986d0000684))
- fix(agents): run live Databricks discovery instead of the fallback
list ([#2890](https://github.com/block/buzz/pull/2890))
([`8eb6e3eb6`](https://github.com/block/buzz/commit/8eb6e3eb601174249642373a6a367262fa476753))
- fix(desktop): retire prepend mode on every reader wheel
([#2913](https://github.com/block/buzz/pull/2913))
([`07d0265cf`](https://github.com/block/buzz/commit/07d0265cfc212ef02e1c26153bf58ff46ce5ffe6))
- fix(desktop): consolidate prepend scroll correction
([#2855](https://github.com/block/buzz/pull/2855))
([`25e7864b3`](https://github.com/block/buzz/commit/25e7864b35f4dfd1c0ff31304a38555230a85f8d))
- fix(desktop): track concurrent agent turns up to the harness maximum
([#2882](https://github.com/block/buzz/pull/2882))
([`20bff5910`](https://github.com/block/buzz/commit/20bff591023daffc5ee1032cff02b54b75da3567))
- fix(relay): preserve reconnect backoff
([#2759](https://github.com/block/buzz/pull/2759))
([`499c5d349`](https://github.com/block/buzz/commit/499c5d349dab13bc906b1af5fe1fcb09ce2afa81))
- refactor(relay): expose reconnect timing policy
([#2310](https://github.com/block/buzz/pull/2310))
([`2f0041595`](https://github.com/block/buzz/commit/2f0041595d72529c06885680d2bd07ddb6a0beb4))
- fix(desktop): clear stale working badges on agent stop/restart
([#2803](https://github.com/block/buzz/pull/2803))
([`a64cc71f6`](https://github.com/block/buzz/commit/a64cc71f6c1605279b1a6fbd0fe904a2984cbdb0))
- fix(desktop): surface agent rename relay profile sync failure as a
warning toast ([#2279](https://github.com/block/buzz/pull/2279))
([`5e3d2e484`](https://github.com/block/buzz/commit/5e3d2e4849c0f2512330801d804fb96f4ab72d28))
- fix(discovery): inject PATH into Codex adapter planning
([#2767](https://github.com/block/buzz/pull/2767))
([`6ab3835f3`](https://github.com/block/buzz/commit/6ab3835f3fe89ee215819fe8d193463c0ae7472b))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
v0.5.0
2026-07-28 07:21:38 -07:00
2ce2d71cc3 feat(relay): make Postgres pool size configurable, default 50 (#3191)
## Summary

- Raise the relay's Postgres pool cap from the `buzz-db` default of 20
to 50 per pool, and expose `BUZZ_DB_POOL_SIZE` for per-deploy tuning
- Applies to the writer pool and, when `READ_DATABASE_URL` is set, the
reader pool; zero/unparsable values fall back to the default
- The `buzz-db` library default is unchanged — only the relay opts into
the larger cap

## Why

During the 2026-07-27 18:40–19:05Z traffic burst on bb-public, per-pod
PG pools pinned at 20 fleet-wide and ~380 requests failed on the 3s
acquire timeout — membership checks, channel access lookups, and
historical queries returning errors to users. The database was nowhere
near a limit: Aurora (db.r8g.8xlarge, ~5,000 max connections) sat at 19%
CPU, 201 connections (~4% of capacity), commit latency flat at 0.01ms.

The 20-connection default was sized for "four relay pods against PG
max_connections=100" (the comment in `buzz-db` says exactly that).
Production now runs 12–15 pods against Aurora — the per-pod cap is the
binding constraint, not the DB.

Budget at the new default: 15 pods × (50 writer + 50 reader + 5 audit) ≈
1,575 potential connections, ~30% of Aurora's ceiling — and actual usage
stays demand-driven (`min_connections` stays 2, connections only open
under load).

Same shape as #2521 (`BUZZ_REDIS_POOL_SIZE`), which fixed the identical
class of ceiling on the Redis side.

## Testing

- `cargo test -p buzz-relay`: 762 passed, 1 failed — the lone red is
`api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo`, the
known pre-existing flake; it fails identically on clean `main` at the
same SHA (verified via `git stash` / rerun)
- New test `db_pool_size_env_override_and_invalid_fallback` covers
override, zero, and unparsable fallback
- `defaults_are_valid` extended to pin the new default
- `cargo clippy -p buzz-relay --all-targets -- -D warnings` and `cargo
fmt --check` clean

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-28 09:17:43 -04:00
3a4bf513df Publish symbol-bearing debug relay images (#3250)
## Why
Native profilers such as `ddprof` need symbols to resolve optimized Buzz
relay stacks, while ordinary deployments should keep the current compact
stripped image.

## What
- Build optimized relay binaries with line-table debug information and
derive the stripped release binaries from the same ELF files
- Publish matching multi-arch `debug-*` tags while preserving existing
stripped tags and runtime behavior
- Document the debug image as an optimized symbol-bearing release, not a
debug-mode build

## Risk Assessment
Medium — this changes the relay image release workflow and adds a second
image variant, but existing tags remain stripped and use the same
runtime base, user, entrypoint, and optimized machine code.

## References
- Follows Envoy's optimized unstripped `debug-*` image pattern:
https://github.com/envoyproxy/envoy/blob/main/distribution/binary/BUILD
- Built both Docker targets locally; verified matching GNU build IDs and
`.text` hashes, with DWARF and symbol sections present only in the debug
variant
- Pre-push checks passed: branch skew, org policy, desktop checks/tests,
Rust tests, mobile tests, Tauri tests, and workspace tests

Generated with Amp

Signed-off-by: David Grochowski <dgrochowski@squareup.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-07-27 23:35:45 -04:00
e94b9aeda0 feat(tracing): add datastore tracing plumbing (#2760)
Configure a dedicated datastore tracing target on the OTLP layer while
preserving explicit logging filters and avoiding span overhead when OTLP
is disabled.

This is in preparation for adding trace spans for datastores used in
Buzz

## Update — 2026-07-27

- Export HTTP requests as `INFO` server spans under `buzz_relay`,
preserving request parentage for datastore spans.
- Configure OTEL span filtering independently with `BUZZ_OTEL_FILTER`,
so `RUST_LOG` changes cannot break trace topology.
- Verify exported HTTP and datastore spans share a trace ID and have the
expected parent/child relationship.

Co-authored-by: Amp <amp@ampcode.com>
2026-07-27 23:32:42 -04:00
925a9a7bf2 fix(buzz-acp): accept id-keyed config options when resolving model switch (#2795)
## Summary

Fixes #2794. Related: #2692.

`resolve_model_switch_method()` reads the `configId` key from each
`session/new` `configOptions` entry and skips entries that lack it.
`claude-agent-acp` (v0.61.0) keys its entries with `id`, so every
model-category entry was skipped, the desired model never matched, and
Claude Code sessions fell back to the CLI default from the user's
`~/.claude/settings.json`. The only trace was a `pool::model` WARN that
never reaches the per-agent log files.

This is the ACP-side half of the symptom reported in #2692. The open
desktop-side PRs (#2695, #2701, #2696) inject `ANTHROPIC_MODEL` at
spawn, which masks the problem for spawn-time selection but leaves the
config-option switch path broken.

## Changes

- `resolve_model_switch_method()` accepts either `configId` or `id` when
extracting the config id. The set request is unchanged: the ACP SDK
schema takes `configId` as the request param and the adapter resolves it
against its `id`-keyed entries, so only the read side needed fixing.
- Regression test with an `id`-keyed `configOptions` payload mirroring
the real adapter response (including `models: null`, so the unstable
fallback path cannot rescue the match).
- Doc comment on `extract_model_config_options()` notes the key drift.

## Testing

`cargo test -p buzz-acp --lib`: 599 passed, 0 failed. The new test fails
on main and passes with this change.

Verified against the real adapter: a stdio JSON-RPC probe of the bundled
`claude-agent-acp` 0.61.0 confirms `session/new` returns `id`-keyed
config options with `opus[1m]` present as a value, and the SDK's
`SetSessionConfigOptionRequest` schema accepts `{sessionId, configId,
value}` as sent by `session_set_config_option()`.

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

Signed-off-by: chillerno1 <gh.chiller@pm.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 02:27:44 +00:00
Will PflegerandGitHub be13b4bb9c fix(desktop): probe legacy Goose install dir on Windows (#3248)
Goose's pre-[#2680](https://github.com/block/buzz/pull/2680) Windows
installer unpacked the CLI to `%USERPROFILE%\goose\goose.exe`. That
directory is on no standard `PATH`, and `common_binary_paths()` never
probed it, so users who installed Goose with the legacy installer stayed
permanently undiscovered — the residual half of #2239.

`resolve_command_uncached` finds binaries outside `PATH` only by
scanning `common_binary_paths()`, so adding the directory there is the
whole fix: Windows basename expansion already supplies
`goose.exe`/`.cmd`/`.bat`, and discovery, readiness probes, and spawn
all route through the same shared resolver. No Goose-specific resolution
path is introduced. The entry sits beside the existing Codex
`%LOCALAPPDATA%\Programs\OpenAI\Codex\bin` probe in the same
`#[cfg(windows)]` block.

The regression test is `#[cfg(windows)]` and is CI-reachable, not dead
code — the `desktop-build-windows` job runs `cargo test --manifest-path
desktop/src-tauri/Cargo.toml --target $env:TARGET` on `windows-latest`.
It asserts the probe list rather than planting a binary:
`common_binary_paths` is a process-lifetime `OnceLock`, so a test cannot
deterministically re-seed `USERPROFILE`, and planting an executable
under the real user profile is not an acceptable side effect. Verified
locally by widening the `cfg` to build on macOS — the test passes with
the probe and fails without it.

The `check-file-sizes.mjs` override for `managed_agents/discovery.rs`
moves 1835 → 1841, the exact post-`cargo fmt` gate count. Verified both
directions: 1841 passes, 1840 fails.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 21:30:21 -04:00
Will PflegerandGitHub 94675e0d25 refactor(desktop): extract install command execution into install_exec (#3251)
`commands/agent_discovery.rs` was pinned at its 2167-line file-size
ceiling with zero headroom, blocking the install-supervision and
install-log work queued behind it. Install command *execution* is a
clean seam and moves into `commands/agent_discovery/install_exec.rs`
together with its tests, matching the existing `managed_node.rs` /
`post_install_verification.rs` split under the same module.

Moved: `INSTALL_MAX_ATTEMPTS`, `run_install_command_with_retry`,
`run_install_with_retry`, `install_failure_is_retryable`,
`install_retry_backoff`, `annotate_retry_attempts`,
`run_install_command`, `truncate_output`, `floor_char_boundary`, and the
install-retry test block. Command *construction*
(`install_shell_command`, `install_powershell_command`,
`build_install_command`) stays in the parent — the new module owns only
what happens once a `Command` exists. Public surface is exactly one
`pub(super) fn run_install_command_with_retry`.

The extraction is behavior-preserving, verified by diffing the moved
text against the original line ranges: the parent is original-minus-cuts
plus the intended edits, and the moved code is byte-identical except for
the `pub(super)` marker, the `build_install_command` →
`prepare_install_command` call site, and the new function described
below. Two parent imports (`std::io::Read`, `InstallStepResult`) became
unused and were dropped.

### Install working directory (#2245)

Absorbed from #3090. A packaged desktop launch inherits `/` as its
working directory, so installers that write relative to the CWD fail on
a read-only root. The new `prepare_install_command` builds the command
and applies `default_agent_workdir()`, and it is the only builder
`run_install_command` calls — so no spawn path can bypass the workdir.

This differs from #3090 in the test: that version spawned `pwd` through
the real install shell and deleted
`test_install_shell_command_returns_ok_on_unix` to make room. Here the
prepared `Command` is asserted directly via `get_current_dir()` —
hermetic, no shell spawn — and the existing test is kept.

### Tests

Four new, on top of the moved retry block:

- `test_prepared_install_command_uses_default_workdir` — every install
child carries `default_agent_workdir()`.
- `test_truncate_output_leaves_short_output_untouched` — under the cap,
byte-for-byte passthrough.
- `test_truncate_output_keeps_head_and_tail_with_marker` — over the cap,
both ends survive and the marker names the omitted byte count.
- `test_truncate_output_does_not_split_multibyte_characters` — the
boundary floor prevents a mid-codepoint cut.

`truncate_output` had no coverage anywhere before this.

### File-size gate

`check-file-sizes.mjs` override for `agent_discovery.rs` moves 2167 →
1808, the exact post-`cargo fmt` gate count — verified both directions
(1808 passes, 1807 fails). `install_exec.rs` is 458 lines and needs no
override; the default 1000-line limit covers it.

Related: #3090, #2245

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 21:29:44 -04:00
morgmartandGitHub d8f9d87c17 Polish composer activity layout and transitions (#3151)
## Summary
- Replace the permanently reserved composer activity row with a
conditional, content-driven accessory for channel and thread composers.
- Keep the composer dock geometrically stable while activity appears, so
the composer’s bottom edge resizes smoothly without shifting the
conversation.
- Preserve translucent backdrop blur during the resize with a stable
dock-level blur layer, and keep thread overlay/focus-mode alignment
consistent.
- Keep bottom-pinned virtualized conversations fully visible as composer
content, zoom, or viewport height changes without repinning readers who
have scrolled into history.
- Refine the activity lockup with aligned avatars/text and a subtle,
reduced-motion-safe shimmer.
- Centralize the dock’s quiet inset, activity rail, released space, and
activity offset in one CSS-variable geometry contract.

This takes a different, systemic route from the spacing reduction
proposed in #2602 and supersedes that approach.

### Related issue
Related PR: #2602

### Testing
- `./scripts/check-branch-skew.sh`
- `just desktop-check`
- `just desktop-test` — 3,638 passing
- `useAnchoredScroll.test.mjs` — virtualized viewport resize follows the
explicit bottom state
- Focused desktop smoke E2E — 6 passing across stable dock geometry,
multiline growth, viewport resize, reduced motion, blur ownership, and
thread overlay alignment
- Pre-push hooks passed for organization safety, branch skew, desktop
checks/tests, Rust tests, workspace tests, and desktop Tauri tests
- Mobile pre-push is independently red on latest `main`; the exact
`activity_page_test.dart` compiler failure reproduces on untouched
`origin/main`
- Visually tested channel and thread composers across quiet/activity
states, multiline composer growth, and thread overlay/focus mode


https://github.com/user-attachments/assets/ed0b08d9-18e0-4061-b272-ab509dbcd8ee

---------

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
2026-07-27 15:47:04 -07:00
d500c2d5cf feat(invites): add use-limited invite links (#3141)
## Summary

- add database-backed v2 invite links with optional maximum-use limits
and atomic final-slot redemption
- preserve v1 invite compatibility while adding
exhausted/expired/invalid client handling across desktop, web, and
mobile
- emit structured claim-outcome logs with community, invite ID, outcome,
maximum uses, and post-claim count

## Verification

- `cargo fmt --all -- --check`
- `cargo test -p buzz-db` (85 passed, 134 Postgres-dependent ignored)
- `cargo clippy -p buzz-db --all-targets -- -D warnings`
- desktop `npm run typecheck`
- push hook: desktop checks/tests, desktop Tauri tests, Rust tests, and
branch-skew passed
- Postgres integration tests were previously reviewed green at the
pre-rebase tree; local rerun on this session was unavailable because
Postgres/Docker were not running
- mobile push-hook check could not start because Flutter is unavailable
locally

---------

Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
Co-authored-by: npub1c4alndp82zyt9veaklm5d965quss79vlhk9awv7qu5erwhmf42qqlvc25c <c57bf9b4275088b2b33db7f746975407210f159fbd8bd733c0e532375f69aa80@buzz.block.builderlab.xyz>
Co-authored-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz>
Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
2026-07-27 15:19:39 -07:00
98a7b13348 fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0 floor (#3218)
## Problem

User report:

```
Buzz Node mismatch: Buzz supplies Node 24.14.0; OpenClaw requires >=24.15.0. All 10 ACP workers immediately crash.
```

Buzz supplies Node to agent processes from two places, and both were
below OpenClaw's floor:

| Supply path | Was | Now |
|---|---|---|
| hermit dev env (`bin/.node-*.pkg`) — the 24.14.0 in the report |
24.14.0 | **24.15.0** (newest hermit publishes; satisfies `>=24.15.0`) |
| Desktop managed runtime (`managed_node.rs` / `managed_node_paths.rs`)
| v24.11.0 | **v24.18.0** (current latest v24) |

The managed runtime sits **first** on the worker PATH
(`managed_agents/runtime/path.rs`), so a user-installed newer Node can't
mask a stale managed one — the pin itself has to move.

## Verification

- SHA-256 digests for all six platform artifacts taken from
`https://nodejs.org/dist/v24.18.0/SHASUMS256.txt`; darwin-arm64
independently re-verified by downloading the tarball (hash match),
extracting, and running `bin/node --version` → `v24.18.0`.
- All artifacts within `MANAGED_NODE_MAX_BYTES` (largest linux-x64 at 57
MB < 90 MB cap); tar.gz layout keeps the `node-vX-platform/bin/node`
shape `verify_node_tree` expects.
- `cargo test --lib` in `desktop/src-tauri`: **1801 passed, 0 failed**
at this commit; `cargo fmt --check` + `cargo clippy --lib -D warnings`
clean.
- Existing readiness check (`node --version == MANAGED_NODE_VERSION`)
makes upgrade automatic: installed v24.11.0 trees fail readiness and the
installer stages v24.18.0 atomically (rename with `.old` rollback —
existing logic, unchanged).

Note: CI `node-version: 24.14.1` pins in
`release.yml`/`windows-canary.yml` are build-env only (already `>=`
nothing OpenClaw touches) and left alone to keep this minimal.

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-27 22:18:02 +00:00
9810d85459 fix(desktop): preserve thread anchor through layout reflow (#3212)
## Summary
- keep a thread presentation-switch anchor pinned while focus/split
width reflow settles
- retire the temporary anchor only after resize correction and a
following paint confirm the row is visible
- preserve the existing external-target resolution behavior and viewport
E2E contract

## Root cause
The focus and split wrappers intentionally retain the same thread
surface, but switching wrappers also changes the message column width.
`useAnchoredScroll` centered the captured message once and immediately
cleared the one-shot layout target. A later text reflow could then move
that message outside the viewport with no remaining target to correct
it.

## Verification
- `pnpm check`
- `pnpm typecheck`
- `pnpm test` — 3,699 passed
- `pnpm build:e2e`
- `pnpm exec playwright test tests/e2e/thread-focus-mode.spec.ts
--project=smoke --repeat-each=10` — 20 passed
- push hook: branch-skew, Desktop check, and Desktop full unit suite
passed

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-27 14:54:52 -07:00
cb2a265b53 feat(search): parse from:/in:/after:/before: and pass them in the filter (#2871)
## Summary
- Adds a pure-TS operator parser (`from:` / `in:` / `after:` /
`before:`) with unit tests. Invalid date tokens stay in the FTS text.
- Extends `search_messages` so the desktop can send `authors` / `since`
/ `until` (and existing `#h`) on the filter the relay already
understands.
- Wires topbar search to strip operators from the prefix query, resolve
`in:` against local channels and `from:` against hex pubkeys / known
agents, then pass the structured fields through.

This is part 1 of #2853 (parser + command plumbing). Autocomplete chips
/ richer `from:@name` resolution can follow in a second PR.

## Test plan
- [x] `node --import ./test-loader.mjs --experimental-strip-types --test
src/features/search/lib/parseSearchOperators.test.mjs`
- [x] Added `search_messages_filter_emits_operator_fields` unit test
(full `buzz-desktop` crate build needs local sidecar binaries in this
environment)
- [ ] Manual: topbar `deploy from:<hex> after:2024-01-01` emits
authors/since on the bridge filter and returns narrowed hits

Made with [Cursor](https://cursor.com)

---------

Signed-off-by: Jatinder Mahajan <jatinder.mahajan@certifyos.com>
Co-authored-by: Jatinder Mahajan <jatinder.mahajan@certifyos.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 21:52:56 +00:00
John Matthew TennantandGitHub 0019f80765 fix(desktop): fetch join policies through native networking (#2862)
## Context

Adding an existing community by relay URL could fail with `Community
rejected: Load failed` even when its WebSocket endpoint was reachable.
The Add Community flow fetched `/api/join-policy` from the WebView, so a
relay without a matching CORS allowance blocked the policy request
before the app could join it.

## Summary

This bug fix fetches join policies through Tauri's native networking
layer for direct URL joins. Invite-code discovery, policy acceptance,
and signed invite claims remain on the WebView path so those operations
can migrate together later.

## Changes

- Uses native networking for Add Community and first-community direct
URL join-policy requests.
- Validates relay schemes, rejects URLs containing credentials, and
refuses redirects.
- Bounds declared and chunked native responses before JSON parsing.
- Preserves existing `404`, non-success status, malformed JSON, and
absent-policy behavior.
- Requires every join-policy caller to choose its transport explicitly.

Public relays using Buzz's default permissive CORS configuration are not
known to be affected.

### Related issue

Related to #2872.

### Testing

#### Reviewer-reproducible examples

End-to-end red/green requires a relay with restrictive CORS and a Buzz
identity authorized to join it.

##### Red: `main`

From a clean checkout of `main`:

```bash
. ./bin/activate-hermit
just staging
```

In Buzz Desktop:

1. Add another community so the restrictive-CORS relay can be removed.
2. Remove that relay.
3. Open Add Community and enter the relay's WebSocket URL.
4. Select Add Community.

Observed result:

```text
Community rejected: Load failed
```

##### Green: this PR

From a clean checkout of this branch:

```bash
. ./bin/activate-hermit
just staging
```

Repeat the same steps above.

Observed result:

```text
The community rejoins successfully.
```

Supporting checks:

- Six native join-policy tests, including oversized declared and chunked
responses.
- Four TypeScript API tests, including the native command contract.
- E2E build and four focused onboarding and sidebar Playwright tests.
- Full `just ci` and pre-push suites.
- Builderbot, Kalvin, and minimize-diff review fanout found no
actionable issues after the final rebase.
2026-07-27 17:52:36 -04:00
7ca0bbd946 fix(desktop): republish agent identity records when a persona rename propagates (#2607)
## Problem

Part of #2423 (renaming personal agents desynchronises identity).

Renaming an agent definition (persona) propagates the new display name
to its
linked agent instances (`propagate_persona_name_rename` in
`desktop/src-tauri/src/commands/personas/mod.rs`) and saves
`managed-agents.json` — but, unlike the instance-rename path
(`update_managed_agent`), it never re-retains the renamed instances'
kind:30177
managed-agent identity records. `record.name` is part of the published
identity
projection (`agent_event_content`), so after a persona rename:

- `managed-agents.json` says the NEW name,
- the retained kind:30177 row (retention.db → relay flush loop) still
carries
  the OLD name, with the OLD `created_at`.

The stale identity record stays live on the relay until the next app
launch,
when the boot-time reconcile (`reconcile_agents_in_dir`) finally notices
the
content diff and republishes. Until that restart, any surface that
resolves
agents from kind:30177 records (second desktop of the same owner, CLI,
other
NIP-AP clients) sees the OLD name bound to the agent pubkey while the
kind:0
profile already shows the NEW one — the name→identity binding desync
described
in #2423, and consistent with the report's observation that repairing
state
required "a separate restart".

## Fix

- Extract the per-record retain body of the boot reconcile into
`managed_agents::reconcile::retain_agent_record(conn, keys, record) ->
Result<bool, String>`
— one shared content-diff + monotonic-`created_at`-bump engine (returns
whether a row was rewritten). `reconcile_agents_in_dir` now calls it per
  record (behavior unchanged; existing reconcile tests still pass).
- `commands::agents::retain_managed_agent_pending` delegates to the
shared
  engine instead of carrying a duplicate implementation (same semantics:
  projection-equality no-op guard, monotonic bump, `pending_sync = 1`).
- `update_persona` (Phase 1, still under the store lock, after
`save_managed_agents`): call `retain_managed_agent_pending` for every
record
the rename propagated to — mirroring `update_managed_agent`. Avatar-only
edits are deliberately excluded (the avatar is not part of the
kind:30177
  projection; retaining would be a guaranteed no-op).

No new events, kinds, or APIs — this uses the existing signed-event
retention
and flush pipeline, per CONTRIBUTING's guidance to prefer a signed Nostr
event
and the existing ingest path over endpoint-specific JSON APIs.

## Out of scope (deliberately)

- Rename → runtime restart is #1823, fixed by open PR #2507
(spawn_hash).
- Surfacing kind:0 relay profile-sync failures on rename is PR
#2302/#2279
  territory (and largely superseded by the merged rollback in #2258).
- Mention-picker UX (owner/status disambiguation) and channel-membership
repair for stale identities: TS-side, noted in #2423, not touched here.

## Test evidence

Two new unit tests in
`desktop/src-tauri/src/managed_agents/reconcile/tests.rs`
(same harness as the existing reconcile tests — tempdir + retention.db +
fresh
keys, no AppHandle):

- `rename_re_retains_identity_record_with_new_name` — retain "Fizz",
confirm
  flush, rename to "Spark", re-retain: row keeps the pubkey coordinate,
  carries the new name only, is `pending_sync`, and its `created_at` is
  strictly past the retained head (replaceable-event acceptance).
- `retain_agent_record_is_noop_when_unchanged` — an unchanged projection
does
  not rewrite the row and produces zero `pending_sync` churn.

Ran scoped per CONTRIBUTING build discipline (from `desktop/src-tauri`):

```
cargo fmt -p buzz-desktop                                  # applied, clean
cargo clippy -p buzz-desktop --lib --tests -- -D warnings  # exit 0, no warnings
cargo test -p buzz-desktop --lib                           # full lib suite
```

Full `buzz-desktop` lib suite: **1562 passed, 0 failed, 13 ignored** —
including all 12 `managed_agents::reconcile` tests (10 pre-existing, all
unmodified in behavior, plus the 2 new regression tests above).

## Links

- Issue: https://github.com/block/buzz/issues/2423
- Adjacent (no overlap): PR #2507 (rename-restart, #1823), PRs
#2302/#2279
(kind:0 sync-failure surfacing), merged #2258 (instance-rename
rollback).

---------

Signed-off-by: Sean Gearin <sgearin@gmail.com>
Co-authored-by: Sean Gearin <sgearin@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 17:44:08 -04:00
thomaspblockandGitHub de13960505 fix(desktop): keep project Inbox previews compact (#3193)
## Summary

Project pull request and issue previews in Inbox no longer expand when
their content begins with a Markdown heading. Heading-form titles now
match neighboring Inbox typography while preserving the existing
two-line truncation.

### Related issue

Related: #3117

### Testing

- `pnpm build:e2e`
- `pnpm exec playwright test tests/e2e/project-inbox.spec.ts
--project=smoke --retries=0` — passed
- `pnpm exec biome check tests/e2e/project-inbox.spec.ts
src/shared/styles/globals/markdown.css` — passed
- `pnpm check:px-text` — passed
- Captured `desktop/test-results/inbox-preview/01-project-preview.png`

The repository-wide desktop file-size gate remains blocked by the
pre-existing 1,026-line `AgentCreationPreview.tsx` on `main`; the change
itself introduces no file-size regression.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
2026-07-27 13:54:54 -07:00
2bd4c24b71 Inbox refactor (#2045)
## Why

The Inbox mixed overlapping feed categories with personal work queues,
so **All** was not actually comprehensive and several filters did not
make it clear why an item appeared. Threads and DMs could produce one
row per event instead of one row per conversation, drafts were hidden
until selected, and reminders appeared through multiple competing
presentations.

This refactor makes the **Inbox** a focused, conversation-oriented place
to catch up on work relevant to you. It is intentionally not a mirror of
every unread event in every channel.

## What changed

- Keep the destination named **Inbox** and use the standard Lucide bell
icon.
- Refocus **All** on DMs, mentions, thread replies, needs-action items,
replies from agents the user owns or controls, due reminders, and active
drafts.
- Exclude generic top-level channel traffic and updates from agents the
user does not own or control.
- Group each thread or DM into one row, sorted by latest activity.
- Resume an unread conversation at its oldest unread message while
opening the full thread or DM in the detail pane.
- Reuse the existing **New** divider at the unread boundary.
- Make the detail title a direct link to the canonical conversation.
- Give Reminders and Drafts the same list/detail interaction and
location metadata as conversation rows.
- Separate Reminders and Drafts from message filters with a subtle
divider, without adding another labeled section.
- Put reminder and draft counts beside their corresponding filter labels
instead of on the generic filter button.
- Preserve the selected conversation when switching filters if it
remains valid; otherwise select a valid replacement without flashing
stale detail.
- Use filter-specific empty states and rename the options toggle to
**Show unread only**.
- Ship the focused behavior directly. The earlier experiment gate,
Custom view, and default-view controls have been removed from this PR to
keep the first pass focused.

## Filter model

| Filter | What appears |
| --- | --- |
| **All** | One row per personally relevant conversation, plus due
reminders and active drafts. Includes DMs, mentions, thread replies,
explicit needs-action items, and replies from agents the current user
owns or controls. Excludes generic top-level channel traffic, other
agents' updates, and reminders that are not due yet. |
| **Mentions** | Conversations containing a direct mention. Each
conversation appears once and opens with full context. |
| **Threads** | Conventional threaded replies, grouped to one row per
thread. Broadcast replies are not treated as conventional thread
replies. |
| **Needs action** | Feed items explicitly classified as requiring
action. |
| **Agents** | Conversations whose representative response was authored
by an agent the current user owns or controls, including top-level DM
responses. If a human replies afterward, the conversation leaves this
filter until an owned agent responds again. |
| **Reminders** | All pending reminders, including upcoming reminders
that stay out of **All** until they are due. |
| **Drafts** | Active drafts, ordered by their last real edit time. |

## Grouping, ordering, and state

- A thread or DM creates one Inbox row rather than one row per event.
- An unread conversation resumes at its oldest unread message so
intervening context is not skipped.
- Conversation rows still sort by their latest activity.
- The detail pane opens the full available conversation and shows the
shared **New** divider before the first unread message.
- Upcoming reminders appear only in **Reminders**.
- When a reminder becomes due, it enters **All** at its trigger time. If
its source conversation is already represented, the reminder state
merges into that row instead of creating a duplicate; otherwise it
appears as a standalone reminder row.
- A due reminder can enrich a row in another relative filter when that
conversation already qualifies for the filter. Reminder lifecycle
remains separate from message read state.
- Drafts appear in **All** by their last real edit time. Opening an
unchanged draft does not move it to the top.
- Reminder and draft rows show their location as `In #channel` or `In DM
with <name>`.
- **Show unread only** hides reminder and draft work queues because they
do not share message unread semantics.

## Removed or narrowed

- **Remove the old Activity filter.** It overlapped with All while still
omitting items All now includes.
- **Narrow Agents.** It no longer gathers every agent participating in a
shared thread or subsequent human follow-ups.
- **Remove duplicate reminder presentations.** The aggregate
pending-reminders jump and duplicate generic feed rows are replaced by
one list/detail model.
- **Remove Custom and default-view settings from this pass.** They added
considerable state and UI before the core model had been validated.
- **Do not add section labels for Reminders and Drafts.** A divider
communicates the distinction without creating another hierarchy in the
menu.

## Risk assessment

Medium implementation risk because this changes composition, grouping,
ordering, read behavior, and personal queues in a primary desktop view.
The implementation is scoped to the desktop UI and its local feed
projection; it does not change relay schemas or public APIs.

## Testing

- Desktop formatting, lint, file-size, text-size, and TypeScript checks
passed.
- Desktop unit suite: **3,663 passed, 0 failed**.
- Desktop E2E production build passed.
- Playwright smoke coverage across every spec touching this surface
(`channels`, `smoke`, `profile`, `project-inbox`, `community-rail`,
`integration`, `drafts-screenshots`): **118 passed, 0 failed**.
- Full Playwright smoke project: **732 passed, 1 skipped**. Three local
failures were investigated and cleared — `community-rail` keyboard
reorder passed on re-run (flaky), while `relay-reconnect:97` and
`video-attachment:223` are untouched by this commit (the only change to
shared `tests/helpers/bridge.ts` is a comment) and pass in CI.
- Unit coverage includes focused All matching, owned-agent filtering,
conversation grouping, oldest-unread selection, selection stability,
chronological reminder/draft composition, trigger-time reminder
ordering, and duplicate reminder suppression.

## Update: July 27, 2026

The naming decision is settled: the surface stays **Inbox**. An earlier
pass in this branch had renamed it to **Activity**; that rename has been
reverted in `9c00d2d6e`, which is naming-only and changes no behavior.

The revert covers file names, component/hook/type/constant identifiers,
the sidebar label and tooltip, the `Inbox options` and `Filter inbox:`
aria-labels, and the corresponding test names, test ids, and fixture
ids.

Three things were deliberately left as `activity`:

- **The feed API contract** — the `activity` / `agent_activity`
categories, the `feed.activity` and `feed.agentActivity` keys, and the
`types=` query parameter. These are the server's names, not the
surface's.
- **Plain-noun usage** — empty states such as "No activity yet", plus
`latestActivityAt` and `PROJECT_ACTIVITY_KINDS`.
- **Pre-existing agent, project, and profile activity code**, which
refers to a different concept entirely.

The earlier experiment-gate approach has also been dropped, so
`tests/helpers/bridge.ts` no longer claims that an Activity preview
feature exists — `preview-features.json` has no such entry and the seed
helper enables every desktop feature.

Generated with Codex

---------

Signed-off-by: Clay Delk <clay.delk@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:45:34 -04:00
99da5b7ebb Fix composer selection formatting and drop overlay (#3172)
## Summary
- scope code blocks and list formatting to the selected composer text
- use the Buzz primary color for the selection formatter
- extend the channel drop overlay over the composer with matching
corners, blur, and accessible contrast across themes

## Validation
- `just ci`
- composer selection formatting E2E tests
- file attachment and all-theme drop contrast E2E tests

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-27 20:39:44 +00:00
klopez4212andGitHub 75588eaff2 Refine pending message status (#3153)
## Summary
- Replace the all-caps, widely tracked pending-message label with
sentence-case `Sending`
- Match the surrounding timestamp and metadata spacing

## Why
The status briefly appeared as `SENDING`, unlike nearby message
metadata.

## Validation
- Desktop typecheck
- Focused Biome and text guards
- 3,637 desktop unit tests

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-07-27 21:06:37 +01:00
f069a85503 feat(admin): show reported message content in report detail (#3149)
## Summary

- include the reported event's complete stored content, author, creation
time, and deletion state in the admin report detail response
- resolve the event through a community-scoped join so an event ID
collision cannot cross tenant boundaries
- render the message only on report detail, with an explicit unavailable
state when retention has removed it
- preserve the existing report list contract so message bodies are not
returned during queue browsing

## Security

- the existing admin host/origin authorization runs before the detail
database read; a route test pins that ordering
- the target event is selected using both `events.community_id =
moderation_reports.community_id` and `events.id =
moderation_reports.target_event_id`
- the client supplies only the report UUID; it cannot choose a community
or arbitrary event ID
- soft-deleted content is visible only through this restricted admin
detail route and is labeled deleted
- responses retain the admin API's `no-store`, CSP, `nosniff`, frame
denial, and referrer policy middleware

## Testing

- `pnpm -C admin-web check`
- `pnpm -C admin-web test:e2e` (10 passed)
- `cargo test -p buzz-db` (84 passed, 130 ignored)
- `cargo clippy -p buzz-db -p buzz-relay --all-targets -- -D warnings`
- focused admin authorization tests
- pre-push Rust and desktop/Tauri suites passed

The new Postgres integration test is ignored under the repository
convention and will run when explicitly enabled against migrated
Postgres. Local Postgres and Redis were unavailable, so the full
`buzz-relay --lib` run had 8 existing infrastructure-dependent failures
after 749 tests passed.

---------

Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
Signed-off-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz>
Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
Co-authored-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz>
2026-07-27 12:49:21 -07:00
174c38e4bd fix(desktop): recover full local storage on startup (#3182)
## Summary
- recover already-full Buzz installs before desktop initialization
without deleting healthy caches
- enforce a global 2 MiB UTF-16 byte budget across disposable message,
channel, timeline-skeleton, and sidebar-skeleton caches, regardless of
relay count
- route all disposable cache writes through quota recovery and reserve
roughly 3 MiB of WebKit's observed ~5 MiB quota for durable state
- preserve communities, identities, preferences, drafts, and read state;
match only delimiter-qualified disposable namespaces

## Context
WebKit enforces an approximately 5 MiB per-origin localStorage quota and
Tauri does not expose an app-level knob to raise it to 50 MiB. Buzz
0.4.26 shipped reactive recovery for selected durable writes, but
disposable writers swallowed quota failures and their existing limits
were count-based per relay rather than byte-based per origin.

This PR handles both halves: upgrade recovery for already-wedged origins
and proactive global headroom so disposable snapshots cannot drive the
origin back to the cliff.

## Safety
- startup first probes a one-byte marker; healthy installs retain their
caches
- only if the marker write fails are the four relay-rehydratable cache
namespaces removed
- namespace matching requires the `v1:` delimiter, preventing future
`v10` or similarly named durable keys from matching
- oversized individual snapshots are rejected; crossing the global
budget evicts disposable snapshots only
- failed recovery leaves the marker absent, so the next launch retries

## Verification
- `pnpm test` — 3,670 passed
- `pnpm check`
- `pnpm typecheck`
- push hooks: branch-skew, desktop-check, desktop-test passed
- byte-budget tests cover UTF-16 accounting, multiple relays, oversized
writes, durable-state preservation, healthy startup, full startup,
marker retry, and namespace near misses

---------

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-27 12:49:07 -07:00
klopez4212andGitHub 01c23810fa Replace mobile reconnect banners with skeleton shimmer (#3143)
## Summary
- replace mobile connecting and reconnecting banners with element-shaped
skeletons for channel lists and message timelines
- add a low-contrast two-second shimmer and same-slot reveal, with
reduced-motion support
- align top, section, loaded-row, and skeleton label columns

## Why
Connection banners shifted content and did not match the desktop loading
treatment. The skeletons preserve layout and make reconnects less
disruptive.

## Testing
- `just mobile-check`
- `just mobile-test` — 704 passed, 1 skipped
- Pixel 10 visual verification in loaded and reconnecting states

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
2026-07-27 19:57:07 +01:00
4d8b676bb2 fix(desktop): keep collapsed table separators out of spoilers (#3169)
## Summary

- preserve collapsed GFM table separator rows as literal text
- prevent `||---:|---|---||` from becoming an animated spoiler canvas
- leave normal spoilers and valid multiline tables unchanged

## Root cause

When table newlines are lost, adjacent row pipes become `||`. The
spoiler remark plugin interpreted the delimiter row between those pairs
as a hidden spoiler, so the reported "static" was the spoiler particle
animation rather than table layout churn.

## Safety

The guard only applies to a paragraph span made entirely of text that
exactly matches a multi-column GFM delimiter row. The narrow syntax
collision is that an intentional spoiler containing only a delimiter row
such as `||---|---||` now renders literally.

## Verification

- desktop pre-push checks passed (3,662 tests)
- desktop TypeScript typecheck passed
- independent review found no blocking issues

Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
2026-07-27 11:40:24 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
137185e056 chore(deps): update plugin org.jetbrains.kotlin.android to v2.2.21 (#3058)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [org.jetbrains.kotlin.android](https://kotlinlang.org/)
([source](https://redirect.github.com/JetBrains/kotlin)) | `2.2.20` →
`2.2.21` |
![age](https://developer.mend.io/api/mc/badges/age/maven/org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin/2.2.21?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin/2.2.20/2.2.21?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/1) for more information.

---

### Release Notes

<details>
<summary>JetBrains/kotlin (org.jetbrains.kotlin.android)</summary>

###
[`v2.2.21`](https://redirect.github.com/JetBrains/kotlin/releases/tag/v2.2.21):
Kotlin 2.2.21

#### Changelog

##### Backend. Wasm

- [`KT-81372`](https://youtrack.jetbrains.com/issue/KT-81372) K/Wasm:
JsException: Exception was thrown while running JavaScript code on
Safari 18.2/18.3
- [`KT-80018`](https://youtrack.jetbrains.com/issue/KT-80018) K/Wasm:
exceptions don't work properly in JavaScriptCore (vm inside Safari,
WebKit)

##### Compiler

- [`KT-81191`](https://youtrack.jetbrains.com/issue/KT-81191) K2: "null
cannot be cast to non-null type ConeTypeParameterLookupTag" with invalid
code
- [`KT-80936`](https://youtrack.jetbrains.com/issue/KT-80936)
NON\_PUBLIC\_CALL\_FROM\_PUBLIC\_INLINE : `@PublishedApi` doesn't work
for fun interfaces

##### JavaScript

- [`KT-79926`](https://youtrack.jetbrains.com/issue/KT-79926) Wrong
export of interfaces with companions with ES Modules
- [`KT-81424`](https://youtrack.jetbrains.com/issue/KT-81424) Kotlin/JS:
Cannot Get / in a simple running application
- [`KT-80873`](https://youtrack.jetbrains.com/issue/KT-80873) KJS:
Stdlib requires ES2020-compatible JS engine due to BigInt type literal

##### Native

- [`KT-79384`](https://youtrack.jetbrains.com/issue/KT-79384) K/N:
Application Not Responding: Thread Deadlock

##### Tools. Gradle

- [`KT-79047`](https://youtrack.jetbrains.com/issue/KT-79047) Gradle
compileKotlin fails with configuration cache
- [`KT-81148`](https://youtrack.jetbrains.com/issue/KT-81148) Publishing
helpers in KGP are incompatible with Isolated Projects
- [`KT-80950`](https://youtrack.jetbrains.com/issue/KT-80950) KGP breaks
configuration cache when signing plugin with GnuPG is applied

##### Tools. Gradle. Multiplatform

- [`KT-61127`](https://youtrack.jetbrains.com/issue/KT-61127) Remove
scoped resolvable and intransitive DependenciesMetadata configurations
used in the pre-IdeMultiplatformImport IDE import
- [`KT-81249`](https://youtrack.jetbrains.com/issue/KT-81249) Kotlin
2.2.20 broke KMP implementation of Parcelize

##### Tools. Gradle. Native

- [`KT-81510`](https://youtrack.jetbrains.com/issue/KT-81510)
`commonizeCInterop` exception with 'kotlinNativeBundleConfiguration' not
found
- [`KT-81134`](https://youtrack.jetbrains.com/issue/KT-81134) Native:
Gradle configuration failure likely related to Klibs cross-compilation
- [`KT-77732`](https://youtrack.jetbrains.com/issue/KT-77732)
`commonizeCInterop` failed with "Unresolved classifier:
platform/posix/size\_t"
- [`KT-80675`](https://youtrack.jetbrains.com/issue/KT-80675) Commonized
cinterops between "test" compilations produce an import failure

##### Tools. Maven

- [`KT-81218`](https://youtrack.jetbrains.com/issue/KT-81218) Kotlin
Maven Plugin 2.2.20: Java classes not resolved with enabled incremental
compilation without daemon

##### Tools. Wasm

- [`KT-80582`](https://youtrack.jetbrains.com/issue/KT-80582) Multiple
reloads when using webpack dev server after 2.2.20-Beta2

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/block/buzz).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-27 18:20:17 +00:00
9b0f744804 resolve findings (#3150)
Fixes all six HIGH findings from the buzz security report, one commit
per finding. Independently reviewed to approval by Max at `0158ae542`,
plus a deep isolated live pass (clean-room compose stack, weird ports,
full product matrix) at the same head — see the buzz-security thread for
evidence. `fe65c07c3` merges current `origin/main` on top (new commit,
no rebase), inheriting the nostr 0.44.6 bump (#3135) and relay-admin ban
gate (#3128).

## Findings and fixes

| Finding | Commit | Fix |
|---|---|---|
| 003 — quinn-proto RUSTSEC-2026-0185 | `e5dcdec72` | Bump quinn-proto
0.11.14 → 0.11.16 (lockfile-only) |
| 002/004 — linkify-it quadratic-parse DoS (GHSA-22p9-wv53-3rq4,
GHSA-v245-v573-v5vm) | `923b3c20f` | pnpm override `linkify-it: ^5.0.2`;
`pnpm why` confirms a single 5.0.2 copy |
| 001 — media reads served unauthenticated by default | `0f277e3e2` |
Helm `requireMediaGetAuth` defaults to `true` + rendered-chart test
pinning the default |
| 006 — removed workflow owners retain webhook-exfiltration authority |
`4749bd56c` | Fail-closed per-fire authority gate (current owner/admin
membership) on **all four** trigger doors (on_event, scheduler
pre-claim, manual trigger, webhook — masked as generic 404), save-time
gate for `call_webhook` defs, durable disable-on-removal wired to kinds
9001 + 9022 |
| 005 — git Smart-HTTP reads ignore channel membership | `e648f2dba` +
`0158ae542` | `authorize_git_read`: caller's **current active
membership** in the repo's bound channel, checked before any
hydration/subprocess on all three read doors (`info_refs` for both
services + `upload_pack` POST). Uniform generic 404 denials (no
membership probing), no repo-owner bypass, first-`buzz-channel`-tag
binding semantics fail closed on ambiguous duplicates (mutation-verified
test). Resolution follows the live kind:30617 announcement, so
deleted/replaced announcements deny immediately. The committed
`e2e-git-perms.sh` guest scenario previously asserted the vulnerability
— now asserts denial. |

## Behavior changes to be aware of

1. **Unbound repos fail closed for git reads.** `buzz repos create`
emits no `buzz-channel` tag, so CLI-created repos without a binding are
unreadable via git HTTP. Correct per finding 005's fail-closed posture;
a follow-up could bind CLI-created repos at creation time.
2. **006 is conservative:** a workflow disabled on owner removal does
not auto-re-enable if the owner is re-added — explicit re-enable
required.
3. Merge conflict resolution in `fe65c07c3`: kept main's
`@radix-ui/react-dismissable-layer` 1.1.19 bump alongside the linkify-it
security override (`pnpm-workspace.yaml` + lockfile).

## Verification at the merge head `fe65c07c3` (same shell)

- buzz-relay `--lib`: 761 passed / 1 failed — the lone red is the known
pre-existing `mesh_demo::demo_join_forwarded_arm_round_trips_echo` 504
flake, present on main
- SEC-005 module incl. PG behavioral matrix: 8/8 (removed-member,
never-member, owner-no-bypass, deleted-30617, malformed/ambiguous
binding, owner-mismatch all denied)
- buzz-workflow 153/0, buzz-db 84/0; `clippy --all-targets -D warnings`
+ `fmt --check` clean
- Desktop JS 3637/3637, tsc clean, biome clean,
file-size/px-text/pubkey-truncation gates clean
- `helm lint` + `helm unittest` (40/40) on `deploy/charts/buzz`
- All five pre-push hooks green (desktop-check, desktop-test,
rust-tests, desktop-tauri-test, branch-skew)

Prior review evidence at `0158ae542` (pre-merge): Max's independent
exact-head approval + clean-room live regression pass
(`WORK_LOGS/2026-07-27_SECURITY_HIGH_LIVE_TEST.md` in his workspace).
Max will re-run the deep local pass at this post-merge head before
merge.

---------

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
2026-07-27 14:18:24 -04:00
Will PflegerandGitHub a041e2d21e Revert "fix(cli,relay): resolve agents by verified owner" (#3168)
Reverts block/buzz#2615
2026-07-27 14:10:16 -04:00
d98da7389e feat(desktop): redesign agent runtime settings (#3093)
**Category:** improvement
**User Impact:** Users can understand, install, authenticate, and manage
agent runtimes from one progressively disclosed Agents settings
experience.

**Problem:** Runtime health and custom harness management were split
across overlapping settings surfaces, exposing low-level configuration
too early while leaving setup and authentication states hard to
understand. **Solution:** Consolidate those operations into one stable
runtime list and an Add runtimes catalog, with task-oriented state
labels, direct setup actions, and custom configuration contained in a
dedicated form.

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

**desktop/playwright.config.ts**
Registers the visual coverage needed for the redesigned runtime catalog.

**desktop/public/harness-logos/CREDITS.md**
Documents bundled runtime-mark provenance and the decision not to ship
the withdrawn OpenAI mark.

**desktop/public/runtime-icons/codex.png**
Removes the obsolete Codex bitmap in favor of the neutral fallback.

**desktop/public/runtime-icons/goose.svg**
Removes the old Goose asset now replaced by the theme-adaptive mark.

**desktop/src-tauri/src/managed_agents/discovery.rs**
Aligns runtime discovery guidance with the new task-oriented setup
language.

**desktop/src-tauri/src/managed_agents/discovery/runtime_metadata.rs**
Updates runtime metadata used by the redesigned settings states.

**desktop/src/features/agents/ui/runtimeAvailabilityWarning.test.mjs**
Updates availability-warning expectations for the revised runtime
guidance.

**desktop/src/features/onboarding/assets/harness-logos/chatgpt.png**
Removes the redundant bitmap from the unified runtime icon pipeline.

**desktop/src/features/onboarding/assets/harness-logos/goose.png**
Removes the redundant Goose bitmap.

**desktop/src/features/onboarding/ui/HarnessMarks.tsx**
Adds theme-adaptive bundled runtime marks with safe fallbacks.

**desktop/src/features/onboarding/ui/RuntimeIcon.tsx**
Centralizes runtime logo rendering so settings and catalog rows cannot
drift.

**desktop/src/features/onboarding/ui/SetupStep.tsx**
Aligns onboarding runtime setup copy with the settings terminology.

**desktop/src/features/onboarding/ui/presetLogos.test.mjs**
Guards bundled-logo behavior and prevents the withdrawn Codex mark from
returning.

**desktop/src/features/settings/ui/CustomHarnessForm.tsx**
Reworks custom runtime creation and editing into a clear, dedicated
catalog form.

**desktop/src/features/settings/ui/HarnessCatalogDialog.tsx**
Introduces the Add runtimes master-detail catalog, grouped setup states,
loading treatment, and pinned actions.

**desktop/src/features/settings/ui/HarnessManagementCard.tsx**
Removes the superseded standalone custom-harness management surface.

**desktop/src/features/settings/ui/HarnessRow.tsx**
Provides stable operational runtime rows with install, update,
authentication, edit, and delete behavior.

**desktop/src/features/settings/ui/HarnessesSettingsPanel.tsx**
Consolidates runtime health and custom management into one Agents
settings panel.

**desktop/src/features/settings/ui/SettingsPanels.tsx**
Wires the consolidated panel into Agents settings.

**desktop/src/features/settings/ui/harnessCatalogCopy.ts**
Adds restrained, source-annotated runtime descriptions and setup
guidance.

**desktop/src/features/settings/ui/harnessCatalogLogic.test.mjs**
Covers grouping, state labels, stable row order, actions, and adapter
warnings.

**desktop/src/features/settings/ui/harnessCatalogLogic.ts**
Centralizes catalog grouping, actions, status labels, and runtime-safe
warning copy.

**desktop/src/features/settings/ui/harnessGalleryLogic.test.mjs**
Removes obsolete gallery-only tests after consolidation.

**desktop/src/features/settings/ui/harnessGalleryLogic.ts**
Retains only the shared custom-runtime safety logic needed by the new
surface.

**desktop/src/shared/ui/config-nudge-attachment.tsx**
Points configuration nudges to Agent runtimes with matching terminology.

**desktop/src/testing/e2eBridge.ts**
Adds deterministic runtime states for authentication and catalog E2E
coverage.

**desktop/tests/e2e/doctor-states.spec.ts**
Verifies ready, setup-required, node-gated, authentication, loading, and
error contracts.

**desktop/tests/e2e/harness-catalog-screenshots.spec.ts**
Captures whole-pane visual states for the runtime catalog experience.

**desktop/tests/e2e/harness-management.spec.ts**
Exercises catalog actions and custom runtime create, edit,
authentication, and deletion flows.

**desktop/tests/e2e/onboarding-agent-defaults.spec.ts**
Updates default-runtime expectations for the consolidated experience.

**desktop/tests/e2e/profile.spec.ts**
Aligns profile navigation assertions with the new settings surface.

</details>

## Reproduction steps

1. Open Settings → Agents and inspect Agent runtimes; ready, signed-out,
installable, and setup-required runtimes should have stable rows and
explicit actions.
2. Open Add runtimes and browse the Setup and Installed groups; select
entries to see sourced guidance and a pinned Install or Setup guide
action.
3. Select Custom harness, create a runtime, then edit and delete it;
verify required-field gating and the blast-radius confirmation.
4. Exercise a signed-out runtime and connect it; the row should move
from Sign-in needed to Ready without reordering.
5. Resize the window and switch themes to verify responsive layout and
adaptive bundled marks.

## Screenshots

| Mixed runtime states | Goose not installed | Sign-in needed |
|---|---|---|
| <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/28705b39-1d91-440a-a954-fb2d7d8ee759"
/> | <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/e65ce8e1-8c85-4272-afb5-e6001b94d560"
/> | <img width="980" height="1000" alt="image"
src="https://github.com/user-attachments/assets/3987f355-960d-4744-8c26-b3746944a33c"
/> |

| Add runtimes catalog | Custom runtime | Setup guide |
|---|---|---|
| <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/a2f32c8a-4dff-4929-af96-b07346933335"
/> | <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/b2e85a0c-3940-4875-a138-4035423c9de7"
/> | <img width="896" height="672" alt="image"
src="https://github.com/user-attachments/assets/5a690c1c-368e-4ee3-98b9-ffefdc3cf804"
/> |

Full 13-state screenshot matrix and review evidence:
https://buzz.block.builderlab.xyz/channels/c5af9e3e-4317-4853-b3e3-ed9c15bc511d?event=ec88f6e472f2b08d6318ae76fd754fd4f218385f67cfe59b73034c4bc34c9252

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
2026-07-27 11:01:42 -07:00
Will PflegerandGitHub 3faea98891 fix(mobile): match markContextRead signature in activity test fake (#3158)
Main's Mobile Analyze job fails with `invalid_override` on
`_FakeReadStateNotifier.markContextRead`.

The fake was added in #2889 against the then-current
`ReadStateNotifier.markContextRead(String, int)`. The forced-unread work
added an optional named `clearForcedMessages` param to the real method.
Both PRs were green independently; the semantic conflict only surfaced
once both were on main.

The fake tracks read state only, so it accepts the flag and ignores it.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 13:56:58 -04:00
8995316844 fix(desktop): use forward slashes for git credential.helper on Windows (#3023)
## Problem

On Windows, Projects **Remote** view shows an empty file tree and
Sync/Clone fails with a mangled credential-helper path, for example:

```
C:\Users\<user>\AppData\Local\Buzz\git-credential-nostr.exe get: line 1:
C:Users<user>AppDataLocalBuzzgit-credential-nostr.exe: command not found
fatal: could not read Username for 'https://<relay>/git/...': terminal prompts disabled
```

Buzz injects an absolute path into `credential.helper` via
`Path::display()`. On Windows that yields backslashes. Git for Windows
runs credential helpers through MinGW bash, which treats `\` as escapes
and destroys the path, so NIP-98 auth never runs and the blobless temp
clone behind Remote view fails.

macOS/Linux are unaffected (paths already use `/`).

This is unrelated to shipping a stub helper - the bundled
`git-credential-nostr.exe` is a real binary. User `~/.gitconfig`
workarounds also cannot help here because Projects git sets
`GIT_CONFIG_GLOBAL=/dev/null` and injects its own helper.

Closes #3025

## Fix

Normalize the helper path to forward slashes before writing
`GIT_CONFIG_VALUE_*`:

- `desktop/src-tauri/src/commands/project_git_exec.rs` (Projects Remote
/ Sync)
- `desktop/src-tauri/src/managed_agents/runtime.rs` (agent spawn git
auth)

Forward slashes are accepted by Git on every platform; on macOS/Linux
the replace is a no-op. No `cfg(windows)`, packaging, or libgit2
changes.

## How to reproduce (before)

1. Install Buzz on Windows with Git for Windows
2. Connect to a relay that has a repository with at least one pushed
branch
3. Open **Projects** -> select the repo -> **Remote**
4. Observe empty tree; Sync/Clone shows the mangled-path / `command not
found` error above

## Test plan

- [x] Unit: `cargo test --manifest-path desktop/src-tauri/Cargo.toml
credential_helper_config_value` (formatter covered on all platforms;
no-op for Unix-style paths)
- [x] Local Windows NSIS build + install of this branch
- [x] Projects -> Remote / Sync against a Buzz relay repo succeeds on
Windows after the fix

---------

Signed-off-by: Bjorn de Jong <bcrdejong@users.noreply.github.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Bjorn de Jong <bcrdejong@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-27 13:56:34 -04:00
32ead93101 fix(mobile): tapping threaded message in Inbox navigates to top level of channel (#2103)
## The bug

When you tap an item in the Activity inbox that refers to a message
inside a thread (for example, someone replied to you or mentioned you in
a thread reply), the app opened the channel at its top level. It did not
open the thread, and it did not show you the message the notification
was about. You had to hunt for the reply manually.

## The fix

Activity items now keep track of two things: the root message of the
thread and the specific message that triggered the notification. Tapping
the item now:

1. Opens the thread detail view for that thread (instead of the
channel's top level).
2. Scrolls to the specific message that triggered the notification.
3. Briefly highlights that message so it is easy to spot.

This works for both direct replies and replies nested deeper in a
thread, and it fetches the thread from the relay if it is not already
loaded (for example, right after app launch).

## Testing

- Flutter analyzer
- 64 focused mobile tests covering direct and nested thread markers plus
Activity navigation
- Full pre-push suite (mobile, desktop, Rust, and Tauri tests)

## Manual verification

Open Activity, tap a mention for a reply inside a thread, and confirm
Buzz opens that thread, scrolls to the reply, and highlights it.

---------

Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1tquskdu6yc4h8l7xxtceculxw600grekeq0xg2ukqfrwl7vrzg3quz3gmp <58390b379a262b73ffc632f19c73e6769ef40f36c81e642b960246eff9831222@buzz.block.builderlab.xyz>
Co-authored-by: npub15w828kxsxu2684ynste0uah2jwkgatd99flt7ds4523hzm8ju6cshdr8hh <a38ea3d8d03715a3d49382f2fe76ea93ac8eada52a7ebf3615a2a3716cf2e6b1@buzz.block.builderlab.xyz>
2026-07-27 10:50:55 -07:00