# Discord conversations

> **Status:** Current Discord workflow for an administrator-provisioned, authorized guild channel.
>
> Telegram shared-contract parity, Matrix channel conversations, and a gateway-wide shared adapter registry are not current features. See [Current versus planned](#current-versus-planned) before using any older channel instructions.
>
> **Audience:** People conversing with an agent through Discord.

This workflow assumes an administrator has configured the Discord bot, gateway connection, allowlists, and a logical-agent binding. Users cannot create a binding or authorize themselves from Discord.

## Current versus planned

| Surface                 | Status                                    | What you can rely on                                                                                                                         |
| ----------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Discord guild messages  | **Current live routing**                  | Authorized messages route to the configured logical agent; parent/thread delivery is tested, but ordinary durable history is not guaranteed. |
| Telegram                | **Not shared-contract parity**            | A raw legacy plugin exists, but its current source has no equivalent documented authorization, pairing, route, or focused package tests.     |
| Matrix                  | **Not implemented as a channel workflow** | No current gateway channel adapter and test boundary establishes a Matrix conversation workflow.                                             |
| Shared channel registry | **Not implemented**                       | The gateway's current registry hosts lifecycle wrappers; it does not provide universal channel routing or health.                            |

## Start in a configured parent channel

Send a normal message in the administrator-configured parent text channel. You do **not** need to mention the bot for an ordinary turn.

For an authorized user, Mosaic:

1. checks the guild, parent channel, user allowlist, pairing, role, and rate limit;
2. keeps the response target in the parent channel; and
3. routes the turn to the binding's logical agent using a stable conversation address.

No Discord thread is created for this untagged parent-channel case. The response is sent back to that same channel.

Messages from an unconfigured guild/channel, an unallowlisted user, an unpaired user, or a user without a role that can send are ignored without creating a thread or dispatching to the gateway. Bot-authored messages are ignored. Direct messages are not handled by the current guild ingress path.

## Start a threaded topic with a mention

Mention the bot in a parent channel when you want a separate topic:

```text
@Mosaic investigate the deployment failure
```

The current Discord adapter creates a public thread for the message, removes the bot mention from the content sent to the agent, and targets the response to that thread. If the message already has a Discord thread attached, the adapter reuses it instead of creating another one.

Authorization happens before thread creation. If the user, guild, parent channel, pairing, role, or rate check fails, no thread is created. If Discord cannot create or fetch the requested thread, the message is not dispatched because Mosaic cannot guarantee the requested response destination.

## Continue inside a thread

Reply in the existing authorized thread without mentioning the bot again. The adapter:

- authorizes the message against the configured parent text channel;
- keeps the thread as the response target; and
- never attempts to create a nested thread.

A category above the text channel is not used as the authorization parent. Only the actual configured text-channel parent grants thread inheritance.

The stable conversation address is formed from the configured logical agent, channel name, and response channel/thread, for example:

```text
<logical-agent-id>:discord:<response-channel-id>
```

It does not contain Claude, Codex, Pi, OpenCode, a model, a provider, a process, or a native runtime-session ID. The gateway owns runtime selection behind that route, so changing the runtime/provider does not require a new Discord address.

### Durability limitation

Treat the current Discord path as **live routing and delivery**, not guaranteed durable conversation history. The Discord conversation address above is an external route string, while persisted conversation/message rows use UUID conversation IDs. No current external-route-to-UUID mapping was found. The gateway can continue dispatching after a persistence/binding failure, so a reply may appear in Discord without durable history or restart/resume continuity.

Do not rely on Discord as the sole record of a conversation. Durable history requires an implementation that maps the external route to a UUID, surfaces persistence failure, and proves fresh-message persistence and restart recovery in an integration test.

## Attachments

An authorized message may contain text, attachments, or an attachment without text. The current adapter maps attachments into the shared message shape and preserves the native attachment ID, name, URL, content type, and optional size.

The gateway accepts only bounded attachment metadata: at most 10 attachments, HTTPS URLs without credentials, query strings, or fragments, and bounded ID, name, URL, MIME-type, size, and total metadata values. An unsafe or malformed attachment is rejected before the message is acknowledged or dispatched. Binary content is not embedded in the gateway message; the attachment remains a validated external reference.

## Runtime controls

The current Discord text controls are:

```text
/approve
/stop <approval>
```

They remain on the current parent/thread route and do not create a new topic. Approval and stop require an already enrolled durable session; ordinary Discord chat does not prove that enrollment occurred. These are privileged operations: the paired user must have the `admin` role and a provisioned `mosaicUserId`, the gateway must have a tenant configured for the control path, and the enrolled durable session must still belong to the bound logical agent. A stop must present the exact approval reference created for that target; approval consumption is one-time.

If these checks fail, the control operation is denied or produces no successful control result. Do not assume that being able to read a channel grants control authority.

## Response and delivery behavior

The gateway emits raw stream events to the current Discord compatibility path. The plugin buffers `agent:start`/`agent:text` output and sends the completed response on `agent:end`; this is not a claim of token-by-token Discord rendering.

Outbound Discord text is split at a 1,900-character boundary. Transient rate-limit, server, and network failures are retried up to three attempts with one deterministic nonce per correlation/chunk; permanent delivery failures are not retried. A response route is checked against the configured logical-agent/channel binding before Discord is contacted.

## If a message gets no response

Check these possibilities with the administrator:

1. You are in a direct message, an unconfigured guild/channel, or a thread whose parent is not configured.
2. Your Discord user ID is missing from the user allowlist or `pairedUsers`.
3. Your pairing is `viewer`, which cannot send ordinary agent turns.
4. The per-user/channel message or mention-thread limit was reached.
5. The bot is not connected to Discord or the gateway Socket.IO `/chat` namespace.
6. A mentioned topic could not create/fetch its thread.
7. The gateway rejected the signed envelope, route, attachment, or replayed native message ID.
8. `/approve` or `/stop <approval>` was attempted without the required admin pairing, tenant, pre-enrolled durable session, or exact approval.

These failures are intentionally fail-closed; an unauthorized or unverifiable message should not create a thread or agent side effect.

## Not current: Telegram and Matrix

Do not substitute Telegram or Matrix instructions for this workflow:

- The current Telegram plugin uses raw Telegraf and Socket.IO messages, maps a chat to `telegram-<chatId>`, accepts text only, and does not establish the Discord-style service-token, allowlist, pairing, shared-route, or attachment boundary.
- No current Matrix gateway channel adapter, channel binding, user authorization flow, or focused channel tests establish a Matrix conversation workflow.
- The current gateway plugin list is lifecycle-only; it is not proof that every channel shares this Discord behavior.

Those are parity/design gaps, not alternate user workflows.

## Evidence and related pages

- [Channel protocol architecture](../../DEVELOPER-GUIDE/architecture/channel-protocol.md) — current shared types, Discord compatibility path, and explicit parity boundary.
- [Discord ingress security](../../ADMIN-GUIDE/security/discord-ingress.md) — administrator configuration, authentication, authorization, and failure controls.
- [`plugins/discord/src/index.ts`](../../../plugins/discord/src/index.ts) — native Discord routing and delivery implementation.
- [`plugins/discord/src/index.test.ts`](../../../plugins/discord/src/index.test.ts) — parent, mention, existing-thread, authorization, attachment, rate, egress, and health tests.
- [`apps/gateway/src/plugin/discord-ingress.security.spec.ts`](../../../apps/gateway/src/plugin/discord-ingress.security.spec.ts) — gateway signature, replay, binding, attachment, approval, and stop tests.
- [`apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts`](../../../apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts) — Discord control-flow test with explicit durable-session pre-enrollment; it is not fresh-message persistence evidence.
- [User Guide](../README.md)
