feat(desktop): invite owned agents from standalone forums - #7125
Conversation
6858c35 to
9a5e328
Compare
This comment has been minimized.
This comment has been minimized.
4cd9736 to
ec5fd56
Compare
d92df38 to
8eba950
Compare
jedwards27
left a comment
There was a problem hiding this comment.
Reviewed exact head 8eba950a86be1a4d0d5847315d140c862eabee01 against base 7ffead0f8ae128782991153054abd388e4ff04d9.
Two author-actionable defects remain:
-
[P1] Same-forum thread navigation can publish the preserved reply to the thread the user left.
ForumComposersuspends submission while the Invite dialog is open, butuseForumMentionPreparationdefines freshness only bychannelId.ForumViewreuses the same unkeyed thread panel/composer whenselectedPostIdchanges within a forum. If a user begins a reply/invite on thread A, navigates to thread B while the add is pending, and the add completes, the continuation remains current and invokes the submitter captured for A while B is visible. Besides surprising publication, the accepted membership side effect cannot be rolled back. The new navigation test covers only a channel change, so it does not exercise this boundary.Author action: bind the pending attempt to the source visit/thread identity (or remount the reply composer by
selectedPostId) and add an A→B same-forum deferred-add regression proving no reply publishes to either thread and draft recovery remains owned by the correct source. This also restores the source-visit contract documented indocs/remote-mention-routing.md. -
[P2] The newly exposed forum Invite dialog is not keyboard/focus or async-error accessible. An exact-head Playwright probe observed
document.activeElement === BODYafter the alert dialog opened and again after Escape dismissed it; the changing invitation error had no alert/live-region semantics. The implementation uses ordinaryButtons rather than the available RadixAlertDialogCancel/AlertDialogActionprimitives, and the dynamically inserted error is an unannotated paragraph. Keyboard users therefore do not enter the decision controls or return to the preserved composer, and assistive technology is not notified when Invite fails.Author action: use the alert-dialog action/cancel primitives or explicit autofocus/restore handling, make the changing error an alert/live region, and add regression coverage for initial safe focus, Escape/Cancel focus restoration, and error announcement. Preserve the shared chat behavior as well.
Validation at this exact, clean head: git diff --check passed; just desktop-check and just desktop-typecheck passed; pnpm build:e2e passed; isolated Playwright forum-agent-invitation.spec.ts passed 8/8. Required CI reported green, including Desktop Core, four Desktop Smoke shards, relay/integration jobs, macOS build, Semgrep, zizmor, and DCO. The broader just desktop-test reviewer run was interrupted after roughly five minutes without an observed assertion failure, so that is a confidence gap rather than author rework. Native packaged Tauri/accessibility observation also remains a reviewer/release verification gap, not a separate author defect.
jedwards27
left a comment
There was a problem hiding this comment.
Review verdict: request changes
Reviewed base 7ffead0f8ae128782991153054abd388e4ff04d9 through exact live head 8eba950a86be1a4d0d5847315d140c862eabee01, integrating both assigned lanes. Two author-actionable defects remain.
1. Same-forum thread navigation does not invalidate a pending reply invitation
ForumComposer retains the captured submission continuation while Invite is pending (desktop/src/features/forum/ui/ForumComposer.tsx:225-251), while useForumMentionPreparation scopes freshness only to channelId (desktop/src/features/forum/ui/useForumMentionPreparation.ts:30-31,50-52,117-120). ForumView reuses the unkeyed thread panel/composer when selectedPostId changes (desktop/src/features/forum/ui/ForumView.tsx:131-166; ForumThreadPanel.tsx:331-339). Navigating thread A→B in one forum therefore leaves the attempt current; completing Invite can resume the captured reply to A while B is displayed.
Author action: bind pending preparation to source visit/thread identity (or remount by selectedPostId) and add a held-add A→B regression proving no reply publishes and draft recovery remains attached to the correct source.
2. The Invite dialog breaks keyboard-focus and error-announcement behavior
NonMemberMentionDialog does not use the Radix action/cancel primitives or explicit autofocus handling, and its dynamic failure text has no alert/live-region semantics (desktop/src/features/messages/ui/NonMemberMentionDialog.tsx:35-85). An exact-head production-build probe observed BODY focused after open and after Escape; the visible async error had no alert/live attributes. This newly exposed forum journey therefore neither moves focus safely into the modal, restores it to the preserved composer, nor announces invitation failure to assistive technology.
Author action: establish safe initial focus and focus restoration, expose changing failure text as an alert/live region, and add regressions for open, Escape/Cancel, and failure semantics while preserving the shared chat behavior.
Verification
Focused forum E2E passed 8/8; check/typecheck and E2E build passed. The incomplete broad local suite and absent packaged-native journey are reviewer/release confidence gaps, not additional author rework.
— :bot: Jude’s code review agent
7ffead0 to
1144465
Compare
8eba950 to
6781210
Compare
This comment has been minimized.
This comment has been minimized.
|
Response to your original exact-head review (both body findings; there are no inline review threads to resolve). Addressed in 67812105430e5d4d3f82bf952e67f55839de0702, on routing base
Independent review found a subsequent repair regression (failed dispatched A after returning with no new intent); source-key recovery now preserves text/uploaded media/exact refs without overwriting newer intent or replaying send. Final desktop 6016/6016; four old-source failure/new-source pass recovery assertions; six independent edge probes and expected media-snapshot mutation failure. Three new browser cases eventually pass: the initial empty-fill deletion fixture failed, then Select All/Backspace plus pre-release emptiness assertion passed the isolated rerun. That history is preserved, not called a clean initial 3/3. Latest-source TypeScript and normal OSS/internal frontend artifact matrix pass; final scoped static/size/E2E build evidence reused where exact. Updated PR body records provenance, prepared screenshots and limitations. Packaged-native accessibility, live relay/signed ownership and root security authorization remain separate; no formal review is dismissed. Requesting fresh formal review of this exact head, not treating local scoped approval as yours. |
|
Response to the integrated review verdict, separately preserving the response to both original review bodies. GitHub exposes no inline thread for either finding, so these linked comments are the response records; no formal review is resolved/dismissed by the author. The repair is 67812105430e5d4d3f82bf952e67f55839de0702, base
Final 6016 desktop tests pass. Reused 16 forum/focus and 67 shared browser passes for unchanged scope; three new recovery browser cases eventually pass (two initially, deletion after correcting an empty-fill fixture with actual keyboard deletion and an emptiness assertion). Independent 6/6 edge probes plus a failing media-snapshot mutant make the cleanup/recovery claim falsifiable. Latest normal OSS/internal artifact matrix + TypeScript pass; scoped static, file-size and E2E build passed. All committed blobs match the reviewed frozen candidate; no publisher source edits. The body includes exact evidence, prior-failure history and limits. This resolves the author-actionable implementation work, not native/release confidence or root/routing security gates. Fresh exact-head technical review and hosted CI are still required. |
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — APPROVE exact head 67812105430e5d4d3f82bf952e67f55839de0702 against base 1144465d00273cf74b7c22544ae5a3299bd98560.
Both assigned lanes found no remaining author-actionable defect.
The changed head closes the prior blockers:
- Pending forum reply intent is keyed by source visit/thread identity. Same-forum A→B→A navigation, held membership add, late completion, cancellation, recovery, and publication retain the captured source authority rather than resuming against the wrong thread.
- The Invite alert dialog now gives safe initial focus to Cancel/Do nothing, restores focus to the current source editor after Cancel or Escape, and exposes asynchronous failure with
role="alert". - Authorization is checked before membership add and again against refreshed membership before publication. Draft identity, attachments, references, retry, and shared-chat behavior remain coherent.
Exact-head evidence:
- Production-seam lifecycle suite: 21/21.
- Production-build Playwright forum invitation suite: 19/19.
- Desktop check, typecheck, file-size validation, direct TypeScript typecheck, E2E production build, and
git diff --check: PASS. - Completed hosted DCO, Desktop builds, integration shards, relay artifact, token guard, and security scanners: green. Desktop Core and four smoke shards were still pending at the final snapshot; no PR-caused required-gate failure was observed.
Confidence gaps, not author action:
- Packaged Tauri plus real screen-reader behavior was not observed. Verification owner: release/native QA for an optional assistive-technology smoke.
- Native/real-relay membership-add-to-publication atomicity cannot be proven by browser mocks. Verification owner: native integration/release validation; no reproducible defect was found.
- Remaining hosted Desktop Core/smoke completion is owned by CI. Author action: none unless a current-head required gate fails.
jedwards27
left a comment
There was a problem hiding this comment.
Review verdict: approve
Reviewed base 1144465d00273cf74b7c22544ae5a3299bd98560 through exact live head 67812105430e5d4d3f82bf952e67f55839de0702, integrating both assigned systems/integration and product/UI/accessibility lanes. The two defects from the prior head are resolved; no author-actionable defect remains.
- Source authority is now keyed to the forum post/thread visit. Pending invitation and publication continuations are invalidated across same-forum A→B navigation, while source-key draft recovery preserves the exact content, attachments, and selected identity without overwriting newer A or B intent or replaying publication.
- Membership add and final publication authorization use the captured channel, with a fresh authoritative revalidation after membership refresh. Cancellation invalidates late add/validation completion; transport failure recovery follows the same source-key ownership rules.
- The alert dialog places initial focus on the safe action, restores the current source editor after Cancel or Escape, keeps cancellation available while Invite is pending, and exposes asynchronous failures with
role="alert". Shared-chat behavior remains covered.
Exact-head evidence: production-seam lifecycle tests passed 21/21 in my clean checkout; the product lane's production-build Playwright run passed 19/19 with invite/error/sent screenshots visually inspected; desktop check, typecheck, file-size validation, and production E2E build passed across the assigned lanes. At submission time, macOS and integration checks had passed; Desktop Core and four smoke shards were still running without a reported failure. Their completion remains CI-owned and requires no author action unless they expose a PR-caused failure.
Residual confidence gap: packaged Tauri with a real relay and real screen reader was not observed. Author action: none. Verification owner: release/QA for an optional packaged-native assistive-technology and add→publish smoke.
— :bot: Jude’s code review agent
🤖 ## Summary An agent you own could be missing from **New message → To:** and **Channel members → Add people and agents** on a machine that has never managed it. This PR lets those existing lists find your agent without requiring a shared channel first. Desktop now checks records proving you own it, rather than looking only at agents in channels you've already joined. **No new screen or control is added.** For example, an agent with verified ownership and **Who can send instructions → Only me (default)** can now appear even with no shared channels. Each screen still applies its existing access rules; this does not make every discovered agent selectable everywhere. | Screen / control | Before | After this PR alone | | --- | --- | --- | | **New message → To:** recipient picker | An owned agent absent from this machine and shared-channel bot lists could be missing. | Its named **agent** row can appear; selecting it adds a recipient chip. This is recipient selection, not a guarantee that a later message will reach or wake the agent. | | **Channel members → Add people and agents** | The same agent could be missing from **Not in this channel** search results. | Its row can appear with the existing **Add** button. If you can add members, that button submits the existing channel-membership request; finding the row alone changes no membership. | | **Stream / forum composer → @ suggestions** | An owned agent already in the channel under an ordinary member role could be missing from agent suggestions. | Its actual membership is recognized without requiring the bot role. Agents not managed on this device still need membership in that channel. | | **Pulse → Agents** | An agent absent from both local management and the server's agent list was omitted from the count and author lookup. | The count and feed's author lookup can include it; notes appear only if it has published them. | Being listed does **not** mean the agent is online, add it to a channel, or grant local Start/Edit controls. For agents not managed on this device, global **Search** still excludes those configured for “Only me”, and DM @ selection is not added here. DM @ selection and message-driven nonmember invitation are addressed in [#7124](#7124); the standalone forum **Invite / Cancel** flow is in [#7125](#7125). <details> <summary>Ownership and membership checks</summary> A discovery lead is not proof: the latest agent profile must have a valid signature and exactly one valid ownership attestation—the owner's signed link to that agent. Its response policy must be signed by that verified owner; an invalid latest policy cannot restore an older permission. Membership comes separately from the latest server-signed roster, including removals. Existing profile cards, owner labels and agent-avatar shapes also use this stricter verification: malformed or forged evidence must not supply ownership/agent classification on its own. Valid ownership was already recognized; no profile-picture or badge design changes. Attestation time conditions apply to the signed event's timestamp, not a live expiry timer. Existing legacy compatibility and builds requiring verified owner policy retain their respective rules. Discovery and sending remain separate operations, not an atomic permission check. </details> ### Review corrections - When runtime and owner policy overlap, **explicit online/away/offline from the verified latest runtime is retained**. Policy still supplies ownership/permissions; claimed runtime membership is not restored. Missing/unrecognized status stays unknown, and invalid latest policy cannot revive runtime permissions. - Discovery without runtime evidence is now **unknown**, not offline: native conversion, both IPC adapters, Pulse, Projects and profile/session consumers preserve that distinction. Unknown has no status dot and is not promoted to a deployed/running agent. - Both relay-only picker paths retain the authenticated owner, including the existing **managed by you** label. The analogous global Search projection is fixed without changing its existing “anyone” filter. - Authorized stored profile activity remains visible when liveness becomes unknown/absent or the active turn ends. History reads do not start a live subscription, grant access, or imply current availability. ### Related issue Independent base: `main`. Child: [#7124](#7124), then [#7125](#7125). Extracted from [#7114](#7114), retained as historical source (`98fe33ec`). [Behavior contract](https://github.com/block/buzz/blob/3a56d17824522580fe04cae463b54f4c7ba66021/docs/owned-agent-discovery.md). Originating [Buzz discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848) · channel `f7a9536a-1738-4bad-a888-b3ea25010ef1`. ### Testing Current candidate: `3a56d17824522580fe04cae463b54f4c7ba66021`, a four-file native/test/doc runtime-status repair atop published `ae23c1c9680a881cee7eed94e259bf15bf8ce3f7`. Branch ancestry is main `1c8321cd08feb597f8bcff5195c21148fb3e98ed`; refreshed main `0e878664b08cdf7fb2d89d940bc2aa92cdc485f7` adds only the independent CI-workflow split. Read-only mergeability succeeds; this is not a tested merged-tree claim. **Local CI attempt and continuation (not an uninterrupted green run):** the new exact-head `just ci` passed formatting/static checks, workspace and Tauri clippy, workspace Rust tests, **5,910 desktop tests**, desktop production build and Tauri check. Its native main target finished **3,073 passed / 1 failed / 19 ignored** (exit 101): `cheap_discovery_reports_absent_before_any_forced_probe` saw a process-global login-shell counter of 2 instead of 0. The counter includes unrelated version/adapter probes whose tests do not hold the failed test's PATH mutex; no managed-agent discovery implementation changed in the runtime repair. The unchanged failing test then passed **three isolated invocations**. Only the failed native workspace lane was retried with `RUST_TEST_THREADS=1 just desktop-tauri-test`: **3,074 main-target tests passed / 19 ignored**, all additional workspace targets passed (exit 0). The previously unrun `just web-build mobile-test` tail then passed (exit 0; **2,019 mobile tests**). Earlier successful lanes were reused; no source/guard changes or blanket CI rerun. The original failure and all diagnostic/retry logs are retained. - **71 native `nostr_convert` tests pass**, including seven new production merge regressions: online/away/offline, missing/invalid status, policy-only, status-less latest replacement and forged latest replacement. Before production repair, those seven yielded **4 failures / 3 passing controls**. - Reused frontend evidence from `ae23c1c9` (frontend is unchanged): Desktop TypeScript and isolated E2E build pass; **9 browser tests / 0 retries**, covering both relay-only picker journeys and seven adjacent stop-control regressions. Real UI with mock Tauri IPC, not live relay/native webview. - Earlier `ae23c1c9` local `just ci` passed without failures, including 3,067 native main-target tests / 19 ignored and 2,019 mobile tests; not substituted for the new source gate above. - Reused unchanged repair evidence: **17 real-store/hook history regressions**, **161 focused tests**, and independent **9 mounted owner/bot/identity revocation/regrant transitions** with zero hook-phase native calls. The regression was falsified before repair (14 failures, 3 controls). - Signed local-server fixtures cover discovery with no local/shared record, ordinary-role membership, forged ownership, invalid signatures, duplicate authentication, wrong-owner/latest-invalid policy, revoked membership and wrong destinations. These establish native data checks, not a live agent response. GitHub checks and renewed technical/security review must apply to the current published head; earlier-head green checks are not replacement-head proof. Local source review is not formal code-owner/latest-push approval or exact-range security authorization. A green security workflow with substantive review skipped is not security clearance. ### Screenshots #### Relay-only picker evidence — `ae23c1c9680a881cee7eed94e259bf15bf8ce3f7` These cropped rows come from the two real production picker journeys in [`owned-agent-discovery.spec.ts`](https://github.com/block/buzz/blob/ae23c1c9680a881cee7eed94e259bf15bf8ce3f7/desktop/tests/e2e/owned-agent-discovery.spec.ts), using mock Tauri IPC with **no local agents and no user-search duplicate**. The fixture supplies verified-owner data and unknown availability; the browser test checks its presentation, not native signature verification. Both exact-tip journeys pass without retries. No live relay, native webview, invitation, delivery or wakeup is claimed. Before the repair, both relay-only candidate constructors discarded the owner, so the existing “managed by you” label was absent. These are after-repair captures; no before image was captured. #### New Message → To The relay-only agent retains its authenticated owner label.  #### Channel members → Add people and agents The matching result retains “managed by you” beside the existing Add action; the test does not click Add or claim membership changed.  --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
1144465 to
e66b949
Compare
6781210 to
870a2a1
Compare
Rebased forum descendant — fresh head
|
🤖 ## Summary An agent you own could be missing from **New message → To:** and **Channel members → Add people and agents** on a machine that has never managed it. This PR lets those existing lists find your agent without requiring a shared channel first. Desktop now checks records proving you own it, rather than looking only at agents in channels you've already joined. **No new screen or control is added.** For example, an agent with verified ownership and **Who can send instructions → Only me (default)** can now appear even with no shared channels. Each screen still applies its existing access rules; this does not make every discovered agent selectable everywhere. | Screen / control | Before | After this PR alone | | --- | --- | --- | | **New message → To:** recipient picker | An owned agent absent from this machine and shared-channel bot lists could be missing. | Its named **agent** row can appear; selecting it adds a recipient chip. This is recipient selection, not a guarantee that a later message will reach or wake the agent. | | **Channel members → Add people and agents** | The same agent could be missing from **Not in this channel** search results. | Its row can appear with the existing **Add** button. If you can add members, that button submits the existing channel-membership request; finding the row alone changes no membership. | | **Stream / forum composer → @ suggestions** | An owned agent already in the channel under an ordinary member role could be missing from agent suggestions. | Its actual membership is recognized without requiring the bot role. Agents not managed on this device still need membership in that channel. | | **Pulse → Agents** | An agent absent from both local management and the server's agent list was omitted from the count and author lookup. | The count and feed's author lookup can include it; notes appear only if it has published them. | Being listed does **not** mean the agent is online, add it to a channel, or grant local Start/Edit controls. For agents not managed on this device, global **Search** still excludes those configured for “Only me”, and DM @ selection is not added here. DM @ selection and message-driven nonmember invitation are addressed in [block#7124](block#7124); the standalone forum **Invite / Cancel** flow is in [block#7125](block#7125). <details> <summary>Ownership and membership checks</summary> A discovery lead is not proof: the latest agent profile must have a valid signature and exactly one valid ownership attestation—the owner's signed link to that agent. Its response policy must be signed by that verified owner; an invalid latest policy cannot restore an older permission. Membership comes separately from the latest server-signed roster, including removals. Existing profile cards, owner labels and agent-avatar shapes also use this stricter verification: malformed or forged evidence must not supply ownership/agent classification on its own. Valid ownership was already recognized; no profile-picture or badge design changes. Attestation time conditions apply to the signed event's timestamp, not a live expiry timer. Existing legacy compatibility and builds requiring verified owner policy retain their respective rules. Discovery and sending remain separate operations, not an atomic permission check. </details> ### Review corrections - When runtime and owner policy overlap, **explicit online/away/offline from the verified latest runtime is retained**. Policy still supplies ownership/permissions; claimed runtime membership is not restored. Missing/unrecognized status stays unknown, and invalid latest policy cannot revive runtime permissions. - Discovery without runtime evidence is now **unknown**, not offline: native conversion, both IPC adapters, Pulse, Projects and profile/session consumers preserve that distinction. Unknown has no status dot and is not promoted to a deployed/running agent. - Both relay-only picker paths retain the authenticated owner, including the existing **managed by you** label. The analogous global Search projection is fixed without changing its existing “anyone” filter. - Authorized stored profile activity remains visible when liveness becomes unknown/absent or the active turn ends. History reads do not start a live subscription, grant access, or imply current availability. ### Related issue Independent base: `main`. Child: [block#7124](block#7124), then [block#7125](block#7125). Extracted from [block#7114](block#7114), retained as historical source (`98fe33ec`). [Behavior contract](https://github.com/block/buzz/blob/3a56d17824522580fe04cae463b54f4c7ba66021/docs/owned-agent-discovery.md). Originating [Buzz discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848) · channel `f7a9536a-1738-4bad-a888-b3ea25010ef1`. ### Testing Current candidate: `3a56d17824522580fe04cae463b54f4c7ba66021`, a four-file native/test/doc runtime-status repair atop published `ae23c1c9680a881cee7eed94e259bf15bf8ce3f7`. Branch ancestry is main `1c8321cd08feb597f8bcff5195c21148fb3e98ed`; refreshed main `0e878664b08cdf7fb2d89d940bc2aa92cdc485f7` adds only the independent CI-workflow split. Read-only mergeability succeeds; this is not a tested merged-tree claim. **Local CI attempt and continuation (not an uninterrupted green run):** the new exact-head `just ci` passed formatting/static checks, workspace and Tauri clippy, workspace Rust tests, **5,910 desktop tests**, desktop production build and Tauri check. Its native main target finished **3,073 passed / 1 failed / 19 ignored** (exit 101): `cheap_discovery_reports_absent_before_any_forced_probe` saw a process-global login-shell counter of 2 instead of 0. The counter includes unrelated version/adapter probes whose tests do not hold the failed test's PATH mutex; no managed-agent discovery implementation changed in the runtime repair. The unchanged failing test then passed **three isolated invocations**. Only the failed native workspace lane was retried with `RUST_TEST_THREADS=1 just desktop-tauri-test`: **3,074 main-target tests passed / 19 ignored**, all additional workspace targets passed (exit 0). The previously unrun `just web-build mobile-test` tail then passed (exit 0; **2,019 mobile tests**). Earlier successful lanes were reused; no source/guard changes or blanket CI rerun. The original failure and all diagnostic/retry logs are retained. - **71 native `nostr_convert` tests pass**, including seven new production merge regressions: online/away/offline, missing/invalid status, policy-only, status-less latest replacement and forged latest replacement. Before production repair, those seven yielded **4 failures / 3 passing controls**. - Reused frontend evidence from `ae23c1c9` (frontend is unchanged): Desktop TypeScript and isolated E2E build pass; **9 browser tests / 0 retries**, covering both relay-only picker journeys and seven adjacent stop-control regressions. Real UI with mock Tauri IPC, not live relay/native webview. - Earlier `ae23c1c9` local `just ci` passed without failures, including 3,067 native main-target tests / 19 ignored and 2,019 mobile tests; not substituted for the new source gate above. - Reused unchanged repair evidence: **17 real-store/hook history regressions**, **161 focused tests**, and independent **9 mounted owner/bot/identity revocation/regrant transitions** with zero hook-phase native calls. The regression was falsified before repair (14 failures, 3 controls). - Signed local-server fixtures cover discovery with no local/shared record, ordinary-role membership, forged ownership, invalid signatures, duplicate authentication, wrong-owner/latest-invalid policy, revoked membership and wrong destinations. These establish native data checks, not a live agent response. GitHub checks and renewed technical/security review must apply to the current published head; earlier-head green checks are not replacement-head proof. Local source review is not formal code-owner/latest-push approval or exact-range security authorization. A green security workflow with substantive review skipped is not security clearance. ### Screenshots #### Relay-only picker evidence — `ae23c1c9680a881cee7eed94e259bf15bf8ce3f7` These cropped rows come from the two real production picker journeys in [`owned-agent-discovery.spec.ts`](https://github.com/block/buzz/blob/ae23c1c9680a881cee7eed94e259bf15bf8ce3f7/desktop/tests/e2e/owned-agent-discovery.spec.ts), using mock Tauri IPC with **no local agents and no user-search duplicate**. The fixture supplies verified-owner data and unknown availability; the browser test checks its presentation, not native signature verification. Both exact-tip journeys pass without retries. No live relay, native webview, invitation, delivery or wakeup is claimed. Before the repair, both relay-only candidate constructors discarded the owner, so the existing “managed by you” label was absent. These are after-repair captures; no before image was captured. #### New Message → To The relay-only agent retains its authenticated owner label.  #### Channel members → Add people and agents The matching result retains “managed by you” beside the existing Add action; the test does not click Add or claim membership changed.  --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> (cherry picked from commit 434dafe)
e66b949 to
a49846a
Compare
14d30d1 to
416a0f3
Compare
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — APPROVE at exact head 416a0f387020f562da7bb4ad6cb30f83b6545201 against base a49846a738c2ce2eae28eeea3d5e627ca97b6ba4.
No unresolved author-actionable defect remains after independent systems/integration and product/UI review.
The changed-head delta adds a post-settlement composer-revision/visit-authority fence before invitation preparation. The integrated review verified source/thread ownership, exact-recipient held membership add, fresh final authorization, cancellation and late-completion invalidation, navigation/A→B→A isolation, dispatch-failure draft/media recovery, accessible Invite/Cancel/Escape and focus/error behavior, and persistence compatibility. Removing the new fence made the targeted stale clipboard-settlement regression fail; the exact head was restored and clean before verdict.
Exact-head evidence integrated: Desktop units 6,352/6,352; forum lifecycle 25/25; production-bound forum invitation Playwright 19/19; desktop check, typecheck, production build, file-size, git diff --check, four hosted smoke shards, Windows/macOS builds, integration, DCO, Semgrep, and zizmor passed.
Confidence gaps, not author rework: Desktop Core remained in progress without observed failure at submission; native screen-reader and real-shell observation were not run. Author action: none unless Core fails causally. Verification owner: CI/integration for Core completion; native accessibility/release QA for optional platform observation.
Any new head invalidates this approval.
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — APPROVE at exact head 416a0f387020f562da7bb4ad6cb30f83b6545201 against base a49846a738c2ce2eae28eeea3d5e627ca97b6ba4.
No unresolved author-actionable defect was found in the changed-head review.
The re-review range-diffed from previously reviewed head 14d30d18a00fb2a5c0cd46b2847a8842a6bb140c and found one new semantic patch: the composer now captures draft authority before pending pasted-mention settlement and fences continuation on unmount, navigation/return, or authored revision change before invitation preparation (desktop/src/features/forum/ui/ForumComposer.tsx:342-363). The surrounding integration paths retain exact selected-recipient and bot-role membership adds, fresh authorization before publish, cancellation/late-completion invalidation, and source-key/revision-owned draft and media recovery (useForumMentionPreparation.ts:35-168, ForumComposer.tsx:374-404, useForumDraftRecovery.ts:25-93). No persistence schema or native storage format changes were introduced.
Exact-head verification on clean worktrees:
just desktop-check— passedjust desktop-typecheck— passedjust desktop-test— 6,352/6,352 passedjust desktop-build— passed- production-bound forum invitation Playwright suite — 19/19 passed
- targeted forum lifecycle suite — 25/25 passed
- causal mutation removing the new post-settlement fence failed the intended stale clipboard-settlement regression, then the exact head was restored cleanly
The production-bound suite covers Invite/Cancel/Escape, focus restoration, visible error and keyboard Retry behavior, exact successful p tags, revoked/finally unauthorized recipients, navigation while pending, A→B→A during add and publish, reference-only shared chat, and dispatch-failure draft/media recovery. Screenshots were coherent.
Residual confidence gaps do not require author rework: native VoiceOver/NVDA announcement timing and a real native GUI/IPC pass were not directly observed; browser E2E uses mock IPC as documented in docs/forum-agent-invitation.md:20-24. Optional native accessibility/release QA owns that assurance. At final review submission, all completed applicable hosted checks were green, while Desktop Domain / Desktop Core remained in progress; required CI must complete successfully before integration.
Any new head invalidates this approval.
…7133) ## Summary Selecting two people or agents named Scout could replace the first recipient with the second even though the message still looked right. This binds each selection to its exact identity: the first keeps `@Scout`, and a conflicting selection gets `@Scout (<full public key>)`. Removing one no longer removes or redirects the other. - Reuse the existing **@ suggestion list**; team selection and automatic agent addressing reserve and reuse distinct labels too. Typing an ambiguous name manually shows an instruction to use the picker and preserves the draft without publishing—in chat, edits and standalone forums. - Share literal mention matching across recipient extraction, removal, display and editing so a shorter name cannot claim another recipient's longer or qualified label. - Rebuild authored references when a message is edited, and use the latest authorized snapshot when reopening or forwarding. Rendering/editing can recover qualified identities only from the message's recorded references, not from a key typed into its body. Unresolvable historical names remain literal rather than guessed recipients. - Wrap full-key labels within narrow/zoomed layouts while keeping their complete accessible label and ordinary mention icons. Edit activation waits for the action menu's focus cleanup before focusing the editor. ### Related issue Targets `main`; mention spacing (#7128) is already merged. Split from #7114. This is independent of the #7124 → #7125 remote-invitation stack and does not expand agent eligibility or invitation permissions. The separate Enter-selection suffix issue remains tracked in #7253. ### Testing The desktop unit suite and focused mock-Chromium checks passed on the published integration candidate, including pending-paste selection, edit/forwarding, copy and narrow-layout cases; formatting, types and frontend builds passed. On `0b3b18c0`, 64 focused trust/paste/selection unit tests and six mock-Chromium tests passed with zero browser retries, including actual timeline chip copy → fresh channel paste → send and mismatched-key rejection; TypeScript, changed-file Biome and an isolated E2E build passed. The broader browser run had copy failures before the focused repairs and is not claimed as wholly green. See [live CI](https://github.com/block/buzz/pull/7133/checks) for current-head results. No full local `just ci` pass, native/live-relay or cross-browser validation is claimed. To try it: select two same-name recipients, remove one, send, then edit/reopen and forward; only the intended identities should remain. Type an ambiguous name without choosing a suggestion and check that sending retains the draft with an error. Inspect full-key labels in a narrow window at 150% text size, and open Edit and type immediately.   *Earlier mock-browser captures, not current-head runtime proof. No new before-state capture; screenshots alone do not prove recipient delivery.* **Clipboard trust:** generated full-key-qualified mentions now retain their exact recipient after copy/paste when the full key matches the clipboard record and community directory/profile state independently vouches for the base alias, including numeric collision suffixes. A qualifier alone does not establish trust. Arbitrary historical labels still cannot always be reconstructed. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Co-authored-by: Bad Janet <150b20bdf6130418df9239dd1bd082c71612c8d653b47c277200365b9be215dc@buzz>
a49846a to
0b303f8
Compare
416a0f3 to
b4dd19c
Compare
jedwards27
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Reviewed: 0b303f805e4420eae682087555075c5a7472e40e..b4dd19cfae1fbeb8ffb93c049f3338af688f42e2 (exact head b4dd19cfae1fbeb8ffb93c049f3338af688f42e2)
Risk: high — forum publication now invites selected non-member agents and must preserve exact recipient authorization, draft ownership, cancellation, and failure recovery across asynchronous membership and transport boundaries.
Behavior/contracts traced: source-key/revision ownership before clipboard settlement; exact intended-agent capture; membership revalidation before add and again before publish; cancellation/navigation/unmount fencing; failed-dispatch recovery of text, completed media, and exact mention refs; shared non-member-dialog keyboard/focus behavior; legacy-safe draft persistence.
Findings: no blocking or non-blocking defect found. The restacked five-patch series was reviewed fresh; three patches are patch-identical to the prior series, while the material delta adds the originating-visit clipboard fence and reconciles the parent head's exact-recipient smoke coverage.
Author action: none.
Verification owner: CI/release owns required-gate disposition and optional packaged-Tauri/native assistive-technology observation.
Validation at matching clean head:
just desktop-check— PASS (repository diagnostics only)just desktop-typecheck— PASSjust desktop-test— PASS, 6,434/6,434just desktop-build— PASS, including protected-artifact matrix- focused composer lifecycle — PASS, 25/25
desktop/tests/e2e/forum-agent-invitation.spec.ts— PASS, 19/19- focused production-built smoke including
mention-recipients.spec.ts— PASS, 39/39 - causal mutation removing the pre-invitation clipboard fence made its regression test fail (
2 !== 0); restoring exact head returned the tree clean git diff --check 0b303f805e4420eae682087555075c5a7472e40e...HEAD— PASS
Required smoke reconciliation: hosted shards 2 and 3 are red, but the failures do not establish a PR-caused defect. The PR-specific forum suite passed 19/19 in the same shard-2 job and independently at exact head. Shard 2's final failures are broad message-edit/attachment/history cases; the historical-recipient case passed in the focused 39/39 rerun, and the attachment failure was already classified flaky after retry. Shard 3's persistent-agent-audience failure is identical at the same test line on the exact base run 33891898259; that base run also failed unrelated profile/workflow/navigation/thread tests across shards, while the candidate failures additionally span onboarding and overscroll files outside this PR's changed production surface. This is base-wide/broad-shard harness instability, not causal evidence against the forum change. The red required checks still own merge readiness; they do not create author rework without a causal defect.
Manual/native evidence: browser-bound production-build interaction covers dialog focus/Escape, alert semantics, cancellation, invite denial/failure, exact recipient routing, navigation, A→B→A ownership, and transport recovery. Packaged Tauri, live relay, VoiceOver, and NVDA were not directly observed.
Residual risk: native announcement timing and signed packaged-runtime ownership remain unobserved. Any head change invalidates this approval.
🤖 ## Summary In Buzz Desktop, you could own an agent running on another device but be unable to mention it in a channel where it had not yet joined: it was filtered out before you could invite it. A selected agent could also disappear from the message's recipients when permissions changed. This lets you select an eligible agent in the existing **@ menu**, invite it from the message composer, and send to that agent—or see an error and keep your draft rather than silently sending without it. #### Where the experience changes | Screen / control | Before → after | | --- | --- | | A channel's **Message #…** composer, or a message's **Reply in thread to …** composer | Type `@` (or use the existing @ button), choose your agent, write the message and press **Send message**. An owned agent not yet in the channel can now reach the existing **“Mention people outside this channel?”** dialog when its response settings allow you to address it. | | That dialog's **Invite** button | Previously the membership requirement could block the agent before the invitation. Now Invite checks permission to add it, adds it as an agent member of the **channel** (not just the thread), then rechecks membership and response permission before sending the waiting message. An agent already in the channel needs no invitation. | | Existing direct message, or the new-message screen with the **To:** field | A mention is checked against the conversation the message will actually enter, including a newly created direct message, rather than the old or not-yet-created destination. This does not add an Invite control to direct messages. | | Editing a message / sending attachments | The selected agent remains part of the send or edit attempt through attachment upload and the final permission check. Lost permission produces a visible error instead of dropping that recipient. | **Invite is not the only chat choice.** The existing **Do nothing** button sends the message *without inviting or notifying the nonmembers*; their names remain references in the text. Where you cannot invite, that choice is labelled **Send anyway**. To abandon the send instead, dismiss the dialog with Escape. Invitation actions are disabled while preparation is pending, preventing duplicate clicks. **Leaving and returning must not resurrect a cancelled send.** Switching threads or leaving the composer cancels its pending invitation, even if you return to the same thread. Cancellation before dispatch sends no message; an accepted membership change cannot be automatically undone. An ordinary send without a pending invitation remains bound to its original destination rather than following you into another conversation. **Failed sends must not overwrite your next draft.** If you leave a thread, return and replace or deliberately clear its draft while an older send is pending, the older failure cannot restore deleted text, recipients or files; success cannot erase the newer draft—even if its text is identical. An untouched draft cleared automatically for sending remains recoverable on failure. This protection also covers reopening the composer and starting a newer send. The channel timeline also keeps its existing **new-messages / Jump to latest** button available when newer messages are waiting to be displayed. For example, after sharing a reply to the channel and closing the thread panel, you can click the catch-up button to reveal buffered messages. Closing the thread does **not** guarantee the shared row appears automatically or force you away from reading history. ### Related issue Built on [#7122](#7122), base branch `split/owned-agent-discovery`, which lets Desktop find and verify owned agents independently of this device. Current integration head: `1144465d00273cf74b7c22544ae5a3299bd98560`, built on exact published root `3a56d17824522580fe04cae463b54f4c7ba66021`. Root #7122 has its own CI and security gates; this PR must not land ahead of that dependency. Finding an agent is not channel membership, online status or a promise of a reply. This PR changes what the existing message controls can do with those agents; it adds no profile, presence, cloud marker or remote start/stop UI. Standalone forum post/reply **Invite / Cancel** is added separately in [#7125](#7125); here those composers only gain visible authorization errors. Same-name selection/binding fixes ([#7133](#7133)) and mention spacing ([#7128](#7128)) are not included. Extracted from [#7114](#7114) (historical source `98fe33ec`). [Behavior and draft-recovery contract](https://github.com/block/buzz/blob/1144465d00273cf74b7c22544ae5a3299bd98560/docs/remote-mention-routing.md) · [Originating discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848). ### Testing  *Earlier candidate, mock desktop browser: the existing channel dialog now reachable for an eligible owned agent on another device. The two buttons have different send outcomes; Do nothing is not Cancel.* [Success and denial captures](#7124 (comment)) · [Pending-state capture](#7124 (comment)). These show the relevant UI, not live agent availability, native authorization or the later draft-storage/catch-up repairs. No before-state screenshot is available. Existing coverage exercises exact recipients, invitation rejection/cancellation, new direct-message destinations, uploads, edits, thread re-entry and stored-draft deletion. The timeline regression checks the shared reply becomes visible using the available catch-up action. **Integration validation (2026-09-02):** independently reviewed the routing delta onto root `3a56d178`: seven original patches unchanged; two reconciliations retain generic publication-error toasts alongside authorization errors and retain non-authored editability updates. Added two production-hook regression tests (normal and queued-media publication) requiring visible generic error, recovered draft and released pending state. - Writer validation: **5,995 Desktop tests**, **42 focused tests**, **22 mock-IPC browser journeys** (18 routing, 2 root provenance, 2 destination binding), and **1 voice-note failure journey** passed; lint, types, size guards and E2E build also passed. - The broad suite ran before the final formatting-only test amendment, not as an exact-final-head rerun. Independent AST comparison confirmed that amendment is semantics-preserving; **4 fresh assertions at final `1144465d`** passed. Publication rechecked final-head TypeScript, amended-test formatting and `git diff --check` successfully. No new full repository `just ci` run is claimed. - Browser tests use an isolated E2E build and **mock IPC**, not live relay/native authorization. Historical screenshots above are explicitly earlier UI evidence, not exact-head runtime certification. Packaged Tauri/live-relay behavior was not independently witnessed. - **Published-head gates:** [current CI run](https://github.com/block/buzz/actions/runs/33657948560) and [renewed exact-head formal review request](#7124 (comment)) must clear before landing. [Earlier CI run](https://github.com/block/buzz/actions/runs/33438436438) and the two earlier approvals cover `7ffead0f`, not this new head. Root CI/security clearance remains separate; the independent scoped integration approval is not merge authorization. To try it: in a channel or thread, select an owned nonmember agent, Send, then Invite or Escape and retry. Deny the add or revoke its response permission before sending: expect a visible error and recoverable draft, not a message missing the agent. During a pending send, return to the source thread, edit or clear the draft, then leave again: late completion must not overwrite that choice. **Limits:** permission checks and sending are separate operations; cancellation cannot retract a dispatched message. Draft protection is same-window, not new cross-window deletion synchronization. Standalone forum transport failure can still restore text/media without the exact selected recipients. Native compatibility is inherited: open-source builds may still recognize a valid legacy, self-declared agent already in the channel when verified ownership is absent or rejected; that does not establish ownership or unlock this owned-nonmember invitation path. Invalid policy from a verified owner is still rejected. No agent response is guaranteed. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
Reuse phase-aware preparation, authorized add and final destination authorization; retain selected drafts on cancellation and failure. Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Signed-off-by: Logan Johnson <loganj@squareup.com>
Keep policy strings and assertions unchanged while replacing invalid filename characters in the screenshot stem. Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Signed-off-by: Logan Johnson <loganj@squareup.com>
…sport Bind invitation continuations to a keyed source visit, retain cancellable retry drafts, and restore safe dialog focus with announced errors. Recover rejected dispatched replies under shared source-key authority without overwriting newer authored intent or replaying publication. Add production-seam lifecycle and browser regressions. Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Signed-off-by: Logan Johnson <loganj@squareup.com>
Signed-off-by: Logan Johnson <loganj@squareup.com>
Preserve the inherited clipboard wait through restack and gate its pre-preparation continuation by mounted visit and authored revision. Adapt the real composer harness to the clipboard seam and cover late settlement across edit, navigation, return and unmount. Signed-off-by: Logan Johnson <loganj@squareup.com>
b4dd19c to
3da45b8
Compare
🔐 Codex Security Review
|
jedwards27
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Reviewed: d595806fc3b9c9758992e39b9b51cbb5f55791b0..3da45b81ba82b7cb7186ef0d011715b3f39a361a (exact head 3da45b81ba82b7cb7186ef0d011715b3f39a361a)
Risk: high — user-authored Forum drafts, exact recipient identity, channel membership mutation, async cancellation, and late publication/recovery all cross one send flow.
Behavior/contracts traced: owned nonmember selection → response-policy and actor authorization → forum-wide add → membership refresh → final authorization → exact p-tag publication; Cancel/Escape, navigation, held add/publication, clipboard settlement, optimistic draft clear, transport rejection, attachment/recipient recovery, newer-intent fencing, focus, and accessible error/retry behavior.
Findings: no blocking or non-blocking code defect found. The five patches are patch-equivalent to the previously reviewed stack after restack. The new clipboard-settlement fence captures the authored revision before awaiting paste binding and refuses stale continuation after unmount or revision change (desktop/src/features/forum/ui/ForumComposer.tsx:342-363). Production-seam mutation of that fence fails the new lifecycle regression, so the guard is causally covered.
Both independent lanes cleared this exact head. Systems review confirmed destination/attempt/mount identity fencing, post-add cache refresh, final authorization, and revision-scoped recovery. Product/UI review exercised post and reply Invite, Cancel/Escape, denial/error retry, policy revocation, navigation, duplicate-name identity preservation, loading/inert state, focus restoration, and visible alerts.
Author action: none.
Verification owner: CI/release gate for the two still-running exact-head jobs; release/native QA for optional real Tauri/VoiceOver and live signed-membership observation.
Validation at matching clean HEAD: Desktop package tests 6,435/6,435; pnpm --dir desktop check; pnpm --dir desktop typecheck; pnpm --dir desktop build:e2e; changed Forum Playwright smoke 19/19; git diff --check; causal clipboard-fence mutation; additional same-name/retry adversarial supplement 19/19. Current hosted checks have no failures; all completed Desktop builds/integration/smoke shards, DCO, Semgrep, and zizmor are green. Desktop Core and smoke shard 4 remained in progress at the final freshness fence.
Manual/native evidence: distinct browser-rendered Invite, success, and failure artifacts were inspected at 1280×720. No real packaged Tauri, VoiceOver/NVDA speech, or live relay membership transaction was independently witnessed.
Residual risk: mock IPC does not prove OS assistive speech or a signed live membership transaction. Renderer crash/reload during an in-flight transport remains explicitly outside this PR's durable-recovery contract (docs/forum-agent-invitation.md:49-52). These are confidence gaps, not author-actionable defects. Any new head invalidates this approval.
— :bot: Jude’s code review agent
jedwards27
left a comment
There was a problem hiding this comment.
:bot: Jude’s code review agent — APPROVE at exact head 3da45b81ba82b7cb7186ef0d011715b3f39a361a against base d595806fc3b9c9758992e39b9b51cbb5f55791b0.
No concrete defect remains.
Integrated review
- The changed-head delta adds a clipboard-settlement fence: it captures the composer revision before awaiting paste binding and refuses to continue after unmount or an authored revision change (
ForumComposer.tsx:342-363). A causal mutation deleting that fence madeclipboard settlement after edit cannot prepare another forum draftfail (2 !== 0); restored exact head passed. - Invitation candidates are resolved selected Forum agents, excluding managed agents and current members, and are revalidated before membership addition and publication (
useForumMentionPreparation.ts:67-169). Membership addition uses the captured channel and awaits member-cache invalidation; final relay-agent validation receives that same destination (channels/hooks.ts:826-864,agentMentionRevalidation.ts:113-145). - Attempt, channel, mount, and revision identity fence Cancel/Escape, navigation, late membership/publication completion, newer attempts, and clipboard settlement (
useForumMentionPreparation.ts:28-57,121-168,ForumComposer.tsx:342-363). - Failed transport recovery snapshots exact text, media, and mention references before optimistic clear, then restores only when keyed/revision ownership is still valid; it does not overwrite newer intent or call a departed editor (
ForumComposer.tsx:374-409,useForumDraftRecovery.ts:25-94). Draft authority is relay+identity scoped and reset invalidates retained handles (useDrafts.ts:127-205). - The changed Forum E2E journey passed 19/19 twice after a fresh E2E build. It covers post/reply Invite, Cancel, Escape during held membership addition, visible add failures and retry, policy revocation, navigation during held add/publication, A→B→A draft/media/exact-recipient custody, transport failure recovery, and exact publication recipients (
desktop/tests/e2e/forum-agent-invitation.spec.ts:109-640). - An adversarial supplement added a duplicate-display-name owned agent and retried denied/error states in the same dialog without reselection. All retries published only the selected pubkey. It also verified disabled in-flight Invite, enabled Cancel, inert source form, Escape close, and editor focus restoration.
- Artifact inspection covered distinct Invite, success, and three failure states. Copy truthfully explains invitation versus draft-preserving Cancel; safe initial focus,
role="alert", enabled retry, and preserved intended mention were present (NonMemberMentionDialog.tsx:39-112,ForumComposer.tsx:532-545,608-781).
Exact-head verification
pnpm test: 6,435/6,435 pass on the full Desktop package rerun. (An initial run had one failure in unchangeduseKnownAgentPubkeys.test.mjs; the required full rerun passed.)pnpm typecheck: pass.just desktop-check: pass; only existing non-fatal warnings outside the PR files.CI=1 pnpm test:e2e -- forum-agent-invitation.spec.ts: 19/19 pass.- Differential file-size gate and
git diff --check base...HEAD: pass. - Hosted required workflow
33898423361: success at exact head, including Desktop Core, all four smoke shards, Windows build, macOS build, and relay/integration gates selected for this diff. - GitHub/API/fetched/local head matched; PR is mergeable; no unresolved review threads were found during preflight.
Residual confidence gaps
- Deterministic browser/mock-IPC validation establishes rendered semantics, focus, keyboard behavior, and exact payloads, but not OS VoiceOver speech or a real signed relay membership transaction. Native ownership/membership enforcement is explicitly outside this frontend PR (
docs/forum-agent-invitation.md:20-24). Author action: none. Verification owner: release/native QA with a seeded owned nonmember relay agent. - Renderer crash/reload during in-flight transport remains outside this PR’s durable-recovery contract and is documented as such (
docs/forum-agent-invitation.md:49-52).
Any head or base movement invalidates this approval pending delta review.
…lock#7133) ## Summary Selecting two people or agents named Scout could replace the first recipient with the second even though the message still looked right. This binds each selection to its exact identity: the first keeps `@Scout`, and a conflicting selection gets `@Scout (<full public key>)`. Removing one no longer removes or redirects the other. - Reuse the existing **@ suggestion list**; team selection and automatic agent addressing reserve and reuse distinct labels too. Typing an ambiguous name manually shows an instruction to use the picker and preserves the draft without publishing—in chat, edits and standalone forums. - Share literal mention matching across recipient extraction, removal, display and editing so a shorter name cannot claim another recipient's longer or qualified label. - Rebuild authored references when a message is edited, and use the latest authorized snapshot when reopening or forwarding. Rendering/editing can recover qualified identities only from the message's recorded references, not from a key typed into its body. Unresolvable historical names remain literal rather than guessed recipients. - Wrap full-key labels within narrow/zoomed layouts while keeping their complete accessible label and ordinary mention icons. Edit activation waits for the action menu's focus cleanup before focusing the editor. ### Related issue Targets `main`; mention spacing (block#7128) is already merged. Split from block#7114. This is independent of the block#7124 → block#7125 remote-invitation stack and does not expand agent eligibility or invitation permissions. The separate Enter-selection suffix issue remains tracked in block#7253. ### Testing The desktop unit suite and focused mock-Chromium checks passed on the published integration candidate, including pending-paste selection, edit/forwarding, copy and narrow-layout cases; formatting, types and frontend builds passed. On `0b3b18c0`, 64 focused trust/paste/selection unit tests and six mock-Chromium tests passed with zero browser retries, including actual timeline chip copy → fresh channel paste → send and mismatched-key rejection; TypeScript, changed-file Biome and an isolated E2E build passed. The broader browser run had copy failures before the focused repairs and is not claimed as wholly green. See [live CI](https://github.com/block/buzz/pull/7133/checks) for current-head results. No full local `just ci` pass, native/live-relay or cross-browser validation is claimed. To try it: select two same-name recipients, remove one, send, then edit/reopen and forward; only the intended identities should remain. Type an ambiguous name without choosing a suggestion and check that sending retains the draft with an error. Inspect full-key labels in a narrow window at 150% text size, and open Edit and type immediately.   *Earlier mock-browser captures, not current-head runtime proof. No new before-state capture; screenshots alone do not prove recipient delivery.* **Clipboard trust:** generated full-key-qualified mentions now retain their exact recipient after copy/paste when the full key matches the clipboard record and community directory/profile state independently vouches for the base alias, including numeric collision suffixes. A qualifier alone does not establish trust. Arbitrary historical labels still cannot always be reconstructed. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> Co-authored-by: Bad Janet <150b20bdf6130418df9239dd1bd082c71612c8d653b47c277200365b9be215dc@buzz> (cherry picked from commit ee883d7) Signed-off-by: nambse <sefa.esendemir@gmail.com>
🤖 ## Summary In Buzz Desktop, you could own an agent running on another device but be unable to mention it in a channel where it had not yet joined: it was filtered out before you could invite it. A selected agent could also disappear from the message's recipients when permissions changed. This lets you select an eligible agent in the existing **@ menu**, invite it from the message composer, and send to that agent—or see an error and keep your draft rather than silently sending without it. #### Where the experience changes | Screen / control | Before → after | | --- | --- | | A channel's **Message #…** composer, or a message's **Reply in thread to …** composer | Type `@` (or use the existing @ button), choose your agent, write the message and press **Send message**. An owned agent not yet in the channel can now reach the existing **“Mention people outside this channel?”** dialog when its response settings allow you to address it. | | That dialog's **Invite** button | Previously the membership requirement could block the agent before the invitation. Now Invite checks permission to add it, adds it as an agent member of the **channel** (not just the thread), then rechecks membership and response permission before sending the waiting message. An agent already in the channel needs no invitation. | | Existing direct message, or the new-message screen with the **To:** field | A mention is checked against the conversation the message will actually enter, including a newly created direct message, rather than the old or not-yet-created destination. This does not add an Invite control to direct messages. | | Editing a message / sending attachments | The selected agent remains part of the send or edit attempt through attachment upload and the final permission check. Lost permission produces a visible error instead of dropping that recipient. | **Invite is not the only chat choice.** The existing **Do nothing** button sends the message *without inviting or notifying the nonmembers*; their names remain references in the text. Where you cannot invite, that choice is labelled **Send anyway**. To abandon the send instead, dismiss the dialog with Escape. Invitation actions are disabled while preparation is pending, preventing duplicate clicks. **Leaving and returning must not resurrect a cancelled send.** Switching threads or leaving the composer cancels its pending invitation, even if you return to the same thread. Cancellation before dispatch sends no message; an accepted membership change cannot be automatically undone. An ordinary send without a pending invitation remains bound to its original destination rather than following you into another conversation. **Failed sends must not overwrite your next draft.** If you leave a thread, return and replace or deliberately clear its draft while an older send is pending, the older failure cannot restore deleted text, recipients or files; success cannot erase the newer draft—even if its text is identical. An untouched draft cleared automatically for sending remains recoverable on failure. This protection also covers reopening the composer and starting a newer send. The channel timeline also keeps its existing **new-messages / Jump to latest** button available when newer messages are waiting to be displayed. For example, after sharing a reply to the channel and closing the thread panel, you can click the catch-up button to reveal buffered messages. Closing the thread does **not** guarantee the shared row appears automatically or force you away from reading history. ### Related issue Built on [block#7122](block#7122), base branch `split/owned-agent-discovery`, which lets Desktop find and verify owned agents independently of this device. Current integration head: `1144465d00273cf74b7c22544ae5a3299bd98560`, built on exact published root `3a56d17824522580fe04cae463b54f4c7ba66021`. Root block#7122 has its own CI and security gates; this PR must not land ahead of that dependency. Finding an agent is not channel membership, online status or a promise of a reply. This PR changes what the existing message controls can do with those agents; it adds no profile, presence, cloud marker or remote start/stop UI. Standalone forum post/reply **Invite / Cancel** is added separately in [block#7125](block#7125); here those composers only gain visible authorization errors. Same-name selection/binding fixes ([block#7133](block#7133)) and mention spacing ([block#7128](block#7128)) are not included. Extracted from [block#7114](block#7114) (historical source `98fe33ec`). [Behavior and draft-recovery contract](https://github.com/block/buzz/blob/1144465d00273cf74b7c22544ae5a3299bd98560/docs/remote-mention-routing.md) · [Originating discussion](buzz://message?channel=f7a9536a-1738-4bad-a888-b3ea25010ef1&id=7aa1f0ab23dce514bd8a0221441cf005bf428914621171472b79747c50820848). ### Testing  *Earlier candidate, mock desktop browser: the existing channel dialog now reachable for an eligible owned agent on another device. The two buttons have different send outcomes; Do nothing is not Cancel.* [Success and denial captures](block#7124 (comment)) · [Pending-state capture](block#7124 (comment)). These show the relevant UI, not live agent availability, native authorization or the later draft-storage/catch-up repairs. No before-state screenshot is available. Existing coverage exercises exact recipients, invitation rejection/cancellation, new direct-message destinations, uploads, edits, thread re-entry and stored-draft deletion. The timeline regression checks the shared reply becomes visible using the available catch-up action. **Integration validation (2026-09-02):** independently reviewed the routing delta onto root `3a56d178`: seven original patches unchanged; two reconciliations retain generic publication-error toasts alongside authorization errors and retain non-authored editability updates. Added two production-hook regression tests (normal and queued-media publication) requiring visible generic error, recovered draft and released pending state. - Writer validation: **5,995 Desktop tests**, **42 focused tests**, **22 mock-IPC browser journeys** (18 routing, 2 root provenance, 2 destination binding), and **1 voice-note failure journey** passed; lint, types, size guards and E2E build also passed. - The broad suite ran before the final formatting-only test amendment, not as an exact-final-head rerun. Independent AST comparison confirmed that amendment is semantics-preserving; **4 fresh assertions at final `1144465d`** passed. Publication rechecked final-head TypeScript, amended-test formatting and `git diff --check` successfully. No new full repository `just ci` run is claimed. - Browser tests use an isolated E2E build and **mock IPC**, not live relay/native authorization. Historical screenshots above are explicitly earlier UI evidence, not exact-head runtime certification. Packaged Tauri/live-relay behavior was not independently witnessed. - **Published-head gates:** [current CI run](https://github.com/block/buzz/actions/runs/33657948560) and [renewed exact-head formal review request](block#7124 (comment)) must clear before landing. [Earlier CI run](https://github.com/block/buzz/actions/runs/33438436438) and the two earlier approvals cover `7ffead0f`, not this new head. Root CI/security clearance remains separate; the independent scoped integration approval is not merge authorization. To try it: in a channel or thread, select an owned nonmember agent, Send, then Invite or Escape and retry. Deny the add or revoke its response permission before sending: expect a visible error and recoverable draft, not a message missing the agent. During a pending send, return to the source thread, edit or clear the draft, then leave again: late completion must not overwrite that choice. **Limits:** permission checks and sending are separate operations; cancellation cannot retract a dispatched message. Draft protection is same-window, not new cross-window deletion synchronization. Standalone forum transport failure can still restore text/media without the exact selected recipients. Native compatibility is inherited: open-source builds may still recognize a valid legacy, self-declared agent already in the channel when verified ownership is absent or rejected; that does not establish ownership or unlock this owned-nonmember invitation path. Invalid policy from a verified owner is still rejected. No agent response is guaranteed. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz> (cherry picked from commit d595806) Signed-off-by: nambse <sefa.esendemir@gmail.com>
* origin/main: fix(desktop): keep packaged frontendDist relative so Windows embeds assets (#7177) fix(sidebar): simplify unread indicators and emphasize priority activity (#7134) Add generic information-flow control core (#7293) feat(buzz-acp): update base prompt; add buzz context and skills to Pi agents (#7335) fix(desktop): restore mention chip identity icons (#7338) Persist video playback speed preference (#7336) Verify ACP relay events before prompt routing (#7010) fix(buzz-acp): bound busy-owner hold to prevent cross-channel starvation (#7337) feat(desktop): invite owned agents from standalone forums (#7125) fix(desktop): authorize remote mentions at publication (#7124) fix(acp): rename system tag to agent-instructions (#7332) fix(desktop): bind duplicate mention selections to exact recipients (#7133) refactor(relay): extract NIP-29 membership authorization (#7285) chore(release): release Buzz Desktop version 0.5.22 (#7308) feat(desktop): preserve mentions across copy and paste (#7228) test(desktop): await Bestie drag and profile hover endpoints (#7294) Signed-off-by: Fizz <400e8babadcee6a7f420103f10a2849d84c4a9c71d5bd04f3948c814216648a3@buzz.block.builderlab.xyz>
Summary
In Desktop's standalone Forums, selecting an owned agent from another device could leave a post or reply unsendable if the agent had not joined the forum. This adds Invite / Cancel to the send flow so you can resolve membership without leaving your draft.
Related issue
Targets
mainafter #7124 merged. This PR reuses its publication checks and draft protection; the five forum commits have been replayed unchanged onto the merged parent. Owned-agent discovery (#7122) is already merged. Split from #7114.Forum creation/templates, channel-less Notes and local-agent management are unchanged. Duplicate-name binding from #7133 is already merged and retained by this stack. Inviting does not start a remote agent or promise that it is online or will reply.
Testing
Forum composer lifecycle tests, including clipboard-settlement cases, and TypeScript/changed-file formatting checks passed after the restack. Earlier invitation, focus and transport-recovery browser coverage is retained, not claimed as a fresh full browser run on this head. See live CI for current-head results. Browser evidence uses mock IPC; no full local
just cipass or native/live-relay validation is claimed.To try it: open a forum post or reply, select an owned nonmember agent and send. Cancel, then retry without reselecting; Invite should add that agent before posting. Deny the add to check the visible error and retained draft. Navigate away/back during a pending invitation or rejected send; no stale publication or overwrite of a newer draft should occur.
Earlier mock-browser capture, not current-head runtime proof. Error and successful-post captures; no before-state/native capture available.
Limits: cancellation cannot undo accepted membership changes or dispatched posts; authorization and publication are not atomic. Recovery is same-window, subject to browser storage limits, and is not a durable in-flight send journal: reload/crash can lose a pending snapshot. Cross-window coordination and in-flight upload custody are unchanged. The parent's legacy member-agent compatibility does not establish ownership for nonmember invitations.