## Summary
- Deletes 6 orphaned files in `desktop/src/features/agents/ui/` (556
lines) that formed a closed cluster with zero imports from the reachable
module graph — orphaned by the 1B dialog consolidation
(`PersonaCatalogSurface`, `PersonaCatalogSection`,
`PersonaCatalogDetailsSheet`, `PersonaCatalogSelectionBadge`,
`PersonaIdentity`, `PersonaLibraryEntryPoints`).
- Removes a stale `check-file-sizes.mjs` override entry for the
no-longer-existent `PersonaDialog.tsx`.
Verified dead two ways: import-graph reachability walk from all entry
points puts all six outside the reachable set, and `tsc --noEmit` passes
clean after deletion. Their `data-testid`s have zero references outside
the cluster. `PersonaCatalogDialog.tsx` is alive (`AgentsView` imports
it) and stays.
Independent of #1968 — pure dead code removal, no behavioral change.
## Summary
- retire Virtua prepend reconciliation on every ordinary reader wheel
event, including events suppressed from its separate wheel-timing
heuristic
- keep Ctrl+wheel browser zoom excluded from reader ownership
- arm the Ctrl+wheel prepend probe before pagination begins so it cannot
miss the commit
- preserve ESM/CJS patch parity and update the patch lock hash
## Why main was red
PR #2855 added the reader-wheel retirement action after Virtua's
existing suppression guard. Once the first event set that guard, later
events in the same wheel burst returned before retiring prepend mode. A
late ResizeObserver correction could then pull the viewport backward by
20–40px. The same test had failed twice on #2855 but passed its final
retry, so the PR job appeared green; the merge commit lost all three
retries.
The separate Ctrl+wheel failure was a test race: its MutationObserver
was registered after the request had already been triggered and could
miss the prepend commit.
## Validation
- desktop full unit suite: 3,515 passed
- pre-push desktop check: passed
- `git diff --check`: passed
- CI is the E2E verification; no local E2E was run
Fixes the main-branch failure in CI run 30180099007.
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
## Summary
- make Virtua the sole prepend geometry/correction owner by accumulating
active prepend ResizeObserver corrections from the live DOM offset
- retire prepend reconciliation on ordinary reader wheel input, while
preserving it for Ctrl+wheel browser zoom
- remove Buzz's competing three-second semantic-anchor watcher and
corrective `scrollBy` loop
- keep ESM/CJS Virtua patch behavior equivalent and update the patch
lock hash
## Why
Buzz admitted prepended rows using seeded estimates, then Virtua
received multiple measurement corrections for the same transaction. Each
correction was based on the same stale model offset, so a later write
replaced an earlier correction instead of accumulating it. In the
reproduced first page, that resurrected 452px of anchor drift; the
app-level watcher merely corrected the lost virtualizer write afterward.
This fixes the correction inside Virtua and deletes the competing app
writer, following the single-owner geometry invariant used by Berd
rather than copying its spacer implementation.
## Validation
- watcher-off desktop virtualization matrix: 11/11 passed, including 15
cascading prepends, continued wheel input, detached rich-row growth,
channel switching, bottom follow, and buffered live arrivals
- focused cascading prepend/Ctrl+wheel regression passed
- desktop typecheck passed
- desktop unit suite passed: 3,495 tests
- Biome passed on changed desktop files
- `git diff --check` clean
## Manual behavior
Load older history repeatedly while scrolling upward, then wheel
downward during/after a prepend. The visible anchor should stay within
the existing 5px contract during reconciliation, and deliberate reader
movement should not be pulled back. Ctrl+wheel during the prepend commit
must not cancel reconciliation.
---------
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Desktop's active-turn store capped tracked concurrent turns per agent at
`4` while the harness runs `DEFAULT_AGENT_PARALLELISM = 24` parallel
agent subprocesses and accepts up to `32` (`--agents` /
`BUZZ_ACP_AGENTS`, `value_parser range(1..=32)`). Turns above the cap
were silently evicted, so a genuinely-running turn lost its working
badge in the sidebar and the agents popover.
The eviction also caused the badge set to rotate indefinitely. Evicted
turns are still alive, so their hosts keep emitting `turn_liveness`
every 10s; `recordActivity` can't find the evicted turn, `resurrectTurn`
recreates it, and that eviction drops one of the surviving turns. With
two live turns above the cap the visible set churned every 10 seconds.
`MAX_TURNS_PER_AGENT` exists only to bound map growth, so it now sits at
the harness's hard upper bound of `32` — unreachable for any
legitimately-configured agent while still keeping the per-agent map
bounded. `MAX_TERMINAL_TOMBSTONES` derives from it (`* 4`), so the
tombstone cap moves from 16 to 128.
Two regressions cover the reported symptom: a default-parallelism agent
working in 24 channels keeps all 24 badges, and the tracked channel set
stays stable as `turn_liveness` arrives for turns that previously would
have been evicted.
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
## Why
Manual relay recovery polls every three seconds, repeatedly consuming
the session's exponential-backoff timer and leaving reconnect behavior
stuck or noisy on degraded networks.
## What
- Replace fixed-cadence Phase 3 polling with observation of the
RelayClient background reconnect loop
- Raise the fast-path deadline above the native websocket timeout and
enforce that contract in a regression test
- Keep the existing 120-second backstop as a soft UI timeout without
stopping background retries
## Risk Assessment
Medium — this changes live relay recovery timing, but removes a
competing retry loop rather than adding one. The existing
connection-state subscription remains the success signal.
## References
- Stacked on #2310 (`lazyjoe/reconnect-testability-refactor`)
- Investigation: `RESEARCH/BUG_RELAY_RECONNECT_HANG.md`
- `just ci`
- `cd desktop && pnpm typecheck && pnpm check && pnpm test` (3485 pass)
- `git diff --check`
Generated with Codex
Signed-off-by: npub1x4hk035p3p9q39a3fcrd2fe30lpkrhr5dwe0cqzzjphxyyh8m0gsq4vqap <356f67c681884a0897b14e06d527317fc361dc746bb2fc0042906e6212e7dbd1@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1x4hk035p3p9q39a3fcrd2fe30lpkrhr5dwe0cqzzjphxyyh8m0gsq4vqap <356f67c681884a0897b14e06d527317fc361dc746bb2fc0042906e6212e7dbd1@sprout-oss.stage.blox.sqprod.co>
## Why
Prepare the relay reconnect controller for an isolated behavior-fix PR
without changing runtime behavior in this one.
## What
- Export the current reconnect timing policy from
`RelayReconnectController`
- Allow controller tests to inject the complete timing policy
- Add characterization coverage for the production timing values and
injected fast-path/poll/backstop timers
## Risk Assessment
Low — this preserves the existing production timing values and only
replaces private module constants with a default policy object used by
the controller. The weak reconnect-timer and backstop wrapper
extractions were removed from this PR.
## References
- `cd desktop && pnpm typecheck`
- `cd desktop && pnpm check`
- `cd desktop && pnpm test` (3373 pass)
- `git diff --check`
- Push hooks were bypassed after the requested desktop validation
because the broad pre-push hook runs without Hermit here and fails on
Node 20/pnpm 11 plus unhealthy local Postgres services.
Generated with Codex
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: npub1m0vvn9qm5md0a080p27qzkm9uaw49e699ukwfq7fc0756xq0y5zqhzhdk2 <dbd8c9941ba6dafebcef0abc015b65e75d52e7452f2ce483c9c3fd4d180f2504@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Carl <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@sprout-oss.stage.blox.sqprod.co>
When Desktop stops or restarts a managed-agent pair, the harness is
SIGKILLed after 1s and `turn_completed` never lands.
`activeAgentTurnsStore` then sees the "all turns silent at once" pattern
and delays badge cleanup for up to 3 min (the pause that protects live
badges during transient relay-stream gaps). This is unnecessary when
Desktop itself issued the kill — there is no relay-gap ambiguity.
## What changed
**`activeAgentTurnsStore.ts`** — new `clearActiveTurnsForAgent(pubkey)`
- Tombstones every live turn for the agent via `recordTerminal` (blocks
in-flight `turn_liveness` frames from resurrecting them via
`resurrectTurn`)
- Removes the agent's entry from `activeTurnsByAgent`
- Preserves `lastProcessed` (watermark) — full-buffer replay after the
clear is a no-op
- Preserves `clockOffsetByAgent` — still valid, harmless
**`managedAgentRuntimeHooks.ts`** — clearing at the successful-stop
boundary
- New `clearActiveTurnsForAgentOnStop(pubkey, relayUrl?)` — relay-scope
gate: only clears when the stopped pair's relay matches the active
community (pair-scoped), or when an active community is configured
(agent-wide ops)
- New `restartManagedAgentPair(pubkey, relayUrl, stop, clear, start)` —
dependency-injected stop → clear → start sequence; the `restart` branch
of `useManagedAgentRuntimeAction`'s `mutationFn` is a single call into
it. The clear fires after a successful stop and before start begins, so
the badge is gone even when start fails, a failed stop clears nothing,
and no clear can run after the new process is spawned (genuinely-new
turns are never wiped)
- `useManagedAgentRuntimeAction.onSuccess` clears for `stop` actions,
before the query-cache update
**`managedAgentControlActions.ts`** — `onStopped` callback on
`respawnManagedAgentWithRules`, invoked after the stop promise resolves
and before start begins
**`welcomeKickoff.ts`** — same `onStopped` boundary on
`restartWelcomeTeammate`
**Call sites covered (all stop/restart UI paths):**
- `useManagedAgentRuntimeAction` — pair-scoped stop (`onSuccess`) and
restart (`restartManagedAgentPair` in `mutationFn`); Members-sidebar +
settings card
- `useMembersSidebarActions.handleRespawnAll` — via `onStopped`
- `useMembersSidebarActions.handleStopAll` — direct local-stop branch
- `useMembersSidebarActions.handleLifecycleAction` — local-stop fallback
branch
- `useAgentLifecycleActions.handleAgentPrimaryAction` — Agents-tab stop
- `useAgentLifecycleActions.handleAgentRestart` — via `onStopped`
- `useManagedAgentActions.handleStop` / `handleBulkStopRunning` — Agents
screen
- `useAutoRestartPolicy` — inline, between stop and start
- `restartWelcomeTeammate` call site — via `onStopped`
Provider agents are excluded at each site: they go through `!shutdown`
(relay message), not a direct harness kill.
## Tests
Twelve behavior tests across three files:
- `activeAgentTurnsStore.test.mjs` (6) — clear removes the agent's turns
and notifies subscribers, other agents untouched; full-buffer replay
after clear is a no-op (watermark preserved); late `turn_liveness` frame
with timestamp ≤ clear time does not resurrect (tombstone); new
`turn_started` after clear is tracked normally; badge gone when stop
succeeds even if start fails; new frame during start-pending does not
resurrect the cleared badge
- `managedAgentControlActions.test.mjs` (3) — `onStopped` fires on
stop-success/start-failure; does not fire on stop-failure; strict stop →
`onStopped` → start ordering
- `managedAgentRuntimeHooks.test.mjs` (3) — pair-restart seam: clear ran
when start fails (rejection propagates); stop failure invokes neither
clear nor start; strict stop → clear → start ordering
---------
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Fixes#1822
## Problem
Renaming an agent via the agent settings dialog performs a local save
(always succeeds) and a best-effort relay kind:0 republish (can fail:
network blip, auth expiry, relay unreachable). On sync failure the
dialog `console.warn`'d the error and closed as a clean success — the
user had no signal that the relay still holds the old name, which breaks
`@mention` resolution and shows the stale name in other agents' `From:`
lines (discovered via #1743).
## Fix
Surface `profileSyncError` as a `toast.warning` in
`AgentInstanceEditDialog`, matching the treatment the create and
persona-save paths already give the same field
(`useManagedAgentActions.ts`, `UserProfilePanelPersonaSubmit.ts`). The
save is not blocked — the local rename is valid and persists, per the
issue's guidance.
The toast points at the retry path that actually works: **restarting the
agent**. Re-saving the same name does not retry — `update_managed_agent`
computes `name_changed` against the already-updated record, so a second
identical save skips the sync — but `start_managed_agent` fires
`reconcile_agent_profile`, which queries the relay's kind:0 and
republishes when the display name diverges.
Scope notes:
- `EditRespondToDialog` (the third caller of the update mutation) never
changes the name, and the Rust side only sets `profile_sync_error` when
the name changed — so no change needed there.
- The alternative fix in the issue (retry-with-backoff in
`sync_managed_agent_profile`) is not taken here;
`reconcile_agent_profile` on agent start already provides self-healing,
and this change makes that path discoverable at the moment of failure.
## Testing
- `just desktop-check` — biome + file-size + px-text + pubkey-truncation
guards clean.
- `just desktop-test` — 3331 passed, 0 failed.
- The handler branch is a straight conditional on the mutation result;
the repo's `.test.mjs` convention covers extracted pure logic, and there
is no extracted logic here to unit-test (consistent with the equivalent
toast branches on the create/persona paths).
---------
Signed-off-by: ayobamiseun <adegokeayobamiseun@gmail.com>
Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@buzz.block.builderlab.xyz>
## Summary
- render mobile pairing QR codes locally with Wallet-inspired styling
- hide the raw pairing URI behind a matching copy button
- add focused rendering and dialog coverage
## Test plan
- desktop lint and 3,489 desktop tests
- focused pairing dialog Playwright test
- decoded the rendered QR back to the exact pairing URI
## Summary
- Replace all references to a specific corporate VPN product name with
generic "VPN" / "corporate VPN" / "VPN tunnel" / "VPN CLI" language
across 15 files (21 lines)
- Comment-only and doc-only changes — zero functional impact
- Keeps the OSS repo free of vendor-specific assumptions
`rg -i warp` returns zero hits after this change (excluding
`node_modules`, lockfiles).
## Why
Installing the Codex, Claude, or Goose desktop app does not install the
command-line harness Buzz needs. The current UI makes that distinction
unclear, links some missing-CLI states to adapter documentation, and can
report a successful install from the installer exit code even when
runtime discovery still fails. On Windows, Buzz also invokes Goose's
Bash installer, which writes the executable somewhere Buzz does not
discover.
## What
- distinguish missing vendor CLIs from missing or outdated ACP adapters
in runtime metadata and UI guidance
- link Codex, Claude Code, and Goose missing-CLI states to their
official CLI installation documentation
- explain in Settings, onboarding, and agent configuration that the
desktop app alone is not sufficient
- use Goose's official PowerShell installer on Windows
- refresh PATH and rediscover the requested runtime after installation,
keeping the control retryable if the runtime is still unavailable
- add Rust and Playwright regression coverage for Windows installer
selection, CLI/adapter guidance, false-success prevention, verified
installs, and onboarding copy
## Risk Assessment
Medium. This changes desktop onboarding and runtime installation
behavior. Successful installs now require the runtime catalog to verify
availability; previously hidden discovery failures will surface as
actionable errors instead of a false success state.
## References
- [Codex CLI installation](https://developers.openai.com/codex/cli/)
- [Claude Code
installation](https://code.claude.com/docs/en/getting-started)
- [Goose
installation](https://goose-docs.ai/docs/getting-started/installation/)
- Follow-up to #2563 and #2587
## Validation
- `just desktop-typecheck`
- `just desktop-test` — 3,455 passed
- focused Rust post-install verification tests
- focused Playwright Doctor/onboarding coverage (in progress; CI and
local sequential rerun will provide final results)
Generated with Codex
---------
Signed-off-by: Atish Patel <atish@squareup.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Goose <opensource@block.xyz>
## Summary
- Preserve plain-string errors returned by Tauri and show an actionable
message when huddle audio is unavailable in the relay deployment.
- Use one error formatter across channel, timeline, wave, and profile
huddle actions while preserving other relay and device errors.
- Complete the huddle lifecycle when audio setup fails after publishing
a start event, preventing peers and reloaded clients from reconstructing
a phantom active huddle.
- Cover unavailable-audio formatting and START → rollback-END
reconstruction with regression tests.
## Behavior
The Tauri huddle commands reject with a plain string. Several desktop
toast call sites only preserved JavaScript `Error` objects, so the relay
message was discarded and replaced with “Failed to join huddle.”
The desktop now recognizes `huddle_audio_unavailable` and the current
relay message, then shows:
> Huddle audio isn’t available on this server. Ask an administrator to
turn it on.
Other relay and device messages remain intact, including microphone
errors.
`start_huddle` also publishes `KIND_HUDDLE_STARTED` before audio setup.
If setup fails, rollback now publishes `KIND_HUDDLE_ENDED` through the
normal end-and-archive path before resetting local state. This makes the
failed start observable to lifecycle reconstruction and prevents stale
join affordances.
## Checks
- `cd desktop && pnpm test` — 3,405 passed
- `cd desktop/src-tauri && cargo test` — 1,560 passed, 13 ignored; 3
diagnostic tests passed
- `cd desktop && pnpm exec playwright test tests/e2e/channels.spec.ts
--project=smoke --grep 'huddle rollback end event'` — passed
- `just desktop-tauri-check`
- `cd desktop && pnpm typecheck`
- `cd desktop && pnpm check`
- Pre-commit and pre-push hooks