fix(discord): limit implicit reply references to first chunk#99150
fix(discord): limit implicit reply references to first chunk#99150qingminglong wants to merge 10 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 5, 2026, 10:22 AM ET / 14:22 UTC. Summary PR surface: Source +50, Tests +110. Total +160 across 10 files. Reproducibility: yes. from source and reporter proof: current sendDiscordText and sendDiscordMedia pass the same replyTo into every chunk, matching the live issue logs for replyToMode "first". Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Land one source-aware Discord send-layer fix that preserves explicit and all-mode reply reuse, then close or supersede the duplicate candidate PRs for the same bug. Do we have a high-confidence way to reproduce the issue? Yes from source and reporter proof: current sendDiscordText and sendDiscordMedia pass the same replyTo into every chunk, matching the live issue logs for replyToMode "first". Is this the best way to solve the issue? Yes; source-aware first-chunk gating at the Discord send layer is narrower than mode-only suppression because it preserves explicit reply tags and replyToMode "all". AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 61ac4ec8a0db. Label changesLabel justifications:
Evidence reviewedPR surface: Source +50, Tests +110. Total +160 across 10 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (8 earlier review cycles)
|
|
I think this is the stronger lane than #99083 because it keeps the Discord sender opt-in/source-aware, so explicit reply tags and all-mode behavior do not get collapsed by a mode-only sender check. One gap I would still close before landing: the
I would either thread the same source-aware first-chunk/first-physical-send signal through this branch, or stop reusing |
|
Updated the PR body for current head This head also fixes the reviewer-noted @openclaw-mantis visual task: verify Discord replyToMode first quotes only the first long-reply chunk while explicit first/all chunk replies keep expected native references. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
…e-main # Conflicts: # extensions/discord/src/outbound-send-context.ts
|
Merge-refresh pushed to clear the Updated the PR body for head
The merge conflict was only in @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Have a nice day, please help merge! |
|
@clawsweeper re-review Follow-up pushed at Current-head proof added to the PR body:
|
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review PR body formatting is repaired on the same head I rewrote the full PR body as multiline Markdown and checked it locally with the same policy helper: |
|
@clawsweeper automerge |
|
Superseded by #100784, landed as The landed fix retains this PR's core first-chunk correction and contributor credit, then broadens it into one source-aware Thanks @qingminglong for the original fix and regression coverage. |
Closes #99068
What Problem This Solves
Fixes an issue where Discord users with
replyToMode: "first"could receive one native reply notification per physical chunk when a single long agent reply was split into multiple Discord messages.Why This Change Was Made
Discord now carries a source-aware first-chunk-only flag from outbound delivery into the shared send layer. The flag is only enabled for implicit single-use reply targets, so explicit
[[reply_to_*]]replies and all-mode replies keep their existing reusable native reply behavior while the implicit turn reply no longer fans out across every physical text or media-caption chunk.The payload send path now uses the same reply fanout contract for each physical Discord send, including
audioAsVoice, native Discord payload media, component follow-up media, and classic component media downgrades. That closes the reviewer-noted gap where one captured implicit reply id could be reused across voice/text/media/component sends.The related open PR #99083 is not a strong blocker for this replacement shape: it now has proof, but its mode-only suppression was previously flagged for collapsing explicit first-mode chunk replies. This PR uses the reply source to avoid that compatibility regression while keeping all-mode and explicit reply tags reusable.
User Impact
Long Discord replies in first/batched implicit reply modes now quote the source message only on the first physical Discord message, avoiding repeated reply notifications for one logical answer. Explicit reply tags and all-mode replies remain reusable across chunks and payload sends as before.
Evidence
Current merge-refresh head
9b39407fff2bdc464fd74c1a67e2f384a6e0f50amerges latestupstream/mainintocodex/issue-99068-discord-reply-chunks. The PR diff against currentupstream/mainremains Discord-only: 10 files, +173/-13.Inspected exact failed QA Smoke CI run
28742547549, job85227804770, artifactqa-smoke-profile-28742547549-1. The root failure wasControl UI chat flow Playwright coverageinui/src/e2e/chat-flow.e2e.test.ts, specificallyscrolls a delayed pending send into view before the ACK resolvesfailing atui/src/e2e/chat-flow.e2e.test.ts:981withexpected 0 to be greater than 200.After the merge refresh,
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH="C:\Program Files\Google\Chrome\Application\chrome.exe" node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/e2e/chat-flow.e2e.test.ts --reporter=verbosepassed on current head: 1 file, 20 tests.After the merge refresh,
node scripts/run-vitest.mjs extensions/discord/src/outbound-adapter.test.ts extensions/discord/src/send.sends-basic-channel-messages.test.ts extensions/discord/src/send.components.test.ts --reporter=verbosepassed on current head: 3 files, 102 tests.git diff --checkpassed on current head.Claim proved on PR code head
71ae6b987eb7b5124e8921f7789ff96b865986ba: a live Discord bot token sent real messages to a real Discord server/channel throughsendMessageDiscord()->sendDiscordText()->createChannelMessage()using the PR code. The proof wrapper captured each realPOST /channels/<channel>/messagesrequest body at the Discord HTTP boundary while still sending the messages to Discord.Merge-refresh head
a9cabf230abeebff149847d2d2bc1026390f8a69resolves the latest upstreammainconflict. The resolution keeps upstream's per-send delivery progress reporting and this PR's source-aware implicit-reply first-chunk-only behavior; no Discord reply-fanout behavior was intentionally changed beyond the already-proved PR code.Follow-up head
3f60b73dfca7e1428ec0338a332c3c5a4e63a55bresolves the July 4 ClawSweeper P2 by carryingreplyToFirstChunkOnlyinto Discord component payload sends and through the classic component media downgrade intosendMessageDiscord(). The focused regression covers an implicit first-mode component media payload and the downgraded send options.Live Discord proof command:
pr-99150-1783017507939: seed message1522310522282704988; implicit first-mode reply produced three physical Discord message POSTs with returned message ids1522310535666864320,1522310543938162912,1522310550229614776and request-bodymessage_reference.message_idvalues[1522310522282704988, null, null].1522310567916863508,1522310574443073566,1522310580915015712and request-bodymessage_reference.message_idvalues[1522310522282704988, 1522310522282704988, 1522310522282704988].message_referenceon the first physical chunk when the reply target is implicit single-use, while direct/default sends, explicit replies, andreplyToMode: "all"keep their reusable native reply behavior.audioAsVoicepayload path no longer captures and reuses one implicit reply id across voice/text/media physical sends; the regression expects the voice send to keep the single implicit reply and later text/media sends to omit it.replyToFirstChunkOnly: truefor implicit first-mode sends, so a direct payload send that internally chunks still suppresses trailing chunk native references.replyToFirstChunkOnly: truefor implicit first-mode sends, and classic component media downgrades forward that flag intosendMessageDiscord().node scripts/run-vitest.mjs extensions/discord/src/outbound-adapter.test.ts extensions/discord/src/send.sends-basic-channel-messages.test.ts src/infra/outbound/message-plan.test.ts --reporter=verbosepassed on71ae6b987eb7b5124e8921f7789ff96b865986ba: 3 files, 89 tests.e7bb7b7b7043a1fcfaefd331cd65525310ec18f4, focused Discord/outbound tests, oxfmt,git diff --check, plugin SDK boundary, and autoreview passed.a9cabf230abeebff149847d2d2bc1026390f8a69,node scripts/run-vitest.mjs extensions/discord/src/outbound-adapter.test.ts extensions/discord/src/send.sends-basic-channel-messages.test.ts extensions/discord/src/send.components.test.ts --reporter=verbosepassed: 3 files, 97 tests.3f60b73dfca7e1428ec0338a332c3c5a4e63a55b,node scripts/run-vitest.mjs extensions/discord/src/outbound-adapter.test.ts extensions/discord/src/send.sends-basic-channel-messages.test.ts extensions/discord/src/send.components.test.ts --reporter=verbosepassed: 3 files, 98 tests.3f60b73dfca7e1428ec0338a332c3c5a4e63a55b,pnpm exec oxfmt --check extensions/discord/src/outbound-payload.ts extensions/discord/src/send.components.ts extensions/discord/src/outbound-adapter.test.ts extensions/discord/src/send.components.test.ts,git diff --check, andnode scripts/check-extension-plugin-sdk-boundary.mjs --mode=src-outside-plugin-sdkpassed.3f60b73dfca7e1428ec0338a332c3c5a4e63a55b,python .agents\skills\autoreview\scripts\autoreview --mode localexited 0 with no accepted/actionable findings.Not tested / proof gaps:
message_reference, while all-mode sent one per physical chunk.discord.com:443twice (UND_ERR_CONNECT_TIMEOUT), so the post-merge validation above rechecks the touched behavior locally.