Skip to content

fix(discord): keep multibot threads responsive#1439

Open
chaodu-obk[bot] wants to merge 17 commits into
mainfrom
fix/1436-discord-multibot-clean-v2
Open

fix(discord): keep multibot threads responsive#1439
chaodu-obk[bot] wants to merge 17 commits into
mainfrom
fix/1436-discord-multibot-clean-v2

Conversation

@chaodu-obk

@chaodu-obk chaodu-obk Bot commented Jul 22, 2026

Copy link
Copy Markdown

Summary

  • keep Discord multibot threads visibly responsive with sanitized presentation streaming
  • separate human-facing presentation from structured bot-to-bot handoffs
  • classify only bot-authored, target-header JSON envelopes as handoff candidates
  • authorize outbound targets against the configured trusted bot allowlist
  • validate schema, trust, exact target, live channel provenance, expiry, hop count, replay, rate, and payload bounds before ACP dispatch
  • preserve the completed presentation after a successful handoff
  • preserve raw prompt text inside the structured control payload while Discord allowlists only the target mention
  • reject handoff envelopes that exceed Discord 2,000-character message limit
  • apply a per-source limit of 10 accepted handoffs per 60 seconds
  • cap the process-local replay cache at 4,096 events and fail closed when full
  • preserve normal Discord reply notifications while disabling automatic presentation mentions

Review fixes

  • clarify directive, adapter, Discord multibot streaming, expiry, and provenance documentation
  • trace invalid handoff directives and narrow candidate detection to avoid schema text collisions and schema-version bypass
  • bind envelope provenance to the receiving channel
  • authorize outbound handoff targets against trusted_bot_ids
  • preserve/finalize presentation output after handoff delivery
  • add regression coverage for multibot streaming, channel provenance, candidate collision, raw payload mentions, rate limiting, replay-cache capacity, schema validation, target authorization, and 1,800-character payload size

Operational limitations

  • replay deduplication is process-local; multi-replica deployments require a shared deduplication store before relying on cross-replica exactly-once behavior
  • accepted handoffs are subject to both the per-source rate limit and the existing per-thread bot-turn cap
  • trusted bot IDs remain startup configuration and require a restart to change; they also define the outbound handoff target allowlist
  • handoff protocol is currently first-hop only; non-zero hop_count values are rejected
  • the handoff control message remains visible in Discord because Discord has no hidden bot-only message channel
  • ordinary Discord presentation messages intentionally disable automatic user/role/everyone mentions; explicit handoff target mentions are the only control-plane mentions

Validation

  • Rust LSP diagnostics: no diagnostics for adapter.rs, discord.rs, config.rs, and main.rs
  • git diff --check
  • Full Cargo validation could not run because the authoring environment lacks cargo, rustc, rustfmt, and rust-analyzer

Closes #1436

Review Contract

Goal

Keep Discord multibot threads visibly responsive during long ACP turns while separating sanitized human-facing presentation from structured, fail-closed bot-to-bot handoffs (closes #1436).

Non-goals

  • Cross-replica exactly-once handoff delivery (replay deduplication remains process-local)
  • Hiding the handoff control message from Discord (Discord has no bot-only message channel)
  • Runtime reconfiguration of trusted bot IDs (startup configuration only)
  • Multi-hop handoff routing (protocol is first-hop only; non-zero hop_count rejected)

Accepted Residual Risks

  • Replay deduplication is process-local; multi-replica deployments need a shared deduplication store before relying on cross-replica exactly-once behavior
  • The handoff control message remains visible in the Discord thread
  • Trusted bot ID changes require a bot restart to take effect
  • Accepted handoffs remain subject to the per-source rate limit and the existing per-thread bot-turn cap

Acceptance Criteria

  • Only bot-authored, target-header JSON envelopes are classified as handoff candidates
  • Envelopes are validated for schema, trust, exact target, live channel provenance, expiry, hop count, replay, rate, and payload bounds before ACP dispatch
  • Outbound handoff targets are authorized against the configured trusted bot allowlist; self-targeting is rejected
  • Presentation output is preserved and finalized after a successful handoff
  • Handoff envelopes exceeding the Discord 2,000-character limit are rejected; per-source limit of 10 accepted handoffs per 60 seconds; replay cache capped at 4,096 events and fails closed when full
  • Regression tests cover multibot streaming, channel provenance, candidate collision, raw payload mentions, rate limiting, replay-cache capacity, schema validation, target authorization, and payload size bounds; CI (fmt, clippy, tests, smoke tests) is green

Follow-ups

  • Introduce a shared deduplication store to support exactly-once handoff semantics across replicas
  • Consider a hidden control-plane transport if Discord ever exposes bot-only messaging
  • Evaluate hot-reload of trusted bot IDs without restart

@chaodu-obk
chaodu-obk Bot requested a review from thepagent as a code owner July 22, 2026 21:59
@chaodu-obk

chaodu-obk Bot commented Jul 22, 2026

Copy link
Copy Markdown
Author

Note

LGTM �

What This PR Does

This PR keeps Discord multibot threads visibly responsive during long ACP turns while separating human-facing presentation from executable bot-to-bot handoffs. Presentation output is sanitized and mention-inert; structured handoffs are explicit, allowlisted, provenance-bound, and fail-closed.

How It Works

  • Discord keeps edit-streaming enabled in multibot threads, while ordinary presentation sends/edits suppress automatic user, role, and everyone mentions.
  • [[handoff:TARGET_BOT_ID]] is parsed only at the output header, requires a decimal Discord snowflake, and is emitted only for a non-self target in the configured trusted_bot_ids allowlist.
  • Receiving bot-authored envelope-shaped messages are validated for exact schema, source/target trust, live channel/thread provenance, expiry, hop count, UUID event ID, replay capacity, per-source rate, and payload bounds before ACP prompt dispatch.
  • Successful and failed handoffs retain the sanitized human-facing presentation; the structured payload preserves formatted content without presentation zero-width characters.

Findings

# Severity Finding Location
1 � Praise Outbound and inbound target authorization, including explicit self-target rejection, are enforced fail-closed. crates/openab-core/src/adapter.rs:1214, crates/openab-core/src/discord.rs:233,3528
2 � Praise Schema-shaped candidate gating, provenance validation, replay cap, and per-source rate limiting prevent malformed or replayed control input from reaching ACP. crates/openab-core/src/discord.rs:649-680,370-430
3 � Praise Presentation/control separation preserves streaming and human visibility while limiting executable mentions to the validated handoff target. crates/openab-core/src/adapter.rs:1019,1069,1115,1191, crates/openab-core/src/discord.rs:126-247
4 � Praise Canonical directive/config/multibot documentation now matches parser behavior, UTF-16 wire limits, receive bounds, and fail-closed semantics. docs/output-directives.md, docs/config-reference.md, docs/discord.md, docs/multi-agent.md, docs/platforms/schema/discord.toml
Finding Details

� F1: Outbound and inbound authorization

The sender checks the configured trusted_bot_ids target allowlist and rejects self-handoff before constructing the envelope. The receiver independently checks source trust, source != receiver, and target == the current bot.

� F2: Control-plane validation and bounded state

Only bot-authored, target-header, JSON-shaped candidates enter the handoff validation path. Invalid candidates fail closed. Replay state has a 4,096-event hard cap and 600-second retention window; accepted sources are limited to 10 handoffs per 60 seconds.

� F3: Safe presentation delivery

All Discord presentation streaming/final paths sanitize mention-like text and use empty automatic allowed_mentions. The explicit handoff path allows only the configured target mention and keeps the sanitized presentation visible on both success and failure.

� F4: Documentation alignment

The output directive is documented as Discord-only, parser-header based, decimal-u64 constrained, self/allowlist checked, and bounded by the full 2,000 UTF-16-code-unit envelope limit plus the 100,000-byte receive defense bound.

Baseline Check
  • Head SHA: 8774c9a60ced187d6bae3fd53099fe5be5cd43a2
  • Base: main at 685973307bf79d6848c21b33a5bd55b6aea1c433
  • Merge-base: 685973307bf79d6848c21b33a5bd55b6aea1c433
  • Scope: 9 files, 743 additions, 204 deletions, 14 commits
  • Net-new value: explicit structured handoff routing, bounded validation, inert multibot presentation streaming, and synchronized documentation
What's Good (�)
  • The two-plane design avoids treating partial presentation output as executable control input.
  • The allowlist, provenance, replay, rate, and size checks are layered rather than relying on a single model-output convention.
  • The self-target guard and fail-closed defaults reduce accidental routing and cross-platform behavior drift.
  • Validation evidence: Rust LSP reported no diagnostics for modified Rust files and git diff --check passed. Full Cargo validation remains unavailable because this environment lacks cargo, rustc, rustfmt, and rust-analyzer.

5�� Three Reasons We Might Not Need This PR

  1. Existing mention-based collaboration was simpler � the structured protocol adds schema, trust, replay, rate, and size machinery; the tradeoff is justified only when controlled handoffs and safe presentation streaming are required.
  2. Always-on multibot streaming is a UX choice � deployments that prefer the older send-once behavior may still need a configuration policy in a future iteration.
  3. Process-local deduplication is not cross-replica exactly-once � multi-replica deployments still need a shared deduplication store, which is documented as an operational limitation.

@chaodu-obk chaodu-obk Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM � � exact-SHA review at 8774c9a confirms outbound target authorization and self-target rejection, validated schema/prompt gating, bounded replay/rate state, inert presentation streaming, handoff size bounds, and synchronized documentation. Canonical review: #1439 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(multibot): keep Discord threads responsive during long-running tasks

1 participant