## 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. - **Invite** checks response policy and your permission to add members, adds the agent to the forum, waits for refreshed membership, then rechecks authorization before posting to the original destination. Membership is forum-wide, not limited to one post. - **Cancel / Escape** keeps the text, attachments and selected recipients for retry. Unlike chat's **Do nothing / Send anyway**, this dialog has no reference-only send choice. Invite is disabled while pending; Cancel remains available. - Leaving the source post/reply cancels its pending invitation, even if you return. Errors remain visible, focus returns to the initiating editor when appropriate, and late completion cannot resume a cancelled post or interfere with a newer attempt. - A rejected send restores text, uploaded media and exact selected recipients to the source draft only if no newer edit, deletion, upload intent or send supersedes it. Clipboard verification settles before recipient capture, with stale edits/navigation fenced out. ### Related issue Targets `main` after #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](https://github.com/block/buzz/pull/7125/checks) for current-head results. Browser evidence uses mock IPC; no full local `just ci` pass 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](https://github.com/block/buzz/pull/7125#issuecomment-5513883462); 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. --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
3.2 KiB
Standalone forum agent invitation
The standalone forum composer reuses the remote mention preparation/publication
contract (docs/remote-mention-routing.md). An eligible owned relay nonmember
opens an explicit Invite/Cancel dialog. There is no implicit reference-only send.
Invite revalidates the captured exact recipients in the preparation phase, performs the existing authorized bot-member add, awaits membership invalidation, then performs a fresh publication-phase check against the captured destination. A successful add alone is never authorization to publish.
Cancel resolves the pending submission without clearing its text or selected identity. Retry can use the same exact key. Rejected adds and preparation errors remain visible inside the dialog; final authorization errors remain visible in the composer and retain its draft. Channel change/unmount cancels pending work, and completions must still belong to the mounted captured channel. An add already accepted by the relay is not rolled back on cancellation; no message is sent.
This adapter does not create/manage local agents or change notes/channel-less
surfaces. Regression coverage in forum-agent-invitation.spec.ts covers exact
p-tags, membership refresh before final authorization, cancel/retry, three add
failures, policy revocation before Invite and during add, and navigation during
an outstanding add. Browser fixtures are mock IPC; signed native ownership and
membership validation live in PR6, not in the frontend bridge.
Dispatched reply recovery
After authorized dispatch, transport rejection recovers the captured text, completed/uploaded media and exact selected mention refs under the source draft key. This extends the standalone-forum follow-up noted in the shared routing document: a cached A → B → A visit with no new intent no longer loses its reply. The departed editor is never called. A pristine, empty current source visit may adopt the recovered store entry; B cannot receive A's text or attachments.
The existing same-window draft authority fences recovery across visits. New text (including edit → delete), completed-media changes/upload intent, selected refs, explicit draft deletion, a new send or a community/identity reset revoke older recovery. A different existing stored payload is not overwritten. Optimistic clear and recovery are programmatic, not authored deletion; synchronous media snapshots protect cleanup immediately after recovery. No recovery path repeats publication, undoes an accepted add or releases another visit's pending send.
ForumComposer.lifecycle.test.mjs exercises ordinary and invited sends,
source-key re-entry, newer intent/deletion/media/refs, scope reset and late
completion. The browser spec additionally rejects deferred send_channel_message
after cached A → B → A with uploaded media and verifies recovery versus newer
text/deletion, B isolation and exact recipients on an explicit retry.
Recovery is not a durable in-flight send journal: renderer reload/crash while the transport is pending can still lose the in-memory snapshot. Local in-flight upload custody, cross-window coordination, native authorization and atomic relay publication are outside this correction.