Skip to content

fix(desktop): centralize known-agent trust set in useKnownAgentPubkeys#1703

Draft
matt2e wants to merge 2 commits into
mainfrom
edit-agent-from-error
Draft

fix(desktop): centralize known-agent trust set in useKnownAgentPubkeys#1703
matt2e wants to merge 2 commits into
mainfrom
edit-agent-from-error

Conversation

@matt2e

@matt2e matt2e commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Different desktop surfaces (channel timeline, thread panel, home inbox) each derived their own "known agent pubkeys" set from different source subsets, so the same event could pass the agent trust gate on one screen and fail it on another. This PR centralizes that baseline in a single shared hook.

  • New useKnownAgentPubkeys hook (desktop/src/features/agents/useKnownAgentPubkeys.ts): the workspace-scoped known-agent baseline — managed agents ∪ relay-registered agents ∪ home-feed agent activity, normalized via normalizePubkey. Backed by React Query (follows workspace switches for free) and content-stabilized via a new useStableSet helper so the set is safe as a memo dependency in render-hot consumers.
  • Pure merge extracted to mergeKnownAgentPubkeys (knownAgentPubkeys.ts) with node unit tests covering source merging, undefined sources, and case/whitespace normalization.
  • MessageRow derives agent-ness itself from the shared baseline plus a per-pubkey profiles[pk]?.isAgent check, instead of receiving an agentPubkeys prop. The prop is removed from the entire pass-through chain (ChannelPaneMessageTimelineTimelineMessageListMessageRow, and MessageThreadPanel), shrinking the React.memo comparator surface.
  • getConfigNudgeAuthorPubkey (the config-nudge trust gate) now takes an isKnownAgentPubkey predicate instead of a raw set, so surface-local signals compose with the shared baseline; signer-vs-attributed-author semantics are unchanged and tests are updated.
  • ChannelScreen and HomeView now widen the shared baseline with their surface-local signals (channel-member roles, profile lookups) rather than building independent sets.

Surface-local signals remain additive on top of the baseline — they can widen it but never diverge from it.

Testing

  • New unit tests for mergeKnownAgentPubkeys (merge, empty, normalization/dedup)
  • Updated configNudgeAuthPubkey tests for the predicate signature
  • Pre-push suite passed: desktop tests, desktop Tauri tests, mobile tests, Rust unit tests (buzz-core, buzz-auth, buzz-db, buzz-conformance)

🤖 Generated with Claude Code

matt2e and others added 2 commits July 10, 2026 16:24
The config-nudge trust gate (and bot avatars/popovers/mention pills)
previously depended on per-surface agent-pubkey sets derived from
different sources: the channel screen merged channel members + managed
agents + relay agents + profile isAgent flags, the Home inbox derived
its set from feed agent activity + profiles, and several surfaces
passed nothing at all. The same event could therefore render the
ConfigNudgeCard on one screen and fail the gate on another.

Centralize the baseline in a new workspace-scoped useKnownAgentPubkeys
hook (managed agents ∪ relay agents ∪ home-feed agent activity, backed
by React Query so it follows workspace switches without a
resetWorkspaceState entry, content-stabilized via a new useStableSet
helper). MessageRow now consumes the hook directly — the agentPubkeys
prop and its drilling through ChannelPane, MessageTimeline,
TimelineMessageList, and MessageThreadPanel are removed — and folds in
the signer profile's isAgent flag with per-pubkey O(1) checks instead
of the old per-surface set scans. getConfigNudgeAuthorPubkey takes the
predicate instead of a set. ChannelScreen and HomeView keep their
surface-local extras (channel-member roles, profile-lookup scans) for
non-MessageRow consumers, but union them on top of the shared baseline
so they can only widen trust, never diverge from it.

Implements step 2 of the config-nudge rendering plan; step 1 (graceful
prose fallback when the gate fails) is separate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Follow-up to the useKnownAgentPubkeys centralization (1911921), whose
review flagged that subscribing the three source queries inside every
MessageRow caused refetch churn (each batch of row mounts re-triggered
stale-data refetches, including the deliberately relaxed unfiltered
listRelayAgents query) and periodic full-timeline render sweeps (source
polls re-rendered every mounted row before useStableSet could bail),
and that agents known only through channel membership (role "bot" or
member isAgent, with no profile flag and no managed/relay/feed
presence) regressed from bot to human rendering in rows.

Restructure useKnownAgentPubkeys as a KnownAgentPubkeysProvider that
owns the app's only subscription to the source queries and publishes
the content-stable merged set over context; the hook keeps its name and
call sites and now just reads the context (empty set outside the
provider). The provider mounts in AppReady around RouterProvider, under
the workspace-keyed WorkspaceQueryProvider remount boundary, so
observers tear down on workspace switch without a resetWorkspaceState
entry. Query churn re-renders only the provider — children are
referentially stable — and consumers re-render only on actual
membership change.

Restore member-only bot trust by folding channel-member agent flags
into the profiles lookup MessageRow already receives and memo-compares:
a new mergeMemberAgentFlagsIntoProfiles (sibling to
mergeAgentNamesIntoProfiles) marks bot-role/isAgent members as
isAgent: true, so MessageRow's per-pubkey profiles[pk]?.isAgent check —
the config-nudge gate, agent mention pills, popover role — sees them
again. The merge + profileLookupsEqual ref stabilisation moved from
ChannelScreen into a new useMessageProfiles hook, ratcheting the
ChannelScreen file-size override 1002 -> 972.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant