docs: correct Discord durability boundary

This commit is contained in:
Jason Woltje
2026-08-10 18:51:04 -05:00
parent 11ffe65c97
commit 063de8cd85
5 changed files with 72 additions and 47 deletions
@@ -32,7 +32,7 @@ The migration from `docs/_old_structure/architecture/channel-protocol.md` is a d
The current source boundaries also distinguish two identities:
1. A channel route carries a configuration-owned logical agent and response destination.
2. The gateway chooses the durable session, provider, model, and runtime session internally.
2. The gateway chooses provider, model, and runtime session internally. Durable-session enrollment is separate and is not proven by the external channel route alone.
A route is therefore not a claim that a channel adapter owns or exposes a harness, provider, model, process, or native runtime-session identity.
@@ -152,15 +152,21 @@ The gateway exposes the `/chat` Socket.IO namespace. A Discord connection authen
This path is intentionally described as compatibility: the gateway receives a signed Discord envelope and reconstructs chat input; it does not currently receive a complete `ChannelIngressDto` from the host registry.
### Persistence and durability boundary
The Discord `conversationId` is an external route string such as `<logical-agent-id>:discord:<channel-or-thread-id>`. Persisted conversations and messages use UUID conversation IDs. No current route-mapping layer was found that resolves the external route to a generated UUID before ordinary Discord writes. The gateway catches persistence/binding failures and may continue dispatch, so live output does not prove durable history or restart/resume continuity.
The focused cross-surface integration test explicitly pre-enrolls a durable session before exercising control flow. It does not prove that a fresh ordinary Discord message creates durable conversation/message rows. Current architecture claims are therefore limited to authenticated routing and live delivery. Durable Discord continuity requires a route-to-UUID mapping, observable persistence failures, ordinary-ingress enrollment where required, and a fresh-database restart test.
### Thread and conversation behavior
| Inbound case | Current route and side effect |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Authorized untagged message in a parent channel | Uses the parent channel as the response target; no thread is created. |
| Authorized bot mention in a parent channel | Creates a public thread, or reuses the thread already attached to that message, and routes the response to that thread. |
| Authorized follow-up in an existing thread | Authorizes against the configured parent channel and keeps the existing thread; it does not create a nested thread. |
| `/approve` or `/stop <approval>` | Uses the current parent/thread durable session and does not create a new topic. |
| Requested thread creation fails | Does not dispatch the message, because the requested response target cannot be honored. |
| Inbound case | Current route and side effect |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Authorized untagged message in a parent channel | Uses the parent channel as the response target; no thread is created. |
| Authorized bot mention in a parent channel | Creates a public thread, or reuses the thread already attached to that message, and routes the response to that thread. |
| Authorized follow-up in an existing thread | Authorizes against the configured parent channel and keeps the existing thread; it does not create a nested thread. |
| `/approve` or `/stop <approval>` | Uses the current parent/thread route and requires an already enrolled durable session; ordinary chat does not prove enrollment. |
| Requested thread creation fails | Does not dispatch the message, because the requested response target cannot be honored. |
### Discord egress and health
@@ -265,7 +271,7 @@ Discord evidence:
- [`apps/gateway/src/chat/chat.gateway.ts`](../../../apps/gateway/src/chat/chat.gateway.ts) — `/chat` namespace, service/session authentication, signed-envelope reconstruction, trusted binding selection, and raw stream egress.
- [`apps/gateway/src/chat/chat.gateway-auth.ts`](../../../apps/gateway/src/chat/chat.gateway-auth.ts) — Discord service-token and BetterAuth session validation.
- [`apps/gateway/src/plugin/discord-ingress.security.spec.ts`](../../../apps/gateway/src/plugin/discord-ingress.security.spec.ts) — signature, allowlist, replay, binding, attachment, approval, stop, and logical-agent checks.
- [`apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts`](../../../apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts) — focused Discord-to-durable-session integration.
- [`apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts`](../../../apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts) — focused control-flow test with explicit durable-session pre-enrollment; not ordinary persistence evidence.
Hosting and compatibility evidence:
@@ -66,7 +66,7 @@ The Discord adapter derives:
<logical-agent-id>:discord:<response-channel-id>
```
The binding address is derived from the configured guild, parent channel, and logical-agent instance. The route intentionally omits runtime provider, harness, model, process, and native runtime-session identifiers. Gateway durable-session and provider layers own those identities.
The binding address is derived from the configured guild, parent channel, and logical-agent instance. The route intentionally omits runtime provider, harness, model, process, and native runtime-session identifiers. Gateway provider/runtime layers own those identities; durable-session ownership applies only after explicit enrollment and is not implied by the external route.
This is a route-integrity rule, not proof that the gateway has a universal channel session API. A future adapter must derive its route from trusted configuration and native channel/thread identity; it must not accept a caller-selected logical agent or runtime target.
@@ -94,10 +94,10 @@ The gateway-hosted plugin is currently constructed without a direct `ChannelIngr
2. Normal sends emit a signed `message` envelope; approval and stop emit `discord:approve` and `discord:stop` envelopes.
3. The HMAC-SHA-256 signature covers the ordered Discord payload using `DISCORD_SERVICE_TOKEN`.
4. The gateway verifies the service token/signature, allowlists, binding/operation role, stable conversation ID, attachment bounds, and replay key before dispatch.
5. The gateway selects the binding's trusted `agentConfigId`, verifies its name equals the logical-agent instance, and creates/resumes the conversation without generic provider routing for Discord ingress.
5. The gateway selects the binding's trusted `agentConfigId` and verifies its name equals the logical-agent instance. It attempts normal conversation binding/persistence before dispatch, but the external route is not a UUID and no route-to-UUID mapping currently proves durable ordinary-chat history.
6. Agent output currently returns as raw Socket.IO `agent:start`, `agent:text`, and `agent:end` events. The plugin buffers the text and calls its typed Discord egress at stream end.
This compatibility path carries enough normalized identity to preserve current security and routing behavior, but it is not a gateway-produced `ChannelIngressDto`/`ChannelEgressDto` flow through a shared host registry.
This compatibility path carries enough normalized identity to preserve current security and routing behavior, but it is not a gateway-produced `ChannelIngressDto`/`ChannelEgressDto` flow through a shared host registry. The gateway can continue live dispatch after persistence failure; adapter authors must not claim durable continuity until external routes map to UUID conversations and fresh-message restart tests pass.
### Egress and health
@@ -172,7 +172,7 @@ These commands do not require starting Gateway, Discord, Telegram, Matrix, a que
- [`apps/gateway/src/chat/chat.gateway.ts`](../../../apps/gateway/src/chat/chat.gateway.ts) — service/session auth, signed envelope validation, replay, trusted agent selection, and raw stream events.
- [`apps/gateway/src/chat/chat.gateway-auth.ts`](../../../apps/gateway/src/chat/chat.gateway-auth.ts) — timing-safe Discord service-token and BetterAuth session checks.
- [`apps/gateway/src/plugin/discord-ingress.security.spec.ts`](../../../apps/gateway/src/plugin/discord-ingress.security.spec.ts) — gateway security and privileged-operation tests.
- [`apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts`](../../../apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts) — current Discord durable-session integration.
- [`apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts`](../../../apps/gateway/src/__tests__/integration/tess-cross-surface.integration.test.ts) — control-flow test with explicit durable-session pre-enrollment; not ordinary Discord persistence evidence.
- [`apps/gateway/src/plugin/plugin.interface.ts`](../../../apps/gateway/src/plugin/plugin.interface.ts), [`plugin.module.ts`](../../../apps/gateway/src/plugin/plugin.module.ts), and [`plugin.service.ts`](../../../apps/gateway/src/plugin/plugin.service.ts) — lifecycle-only host registry.
- [`plugins/telegram/src/index.ts`](../../../plugins/telegram/src/index.ts) and [`plugins/telegram/package.json`](../../../plugins/telegram/package.json) — raw Telegram behavior and no-test script.
- [Canonical channel protocol architecture](../architecture/channel-protocol.md) — shared contract and explicit current/draft boundary.