The lazy-activation comment rewrite tipped lib.rs over the 1000-line
guard (desktop-check, pre-push). Move the self-contained window-chrome
block — sidebar haptic, title-bar double-click, and the initial-window
reveal sequence — into window_chrome.rs, following the archive module's
qualified-path pattern in the invoke handler. No behavior change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revert 0f8fa7e1841cf49a33e0e4c7158cb6e54ff762a1 ("feat(desktop): show an
active-agents dot on the community rail"), backing out the rail status
dot and its supporting plumbing:
- countActiveAgentsByCommunity / hasRunningAgentAnywhere leave
agentRelayScope, the dot and communityRailTooltipLabel leave
CommunityRail, the slow 60s cross-community tier leaves the
useManagedAgentsQuery poll gate, and the E2E bridge drops the
managed-agent relayUrl seed plus the rail dot spec test.
One deliberate deviation from a pure revert: playwright.config.ts keeps
matching community-rail.spec.ts. The reverted commit had fixed a stale
workspace-rail.spec.ts testMatch left over from the spec's rename;
restoring that line would silently de-register the spec's seven
remaining pre-existing tests, which are unrelated to the dot feature.
The later hardening commit a4974089's fixture-agreement test and
normalizer doc comments in agentRelayScope are untouched by the revert.
Tested: desktop pnpm test (2942 passed), tsc --noEmit, biome check,
Playwright community-rail spec (7 passed) against a fresh build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Resolve the review findings on the lazy multi-workspace agent stack
(review of f3baa96) — four hardening fixes, no behavior change on the
happy path:
- Serialize apply_workspace's .repos-dirs.json upsert on
managed_agents_store_lock. A community relay edit fires
rebind_agent_relay (which moves map entries under that lock) nearly
simultaneously with the reinit apply, and both are read-modify-writes
of the same file — unserialized, one side's update could be lost.
- Write .repos-dirs.json via temp file + rename. A crash mid-write left
truncated JSON, which read_repos_dir_map degrades to an empty map —
silently sending spawns back to the shared REPOS fallback, exactly
the cross-workspace hazard the map exists to prevent.
- Pin the Rust/TS relay-URL normalizers together with a shared fixture
(desktop/fixtures/relay-url-normalization.json) consumed by both
relay tests and agentRelayScope.test.mjs, so an edit that lands on
only one side fails the other side's tests instead of shipping a
scoping skew. Both normalizers' doc comments now point at it.
- Sequence updateCommunity's state commit (and the reinit it triggers)
after the rebindAgentRelay IPC settles. Fire-and-forget let
apply_workspace(newUrl) race ahead of the rebind: activation ran
while start-on-launch agents were still pinned to the old URL and
marked the relay activated for the session, silently skipping them
until app relaunch. A rebind failure still commits — pins stay
recoverable by re-editing the community.
The fifth finding (stale "running in other communities" count) was
already resolved by the slow cross-community poll tier added in
0f8fa7e. Split relay.rs's inline test module into relay/tests.rs
(file-size guard, mirroring repos/tests.rs).
Tested: cargo test --manifest-path desktop/src-tauri/Cargo.toml (1435
passed, incl. the new temp-file and fixture-agreement tests), clippy
--all-targets -D warnings, rustfmt, desktop pnpm test (2955 passed,
incl. the new fixture-agreement test), tsc --noEmit, biome check,
pnpm check:file-sizes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Give each community button in the experimental far-left rail
(workspaceRail) a green status dot when that community has active
managed agents, so concurrently running workspaces stay visible at a
glance (extends the Phase 3 per-community agents UI scoping):
- Add countActiveAgentsByCommunity to agentRelayScope: active (running
or deployed, mirroring isManagedAgentActive) agents per community id,
matched on the normalized relay pin. A blank pin counts toward the
active community only — the per-surface "blank follows the community
being viewed" fallback would light every rail dot for one stray
unstamped record. Communities sharing a relay URL both light up.
- Render the dot at the button's top-right (unread badge/dot keeps
bottom-right), emerald to mirror the agents list's "online"
PresenceDot, with the count woven into the tooltip/aria label via a
new pure communityRailTooltipLabel ("Bravo — 1 agent active").
- Add a slow 60s tier to the useManagedAgentsQuery poll gate while
agents run only in other communities, so a background process dying
clears their rail dots (and the "running in other communities" line)
instead of staying green forever — previously that state could go
permanently stale once the active community had nothing running.
- Seed support: mock managed agents accept a relayUrl pin in the E2E
bridge, and the rail spec covers dot-on-running / no-dot-on-stopped
across two communities.
- Re-register the rail Playwright spec: playwright.config.ts still
matched the old workspace-rail.spec.ts name after the community-rail
rename, so the spec had silently stopped running.
Tested: desktop pnpm test (2954 passed, incl. 13 new unit tests), tsc
--noEmit, biome check, Playwright community-rail spec (8 passed, incl.
the new dot test), plus a visual check of the rendered dot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
buzz users presence misidentified who is online: the relay's /query
bridge intercepts presence filters and synthesizes relay-signed
kind:20001 events (bridge.rs synthesize_presence) with the subject in
the p tag, but cmd_get_presence mapped event.pubkey — the relay
keypair — so every row reported the relay signer's pubkey.
Extract the row mapping into map_presence_events, which reports the
p-tag subject when present and falls back to the event author
otherwise. The fallback keeps real user-signed kind:20001 updates
correct (build_presence_update emits no p tag — the author is the
subject there) and guards against a malformed bare ["p"] tag blanking
the pubkey.
Regression tests cover all three shapes: synthesized (p-tag subject
wins over the relay signer), user-signed (author reported), and
malformed p tag (author fallback).
Tested: cargo test -p buzz-cli --lib (149 passed, incl. the 3 new
tests), cargo clippy -p buzz-cli --all-targets -D warnings, cargo fmt
--check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
applyCommunity sent a `token` arg that the Rust apply_workspace command
stopped declaring when the relay moved to pure Nostr key auth ("Sprout
speaks Nostr. Nothing else.", #475). Tauri silently drops invoke args
the command doesn't declare, so the Community.token collected by the
add/edit dialogs was persisted to localStorage and never applied
anywhere — a secret-shaped field with no consumer. Resolve the arg
mismatch by deleting the plumbing rather than re-accepting the token:
- Stop sending `token` from applyCommunity and align its invoke args
with the Rust signature (relayUrl, nsec, reposDir).
- Remove the API Token field from the Add/Edit Community dialogs, and
drop token handling from useCommunities (update-result matrix,
dup-merge on add, reinit trigger) and the sidebar/switcher prop
types.
- Retire Community.token to a `@deprecated token?: never` marker and
strip the stale secret from persisted entries in loadCommunities
(extending the existing nsec-strip migration), so it cannot leak
from localStorage into future sessions.
- Regression tests: applyCommunity must send exactly the arg keys
apply_workspace declares (guards the silent-drop IPC contract), and
loadCommunities strips token/nsec and persists the cleaned list.
Tested: desktop pnpm test (2941 passed, incl. the 2 new regression
tests), tsc --noEmit, biome check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Make agents immune to the shared REPOS symlink re-pointing on workspace
switches (Phase 4 of lazy multi-workspace agents, building on the Phase
1-3 relay pinning, lazy activation, and UI scoping):
- Persist repos_dir per relay URL in a new .repos-dirs.json nest dotfile
(keys normalized like agent relay pins), written by every
apply_workspace alongside the single-value .repos-dir that keeps
driving the boot-time symlink resolve. The REPOS symlink itself keeps
following the active workspace as a human/tooling convention.
- At spawn, resolve the agent's own workspace entry (keyed by its
effective relay) to a canonical real path and hand it to the child as
BUZZ_REPOS_DIR, so a mid-task switch that re-points REPOS can no
longer land a workspace-A agent in workspace B's checkouts. A
configured-but-unresolvable entry fails the spawn closed (same
rationale as resolve_repos_at_boot); no entry falls back to the nest
REPOS path, preserving pre-map behavior.
- Adjust the nest AGENTS.md instructions (template v5) that referenced
REPOS/ relatively: agents are told to address checkouts through
$BUZZ_REPOS_DIR whenever it is set.
- Hash the raw per-relay map value into the spawn-config hash so a
repos-dir edit badges needsRestart while a pure workspace switch (and
filesystem state alone) cannot.
- Migrate the map entry in rebind_agent_relay when a community's relay
URL is edited, so rebound agents keep their repos-dir isolation.
- Split the grown repos.rs test module into repos/tests.rs (file-size
guard), mirroring nest/tests.rs and spawn_hash/tests.rs.
Tested: cargo test --manifest-path desktop/src-tauri/Cargo.toml (1433
passed, incl. 7 new repos-map/resolution tests and 2 new spawn-hash
tests), clippy --all-targets -D warnings, rustfmt --check, cargo check
--features mesh-llm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Filter every managed-agent surface to the active community's relay so
concurrently running workspaces stop bleeding into each other's UI
(Phase 3 of lazy multi-workspace agents, building on the Phase 1 relay
pinning and Phase 2 lazy activation):
- Add agentRelayScope helpers: a frontend mirror of the backend relay
normalizer plus agentBelongsToRelay / partitionAgentsByRelay /
hasRunningAgentInCommunity. A blank pin follows the active community
(same defense-in-depth fallback as effective_agent_relay_url), and a
missing provider/community degrades to unscoped rather than blanking
every surface.
- Scope the useManagedAgentActions list — and the bulk stop, start/
stop/delete lookups, and presence derived from it — to the active
relay via a new lenient useActiveRelayUrl hook. Persona delete keeps
counting instances against the unscoped record set since deleting a
persona removes instances in every community.
- Gate the 5s managed-agents liveness poll on a running agent in this
community; agents running in other communities render no process
state here, so they no longer keep the poll alive.
- Hold auto-restart for agents pinned to other communities — their
working/observer signals are read from the active relay only, so a
pure workspace switch must never fire a restart — and re-check the
pin in the pre-fire re-fetch so a rebind cannot restart a foreign
agent.
- Scope mergeKnownAgentPubkeys' managed-agent source to the active
relay; the relay-agent (kind:10100) source stays unfiltered.
- Surface an "N agents running in other communities" line in the
agents header so concurrent background agents stay discoverable.
Tested: desktop pnpm test (2939 passed, incl. new agentRelayScope,
knownAgentPubkeys, and autoRestartPolicy unit tests), tsc --noEmit,
biome check, Playwright smoke project (537 passed, 5 failed; 2 of the
failures passed on rerun and the other 3 — channels intro-scroll,
video review mode, shared-compute empty state — reproduce on baseline
HEAD without this change, so all 5 are pre-existing flakes).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Extract agent activation out of the one-shot boot restore into a
relay-filtered step that runs on every workspace apply (Phase 2 of lazy
multi-workspace agents, building on the Phase 1 relay pinning):
- Rename restore_managed_agents_on_launch to activate_workspace_agents,
taking the applied workspace relay and starting only local
start-on-launch agents pinned to that relay (normalized match; a blank
pin matches the visited workspace, mirroring the
effective_agent_relay_url defense-in-depth fallback).
- Call it from every apply_workspace instead of only the first — boot
restore becomes the session's first activation. The one-shot
managed_agent_restore_pending flag now gates only the mesh-llm Share
Compute restore.
- Track activated relays in AppState (activated_agent_relays) so each
workspace activates at most once per app session: bouncing A→B→A never
resurrects agents the user manually stopped in A. Nothing is stopped
on switch — each workspace's agents keep running against their own
relay.
- Gate the boot-time repos-dir/identity-recovery safety checks behind a
new session-wide managed_agent_activation_enabled flag, and run the
stale-process/orphan sweeps only on the session's first activation so
a later activation cannot reap a concurrent activation's
not-yet-tracked children.
Tested: cargo test --manifest-path desktop/src-tauri/Cargo.toml (1424
passed, incl. 7 new activation unit tests), clippy -D warnings, rustfmt,
cargo check --features mesh-llm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Stop agent records from floating to whichever workspace is active — the
relay URL becomes the backend-side ownership key for agents (Phase 1 of
lazy multi-workspace agents):
- Stamp the resolved workspace relay onto records at create, on an
explicit relay edit that clears the field, and on snapshot/team
imports, instead of persisting blank.
- Migrate legacy blank-relay records on the first apply_workspace after
boot. Behavior-preserving: blank resolved to exactly that relay at
boot restore.
- Add a shared relay-URL normalizer (trailing slash, scheme/host case)
and hash the normalized resolved relay in the spawn-config hash so
cosmetic URL differences no longer trip the restart badge.
- Add a rebind_agent_relay command, invoked by updateCommunity when a
community's relay URL is edited, re-pinning records from the old URL
onto the new one so those agents don't orphan.
effective_agent_relay_url keeps its blank-to-workspace fallback as
defense-in-depth for records that escaped stamping. The pure record
mutations live in the new managed_agents::relay_pinning module.
Tested: cargo test --manifest-path desktop/src-tauri/Cargo.toml (1420
passed), clippy -D warnings, rustfmt, desktop pnpm test (2920 passed),
tsc, biome.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>