fix(discord): surface result delivery failures - #2217
Conversation
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 58%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
|
Author follow-up from the two-week sweep: CI is blocked only by the Python diff-coverage gate (38.5%; uncovered delivery-failure fallback/owner-DM branches around src/discord-bridge.py:3664-3700). The full test suite and other gates pass. Add focused async tests for those branches before re-requesting review. |
qingyun-wu
left a comment
There was a problem hiding this comment.
Approve. The bug is real (silent archive after a 413 attachment failure), the before/after is a genuine live reproduction on main, and the test exercises the real path rather than asserting through a mock.
I focused verification on the one thing that can turn a failure-notifier into a new failure mode: does the notice path itself stay contained? It does, and I checked each layer rather than trusting the docstring:
- The DM-send block (fetch_user → create_dm → send) is inside
try/except Exception as notice_error, so an owner DM that also 413s is logged, not raised. _emit_channelis separately wrapped.result_audit.record()runs before those try blocks, which briefly worried me — but it's self-guarded (src/result_audit.py:40, "Never raises", full try/except pass). So the helper's "deliberately never raises" contract actually holds end-to-end, which matters because the call site is insidepoll_results's ownexcept— a raise there would propagate into the handler that invoked it.
The only lines in the helper outside a guard are the DeliveryFailure(...) dataclass construction and str(error). Both are effectively non-raising; not worth changing, noting only so the "never raises" claim is understood as "in practice" rather than "structurally enforced."
Test quality — the assert channel.file_attempts == 1 ("the attachment failure was not exercised") is the load-bearing guard: without it, a test that stopped triggering the 413 would still pass every other assertion and silently stop testing the bug. That's exactly the fixture-trap that's easy to ship, and it's guarded here. The ledger assertions check both the presence of failed and the absence of delivered, which is the right pair.
Consistent with the delivery-visibility theme across the current bridge PRs (#2216, #2226). Nice to have the 413 → owner-visible path closed on discord.
…r-raise branches The diff-coverage gate flagged _report_delivery_failure: the existing test only hit the owner+DMChannel happy path. Extend it to exercise the remaining branches by calling the reporter directly: - non-owner/non-DM channel → resolve_owner_id resolves the canonical owner - resolve_owner_id None → fall back to scanning allowFrom for a non-bot user - no owner resolvable → log + return, no DM, no raise - owner DM resolves but send() raises → final handler swallows it (never raises) Test-only change; behavior unchanged. All cases pass locally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013LvRyLAxRugJ9ygj8BgNR8
…e/fetch-user)
diff-cover still flagged 3 defensive except paths in _report_delivery_failure:
the best-effort _emit_channel (except: pass), an unreadable ACCESS_FILE
(except → {}), and a fetch_user error mid allowFrom scan (except: continue).
Add/adjust cases so all three execute; local coverage of the function is now
100% (0 missing lines). Test-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013LvRyLAxRugJ9ygj8BgNR8
|
@cla-assistant check |
What changed, and why?
Discord result delivery now handles API exceptions as a real failed-delivery outcome instead of a console-only log:
failedrow toresult-audit.logchannel.discord.outwithoutcome=errorand the task/error detailsThis fixes the observed path where reply text was sent first, an oversized video upload then failed, and the bridge archived the task/result without telling the owner.
Review first
src/discord-bridge.py:_report_delivery_failure()and thepoll_results()exception hooktests/discord-bridge-delivery-failure-visible.test.py: end-to-end poller regressionUser behavior proved
Before: Discord showed success-looking text but no attachment; only the bridge console contained
Reply failed: 413 Payload Too Large.After: the same simulated attachment rejection produces an explicit owner-visible failure notice naming the task and error, a
failedaudit row, and an error observability event. It never records the task as delivered.Verification
Manual live reproduction on unpatched
main:task-1784486069331sent text, then the 17 MB MP4 upload failed.sutando-discordpane recorded:Reply failed: 413 Payload Too Large (error code: 40005): Request entity too large.task-1784556712838reproduced the same 413 and silent archive behavior.Failing-before regression on
origin/main:Passing-after:
Additional passing checks:
Edge cases
unknown; they are never silently upgraded to owner.Migrations, permissions, rollback, and risks
Known gaps