Skip to content

fix(discord-bridge): dm-fallback honors [no-send]/[REPLIED]/[deduped:] markers - #726

Merged
sonichi merged 1 commit into
mainfrom
fix/dm-fallback-honors-suppression-markers
May 16, 2026
Merged

sonichi merged 1 commit into
mainfrom
fix/dm-fallback-honors-suppression-markers

Conversation

@sonichi

@sonichi sonichi commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • The main reply path in discord-bridge.py:2660 skips Discord delivery when a result body starts with [no-send], [REPLIED], or [deduped:. The poll_dm_fallback loop did not have the same check — so results written specifically to suppress delivery still got DM'd to the owner when voice-agent was offline.
  • Add the same marker check to the dm-fallback loop; on match, archive both result + task (parity with main path) and continue.

Repro

Today: I wrote [no-send] markers on results/task-phone-1778876451343.txt and results/task-phone-1778876570451.txt (voice agent had already delivered the inbox-query answer live during the call — re-DM would be duplicate). Bridge log:

[dm-fallback] sent task-phone-1778876451343.txt via dm-result.py
[dm-fallback] sent task-phone-1778876570451.txt via dm-result.py

Both got DM'd to Chi despite the marker.

Test plan

  • Write a result with [no-send] while voice-agent is offline; verify [dm-fallback] skipped (suppression marker) log line + no DM
  • Repeat for [REPLIED] and [deduped: task-XYZ]
  • Sanity: result without marker still delivers via dm-fallback as before

🤖 Generated with Claude Code

…] markers

The main reply path (line ~2660) skips Discord delivery when a result body
starts with `[no-send]`, `[REPLIED]`, or `[deduped:`. The dm-fallback poll
loop, which kicks in when voice-agent is offline / didn't pick up the
result, was calling dm-result.py unconditionally — so results written
specifically to suppress delivery (e.g., deduped phone-task follow-ups)
got DM'd to the owner anyway.

Repro from today: I wrote `[no-send]` markers on
results/task-phone-1778876451343.txt and results/task-phone-1778876570451.txt
to suppress duplicate DMs (voice agent already delivered the content live
during the call). The bridge dm-fallback poll picked them up and DM'd the
owner anyway:
  [dm-fallback] sent task-phone-1778876451343.txt via dm-result.py
  [dm-fallback] sent task-phone-1778876570451.txt via dm-result.py

Fix: peek the result body at the top of the dm-fallback loop; on marker
match, archive both result+task (parity with main path) and continue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonichi
sonichi merged commit d2a3f56 into main May 16, 2026
1 check passed
@sonichi
sonichi deleted the fix/dm-fallback-honors-suppression-markers branch May 16, 2026 02:29
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…] markers (sonichi#726)

The main reply path (line ~2660) skips Discord delivery when a result body
starts with `[no-send]`, `[REPLIED]`, or `[deduped:`. The dm-fallback poll
loop, which kicks in when voice-agent is offline / didn't pick up the
result, was calling dm-result.py unconditionally — so results written
specifically to suppress delivery (e.g., deduped phone-task follow-ups)
got DM'd to the owner anyway.

Repro from today: I wrote `[no-send]` markers on
results/task-phone-1778876451343.txt and results/task-phone-1778876570451.txt
to suppress duplicate DMs (voice agent already delivered the content live
during the call). The bridge dm-fallback poll picked them up and DM'd the
owner anyway:
  [dm-fallback] sent task-phone-1778876451343.txt via dm-result.py
  [dm-fallback] sent task-phone-1778876570451.txt via dm-result.py

Fix: peek the result body at the top of the dm-fallback loop; on marker
match, archive both result+task (parity with main path) and continue.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
john-the-dev added a commit that referenced this pull request Aug 20, 2026
…not a conflict

Self-review with the lesson from the #726 finding: `except OSError` on the
stale-clear covered two cases whose correct responses are opposite. If an
executor consumes the stale intent between our peek and our unlink, the path
is now FREE and we should claim it — but the bare OSError catch reported
IntentPending, telling the owner a request is pending that nobody made.

FileNotFoundError now falls through to the retry; a genuine unlink failure
(permissions, read-only) still refuses.

Control: with the FileNotFoundError branch removed the new test errors; with
it, 27/27 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant