COS-710: upgrade MAC Workspace to Buzz v0.5.0 - #9
Merged
Conversation
## Summary - align the multi-community rail with the content surface and balance its visible 10px side gutters - center Mobile pairing, start sessions on demand, and keep retry states inside the QR area - reveal the QR code and copy action with 250ms motion and use the standard loading spinner The rail was centered within its own box, but the adjacent sidebar added another 11px to the visible right gap. Mobile pairing also started before user intent, which could leave an idle session waiting for EOSE. ## Validation - `pnpm -C desktop build:e2e` - `pnpm -C desktop test` — 3,516 passed - `pnpm -C desktop exec playwright test tests/e2e/community-rail.spec.ts --project=smoke` — 19 passed - `pnpm -C desktop exec playwright test tests/e2e/mobile-pairing-qr.spec.ts --project=smoke` — 1 passed `pnpm -C desktop check` is currently blocked by the existing `src-tauri/src/managed_agents/runtime.rs` file-size baseline (2,220 lines; limit 2,216).
**Category:** improvement **User Impact:** Developers can identify which worktree produced a mobile debug app, keep a bounded set of worktree builds installed side by side, and preserve each worktree app's login and local state while switching branches. **Problem:** Mobile debug builds from every checkout currently appear as the same “Buzz” app and share one application identity, so the running source is ambiguous and one worktree build replaces another. A branch-keyed identity would avoid replacement but create stale installs and fresh app state on every branch switch. **Solution:** Give each linked worktree a stable Debug-only application identity derived from its sanitized directory name. Show the sanitized branch name (or short commit SHA when detached) in the display label, persist generated native overrides for direct IDE builds, and leave Release/Profile identities unchanged. Worktree defaults remain lower precedence than a developer's iOS `AppOverrides.xcconfig`. `just mobile-clean` provides a safe cleanup path for suffixed worktree installs while preserving production Buzz. <details> <summary>File changes</summary> **.github/workflows/ci.yml** Runs the expanded worktree override contract when relevant mobile or native configuration changes. **AGENTS.md** Documents worktree-aware mobile development and cleanup for contributors and agents. **Justfile** Generates overrides before mobile development and Android debug builds, and exposes `just mobile-clean`. **mobile/README.md** Explains stable per-worktree identities, branch/SHA labels, direct IDE usage, cleanup, and Release/Profile guarantees. **mobile/android/.gitignore** Ignores generated worktree properties. **mobile/android/app/build.gradle.kts** Loads and validates generated properties, then applies the application ID suffix and display label to Android Debug only. **mobile/android/app/src/main/AndroidManifest.xml** Resolves the Android app label through an overridable string resource. **mobile/ios/.gitignore** Ignores generated iOS worktree settings. **mobile/ios/Flutter/Debug.xcconfig** Loads generated worktree defaults before developer `AppOverrides`, so personal signing overrides retain precedence. **mobile/ios/Flutter/Release.xcconfig** Pins the production display name and bundle identifier for Release/Profile builds. **mobile/ios/Runner/Info.plist** Resolves the visible iOS app name from build settings. **scripts/mobile-worktree-overrides.sh** Detects linked worktrees, derives a stable directory-keyed identity, sanitizes branch/SHA display context, writes native Debug overrides, and removes stale overrides in the main checkout. **scripts/mobile-worktree-clean.sh** Lists or removes suffixed Buzz worktree installs from booted iOS simulators and connected Android emulators without matching production IDs; supports `--dry-run`. **scripts/test-mobile-worktree-overrides.sh** Covers worktree detection, branch-switch identity stability, detached HEAD fallback, special-character sanitization, iOS override precedence, brace-aware Release/Profile purity, cleanup safety, ignores, and command integration. </details> ## Reproduction steps 1. From a linked worktree, activate the repository toolchain and run `just mobile-dev`. 2. Inspect the running app: its label should be `Buzz (<sanitized-branch>)`, while its application ID suffix is derived from the worktree directory. 3. Switch branches in the same worktree, rerun the override script, and confirm the application ID remains stable while the display label updates. In detached HEAD, confirm the label uses a short SHA. 4. Build Debug from a second worktree and confirm both apps remain installed side by side with independent state. 5. Build from Xcode after setting `AppOverrides.xcconfig` and confirm developer overrides still win over generated worktree defaults. 6. Run `just mobile-clean --dry-run`, then `just mobile-clean`, and confirm suffixed worktree installs are targeted while the production app is preserved. 7. Build Release/Profile and confirm the production name and application identity remain unchanged. 8. Run `scripts/test-mobile-worktree-overrides.sh`, `just mobile-check`, `just mobile-test`, and `just mobile-build-android`. ## Screenshots / demos | iOS — labeled app switcher | iOS — side-by-side installs | | --- | --- | | <img width="360" alt="Buzz worktree label in the iOS app switcher" src="https://github.com/user-attachments/assets/4bcae067-7ce5-4333-bb11-2803c4107663" /> | <img width="360" alt="Buzz production and worktree debug apps installed side by side on iOS" src="https://github.com/user-attachments/assets/08a107b5-fdf2-463a-8a4c-81d41d7bf5e7" /> | | Android — side-by-side installs | Android — labeled app switcher | | --- | --- | | <img width="360" alt="Buzz production and worktree debug apps installed side by side on Android" src="https://github.com/user-attachments/assets/4f5841a1-adae-42da-ae84-47c09ec85fb9" /> | <img width="360" alt="Buzz worktree label in the Android app switcher" src="https://github.com/user-attachments/assets/0546ff51-efcc-4cb6-a4bd-2a3af26cd60f" /> | --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
## What One-line default change: `DEFAULT_AGENT_PARALLELISM` 24 → 10 (`desktop/src-tauri/src/managed_agents/types.rs`), requested by Tyler in [buzz-generic-acp-harnesses](buzz://message?channel=d6d84fdc-47fe-4e41-acf5-a08a4a013e6b&id=a047ead3a34ba804c376bf79f29e355d304162a88c7b2a7121e092547c572022). ## Why Each unit of parallelism is a **full agent subprocess** per agent × community pair, spawned eagerly and serially at create/start before the harness connects to the relay: - At 24, a desktop with a few dozen agent×community pairs carries >1,000 potential idle subprocesses. - Slow-starting harnesses (OpenClaw measured at 4–10s per worker) take minutes to create, and messages sent during that window can be permanently missed (the startup watermark is captured only after eager pool init). - Parallelism is per-channel concurrency — a channel never uses more than one worker — so 10 still covers concurrent-channel fan-out for typical agents. Context: verified RCA + live 1-worker/3-mention experiment in the same thread. Demand-driven pool growth (seed 1, grow on contention up to the cap) is agreed as a follow-up; this is the interim risk reduction. ## Scope / blast radius - Existing agent records keep their persisted `parallelism` value — only new records and unset defaults change. - Per-agent override unchanged (1..=32 via agent editor / `BUZZ_ACP_AGENTS`). - Readers of the constant (`team_snapshot`, `personas/snapshot/import`, `commands/agents` mint path) all take the new default transitively; no other code pins 24. - `activeAgentTurnsStore.test.mjs` comments updated so the 24-turn eviction case is framed against the harness cap (32) instead of the default — coverage unchanged, no assertion touched. ## Verification At 63bb0c1, same shell: desktop Rust `cargo test --lib` 1693/0 (14 ignored) · JS suite 3523/3523 · tsc clean · biome clean on touched file · cargo fmt 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>
## Summary - Redesign community switching with relay-provided avatars, selected-state checks, edit-to-remove controls, and confirmation. - Bring desktop-inspired pairing onboarding to mobile with QR scanning as the primary action, an expandable pairing-code flow, and a tappable flapping bee. - Polish Search and Activity headers, filter chips, typography, keyboard stability, and Android adaptive-icon sizing. ## Validation - `just mobile-check` - `just mobile-test` — 661 passed, 1 skipped - Pixel 10 snapshot review
block#3035) Agents in a team receive two `Team Instructions` blocks per turn, and the observer feed renders two Team Instructions cards for them. There are two producers. `with_team()` in `crates/buzz-acp/src/pool.rs` appends the LIVE `[Team Instructions]` block from the runtime `TeamRecord` — that one is correct. The second is baked into the stored `system_prompt` itself: records written before the runtime framing landed were composed by the now-removed `compose_prompt()` in `buzz-persona`, which appended `"\n\n---\n# Team Instructions\n"` plus a frozen copy of the team instructions. So an affected agent is fed a stale roster ahead of the current one, and the transcript parser — correctly — reports both. Fixing this in `parseSystemPromptSections` would hide the symptom while the agent kept receiving the stale bytes, so the suffix is removed at rest by a boot migration. `strip_baked_team_instructions` splits each stored `system_prompt` at the LAST occurrence of the exact delimiter and keeps the text before it. Last-occurrence matches the parser's own `lastIndexOf` guard: a persona body may quote a delimiter-shaped passage, and only the final one is the producer boundary. The match is byte-exact — a bare `---`, a `# Team Instructions` heading at a different position, or a single preceding newline are author content and are left alone. It applies to every record regardless of `team_id` / `persona_id` / `pubkey`: the key-less definition records carry the suffix exactly as the instances minted from them do. A prompt that was nothing but the suffix becomes `None`, not `Some("")`, matching the absent-prompt convention in `AgentDefinition::into_agent_record`. Stripping a definition's prompt changes its `persona_content_hash`, which is the drift basis behind the Agents-menu "out of date" badge. Left alone, every linked instance would light up stale for a change the user never made. The migration therefore advances the pin of instances whose `persona_source_version` still equals the definition's PRE-strip hash — the same conditional `refresh_builtin_agent_avatars` already uses. An instance that had genuinely drifted keeps its stale pin, and its badge. The migration runs after `fold_personas_into_agent_store` so definitions lifted out of the legacy `personas.json` are cleaned in the same boot, and before `backfill_standalone_agents` so a manufactured definition never snapshots a suffix about to be removed. It writes only when at least one record changed, so a second boot is a true no-op, and takes a create-if-absent backup at `managed-agents.json.pre-team-suffix-strip.bak` following the `pre-backfill.bak` contract — a re-run after a partial failure cannot replace the pristine backup with a half-migrated snapshot. An unparseable store errors without writing and without taking a backup, leaving the file for manual recovery. Pass 5's legacy branch in `agentSessionTranscriptHelpers.ts` is deliberately untouched: un-migrated installs and snapshot imports still need it. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Uses MeshLLM built-in `mesh` collective intelligence / Mixture of Agents when Buzz Auto sees two or more distinct physical models. With zero or one model, Auto remains ordinary `auto`. This is an alternative way to improve tool responses, accuracy, and resistance to hallucination when a high-latency distributed mesh contains diverse models. Models and members may come and go: collective routing enables only after stable capacity, drops on confirmed contraction, and can recover later. Mesh-specific failures retry once through ordinary Auto. This update also pins MeshLLM to a v0.73.1-compatible backport of [MeshLLM block#1074](Mesh-LLM/mesh-llm#1074), so client-only Buzz nodes cannot enter model election or download a remote provider model. Buzz preserves the selected local sharing model and switches an existing client to sharing across a controlled app restart, retaining one runtime and one `:9337` / `:3131` pair per machine. Validation: - Full local `just ci` passes on the cleaned branch. - MeshLLM host-runtime suite: 1,568 passed, 0 failed; strict Clippy passes. - Buzz desktop Tauri suite with `mesh-llm`: 1,721 passed, 0 failed; strict feature Clippy passes. - Playwright covers client-to-share using the saved local model and no destructive stop. - Packaged two-machine testing proved single-model routing, dual-model collective routing, tool-markup fallback, and runtime reuse. - Packaged client-only recheck routed a real Mini Buzz turn through M5 while Mini stayed `is_client=true`, `is_host=false`, hosted no models, and created no Gemma cache. Builds on the recovery work merged in block#2823; this PR does not duplicate it. --------- Signed-off-by: Michael Neale <michael.neale@gmail.com> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
… settings gallery (block#2773) ## What Implements a "bring your own harness" (BYOH) generic ACP mechanism — replacing per-harness backend code with a data-driven 3-tier system: - **Tier 1 (compiled-in builtins):** goose, claude, codex, buzz-agent — unchanged behavior - **Tier 2 (bundled presets):** cursor, omp, grok, opencode, kimi, amp, hermes, openclaw, and any future additions — defined in `PRESET_HARNESSES`, no code duplication, icons stay TerminalSquare/bundled-asset-only - **Tier 3 (user-defined custom):** JSON definitions saved to `custom_harnesses/` under app data; managed via Settings → Agents UI ## Changes ### Core data model - `HarnessDefinition` — id, label, command, args, env, install URL/hint - `PRESET_HARNESSES` static table — single source of truth for all presets; `preset_harness_ids()` derives reserved IDs (D-11: no hand-maintained copy) - `source: "builtin" | "preset" | "custom"` tagging on every catalog entry ### Persistence (B-4, B-6) - `save_custom_harness_to_dir(dir, definition, rename_old_id)` — backup-swap atomic write (backs up target → .bak, commits temp → target, restores .bak on failure, removes .bak on success); safe on Windows where `fs::rename` over an existing file is "access denied" - `save_and_warm` / `delete_and_warm` — hold `PERSIST_MUTEX` for the write + registry-warm pair, eliminating the lost-update race (B-6) where two concurrent saves could interleave their warm calls and leave a stale registry snapshot - Validate-before-mutate: both IDs and env validated before any filesystem mutation ### Env validation boundary (B-3) - `validate_harness_definition_pub` calls `validate_user_env_keys` on definition env at save AND load - Rejects malformed keys (BUZZ_AUTH_TAG=x forgery shape), reserved keys (BUZZ_MANAGED_AGENT etc.), NUL bytes, oversized values ### TypeScript boundary (B-2 / Thufir CRITICAL) - `RawAcpRuntimeCatalogEntry` now declares `definition_env?: Record<string,string>` and `source: "builtin" | "preset" | "custom"` - `fromRawAcpRuntimeCatalogEntry` maps `definition_env → definitionEnv` (camelCase); absent field defaults to `{}` - Edit form reads `entry.definitionEnv` — env no longer erased on save-then-edit cycle ### Unified descriptor (Phase A / Thufir F4) - `EffectiveHarnessDescriptor { command, args, env }` in `readiness.rs` - `resolve_effective_harness_descriptor()` — single resolver used by spawn, spawn_hash, summary, get_agent_models (both saved and unsaved), and readiness - No competing arg-resolution forms ### Other fixes - B-5: stop freezing `runtime.defaultArgs` into `record.agent_args` on normal create paths - B-7: readiness exec-check — `MissingBinary` variant for custom commands not found on PATH - B-8: onboarding transition — `setTimeout(0)` removed, parent-owned route intent via `navigateAfterComplete` prop - C-9: collector-discriminating sweep tests with injectable filters - C-10: `HarnessManagementCard` uses `harnessGalleryLogic` helpers (killed duplicate filter/sort) - D-11: `BUILTIN_IDS` derived from `PRESET_HARNESSES` (no hand-maintained copy) - D-12: `mobile/pubspec.lock` churn reverted - D-13: false ownership fast-path comment fixed - D-14: URL scheme validation for `installInstructionsUrl` - D-15: OpenClaw Gateway env-locus README line ### Tests added **B-4 persistence (6 tests):** `save_to_dir_create_writes_file_and_loads_back`, `save_to_dir_same_id_edit_replaces_content`, `save_to_dir_backup_is_cleaned_up_after_same_id_edit`, `save_to_dir_rename_removes_old_file_and_creates_new`, `save_to_dir_rename_nonexistent_old_id_is_non_fatal`, `save_to_dir_roundtrip_with_env_preserves_values` **B-3 env validation (6 tests):** `validate_rejects_malformed_key_with_equals_sign`, `validate_rejects_reserved_key_buzz_managed_agent`, `validate_rejects_reserved_key_case_insensitive`, `validate_rejects_nul_byte_in_value`, `validate_rejects_value_over_per_value_size_limit`, `validate_accepts_well_formed_env` **B-2 API boundary (4 TS tests in tauri.test.mjs):** `fromRawAcpRuntimeCatalogEntry maps definition_env to definitionEnv`, `defaults definitionEnv to {} when absent`, `preserves source preset`, `env round-trips through edit payload shape` ## Preset catalog | ID | Label | Command | |----|-------|---------| | `cursor` | Cursor | `cursor-agent acp` | | `omp` | Oh My Pi | `omp acp` | | `grok` | Grok Build | `grok agent --always-approve stdio` | | `opencode` | OpenCode | `opencode acp` | | `kimi` | Kimi Code | `kimi acp` | | `amp` | Amp | `amp-acp` | | `hermes` | Hermes Agent | `hermes-acp` | | `openclaw` | OpenClaw | `openclaw acp` | ## Review-fix pass (2026-07-26, Eva) Fixes from the three-way review (Wren / Dawn / Eva) in the buzz-generic-acp-harnesses thread, pushed as new commits (no rewrite): 1. **installHint edit round-trip** — form seeding extracted to `formValuesFromCatalogEntry` (single source of truth), input rendered, full-definition lossless round-trip regression. 2. **Dangling-delete coherence** — delete allowed; confirm counts referencing agents (direct pin + persona-inherited); summary rows render `harness (deleted): <id>`; spawn errors become actionable sentences (`user_facing_harness_error`); composed delete→summary→start test. 3. **Comma-in-args** — rejected at `validate_harness_definition` (shared by save AND disk load), mirrored inline in the form. 4. **Registry publish race** — collision/dup filtering moved into `load_custom_harnesses` (both loaders inherit shadowing rules); discovery publishes by re-reading the dir under `persist_mutex` (lock scoped to publish only); deterministic interleaving regressions for save-during-discovery and delete-during-discovery. 5. **Mechanical** — discarded `belongs_to_us` sweep arg deleted, `load_global_agent_config` hoisted out of the per-record summary loop, duplicated doc paragraph + stray SAFETY comment removed. 6. **PGID test de-flaked** — leader kept alive through the assertion. Known follow-up (filed in review, not blocking): file-size split-outs queued in `check-file-sizes.mjs` entries. ## Gate table — head `bf53f1d60` | Gate | Result | |------|--------| | `cargo test --lib` (desktop/src-tauri) | **1701 passed**, 0 failed, 14 ignored | | desktop JS suite (`pnpm test`) | **3605 passed**, 0 failed | | `tsc --noEmit` | clean | | `biome check` + file-size/px/pubkey checks | clean | | `cargo clippy --lib -- -D warnings` | clean | | `cargo fmt --check` | clean | PR head: `bf53f1d60e3cbd07392e1287b83bb37ba90d0d33` — includes merge of origin/main (`c2a4ee711`, conflicts in agent_models composed with block#2890's live Databricks discovery) --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com> Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co> Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
…ock#3057) This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@tanstack/react-virtual](https://tanstack.com/virtual) ([source](https://redirect.github.com/TanStack/virtual/tree/HEAD/packages/react-virtual)) | [`3.14.6` → `3.14.8`](https://renovatebot.com/diffs/npm/@tanstack%2freact-virtual/3.14.6/3.14.8) |  |  | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/1) for more information. --- ### Release Notes <details> <summary>TanStack/virtual (@​tanstack/react-virtual)</summary> ### [`v3.14.8`](https://redirect.github.com/TanStack/virtual/blob/HEAD/packages/react-virtual/CHANGELOG.md#3148) [Compare Source](https://redirect.github.com/TanStack/virtual/compare/@tanstack/react-virtual@3.14.7...@tanstack/react-virtual@3.14.8) ##### Patch Changes - [#​1237](https://redirect.github.com/TanStack/virtual/pull/1237) [`aa536e7`](https://redirect.github.com/TanStack/virtual/commit/aa536e7746a88d9f55ca8a4b50d2f548a888fea6) - Fix a gap at the top of the list after an end-anchored prepend in `directDomUpdates` mode. The prepend grows the total size and bumps `scrollOffset` to the new bottom in the same pass, but the size container's height was written *after* `_willUpdate` synced the scroll position — so the browser clamped the `scrollTop` write to the stale (shorter) `scrollHeight`, leaving whitespace at the top until the next scroll. The container is now grown before the scroll sync. Only affected `directDomUpdates` mode (React-rendered sizers receive their height during render). - Updated dependencies \[[`7ae32b5`](https://redirect.github.com/TanStack/virtual/commit/7ae32b55887fd044a48c788546cd940279b338e0)]: - [@​tanstack/virtual-core](https://redirect.github.com/tanstack/virtual-core)@​3.17.6 ### [`v3.14.7`](https://redirect.github.com/TanStack/virtual/blob/HEAD/packages/react-virtual/CHANGELOG.md#3147) [Compare Source](https://redirect.github.com/TanStack/virtual/compare/@tanstack/react-virtual@3.14.6...@tanstack/react-virtual@3.14.7) ##### Patch Changes - Updated dependencies \[[`1e3b908`](https://redirect.github.com/TanStack/virtual/commit/1e3b908705e04e45be2615f2277580cb09f5cdef), [`7dcfc07`](https://redirect.github.com/TanStack/virtual/commit/7dcfc07b877479697124157d3124c09537b87a75)]: - [@​tanstack/virtual-core](https://redirect.github.com/tanstack/virtual-core)@​3.17.5 </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>
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@radix-ui/react-alert-dialog](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/alert-dialog)) | [`1.1.19` → `1.1.23`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-alert-dialog/1.1.19/1.1.23) |  |  | | [@radix-ui/react-checkbox](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/checkbox)) | [`1.3.7` → `1.3.11`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-checkbox/1.3.7/1.3.11) |  |  | | [@radix-ui/react-dialog](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog)) | [`1.1.19` → `1.1.23`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-dialog/1.1.19/1.1.23) |  |  | | [@radix-ui/react-dismissable-layer](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/dismissable-layer)) | [`1.1.15` → `1.1.19`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-dismissable-layer/1.1.15/1.1.19) |  |  | | [@radix-ui/react-dropdown-menu](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu)) | [`2.1.20` → `2.1.24`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-dropdown-menu/2.1.20/2.1.24) |  |  | | [@radix-ui/react-focus-scope](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/focus-scope)) | [`1.1.12` → `1.1.16`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-focus-scope/1.1.12/1.1.16) |  |  | | [@radix-ui/react-popover](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/popover)) | [`1.1.19` → `1.1.23`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-popover/1.1.19/1.1.23) |  |  | | [@radix-ui/react-separator](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/separator)) | [`1.1.11` → `1.1.15`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-separator/1.1.11/1.1.15) |  |  | | [@radix-ui/react-slot](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/slot)) | [`1.3.0` → `1.3.3`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-slot/1.3.0/1.3.3) |  |  | | [@radix-ui/react-tabs](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/tabs)) | [`1.1.17` → `1.1.21`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-tabs/1.1.17/1.1.21) |  |  | | [@radix-ui/react-toggle](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/toggle)) | [`1.1.14` → `1.1.18`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-toggle/1.1.14/1.1.18) |  |  | | [@radix-ui/react-tooltip](https://radix-ui.com/primitives) ([source](https://redirect.github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip)) | [`1.2.12` → `1.2.16`](https://renovatebot.com/diffs/npm/@radix-ui%2freact-tooltip/1.2.12/1.2.16) |  |  | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/1) for more information. --- ### Release Notes <details> <summary>radix-ui/primitives (@​radix-ui/react-alert-dialog)</summary> ### [`v1.1.23`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/alert-dialog/CHANGELOG.md#1123) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-context@1.2.2`, `@radix-ui/react-dialog@1.1.23`, `@radix-ui/react-primitive@2.1.10` ### [`v1.1.22`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/alert-dialog/CHANGELOG.md#1122) - Updated dependencies: `@radix-ui/react-dialog@1.1.22`, `@radix-ui/react-primitive@2.1.9` ### [`v1.1.21`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/alert-dialog/CHANGELOG.md#1121) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-dialog@1.1.21`, `@radix-ui/react-primitive@2.1.8` ### [`v1.1.20`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/alert-dialog/CHANGELOG.md#1120) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-dialog@1.1.20`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-primitive@2.1.7` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-checkbox)</summary> ### [`v1.3.11`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/checkbox/CHANGELOG.md#1311) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-context@1.2.2`, `@radix-ui/react-presence@1.1.10`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-use-controllable-state@1.2.6`, `@radix-ui/react-use-size@1.1.4` ### [`v1.3.10`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/checkbox/CHANGELOG.md#1310) - Updated dependencies: `@radix-ui/react-primitive@2.1.9` ### [`v1.3.9`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/checkbox/CHANGELOG.md#139) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-presence@1.1.9`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-use-controllable-state@1.2.5`, `@radix-ui/react-use-size@1.1.3` ### [`v1.3.8`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/checkbox/CHANGELOG.md#138) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Fixed a bug where updating a `Checkbox`, `Switch`, or `RadioGroup` value programmatically (eg. a "select all" control) while inside a `<form>` would dispatch a `click` event from the hidden bubble input that propagated to ancestor `onClick` handlers. - Updated dependencies: `@radix-ui/react-presence@1.1.8`, `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-primitive@2.1.7`, `@radix-ui/react-use-size@1.1.2` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-dialog)</summary> ### [`v1.1.23`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dialog/CHANGELOG.md#1123) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-context@1.2.2`, `@radix-ui/react-dismissable-layer@1.1.19`, `@radix-ui/react-focus-guards@1.1.6`, `@radix-ui/react-focus-scope@1.1.16`, `@radix-ui/react-id@1.1.4`, `@radix-ui/react-portal@1.1.17`, `@radix-ui/react-presence@1.1.10`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-slot@1.3.3`, `@radix-ui/react-use-controllable-state@1.2.6`, `@radix-ui/react-use-layout-effect@1.1.4` ### [`v1.1.22`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dialog/CHANGELOG.md#1122) - Updated dependencies: `@radix-ui/react-slot@1.3.2`, `@radix-ui/react-primitive@2.1.9`, `@radix-ui/react-dismissable-layer@1.1.18`, `@radix-ui/react-focus-scope@1.1.15`, `@radix-ui/react-portal@1.1.16` ### [`v1.1.21`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dialog/CHANGELOG.md#1121) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-dismissable-layer@1.1.17`, `@radix-ui/react-focus-guards@1.1.5`, `@radix-ui/react-focus-scope@1.1.14`, `@radix-ui/react-id@1.1.3`, `@radix-ui/react-portal@1.1.15`, `@radix-ui/react-presence@1.1.9`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-slot@1.3.1`, `@radix-ui/react-use-controllable-state@1.2.5`, `@radix-ui/react-use-layout-effect@1.1.3` ### [`v1.1.20`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dialog/CHANGELOG.md#1120) - Fixed broken ARIA references in Dialogs where a title or description elements are not rendered. - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-dismissable-layer@1.1.16`, `@radix-ui/react-focus-scope@1.1.13`, `@radix-ui/react-portal@1.1.14`, `@radix-ui/react-presence@1.1.8`, `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-focus-guards@1.1.4`, `@radix-ui/react-id@1.1.2`, `@radix-ui/react-primitive@2.1.7`, `@radix-ui/react-slot@1.3.0`, `@radix-ui/react-use-layout-effect@1.1.2` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-dismissable-layer)</summary> ### [`v1.1.19`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dismissable-layer/CHANGELOG.md#1119) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-use-callback-ref@1.1.4`, `@radix-ui/react-use-effect-event@0.0.5` ### [`v1.1.18`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dismissable-layer/CHANGELOG.md#1118) - Updated dependencies: `@radix-ui/react-primitive@2.1.9` ### [`v1.1.17`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dismissable-layer/CHANGELOG.md#1117) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-use-callback-ref@1.1.3`, `@radix-ui/react-use-effect-event@0.0.4` ### [`v1.1.16`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dismissable-layer/CHANGELOG.md#1116) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-primitive@2.1.7`, `@radix-ui/react-use-callback-ref@1.1.2`, `@radix-ui/react-use-effect-event@0.0.3` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-dropdown-menu)</summary> ### [`v2.1.24`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dropdown-menu/CHANGELOG.md#2124) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-context@1.2.2`, `@radix-ui/react-id@1.1.4`, `@radix-ui/react-menu@2.1.24`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-use-controllable-state@1.2.6` ### [`v2.1.23`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dropdown-menu/CHANGELOG.md#2123) - Updated dependencies: `@radix-ui/react-menu@2.1.23`, `@radix-ui/react-primitive@2.1.9` ### [`v2.1.22`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dropdown-menu/CHANGELOG.md#2122) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-id@1.1.3`, `@radix-ui/react-menu@2.1.22`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-use-controllable-state@1.2.5` ### [`v2.1.21`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/dropdown-menu/CHANGELOG.md#2121) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-menu@2.1.21`, `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-id@1.1.2`, `@radix-ui/react-primitive@2.1.7` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-focus-scope)</summary> ### [`v1.1.16`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/focus-scope/CHANGELOG.md#1116) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-use-callback-ref@1.1.4` ### [`v1.1.15`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/focus-scope/CHANGELOG.md#1115) - Updated dependencies: `@radix-ui/react-primitive@2.1.9` ### [`v1.1.14`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/focus-scope/CHANGELOG.md#1114) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-use-callback-ref@1.1.3` ### [`v1.1.13`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/focus-scope/CHANGELOG.md#1113) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-primitive@2.1.7`, `@radix-ui/react-use-callback-ref@1.1.2` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-popover)</summary> ### [`v1.1.23`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/popover/CHANGELOG.md#1123) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-context@1.2.2`, `@radix-ui/react-dismissable-layer@1.1.19`, `@radix-ui/react-focus-guards@1.1.6`, `@radix-ui/react-focus-scope@1.1.16`, `@radix-ui/react-id@1.1.4`, `@radix-ui/react-popper@1.3.7`, `@radix-ui/react-portal@1.1.17`, `@radix-ui/react-presence@1.1.10`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-slot@1.3.3`, `@radix-ui/react-use-controllable-state@1.2.6` ### [`v1.1.22`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/popover/CHANGELOG.md#1122) - Updated dependencies: `@radix-ui/react-slot@1.3.2`, `@radix-ui/react-primitive@2.1.9`, `@radix-ui/react-dismissable-layer@1.1.18`, `@radix-ui/react-focus-scope@1.1.15`, `@radix-ui/react-popper@1.3.6`, `@radix-ui/react-portal@1.1.16` ### [`v1.1.21`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/popover/CHANGELOG.md#1121) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-dismissable-layer@1.1.17`, `@radix-ui/react-focus-guards@1.1.5`, `@radix-ui/react-focus-scope@1.1.14`, `@radix-ui/react-id@1.1.3`, `@radix-ui/react-popper@1.3.5`, `@radix-ui/react-portal@1.1.15`, `@radix-ui/react-presence@1.1.9`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-slot@1.3.1`, `@radix-ui/react-use-controllable-state@1.2.5`, `@radix-ui/react-use-layout-effect@1.1.3` ### [`v1.1.20`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/popover/CHANGELOG.md#1120) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-popper@1.3.4`, `@radix-ui/react-dismissable-layer@1.1.16`, `@radix-ui/react-focus-scope@1.1.13`, `@radix-ui/react-portal@1.1.14`, `@radix-ui/react-presence@1.1.8`, `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-focus-guards@1.1.4`, `@radix-ui/react-id@1.1.2`, `@radix-ui/react-primitive@2.1.7`, `@radix-ui/react-slot@1.3.0` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-separator)</summary> ### [`v1.1.15`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/separator/CHANGELOG.md#1115) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-primitive@2.1.10` ### [`v1.1.14`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/separator/CHANGELOG.md#1114) - Updated dependencies: `@radix-ui/react-primitive@2.1.9` ### [`v1.1.13`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/separator/CHANGELOG.md#1113) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/react-primitive@2.1.8` ### [`v1.1.12`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/separator/CHANGELOG.md#1112) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-primitive@2.1.7` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-slot)</summary> ### [`v1.3.3`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/slot/CHANGELOG.md#132-133) - Reverted breaking changes that caused compatibility issues with React Server Components. ### [`v1.3.2`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/slot/CHANGELOG.md#132-133) - Reverted breaking changes that caused compatibility issues with React Server Components. ### [`v1.3.1`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/slot/CHANGELOG.md#131) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-tabs)</summary> ### [`v1.1.21`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tabs/CHANGELOG.md#1121) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-context@1.2.2`, `@radix-ui/react-direction@1.1.4`, `@radix-ui/react-id@1.1.4`, `@radix-ui/react-presence@1.1.10`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-roving-focus@1.1.19`, `@radix-ui/react-use-controllable-state@1.2.6` ### [`v1.1.20`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tabs/CHANGELOG.md#1120) - Updated dependencies: `@radix-ui/react-primitive@2.1.9`, `@radix-ui/react-roving-focus@1.1.18` ### [`v1.1.19`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tabs/CHANGELOG.md#1119) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-direction@1.1.3`, `@radix-ui/react-id@1.1.3`, `@radix-ui/react-presence@1.1.9`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-roving-focus@1.1.17`, `@radix-ui/react-use-controllable-state@1.2.5` ### [`v1.1.18`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tabs/CHANGELOG.md#1118) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-presence@1.1.8`, `@radix-ui/react-roving-focus@1.1.16`, `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-direction@1.1.2`, `@radix-ui/react-id@1.1.2`, `@radix-ui/react-primitive@2.1.7` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-toggle)</summary> ### [`v1.1.18`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/toggle/CHANGELOG.md#1118) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-use-controllable-state@1.2.6` ### [`v1.1.17`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/toggle/CHANGELOG.md#1117) - Updated dependencies: `@radix-ui/react-primitive@2.1.9` ### [`v1.1.16`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/toggle/CHANGELOG.md#1116) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-use-controllable-state@1.2.5` ### [`v1.1.15`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/toggle/CHANGELOG.md#1115) - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-primitive@2.1.7` </details> <details> <summary>radix-ui/primitives (@​radix-ui/react-tooltip)</summary> ### [`v1.2.16`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tooltip/CHANGELOG.md#1216) - Reverted breaking changes that caused compatibility issues with React Server Components. - Updated dependencies: `@radix-ui/react-compose-refs@1.1.5`, `@radix-ui/react-context@1.2.2`, `@radix-ui/react-dismissable-layer@1.1.19`, `@radix-ui/react-id@1.1.4`, `@radix-ui/react-popper@1.3.7`, `@radix-ui/react-portal@1.1.17`, `@radix-ui/react-presence@1.1.10`, `@radix-ui/react-primitive@2.1.10`, `@radix-ui/react-slot@1.3.3`, `@radix-ui/react-use-controllable-state@1.2.6`, `@radix-ui/react-use-layout-effect@1.1.4`, `@radix-ui/react-visually-hidden@1.2.11` ### [`v1.2.15`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tooltip/CHANGELOG.md#1215) - Updated dependencies: `@radix-ui/react-slot@1.3.2`, `@radix-ui/react-primitive@2.1.9`, `@radix-ui/react-dismissable-layer@1.1.18`, `@radix-ui/react-popper@1.3.6`, `@radix-ui/react-portal@1.1.16`, `@radix-ui/react-visually-hidden@1.2.10` ### [`v1.2.14`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tooltip/CHANGELOG.md#1214) - Republish through CI to attach provenance attestations. The previous versions of these packages were published manually outside of CI and therefore shipped without provenance; this patch re-releases the same code through the CI pipeline so every package includes an attestation. - Updated dependencies: `@radix-ui/primitive@1.1.7`, `@radix-ui/react-compose-refs@1.1.4`, `@radix-ui/react-context@1.2.1`, `@radix-ui/react-dismissable-layer@1.1.17`, `@radix-ui/react-id@1.1.3`, `@radix-ui/react-popper@1.3.5`, `@radix-ui/react-portal@1.1.15`, `@radix-ui/react-presence@1.1.9`, `@radix-ui/react-primitive@2.1.8`, `@radix-ui/react-slot@1.3.1`, `@radix-ui/react-use-controllable-state@1.2.5`, `@radix-ui/react-use-layout-effect@1.1.3`, `@radix-ui/react-visually-hidden@1.2.9` ### [`v1.2.13`](https://redirect.github.com/radix-ui/primitives/blob/HEAD/packages/react/tooltip/CHANGELOG.md#1213) - Fixed a bug where `Tooltip.Content` children were mounted to the DOM twice. - Improved tree-shaking so bundlers can drop unused components. Component parts are now marked `/* @​__PURE__ */` and use named render functions instead of `Component.displayName = ...` assignments, which previously prevented dead-code elimination with some bundlers. - Updated dependencies: `@radix-ui/react-popper@1.3.4`, `@radix-ui/react-dismissable-layer@1.1.16`, `@radix-ui/react-portal@1.1.14`, `@radix-ui/react-presence@1.1.8`, `@radix-ui/react-visually-hidden@1.2.8`, `@radix-ui/react-use-controllable-state@1.2.4`, `@radix-ui/primitive@1.1.6`, `@radix-ui/react-compose-refs@1.1.3`, `@radix-ui/react-context@1.2.0`, `@radix-ui/react-id@1.1.2`, `@radix-ui/react-primitive@2.1.7`, `@radix-ui/react-slot@1.3.0`, `@radix-ui/react-use-layout-effect@1.1.2` </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>
## Summary Project issues, pull requests, reviews, and commit details no longer flatten rich content into inert or plain text. They now share the message markdown and media pipeline, preserving NIP-92 `imeta` metadata so links, images, and videos render consistently. Commit bodies are fetched only when a single-commit detail view is opened, keeping list queries lightweight while exposing full context beside the diff. ### Related issue None found. ### Testing - Pre-push suite: `desktop-check`, `desktop-test`, `desktop-tauri-test`, `rust-tests`, and `mobile-test` - Project issue and pull request regression tests cover preserving attachment metadata on roots, updates, and comments - The project commit detail smoke scenario verifies linked text, images, and video in commit bodies Signed-off-by: Thomas Petersen <thomasp@squareup.com>
…lock#3037) ### What changed? Adds optimistic rendering for newly locally posted messages in channels and threads. - Messages appear immediately - Relay echoes and history are deduplicated by event ID - Rejected or timed-out publishes erase the optimistic rendering. The implementation covers reconnect and hydration races, channel-window and legacy WebSocket paths, thread-local overlays, and rapid concurrent sends. ### Why? This is a valuable partial mitigation for [BOT-1449](https://linear.app/squareup/issue/BOT-1449/buzz-mobile-posted-messages-dont-appear-until-leavingre-entering-the): senders no longer depend on receiving a relay echo before seeing their own post. It does not address the likely primary cause of stale channels. Mobile currently does not recover live subscriptions after a rate-limited relay `CLOSED`; that recovery is being handled separately. ### How is it tested? Full mobile suite: 676 passed, 1 skipped. Added regression coverage for optimistic insertion, authoritative deduplication, rollback, reconnect and hydration, thread replies, rapid and equal-time sends, never-echoed successful sends, and legacy WebSocket retirement. --------- Signed-off-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz> Co-authored-by: npub102wg7q285p64ch2fjvstmf2ntn2sz3c4u5hmwatalc76mhsuauysftjtfj <7a9c8f0147a0755c5d499320bda5535cd5014715e52fb7757dfe3dadde1cef09@buzz.block.builderlab.xyz>
) > [!NOTE] > **Part 1 of a multi-PR onboarding rework.** This PR only restores the hidden harnesses, adjusts the card layout, and gates onboarding completion on valid config. A follow-up PR (stacked on this branch) restructures the flow: the harness page becomes a single-choice "pick your default harness" step, and install/sign-in/provider setup moves to the following page. Review this one on its own terms — the flow rework is intentionally not here. ## Summary - Restore **Goose** and **Buzz Agent** to onboarding harness selection, reverting the launch-only restriction from block#2233 - The restore is the single centralized allowlist (`ONBOARDING_RUNTIME_ORDER` in `onboardingRuntimeSelection.ts`) that block#2233 deliberately set up for this moment — setup cards, readiness handoff, and the defaults harness picker all derive from it - Lay the four harness cards out as a single row at `lg` and above (`lg:grid-cols-4`); below 1024px (including the app's 800px minimum window width) the grid is 2×2, and 1-up on narrow viewports - **Gate onboarding Finish on actual config validity** (review finding): the defaults page rendered provider/model/credential fields for provider-required harnesses but Finish only checked that a harness was selected — a fresh user picking Buzz Agent could persist a default that fails at first spawn. The Finish gate now consumes `AgentConfigFields`' existing `onValidityChange` signal. Baked build env and runtime-file config satisfy the gate, so internal builds and existing Goose users are never blocked - Update the unit + E2E specs that pinned the hidden behavior, plus two new E2E cases pinning the Finish gate (blocked-until-configured, and baked-env never blocked) - AGENTS.md rule 7 updated to document the completion gate ## Testing - `onboardingRuntimeSelection.test.mjs` — 4 passed - `pnpm typecheck` — clean - `pnpm exec playwright test tests/e2e/onboarding-agent-defaults.spec.ts --project=smoke` — 21 passed - `onboarding-docked-cta-screenshots.spec.ts` — 3 passed - Biome — clean ## Known cosmetic issue (deferred to PR 2) At the app's minimum window size (800×500) the 2×2 grid extends past the visible area and the footer CTA overlaps card space. Next still hit-tests correctly. PR 2 redesigns this page entirely (cards become a single-choice chooser with no inline setup), so this is deferred rather than patched twice. --------- Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
## Summary - backing out of the backup step and clicking the CTA again reuses the existing key - the button still said "Create a new identity key", which is misleading for a security-sensitive action - when a key is already loaded, label it "Continue with existing identity key" Closes block#2318 ## Test plan - [ ] first-run: button still says "Create a new identity key" - [ ] create key → backup → back: button says "Continue with existing identity key" and shows the same key Made with [Cursor](https://cursor.com) --------- Signed-off-by: Taksh <takshkothari09@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
## Summary Pull requests and issues that mention you now appear as repository-scoped Inbox conversations, so project work can be reviewed without first navigating to Projects. Opening a project item resolves its current canonical state and reuses the existing review, comment, merge, and issue actions. Repository-aware grouping keeps identical event IDs from different repositories separate, while loading, missing-data, and partial-query states avoid exposing stale actions. ### Related issue None found. ### Testing - `node --import ./test-loader.mjs --experimental-strip-types --test src/features/home/lib/projectInbox.test.mjs` — 6 tests passed - `CI=1 pnpm exec playwright test tests/e2e/project-inbox.spec.ts --project=smoke` — passed - Pre-push desktop, mobile, Tauri, and Rust checks — passed --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com>
…lock#3017) ## Summary NIP-29 `kind:9000` (PUT_USER) role changes were only authorized when the **new** role was elevated. Demotions were unauthorized, so any authenticated user could strip a channel owner to `member` with a single event — and the demotion was unrecoverable, since the ex-owner then lacked the privilege to restore themselves. Reported by @tyler in `#buzz-security`. Verified true, plus two adjacent defects the report flagged and one it did not. ## The defects 1. **Demotion unauthorized.** The actor check only fired when the *requested* role was elevated. Lowering someone's role skipped it entirely. 2. **Open channels skipped the actor check.** It was nested under `visibility == "private"`. 3. **`add_member` had no last-owner guard** while `remove_member` did — so a channel could be left with zero owners. 4. **(Not in the report.)** An absent `role` tag defaulted to `Member`, so a bare self-targeted PUT_USER silently demoted the sender. No attacker required. ## The fix **`crates/buzz-db/src/channel.rs`** — the authority, because it also covers the desktop/admin callers that bypass the relay validator: - Changing an **active** member's role requires an elevated actor **in both directions**. Re-adding at the same role stays unguarded and idempotent (the huddle bot-add and `kind:9021` join paths depend on this). - Last-owner guard in `add_member`, mirroring `remove_member`. - Keyed on the **active** role (`removed_at IS NULL`). A soft-removed row's role is history, not live authority — otherwise soft-deleted ownership becomes a resurrection token: a kicked owner self-rejoins via `9021` and silently regains ownership. - New `pg_advisory_xact_lock` on a channel-membership namespace, taken as the first statement in both `add_member` and `remove_member`. Both read an owner `COUNT` and then write a *different* row, so READ COMMITTED alone lets two concurrent demotions each observe 2 owners and together leave 0. - `remove_member`'s `is_agent_owner` lookup moved before the transaction opens — it borrows a second pool connection, and issuing it while holding the lock could self-deadlock on a small pool. Safe because `agent_owner_pubkey` is immutable (first-mint-wins). **`crates/buzz-relay/src/handlers/side_effects.rs`**: - Role tag is now `Option` — absent means "no role change requested" rather than defaulting to `Member`. - Actor-role lookup hoisted out of the `visibility == "private"` block, so open channels are covered. - Role-change and last-owner guards on every visibility. Rejecting here *as well as* in the DB means clients get a real error instead of an `OK` whose side effect then fails silently. ## Verification **Mutation tested — every guard stubbed individually to confirm a test actually dies.** Three of eight guards were originally uncovered and survived being disabled with the suite fully green: | Guard | Dying test | |---|---| | DB actor-auth | *survived* → **new** `unprivileged_member_cannot_demote_a_co_owner` | | DB last-owner | `owner_can_still_manage_roles_after_demotion_guard` | | DB active-role (soft-remove) | `kicked_owner_rejoins_as_member_not_owner` + 3 | | `add_member` advisory lock | `membership_writes_serialize_on_the_shared_channel_lock` | | `remove_member` advisory lock | + `remove_member_rejects_an_actor_demoted_while_it_waited` | | relay no-role-tag preservation | `test_nip29_put_user_without_role_tag_preserves_role` | | relay actor-auth | *survived* → **new** `test_nip29_relay_rejects_role_change_by_unprivileged_actor` | | relay last-owner | *survived* → **new** `test_nip29_relay_rejects_last_owner_self_demotion` | The three gaps shared one cause: every existing test asserts resulting **state** ("the role did not change"), and the DB guards enforce that state, masking every layer above them. With a relay guard stubbed the relay answers `accepted:true` and logs `Side effect failed: access denied: ...` while the state assertion still passes — the entire relay validator could be deleted unnoticed. The new relay tests assert `accepted == false` instead, the one observable only the validator controls. Each new test is verified in both directions: green against the real fix, failing with its intended message when its guard alone is stubbed. **Test runs** (at `9461eedb`): - `buzz-db`, serial: **210 passed / 3 failed** — the same 3 failures as clean `main` (202/3), which are pre-existing and unrelated (`concurrent_same_owner_create…`, `create_community_with_owner_is_atomic…`, `test_usage_metrics_lock_has_single_owner…`). +8 = the new tests. - `e2e_relay --ignored`: **40 passed / 3 failed**. Clean `main` on the same relay is 35/6 — the same 3 infra failures (`test_invite_mint_and_claim…`, `test_subscription_limit_enforced`, `test_unarchive_emits_member_added_notification`) plus the 3 security tests that fail unpatched and pass here. - `cargo fmt`, `clippy`, `git diff --check` all clean. **Live manual drive** against a locally running relay, using raw `nak`-signed events (the `buzz` CLI refuses malformed `kind:9000`, so the guards have to be exercised directly): - *Rejected:* member demotes owner; member demotes admin; self-promote to owner; self-promote to admin; admin demotes the last owner; sole owner self-demote; demoted ex-owner demotes last owner; private-channel member demotes owner; non-member demotes owner in private. - *Allowed:* bare PUT_USER with no role tag (owner keeps role); idempotent re-add at same role; owner promotes admin→owner, then owner2 legitimately demotes owner1. - *Resurrection defeated:* owner promotes attacker to admin → kicks them (`9001`) → attacker self-rejoins (`9021`) → returns as **member**, not admin, and cannot demote the owner. - Normal ops unaffected throughout: channel creation, messaging, member listing, and legitimate governance all work. ## Behavior change to be aware of Huddle bot-add sends `role="bot"`. If the target is **already an active member at a different role**, that is now a role change and requires an elevated actor. Previously it silently re-roled them — the same privesc primitive through a different door, so narrowing it is intended. This does not break the huddle flow in the path that matters: the ephemeral channel add (the one that fails hard) is performed by the host, who *created* that channel and is therefore its owner — verified live. The parent-channel add is already explicitly best-effort, capturing the error into `parent_error` with a comment anticipating "may already be member"; adding a non-member agent there still works. Flagging it rather than burying it. ## Notes - Commit is **signoff-only, not cryptographically signed** — `-S` fails in this environment (git tries to load the agent npub as an SSH key file). DCO trailers are present and correct. - Branch was merged with `origin/main` via `--no-ff` (not rebased). Upstream had 17 commits, none touching these files, no migration changes. --------- Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com> Co-authored-by: Dawn (sprout agent) <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
Harnesses became per (agent, relay) pair in block#2122 and now write `agents/logs/{pubkey}__{sha256(relay_url)}.log` via `managed_agent_runtime_log_path`. `get_managed_agent_log` was never updated and still read the legacy `agents/logs/{pubkey}.log`, so agent profile → Runtime → Harness Log froze at each agent's last single-runtime line while live output accumulated in files the reader never opened. The reader now resolves the log through `latest_managed_agent_log_path`, which picks the most recently modified file belonging to the agent — pair-scoped `{pubkey}__*.log` or legacy `{pubkey}.log` — and falls back to the legacy path when the agent has no log on disk at all. Agents that have not restarted since the update keep working, and the panel follows whichever harness is currently writing. The response already carried `log_path`, so the panel header names the file being shown. Selection is deterministic: equal mtimes break toward the higher filename, and files belonging to other agents or without a `.log` extension are never candidates. `storage.rs`'s inline test module moves to a `#[path]`-included sibling `storage_tests.rs`, matching the existing pattern in `teams.rs` and `archive/mod.rs`. This drops both halves under the desktop file-size limit (1383 → 826 / 701), so the ratchet entries tighten instead of growing. Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
…lock#3140) ## Summary Follows up on the `CONTRIBUTING.md` refresh in block#2780. With contributor volume up, the guide describes what a good PR looks like but never says what won't land or what happens after you open one. This closes those gaps in three additions, keeping the welcoming tone of the refresh: - **UI screenshot requirement** — a new item under "What a Good PR Looks Like": PRs changing desktop or mobile UI must include before/after screenshots (or a short recording). Also adds a one-line prompt to the PR template's Testing section. - **"PRs We're Unlikely to Merge"** — a short, positively-framed list (large refactors/dependency swaps without a prior issue, style-only churn, undiscussed new features, drive-by bundled changes) with a pointer to open an issue first. - **"What to Expect After You Open a PR"** — replaces the "Review Process" section: best-effort triage cadence, guide-skipping PRs may be closed with a pointer here, and a close isn't a rejection — address the gaps and reopen anytime. Retains the existing no-force-push and squash-merge guidance. ### Related issue N/A — follow-up to block#2780; no duplicate PRs found. Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…DoS) (block#3135) ## Summary `cargo-deny` started failing on **every** PR and on `main` when **RUSTSEC-2026-0216** was published mid-afternoon today. Nothing in the tree changed — cargo-deny fetches the advisory DB at run time, so main's own `Security` job passed at `00ecf2c` and then began failing on the same commit. ``` error[vulnerability]: Remote Denial of Service via malformed NIP-44 v2 payload Cargo.lock:432 nostr 0.44.3 — RUSTSEC-2026-0216 advisories FAILED, bans ok, licenses ok, sources ok ``` The `nostr` NIP-44 v2 decrypt path reads a 2-byte unpadded-length prefix via `buffer[0..2]` **after** the HMAC check passes, without verifying the decrypted buffer holds 2 bytes. A sender who holds the conversation key — i.e. any DM sender — can craft a payload that decrypts to 0 or 1 bytes and panic the receiver. Remote DoS through any relay that delivers the event. No key material, plaintext, or memory corruption. Affects `0.26.0` through `0.44.4`. Fixed in `0.44.5`. ## The change Lockfiles only, 6 insertions / 6 deletions. The manifest already declares `nostr = "0.44"` — a caret range — so `0.44.6` needs no `Cargo.toml` edit. | Lockfile | Before | After | |---|---|---| | `Cargo.lock` | 0.44.3 | 0.44.6 | | `desktop/src-tauri/Cargo.lock` | **0.44.4** | 0.44.6 | **The desktop lockfile is the part worth reviewing.** `desktop/src-tauri` is excluded from the root workspace (`Cargo.toml:31`), and the `Security` job runs `cargo-deny check` at the repo root — so it never sees that lockfile. It was pinning a vulnerable *and* yanked `0.44.4` that no CI check would ever have flagged. Desktop calls `nip44::decrypt` at `commands/identity.rs:495`. Credit to @eva for catching this; I'd have shipped the root-only fix and left it sitting there. **This isn't optional maintenance.** `0.44.0` through `0.44.4` are all yanked on crates.io. `0.44.5` and `0.44.6` are the only live versions in our range — staying put isn't an available option. ### On the two extra lines in the desktop lockfile The desktop bump also repoints two existing dependency edges: ``` nostr-derive: syn 2.0.118 -> syn 1.0.109 tempfile: getrandom 0.4.3 -> getrandom 0.3.4 ``` I checked these rather than waving them through: **no packages are added or removed** — both versions were already present in the graph, so only which edge points where changed. The resolution is stable across repeated re-resolves, and a plain re-resolve without the bump produces zero diff, so this isn't pre-existing lockfile staleness leaking in. ## Verification At this commit, in a clean worktree off `origin/main`: - `cargo-deny check advisories` → **`advisories ok`**, exit 0. The same tree before the bump reported `advisories FAILED` with this advisory, so the check is doing real work, not passing vacuously. - `./scripts/run-tests.sh unit` → all five packages pass. - `cargo test -p buzz-core` 229/229, `-p buzz-cli` 250/250, `-p buzz-relay --lib` 750 pass / 1 fail — the sole failure `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo` is pre-existing and reproduces identically at unmodified `00ecf2c`. - `desktop-tauri-test` passed in the pre-push hook, which exercises the crate whose lockfile changed. ## Why not a `deny.toml` ignore Considered and rejected. This is a reachable panic triggerable by any DM sender, and buzz-acp agents decrypt DMs from arbitrary senders. Suppressing it would ship a live remote-DoS to every agent and client in order to make a dashboard green. ## Note on `spin` The yanked `spin 0.9.8` / `0.10.0` warnings in the same job are **not** what fails CI — the log has exactly one hard error, this one. They're `warning[yanked]`, and warnings don't fail the build. `spin` is also three levels transitive (`mesh-llm-host-runtime → mdns-sd → flume → spin`) under a dev-dependency, so it isn't ours to bump. Left alone deliberately. ## Follow-up Unblocks block#3128 (relay-admin ban gate), which has a zero dependency-file delta and will inherit this cleanly once main is merged in. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whm <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [react](https://react.dev/) ([source](https://redirect.github.com/react/react/tree/HEAD/packages/react)) | [`19.2.7` → `19.2.8`](https://renovatebot.com/diffs/npm/react/19.2.7/19.2.8) |  |  | | [react-dom](https://react.dev/) ([source](https://redirect.github.com/react/react/tree/HEAD/packages/react-dom)) | [`19.2.7` → `19.2.8`](https://renovatebot.com/diffs/npm/react-dom/19.2.7/19.2.8) |  |  | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/1) for more information. --- ### Release Notes <details> <summary>react/react (react)</summary> ### [`v19.2.8`](https://redirect.github.com/react/react/compare/v19.2.7...1dd4ecbdabf826f527fc9a58c05ea70375b7d170) [Compare Source](https://redirect.github.com/react/react/compare/v19.2.7...v19.2.8) </details> <details> <summary>react/react (react-dom)</summary> ### [`v19.2.8`](https://redirect.github.com/react/react/compare/v19.2.7...1dd4ecbdabf826f527fc9a58c05ea70375b7d170) [Compare Source](https://redirect.github.com/react/react/compare/v19.2.7...v19.2.8) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/block/buzz). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…nds 9030-9033 (block#3128) ## Summary `ingest_event`'s durable write-path restriction gate exempts NIP-43 relay-admin kinds **9030–9033**, so that a *timed-out* admin keeps administrative capability. That exemption was ban-blind, and `handle_relay_admin_event` performed no restriction check of its own. A **banned** admin or owner could still add members, remove members, change member roles, and set the workspace icon by posting a signed NIP-98 request to `POST /events`. No open WebSocket required. Reported externally by **Bilal Syed** (also filed publicly as block#3020 before he read `SECURITY.md`). Verified true, reproduced live, and found slightly worse than reported. Same class as BUZZ-SEC-007, which PR block#1915 closed for moderation command kinds 9040–9044. That fix was never extended to the 9030 range. ## Why it worked - `handlers/ingest.rs:1639` skipped the restriction check when `is_relay_admin_kind(kind)` was true. - `handlers/relay_admin.rs` did a freshness check and a role lookup only — zero restriction reads in the file. - A ban does not remove the role: `ban_member` (`buzz-db/src/moderation.rs:314`) writes only `community_bans`, so the `relay_members` admin row survives. - The HTTP path never consulted ban state — `enforce_relay_membership` is a bare `SELECT 1 FROM relay_members`. - The ban was enforced only at the NIP-42 auth seam, which an HTTP request never crosses. **Worse than reported:** the report covered remove (9031) and icon (9033). Add (**9030**) works too, so a banned admin can *plant* new members. That matters because `moderation_authz.rs:163-170` derives "an admin cannot ban an owner or fellow admin" from `relay_members` — the very table 9030/9031 mutate. A banned admin could seed accomplices into the roster the ban was meant to stop them touching. Also of note: `moderation_authz.rs:158-165` already asserts in a comment that *"The command handler separately rejects a banned actor on every transport."* `relay_admin.rs` was the one command handler not holding that invariant. ## The fix Enforce the durable ban **inside `handle_relay_admin_event`** — the reporter's own suggested shape, and the `moderation_commands.rs:99-108` precedent. Deliberately **not** the one-token alternative of dropping `&& !is_relay_admin_kind(kind_u32)` at `ingest.rs:1639`: that would also start blocking *timed-out* admins, silently changing policy. Bans are refused; timeouts still administer, which is the entire reason the exemption exists. `handle_relay_admin_event` becomes a thin admission wrapper around an unchanged `execute_relay_admin_command` body, so no future early return inside that body can precede the check. The check therefore also necessarily precedes the freshness check. **The refusal category is part of the security contract**, so this returns a typed `RelayAdminError` rather than a string. A `blocked:` string would have kept the right wire text but returned **400** instead of **403** (`api/bridge.rs:845` vs `:858`), and would have reported a restriction-DB outage as a client error: | Variant | Ingest | Wire | HTTP | |---|---|---|---| | `Banned` | `AuthFailed` | `blocked: you are banned from this community` | **403** | | `Rejected(..)` | `Rejected` | `invalid: …` | 400 (unchanged) | | `Internal(..)` | `Internal` | `error: …` (sanitized) | **500** | ## Verification Live over real HTTP against an isolated relay, all four exempt kinds refused, DB checked after each for non-mutation: ``` [banned] 9031 remove -> 403 blocked: you are banned from this community [banned] 9030 add -> 403 blocked: you are banned from this community [banned] 9032 change role -> 403 blocked: you are banned from this community [banned] 9033 set icon -> 403 blocked: you are banned from this community ``` Victim still `member`, planted key absent, role target unchanged, icon still NULL. 9032 required a banned **owner** to be a real test, since it is owner-only. - **Mutation-tested.** The admission decision is the pure `admits_relay_admin_command(&RestrictionState)`, covered by the *default* suite. Neutering it fails `banned_actor_is_not_admitted_to_a_relay_admin_command`. The first version of this patch would have stayed green if someone deleted the check — that gap is closed. The unit test does not prove handler *wiring*; the `#[ignore]`d live E2E is what checks linkage. - **Fail-closed proven empirically**, by manual fault injection rather than assertion: renaming `community_bans.banned` out from under the running relay yields 500, no mutation, and no schema detail leaked to the client. - Negative/positive controls: timed-out admin still administers *and* is still content-write-blocked; clean admin unaffected with mutation confirmed; non-admin still gets `invalid:`/400. - Reviewed iteratively by **@mari** over three rounds; final approval at 9/10+ on minimalness, elegance, and correctness. She also ran an independent deep regression pass on an isolated stack (odd port 44391) covering channel lifecycle, membership, messages/replies/search/edit/delete, reactions, canvas, DMs, and moderation transitions — no regressions. - `cargo fmt --all --check`, `cargo clippy -p buzz-relay --all-targets -D warnings`, `buzz-core` 229/229, `buzz-cli` 250/250, `run-tests.sh unit` all five packages green. - `buzz-relay --lib`: **756 passed / 1 failed**. The sole failure `api::mesh_demo::tests::demo_join_forwarded_arm_round_trips_echo` (504 vs 200) is **pre-existing** — reproduced identically in a detached worktree at merge base `00ecf2c`. ## Notes for the reviewer - Merged `origin/main` in as a merge commit rather than rebasing, per instruction. No conflicts; the eight incoming commits touch none of the three files here. Closest neighbour is `00ecf2c` (kind:9000 NIP-29 *channel* role authz) — disjoint from this NIP-43 *relay-admin* fix. - **This does not close the class.** Two separate items remain open, deliberately excluded to keep an externally-known security fix reviewable: 1. **Command kinds dispatch before the gate.** `is_command_kind` fires at `ingest.rs:1561`, ~80 lines *before* the restriction gate, and `command_executor.rs` has no restriction read. Measured live: a banned member can still open a DM (41010 → 200). 41011/41012/30620/46030/46031 unprobed. Needs per-kind semantics enumerated first (reports allowed while banned; moderation commands allow timeouts but reject bans; ordinary writes reject both). 2. **`moderation_commands.rs` maps its own restriction-DB failure to 400, not 500**, and leaks the raw Postgres message to the client. - One correction for the public issue: its repro step 1 says `kind:9041`, which is **unban**. The ban is **9040** (`KIND_MODERATION_BAN`, `buzz-core/src/kind.rs:298`). Following the steps verbatim yields a false negative. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz> --------- Signed-off-by: Tyler Longwell <tlongwell@block.xyz> Co-authored-by: npub1jmc9dt2lyvzu3h0kxlwxt5zg4fxp9476awyxw6gwxn72g6cw7exqs64whm <96f056ad5f2305c8ddf637dc65d048aa4c12d7daeb8867690e34fca46b0ef64c@buzz.block.builderlab.xyz> Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Agent commits were authored by a raw 63-character npub, which makes `git log`, `git blame`, and GitHub's author column effectively unreadable. This uses the agent's display name for `user.name` instead, while leaving the pubkey where it does real work. ## What changes `build_git_env` in `crates/buzz-dev-mcp/src/shim.rs` now reads `BUZZ_ACP_DISPLAY_NAME`, sanitizes it, and uses the result as `user.name`. When the variable is absent or unusable it falls back to `info.npub` — byte-identical to today's behavior. `user.email`, `user.signingkey`, and the whole credential/signing block are untouched. The pubkey is what NIP-98 auth, NIP-GS signing, and contributor matching key on, and it stays in the email verbatim. `crates/buzz-acp/src/lib.rs` forwards the variable into the dev-mcp server's declared env, mirroring the existing `BUZZ_AUTH_TAG` block. It reads `std::env::var` directly rather than going through `Config`, so the variable is picked up whenever the process has it. `crates/buzz-agent/src/mcp.rs` adds one `PASSTHROUGH_ENV` entry so ACP clients that spawn `buzz-agent` without declaring the variable on the wire still propagate it. ## Why a dedicated variable `BUZZ_ACP_DISPLAY_NAME` is its own contract rather than a reuse of the ACP session title. Commits outlive sessions: a session title is per-session UI chrome and may be composed downstream into `Agent · #channel`, and if that composed form ever reached the env var, git attribution would change silently with no test able to catch it. Git identity gets a variable whose contract is "bare agent display name, never channel-qualified." Nothing writes it yet — a one-line Desktop write lands as a follow-up. Until then `std::env::var` returns `Err`, the npub fallback fires, and behavior is byte-for-byte current `main`. ## Sanitizing Strip control characters, Unicode format characters, and angle brackets; collapse whitespace runs, trim, cap at 80 characters (by `chars()`, so a multi-byte name is never split mid-UTF-8). Angle brackets go because git drops them silently rather than erroring: `Duncan <evil@x.com>` renders as `Duncan evil@x.com <hex@relay>`. It forges nothing, but it reads as though it might. The empty result also has to cover more than literal emptiness. git's `ident.c` treats a set of characters as "crud" — stripped from both ends, and fatal when a name is *nothing but* those characters: ``` $ git -c user.name=';;' commit -m t fatal: name consists only of disallowed characters: ;; ``` Verified against git 2.54.0 by committing with each ASCII byte 32..=126 as the entire `user.name`: exactly space, `"`, `'`, `,`, `:`, `;`, `<`, `>`, `\` abort, plus all control characters (the predicate is `c <= 32`). `.` is not crud in this version, despite older lore. Names that merely *contain* crud are fine — `O'Brien` and `Smith, Jr.` both commit cleanly — so the check is "at least one non-crud character survives," not "no crud present." Without it, a display name of `;;` or `""` would abort every commit that agent makes. ## Unicode format characters `char::is_control` covers only category `Cc`. Category `Cf` — zero-width spaces and joiners, bidi embedding and override marks, invisible math operators, tag characters — is neither control, nor whitespace, nor git crud, so those characters survived every one of the checks above. A display name of nothing but U+200B ZERO WIDTH SPACE therefore satisfied "at least one non-crud character survives" and git accepted the commit with a visually blank author: ``` # pre-fix, BUZZ_ACP_DISPLAY_NAME set to two U+200B $ git log -1 --format='%an' | xxd -p e2808be2808b0a ``` Embedded marks were the other half: a trailing U+202E RIGHT-TO-LEFT OVERRIDE reorders everything after it, so a stored author line renders as something other than what it stores — the same confusion class the angle-bracket filtering exists to prevent. `is_unicode_format` rejects the whole `Cf` category rather than the known-bad marks, because the boundary that matters is "invisible or reorders text", not "the codepoint someone thought of". The 21 ranges come from the UCD's `DerivedGeneralCategory.txt` (17.0.0), cross-checked against Python's `unicodedata` (16.0.0); both yield exactly the same set. They are inlined as a `matches!` rather than pulling in a Unicode-tables crate for one predicate, and a test asserts both endpoints of every range plus the codepoints immediately outside them — including U+2065, which sits inside the U+2060 block but is unassigned rather than `Cf`. Filtering happens inside the existing per-word filter, so a format-only name collapses to empty and falls out through the same `None` → npub path as a crud-only name. No new fallback logic. And because filtering precedes truncation, invisible padding cannot eat the 80-character budget. ## NUL is handled one layer up An interior NUL is a sibling constraint that cannot be fixed here: it makes `Command::env` fail the entire spawn before this code runs, so it has to die at the writer. block#3028 establishes that pattern for the session title in `resolve_session_title` via `filter(|c| !c.is_control())`, and the Desktop follow-up that writes `BUZZ_ACP_DISPLAY_NAME` inherits it. The shim sanitizer is a second line of defense for values that arrive from somewhere other than Desktop. ## Verified end to end Driving the real `buzz-dev-mcp` binary over stdio MCP and committing inside its shimmed environment: ``` # BUZZ_ACP_DISPLAY_NAME="Duncan Idaho" Duncan Idaho <dcfd242e...0f95@buzz.block.builderlab.xyz> verify_exit=0 # BUZZ_ACP_DISPLAY_NAME unset npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e...0f95@buzz.block.builderlab.xyz> verify_exit=0 # BUZZ_ACP_DISPLAY_NAME=";;" (crud-only; would otherwise be fatal) npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e...0f95@buzz.block.builderlab.xyz> verify_exit=0 # BUZZ_ACP_DISPLAY_NAME=U+200B U+200B (format-only; would otherwise be blank) npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e...0f95@buzz.block.builderlab.xyz> verify_exit=0 # BUZZ_ACP_DISPLAY_NAME="Duncan" + U+202E (bidi override stripped) Duncan <dcfd242e...0f95@buzz.block.builderlab.xyz> verify_exit=0 # BUZZ_ACP_DISPLAY_NAME="Dun" + U+200B + "can" (zero-width removed, word not split) Duncan <dcfd242e...0f95@buzz.block.builderlab.xyz> verify_exit=0 ``` Signature verification passes in every case — the signing identity is unchanged. `Related: block#3028` — it establishes the Desktop-side env plumbing this builds beside; the one-line Desktop follow-up that writes `BUZZ_ACP_DISPLAY_NAME` alongside the session title ships after it merges. Not a dependency: with the variable absent, `std::env::var` returns `Err` and the npub fallback keeps current behavior exactly. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
**Category:** new-feature **User Impact:** Mobile users can copy permalinks, revisit, follow, and manage messages through a clearer long-press menu that matches desktop capabilities. **Problem:** The mobile message menu exposed only a small subset of desktop actions, and important workflows such as copying a permalink or scheduling a reminder were unavailable or hard to discover. **Solution:** Bring applicable desktop actions to mobile using native patterns, promote Reply, Copy link, and Remind me above the fold, and group the remaining actions by intent in a scrollable sheet. **Intentional behavior changes (per review):** - The quick-reaction row goes from 6 emojis to 4 (👀 and 🙏 dropped) to make room for larger 52px tap targets alongside the `+` picker, which still offers the full set. - **Copy link — not a native share sheet — is the permalink path.** An earlier revision shipped a `share_plus` Share message row; it was removed in review since Copy link covers the job and custom-scheme `buzz://` URIs are handled inconsistently by share targets. Native share can return as a follow-up with an https fallback. - Mark unread is message-scoped and session-local: it forces just that message unread (surfacing its channel as unread), and message-level Mark read can never clear a channel-level unread set from the channel tile. <details> <summary>File changes</summary> **mobile/lib/features/channels/channels_provider.dart** Feeds followed thread roots into unread and notification evaluation so following a thread has meaningful behavior. **mobile/lib/features/channels/message_actions.dart** Reworks the long-press sheet with promoted fast actions, message links, reminders, read state, thread following, and clearer action grouping while preserving existing guards. Quick-reaction circles share one extracted widget. **mobile/lib/features/channels/read_state/message_read_state.dart** Centralizes message-level unread evaluation across channel, message, and thread markers; channel-level forced unread deliberately does not leak into message state. **mobile/lib/features/channels/read_state/read_state_provider.dart** Forced-unread flags are per-context (channel id or `msg:` key) mapped to their channel, so message- and channel-level unread choices round-trip independently. **mobile/lib/features/channels/thread_follows/thread_follows_provider.dart** Exposes per-identity thread follow state to the message menu and notification pipeline. **mobile/lib/features/channels/thread_follows/thread_follows_storage.dart** Persists a bounded, validated set of followed thread roots on the device. **mobile/lib/shared/reminders/remind_me_later_sheet.dart** Adds reminder presets and a native custom date/time flow for deferring a message. Lives under `shared/` so the channels feature never imports another feature module. Cancelling the custom picker keeps the preset sheet open; submission failures show stable copy and log the underlying error. **mobile/lib/shared/reminders/reminder_service.dart** Creates desktop-compatible, self-encrypted kind-30300 reminder events. **mobile/lib/shared/reminders/reminder_time_presets.dart** Defines reminder choices that match the desktop experience. **mobile/lib/shared/deeplink/deep_link.dart** Builds canonical Buzz message links, including thread context when present. **mobile/lib/shared/relay/nostr_models.dart** Adds the reminder event kind to the shared Nostr model constants. **mobile/lib/shared/widgets/sheet_divider.dart** Shared bottom-sheet section divider used by the message actions and reminder sheets. **mobile/test/features/channels/message_actions_test.dart** Covers action visibility and guards, promoted actions, read/unread round-tripping (including channel- vs message-level force isolation), thread follows, and canonical links. **mobile/test/features/channels/read_state/message_read_state_test.dart** Covers unread precedence for channel, message, and thread contexts. **mobile/test/features/channels/thread_follows/thread_follows_storage_test.dart** Covers follow persistence, identity separation, validation, and storage bounds. **mobile/test/shared/reminders/reminder_service_test.dart** Covers reminder payloads, tags, crypto round-tripping, and preset behavior. **mobile/test/features/channels/read_state/read_state_provider_test.dart** Drives the production ReadStateNotifier/ReadStateManager (no fake bookkeeping) through message unread → read → unread round-trips, explicit channel-level Mark read clearing forced messages, and automatic channel-open reads preserving them. **mobile/test/shared/reminders/remind_me_later_sheet_test.dart** Covers custom-picker cancel keeping the sheet open, stable failure copy without the raw error, and the happy preset path. **mobile/test/shared/deeplink/deep_link_test.dart** Covers canonical top-level and threaded message-link generation. </details> ## Reproduction steps 1. Run the mobile app with a signed-in identity and open a channel containing regular messages and threads. 2. Long-press a message and confirm reactions plus Reply, Copy link, and Remind me appear as fast actions above the fold. 3. Use Copy link; confirm the resulting `buzz://message` link opens the correct channel and thread context. 4. Toggle Mark unread/Mark read and Follow thread/Unfollow thread, reopening the sheet to confirm each state changes correctly. Force a channel unread from the channel tile, then mark a message read — the channel stays unread. 5. Choose a reminder preset and a custom date/time; confirm the reminder is created and appears in the desktop reminder experience. Cancel the custom date picker and confirm the reminder sheet stays open. 6. Long-press a system message and a message you cannot manage; confirm utility and destructive actions remain appropriately hidden. ## Screenshots / demos <img width="1206" height="2622" alt="image" src="https://github.com/user-attachments/assets/81096cd6-329b-408f-bcff-712e23b268a4" /> --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
…k#3028) ACP harnesses that name a session from the first text they receive all land in the same place: every managed Buzz agent opens with the identical `[Base] You are operating inside the Buzz platform…` framing, so the harness session list shows a wall of indistinguishable rows. Because sessions are keyed per channel, one agent active in several channels produces several of them. This sends the name out of band instead. `session/new` carries `_meta.sessionTitle` with `Agent · #channel`, composed from the agent's `display_name` (or its unique `name` handle) and the channel it is serving. The prompt is untouched — no tokens spent, no perturbation of the prompt contract, and nothing new for the desktop observer's section parsing to handle. The mechanism is harness-agnostic: Buzz sends the field on every ACP `session/new` regardless of which harness is behind it, and adapters that don't read it ignore it per spec. ## Inert until a consuming adapter ships ACP adapters ignore `_meta` members they do not recognize, so against an adapter with no reader a Buzz session gets no title and nothing else changes. Three adapter halves consume it — Codex, Goose, and Claude Code (linked below); this half and each reader are only useful together, and each reader lands independently. No version floor is added. `codex_adapter_is_outdated_with_path` already gates codex-acp on major version `>= 1` (`desktop/src-tauri/src/managed_agents/discovery.rs:1276-1284`) and this feature needs nothing above that — an older adapter is not broken by the extra member, it simply ignores it. ## What changes **`crates/buzz-acp`** owns sanitization and composition. `sanitize_session_title` collapses whitespace, drops control characters, and caps at `SESSION_TITLE_MAX_CHARS` (80) by character, not byte, so a multi-byte character cannot be split. `compose_session_title` truncates only the channel part against that cap, so the agent name always survives; when the agent name alone fills the cap the channel is dropped rather than the name. `session_new_full` sets `_meta.sessionTitle` when a title exists and omits `_meta` entirely when it does not, since an adapter may distinguish an absent member from a null one. **`desktop/src-tauri`** only resolves and exports. `resolve_session_title` picks `display_name` or falls back to `name`, and `spawn_agent_child` writes it to `BUZZ_ACP_SESSION_TITLE` — or removes the variable when neither candidate yields anything printable. DMs, unresolved channels, and heartbeat sessions get the bare agent name with no channel suffix. ## Four properties that are easy to remove by accident **Control characters are stripped at the desktop boundary, not in the harness.** An interior NUL cannot cross the environment boundary at all — `Command::env` fails the entire spawn rather than passing it through. Deferring the strip to `buzz-acp` would let a corrupted display name turn display chrome into a spawn failure. A display name that is *only* control characters falls back to `name`. **The title is hashed into `spawn_config_hash`.** Without it, renaming an agent left the running process with a stale title and no restart badge. The hash runs the same `resolve_session_title` the spawn writes, and skips it when a user env override shadows `BUZZ_ACP_SESSION_TITLE` — spawn writes the title *before* the layered user env, so the override is what actually runs, and it already reaches the hash through `descriptor.env`. Hashing the record-derived value under an override would badge a rename that changes nothing. **One channel resolve serves both consumers.** `resolve_new_session_channel_context` returns `(is_dm, title_channel)` from a single metadata lookup, feeding both the canvas block's DM check and the title. `ChannelInfoResolver` caches only `Some`, so two independent calls against an unresolvable channel pay the full `fetch_channel_info` retry sequence twice — two timeouts plus a retry delay each — directly in front of `session/new`, precisely when the relay is already degraded. **The `"unknown"` channel name is treated as absent.** `fetch_channel_info` substitutes the literal `"unknown"` for a metadata event with no `name` tag. Composing that sentinel would title every unnamed channel `Agent · #unknown`, reintroducing the exact collision the suffix exists to remove while naming a channel something it isn't. The startup cache already refuses `channel_type == "unknown"` for the same reason. Closes block#2334 Related — the adapter halves that consume `_meta.sessionTitle`: - [codex-acp#338](agentclientprotocol/codex-acp#338) — Codex - [aaif-goose/goose#10712](aaif-goose/goose#10712) — Goose - [claude-agent-acp#920](agentclientprotocol/claude-agent-acp#920) — Claude Code --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…ock#2854) ## Problem With macOS in dark mode (the default for fresh profiles is to follow the system scheme), the onboarding "What's an identity key?" help dialog renders its title in near-white on the always-white textured card, making it unreadable. The body paragraphs stay readable because they use the fixed olive `--buzz-onboarding-backup-ink`; only the `text-foreground` title (and the close button's hover color) flip with the theme. ## Cause The dialog's `DialogContent` carries `buzz-onboarding-neutral-theme` but is portaled outside the `buzz-startup-shell` subtree, so in dark mode it matches `.dark .buzz-onboarding-neutral-theme:not(.buzz-startup-shell)` (`components.css`), which flips `--foreground` to `0 0% 98%`. The textured powder card (`buzz-card-textured`) has no dark variant — it is baked light — so the near-white title disappears against it. ## Fix One attribute: pin the dialog to the light neutral theme with `data-system-color-scheme="light"`. This is the established pattern for always-light onboarding dialogs (`HostedCommunityOnboarding.tsx`, the `CommunityOnboardingFlow.tsx` avatar dialog), and the pinned-light CSS rule already exists and out-specifies the dark-mode flip. No new CSS. ## Testing - Added a dark-mode regression test to `tests/e2e/identity-key-help.spec.ts` (already registered in the Playwright smoke project): emulates `prefers-color-scheme: dark`, opens the dialog, and asserts the title resolves to the pinned light-neutral ink `rgb(23, 23, 23)`. Before the fix it rendered `rgb(250, 250, 250)`. - Manual repro: macOS appearance set to Dark → fresh profile → machine onboarding → click "What's an identity key?". Before/after screenshots are in the comment below. Signed-off-by: Michael Pfister <pfista@gmail.com>
## Why The agent avatar picker disables Emoji Mart search, making emojis difficult to find when creating or editing an agent. ## What - Enable sticky search in the shared agent avatar picker - Focus the search field when the Emoji tab opens - Add end-to-end coverage for search, focus, and selection ## Risk Assessment Low — this is limited to the desktop agent avatar picker and does not change avatar persistence or agent configuration. ## References - `pnpm --dir desktop test` — 3,448 passed - `pnpm --dir desktop typecheck` — passed - `pnpm --dir desktop exec playwright test tests/e2e/agents.spec.ts` — 20 passed --- **Update Jul 24, 16:28 EDT:** Completed `CONTRIBUTING.md` validation. ### Manual test 1. Create an agent and open Add avatar → Emoji. 2. Confirm the search field is focused and filters results. 3. Select an emoji and confirm it becomes the avatar. 4. Repeat while editing an existing agent. ### Validation - `just ci` — passed - `just test` — passed ### Follow-up work None. Generated with Codex
**Category:** improvement **User Impact:** Mobile users can scan Activity as a focused conversation inbox and open the exact unread message or thread represented by each item. ## Context Mobile's Activity tab had not kept pace with Desktop: it presented isolated event headlines, advertised categories that were often empty, and opened a channel without clearly landing on the selected item. This PR brings the Mobile surface toward the conversation-oriented direction explored in Clay Delk's Desktop [Inbox refactor PR block#2045](block#2045), while adapting it to Mobile rather than copying the Desktop split-pane implementation. The related product/UX discussion is captured in the originating [Buzz thread](buzz://message?channel=a9bbc0e5-d25d-4740-849c-93c34bb578a4&id=a7d9a4d33dcd8c6bf0dc67d81c328892b9e38dedaa8548920224ef388301b6ab). ## UX decisions in this PR - **Conversation-oriented, not event-oriented:** related updates collapse into one row per thread/DM conversation, represented by the latest update and ordered by latest activity. Separate top-level conversations in the same channel remain separate rows. - **Resume at the oldest unread:** tapping a grouped row opens the represented canonical message/thread/DM at its oldest unread item, rather than merely opening the channel at an arbitrary position. - **Desktop-aligned row hierarchy:** rows lead with a full avatar and sender, followed by contextual location/type metadata, unread dot + time, and a two-line preview. A **New** boundary separates unread and read content. - **Mobile-native navigation:** Mobile keeps a single-column `Activity → canonical conversation → Back` flow. It does not introduce Desktop's persistent detail pane. - **Compact filtering:** the old horizontal chip rail becomes a compact filter menu so the source set fits a phone viewport without horizontal scanning. Filters are All, Mentions, Threads, Needs Action, Activity, Agents, Reminders, and Drafts. - **Focused source semantics:** All covers personally relevant work—DMs, mentions, thread replies, needs-action events, owned-agent activity, due reminders, and active drafts—rather than becoming a generic stream of every channel message. Mobile's standalone Activity source is currently limited to DM traffic because it does not have Desktop's aggregated channel-activity feed. - **Shared read behavior:** rows project canonical channel/thread/message markers, support unread-only and mark-all-read, and use local overrides only where canonical markers cannot represent an item. - **Reminders and drafts are real data:** reminders use the same encrypted NIP-ER events as Desktop. Drafts persist device-local composer state, restore on return, survive failed sends, and clear after successful sends. - **Explain navigation failures:** an unavailable destination produces an explanatory message rather than silently doing nothing or falling back to an unrelated channel position. ## Implementation summary - Adds a Mobile inbox model for conversation grouping, category priority, contextual labels, sorting, filtering, and oldest-unread targets. - Expands relay-backed sources for mentions, approvals, owned-agent lifecycle events, and DM traffic. - Adds fail-closed NIP-ER reminder decryption and device-local compose-draft persistence. - Redesigns Activity rows, boundaries, filters, unread controls, and empty/loading states. - Routes rows through Mobile's existing canonical channel/thread screens with precise target IDs. - Adds model, provider, widget, reminder, read-state, draft-lifecycle, and deep-link coverage. ## Reproduction steps 1. Run Mobile and open **Activity**. 2. Confirm full avatars, sender-first rows, context labels, unread indicators, timestamps, two-line previews, and the compact filter control. 3. Open the filter menu and verify All, Mentions, Threads, Needs Action, Activity, Agents, Reminders, and Drafts. 4. Tap a grouped thread row and confirm the canonical conversation opens at its oldest unread message. 5. Mark rows read/unread, enable unread-only mode, and use mark-all-read; confirm state agrees with the channel/thread destination. 6. Type without sending in a channel or thread, leave, and confirm the draft appears in Activity and restores in the composer. ## Screenshots | Before — merge-base `dd222a509` | After — PR head `52ad40aee` | |---|---| | <img width="1206" height="2622" alt="image" src="https://github.com/user-attachments/assets/ae961b08-bf8a-4bd5-b487-f6321ae8d85b" /> | <img width="1206" height="2622" alt="image" src="https://github.com/user-attachments/assets/bcbe7ab0-552a-417c-9e85-7a85eb4592ee" /> | Recaptured on the same authenticated iPhone 17 simulator, account, theme, and Activity view, at this PR's current merge-base (`dd222a509`) and head (`52ad40aee`). Both frames were taken within a few minutes on the same live feed, so the visible conversation set overlaps closely (the recent Ned/Bart/Tommy items appear in both). The compared change is the row *structure*: Before leads with an `@ Mention` headline over a small inline avatar and a horizontal chip rail; After leads with a full avatar, a compact `labelMedium` sender label, contextual "Mentioned in" metadata, and a filter menu. The sender username now renders at the same compact scale the old `@ Mention` label used. ## Verification - Current rebased head: `5bd87f4f4` on `origin/main` at `dd222a509`; GitHub reports the PR mergeable. - `flutter analyze` — clean at `5bd87f4f4`. - Full Mobile suite — 698 passed, 1 skipped, 4 failed; all four failures reproduce identically on clean `origin/main` (`channels_page_test` create-channel sheet and three `compose_bar_test` agent-mention cases). - The prior PR-specific `home_page_test` failures were fixed by providing the Activity local-state dependency in that harness. - Independent code and simulator UI review — approved. - Post-rebase GitHub checks are running. --------- Signed-off-by: Taylor Ho <taylorkmho@gmail.com> Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz> Co-authored-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz> Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
## Summary
On a Windows checkout the desktop quality gate does not work. This fixes
four defects in it. Two checks report success without examining
anything, one fails on every file, and one reports violations that its
own allowlist already covers.
**1. `pnpm test` finds no tests and still exits 0.** The `test` script
quotes the glob with single quotes. On Windows pnpm runs scripts through
`cmd.exe`, which does not strip single quotes, so node receives them as
part of the pattern and matches nothing. The run prints `# tests 0` and
exits 0 — a silent green. Double quotes are stripped by `cmd.exe` and by
POSIX shells alike, so Linux CI behaviour is unchanged.
**2. Every text file is checked out as CRLF.** There is no
`.gitattributes`, and `core.autocrlf=true` is the Git for Windows
default. Biome formats with LF, so `biome check .` fails on 1632 of 1633
files. `desktop/src/features/messages/ui/virtuaWheelModePatch.test.mjs`
fails too, because it matches `patches/*.patch` with `\n`-joined
patterns. The stored blobs are already LF, so `eol=lf` adds no
renormalisation churn — `git status` stays clean after the change.
**3. `check:px-text` never finds its own allowlist.**
`scripts/check-px-text-core.mjs` builds the key from `path.relative`,
which returns `\` separators on Windows, while the allowlist in
`desktop/scripts/check-px-text.mjs` is written with `/`. Nothing
matches, so the check reports 5 false violations on a clean tree.
**4. `check:file-sizes` examines nothing at all.** `findRule` compares
against `` `${rule.root}${path.sep}` ``. The roots are multi-segment
(`src/app`, `src/features`, `src-tauri/src`), so on Windows `src/app\`
never matches `src\app\...`. No rule matches any file: the check walks 0
of 1097 files and exits 0.
`scripts/check-pubkey-truncation-core.mjs` already normalises paths this
way (`relativePath.split(path.sep).join("/")`). This applies the same
idiom to the other two.
### Related issue
None found — no open issue covers this. The closest open PR is block#2758,
which fixes a fifth Windows defect in `desktop/test-loader-hooks.mjs`;
it is required before the desktop unit tests can pass here, and it does
not overlap with these files. I checked the changed-file list of every
open PR: none touch `.gitattributes`, `desktop/package.json`,
`scripts/check-px-text-core.mjs` or `scripts/check-file-sizes-core.mjs`.
### Testing
Windows 11 (10.0.26200), node 22.17.1, pnpm 11.4.0, clean checkout with
the default `core.autocrlf=true`.
| Command | Before | After |
| --- | --- | --- |
| `pnpm test` | `# tests 0`, exit 0 | 374 test files discovered, exit 1
|
| `biome check .` | 1632 of 1633 files fail | 1633 checked, 0 errors |
| `pnpm check:px-text` | 5 false violations | passes |
| `pnpm check:file-sizes` | 0 of 1097 files examined, exit 0 | 1097
examined |
`check:file-sizes` now reports `src-tauri/src/managed_agents/runtime.rs:
2220 lines (limit 2216)`. That violation is pre-existing and not
introduced here — `main` currently fails on the same line in CI (Desktop
Core, run 30185213010, commit c2a4ee7). Before this change Windows
reported success while CI was red; now the Windows result agrees with
CI.
Desktop unit tests still fail on Windows until block#2758 lands. With block#2758
applied on top of this branch the full suite passes: 3515 tests, 0
failures. This change stops hiding those failures rather than fixing
them.
Signed-off-by: Seydi Charyyev <seydi.charyev@gmail.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
The resolve hook hands nextResolve absolute filesystem paths. Node's ESM resolver requires URLs or relative specifiers: POSIX absolute paths happen to be coerced, but a Windows path like C:\... parses as a URL with protocol 'c:', so every desktop unit-test run on Windows dies immediately with ERR_UNSUPPORTED_ESM_URL_SCHEME - on a clean tree, before any test executes. CI never sees it (Linux runners). Convert absolute paths to file:// URLs (pathToFileURL) at the three nextResolve call sites. On POSIX the resulting URL is identical to what node coerced before; on Windows the loader now works. With this change the full desktop suite (318 files, 3487 tests) passes on Windows 11 / node 24.14.1. Independently reported by another Windows contributor in block#2634's testing notes. Claude-Session: https://claude.ai/code/session_01YFkHsUe1UUBBuvL81Zoe3n --------- Signed-off-by: technicallybrantley <77166260+technicallybrantley@users.noreply.github.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
## Context
`buzz users get --name Honey` searches relay-wide profiles and returns
up to 100 identically named results without verified ownership metadata.
An agent resolving “my Honey” cannot distinguish the requesting human's
agent from another owner's agent, and the owned match can be excluded by
the result limit. This caused the wrong Honey and Bumble pubkeys to be
added to a channel.
## Summary
This bug fix makes personal-agent resolution owner-aware. Callers can
filter profiles by a verified owner identity before result limits are
applied, and all profile results expose enough ownership context to
diagnose duplicate names.
## Changes
- Adds `buzz users get --owner me|<hex>|<npub>` for name and pubkey
lookups.
- Resolves `me` to the NIP-OA owner identity when the CLI runs as an
agent.
- Filters profiles by the relay's verified `agent_owner_pubkey`
relationship before applying the result limit.
- Returns `owner_pubkey`, `owner_display_name`, and client-relative
`owned_by_me` in compact and JSON output.
- Returns an empty result when no owned profile matches instead of
removing the ownership constraint.
- Rejects malformed owner values instead of silently running an unscoped
query; explicit `null` remains equivalent to no owner filter for
ordinary CLI lookups.
- Rejects owner constraints on specialized channel-window, feed, and
thread filters that cannot enforce author filtering.
- Scopes owner filtering and enrichment to the active community.
- Adds a partial `(community_id, agent_owner_pubkey)` index for owner
lookups.
- Documents the safe `users get --name Honey --owner me` lookup.
## Reviewer-reproducible examples
The relay-backed test creates two same-name agents with different
verified owners, queries through the HTTP `/query` route, verifies only
the selected owner's agent is returned with verified owner metadata, and
verifies a missing owner returns `[]`.
```bash
cargo test -p buzz-relay query_agent_owner_returns_only_verified_owner_matches --lib -- --ignored
```
The owner/author intersection and unsupported-specialized-filter
contracts also have infrastructure-free relay tests:
```bash
cargo test -p buzz-relay agent_owner --lib
```
The CLI surface is visible in command help:
```bash
cargo run -q -p buzz-cli -- users get --help | grep -- --owner
```
```text
--owner <OWNER> Filter agents by verified owner (`me`, 64-char hex, or npub)
```
## Validation
- `cargo test -p buzz-cli` (252 passed)
- `cargo test -p buzz-db` (84 passed, 122 infrastructure tests ignored)
- `cargo test -p buzz-relay --lib` (owner-filter tests pass; the full
local suite is blocked by unrelated Postgres pool timeouts in
media/admin tests)
- `cargo test -p buzz-relay
query_agent_owner_returns_only_verified_owner_matches --lib --
--ignored` (passed)
- `cargo check --workspace --all-targets`
- `cargo fmt --all -- --check`
- Pre-push Rust, Desktop, and Desktop Tauri suites passed
- Pre-push mobile suite could not start because `flutter` is not
installed
- `pnpm check:file-sizes` (passed after rebasing onto current `main`)
---------
Signed-off-by: npub1qye6rec0htgg3np8yt6plpyyg8cyffaq66emt3kmk05eylckkzhq0hnf2k <0133a1e70fbad088cc2722f41f848441f044a7a0d6b3b5c6dbb3e9927f16b0ae@buzz.block.builderlab.xyz>
Co-authored-by: npub1qye6rec0htgg3np8yt6plpyyg8cyffaq66emt3kmk05eylckkzhq0hnf2k <0133a1e70fbad088cc2722f41f848441f044a7a0d6b3b5c6dbb3e9927f16b0ae@buzz.block.builderlab.xyz>
…k main CI (block#3154) `Desktop Core` is currently red on `main`, and every open PR that picks up current main inherits the failure. [block#2630](block#2630) added a shadow-root search-input autofocus effect to `AgentCreationPreview.tsx`, taking the file from 999 to 1026 lines. It sat one line under the 1000-line default beforehand, so that PR's own CI was green while the merged file crossed the cap with no override entry in `desktop/scripts/check-file-sizes.mjs`. This adds the missing entry at 1026, following the pattern the rest of the overrides list uses. The split stays queued along with the others. ``` - src/features/agents/ui/AgentCreationPreview.tsx: 1026 lines (limit 1000) ``` The override is tight in both directions: at `1026` the gate passes, and at `1025` it reproduces the failure above. Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
block#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 block#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>
…lock#3158) Main's Mobile Analyze job fails with `invalid_override` on `_FakeReadStateNotifier.markContextRead`. The fake was added in block#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>
**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>
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 (block#3135) and relay-admin ban gate (block#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>
…lock#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` |  |  | --- > [!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>
…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>
## 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>
## 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>
## 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>
## 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>
## 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>
## 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>
## 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: block#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>
…propagates (block#2607) ## Problem Part of block#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 block#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 block#1823, fixed by open PR block#2507 (spawn_hash). - Surfacing kind:0 relay profile-sync failures on rename is PR block#2302/block#2279 territory (and largely superseded by the merged rollback in block#2258). - Mention-picker UX (owner/status disambiguation) and channel-membership repair for stale identities: TS-side, noted in block#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: block#2423 - Adjacent (no overlap): PR block#2507 (rename-restart, block#1823), PRs block#2302/block#2279 (kind:0 sync-failure surfacing), merged block#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>
## 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 block#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.
…ter (block#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 block#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>
## 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>
… floor (block#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>
## 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>
## 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 block#2602 and supersedes that approach. ### Related issue Related PR: block#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>
block#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 (block#2245) Absorbed from block#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 block#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: block#3090, block#2245 Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Goose's pre-[block#2680](block#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 block#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>
…itch (block#2795) ## Summary Fixes block#2794. Related: block#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 block#2692. The open desktop-side PRs (block#2695, block#2701, block#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>
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>
## 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>
…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 block#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>
## Buzz Desktop release v0.5.0 ### Changes since v0.4.26: - feat(invites): add use-limited invite links ([block#3141](block#3141)) ([`d500c2d5c`](block@d500c2d)) - fix(node): bump Buzz-supplied Node runtimes past OpenClaw's >=24.15.0 floor ([block#3218](block#3218)) ([`98a7b1334`](block@98a7b13)) - fix(desktop): preserve thread anchor through layout reflow ([block#3212](block#3212)) ([`9810d8545`](block@9810d85)) - feat(search): parse from:/in:/after:/before: and pass them in the filter ([block#2871](block#2871)) ([`cb2a265b5`](block@cb2a265)) - fix(desktop): fetch join policies through native networking ([block#2862](block#2862)) ([`0019f8076`](block@0019f80)) - fix(desktop): republish agent identity records when a persona rename propagates ([block#2607](block#2607)) ([`7ca0bbd94`](block@7ca0bbd)) - fix(desktop): keep project Inbox previews compact ([block#3193](block#3193)) ([`de1396050`](block@de13960)) - Inbox refactor ([block#2045](block#2045)) ([`2bd4c24b7`](block@2bd4c24)) - Fix composer selection formatting and drop overlay ([block#3172](block#3172)) ([`99da5b7eb`](block@99da5b7)) - Refine pending message status ([block#3153](block#3153)) ([`75588eaff`](block@75588ea)) - fix(desktop): recover full local storage on startup ([block#3182](block#3182)) ([`174c38e4b`](block@174c38e)) - fix(desktop): keep collapsed table separators out of spoilers ([block#3169](block#3169)) ([`4d8b676bb`](block@4d8b676)) - feat(desktop): redesign agent runtime settings ([block#3093](block#3093)) ([`d98da7389`](block@d98da73)) - fix(desktop): use forward slashes for git credential.helper on Windows ([block#3023](block#3023)) ([`899531684`](block@8995316)) - chore(desktop): add AgentCreationPreview file-size override to unblock main CI ([block#3154](block#3154)) ([`b92a1f4bf`](block@b92a1f4)) - fix(desktop): make the test loader work on Windows ([block#2758](block#2758)) ([`8bb43d519`](block@8bb43d5)) - fix(desktop): make lint and unit-test gates work on Windows ([block#2943](block#2943)) ([`545bb46b8`](block@545bb46)) - feat(desktop): add search to agent emoji picker ([block#2630](block#2630)) ([`313f793c8`](block@313f793)) - fix(desktop): keep identity key help dialog readable in dark mode ([block#2854](block#2854)) ([`be275cfc6`](block@be275cf)) - feat(acp): title agent sessions from the agent and channel name ([block#3028](block#3028)) ([`f2fe3b63c`](block@f2fe3b6)) - feat(git): use agent display name as git author name ([block#3040](block#3040)) ([`18eef633d`](block@18eef63)) - fix(deps): bump nostr to 0.44.6 for RUSTSEC-2026-0216 (NIP-44 remote DoS) ([block#3135](block#3135)) ([`31e2de196`](block@31e2de1)) - fix(desktop): read the newest pair-scoped harness log ([block#3134](block#3134)) ([`654f38490`](block@654f384)) - feat(desktop): handle project work from Inbox ([block#3117](block#3117)) ([`c5c4f390b`](block@c5c4f39)) - fix(desktop): clarify identity key button when key exists ([block#2357](block#2357)) ([`87b3fcd3c`](block@87b3fcd)) - Restore Goose and Buzz Agent to onboarding harness selection ([block#2731](block#2731)) ([`7fc0cc82d`](block@7fc0cc8)) - fix(desktop): render rich project work item content ([block#3100](block#3100)) ([`afb272bb7`](block@afb272b)) - feat(acp): bring your own harness (BYOH) — generic ACP runtime seam + settings gallery ([block#2773](block#2773)) ([`95fdf9788`](block@95fdf97)) - feat(desktop): use collective mesh routing for Auto ([block#2825](block#2825)) ([`16d4ec335`](block@16d4ec3)) - fix(desktop): strip legacy baked team instructions from stored prompts ([block#3035](block#3035)) ([`aee631448`](block@aee6314)) - feat(agents): lower default agent parallelism from 24 to 10 ([block#3038](block#3038)) ([`5d8ede446`](block@5d8ede4)) - Polish community rail and mobile pairing ([block#2972](block#2972)) ([`e6c90bb7c`](block@e6c90bb)) - fix(desktop): remove bundled libsystemd from AppImage ([block#2353](block#2353)) ([`a31fc4d2f`](block@a31fc4d)) - fix(desktop): make agent definition authoritative for model/provider/prompt ([block#1968](block#1968)) ([`8c0e8cb16`](block@8c0e8cb)) - chore(desktop): delete dead persona catalog UI cluster ([block#2886](block#2886)) ([`8e67cf399`](block@8e67cf3)) - fix(desktop): surface install failures hidden by curl-pipe exit codes ([block#2892](block#2892)) ([`166c6655e`](block@166c665)) - Refactor managed-agent runtime into cohesive modules ([block#2974](block#2974)) ([`74b63e184`](block@74b63e1)) - fix(desktop): make Linux AppImage GStreamer work on non-Debian distros ([block#2176](block#2176)) ([`cc6c4d347`](block@cc6c4d3)) - refactor(desktop): remove Agent directory section from Agents page ([block#2290](block#2290)) ([`5d1233e84`](block@5d1233e)) - fix(desktop): enable arboard Wayland backend so Linux copies reach the Wayland clipboard ([block#2904](block#2904)) ([`ab7aa8b12`](block@ab7aa8b)) - fix(desktop): supervise and re-arm relay-mesh runtime ([block#2823](block#2823)) ([`aa51dab9d`](block@aa51dab)) - fix(agents): run live Databricks discovery instead of the fallback list ([block#2890](block#2890)) ([`8eb6e3eb6`](block@8eb6e3e)) - fix(desktop): retire prepend mode on every reader wheel ([block#2913](block#2913)) ([`07d0265cf`](block@07d0265)) - fix(desktop): consolidate prepend scroll correction ([block#2855](block#2855)) ([`25e7864b3`](block@25e7864)) - fix(desktop): track concurrent agent turns up to the harness maximum ([block#2882](block#2882)) ([`20bff5910`](block@20bff59)) - fix(relay): preserve reconnect backoff ([block#2759](block#2759)) ([`499c5d349`](block@499c5d3)) - refactor(relay): expose reconnect timing policy ([block#2310](block#2310)) ([`2f0041595`](block@2f00415)) - fix(desktop): clear stale working badges on agent stop/restart ([block#2803](block#2803)) ([`a64cc71f6`](block@a64cc71)) - fix(desktop): surface agent rename relay profile sync failure as a warning toast ([block#2279](block#2279)) ([`5e3d2e484`](block@5e3d2e4)) - fix(discovery): inject PATH into Codex adapter planning ([block#2767](block#2767)) ([`6ab3835f3`](block@6ab3835)) **To release:** merge this PR. The tag and build will happen automatically. Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Signed-off-by: marccopson <marc.copson@macsurfacing.co.uk>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Outcome
Upgrades MAC Workspace from its current
macfoundation to upstream Buzzv0.5.0while retaining MAC branding, COS Running Order, My Actions, role gates and the closed-loop bridge contracts.Exact revisions
20f87942b67c8059269198acbdb7958038cd3c78v0.5.0/4a977c588a540be38bd8ddb268cd24437bac81659b4aa8c61cf7c0f5478c1b93d9d899beb86c9791Verification
just ci— PASSsame_vendor_isolatedGitHub PR CI must additionally pass the native Android debug APK build and Docker builds before merge.
Boundaries
This does not activate Jake, deploy production, or expand the approved pilot. Those remain separate Tier-3 boundaries.