Files
buzz/desktop/tests
Wintermute a3b0d04593 fix(desktop): pin the signing identity across scoped sends; scope agent startup; require self-membership on restore
Round-six review found three holes in the tenant-scope guard:

1. Relay URL and signing keys mutate under SEPARATE locks during a
   workspace switch, so a switch landing between the relay check and the
   key read signed (and NIP-98-authenticated) the old tenant's content
   under the NEW tenant's identity. Commands now read ONE key snapshot,
   assert it against a caller-captured `expected_signer_pubkey`
   (`assert_expected_signer` in relay/scope.rs, fail-closed, None =
   unscoped), and use that exact snapshot for every signature and
   NIP-98 auth: `open_dm` pins the submit and the metadata read
   (`query_relay_at_with_keys`); `send_channel_message` pins the submit
   and threads the snapshot through `resolve_thread_ref`;
   `submit_event_at_created_at` takes explicit `&Keys` instead of
   re-reading state.

2. `start_managed_agent` was an unscoped side effect — a stale Projects
   callback suspended on the startup await could activate the
   (agent, relay) pair in the new tenant. It now takes
   `expected_relay_url`/`expected_signer_pubkey`, asserts both up
   front, re-asserts the relay scope after the mesh-preflight await
   before the local spawn, and asserts the provider deploy against the
   relay embedded in the deploy payload rather than a re-read.

3. `restoreProjectsAgentConversation` rejected strangers but never
   required the signed-in user's own membership; a stale pointer naming
   an agent-only channel had no stranger to reject. `hasSelf` is now
   required alongside `hasAgent` and the stranger check.

The scope pair rides all three side effects of a Projects agent send
(start/openDm/send) in projectAgentConversation.ts; both UI callers
capture `signerScope` from the identity query before the first await.
The e2e bridge mirrors `assertExpectedSigner` on all three handlers and
gains `startManagedAgentDelayMs` for racing startup in specs.

Unit regressions: signer match/mismatch/absent in scope.rs; an
identity-swap-racing-the-send test and a blocked-startup rewrite plus
scope-pair assertions in projectAgentConversation.test.mjs; an
agent-only-channel restore rejection.

The retention-queue helpers (retain/tombstone/archive pending,
build_agent_archive_request) move verbatim from agents.rs to
commands/agents_pending.rs to satisfy the file-size ratchet agents.rs
would otherwise breach.

Co-authored-by: Thomas Petersen <thomasp@squareup.com>
Signed-off-by: Thomas Petersen <thomasp@squareup.com>
2026-08-17 12:28:41 -04:00
..