Skip to content

fix: deliver a Discord member viewport chosen while offline after the session resumes - #50

Open
itz4blitz wants to merge 1 commit into
mainfrom
fix/discord-resume-member-subscription
Open

itz4blitz wants to merge 1 commit into
mainfrom
fix/discord-resume-member-subscription

Conversation

@itz4blitz

Copy link
Copy Markdown
Owner

Cause

members.rs::subscribe records Watched.sent when a subscription is queued, not when it is written. gateway.rs::run cleared the whole outbox on every new connection, so a subscription queued while offline, or still unwritten when the socket dropped, was thrown away. Because sent already named it, later identical Watches were deduplicated, and RESUMED never re-sent anything. The session resumed with Discord still holding the old viewport, and the member pane stayed pending.

Fix

  • SendQueue::reconnected(resuming): when the new connection resumes an existing session, keep the coalesced subscriptions that were never written and drop only the commands. That coalesced map is exactly the change from what Discord last received to what is on screen now, including the unsubscribe from an old guild. A connection that identifies a new session still clears everything, as before.
  • On RESUMED, resubscribe() sends the current subscription again (subscribe(true)), because a frame written just as the socket died may never have reached Discord. This costs one frame per resume and merges with any kept snapshot.

Nothing changes for READY or identify, so this should stay clear of #43 and #44.

Validation

  • New integration test member_viewport_changed_while_offline_is_delivered_after_resume in crates/chatd/tests/discord.rs, not ignored. It subscribes to 4101, drops the socket, sends a Watch for 4105 while offline, waits for the resume, then asserts that an op 37 for 4105 arrives after the op 6 resume frame and that the member cache fills from a SYNC. It fails on main and passes with this change.
  • New unit test a_resumed_connection_keeps_unwritten_subscriptions_and_a_new_session_does_not.
  • cargo test -p chatd: all passing. 135 lib unit tests, plus integration tests: blobs 14, calls 10, discord 31, e2e 25, messages 5, playback 4, polish 12, signal 10, slack 15, teams 7, telegram 17, upload 5. Total 290 passed, 0 failed, 0 ignored.

Fixes #42

🤖 Generated with Claude Code

… session resumes

A new gateway connection cleared the whole send queue, so a subscription queued while
the socket was down (or not yet written when it dropped) was lost while Watched.sent
already named it, and RESUMED never said it again. A resumed connection now keeps the
unwritten, coalesced subscriptions (commands are still dropped, and a new session still
starts empty), and RESUMED says what is on screen once more in case a frame written as
the socket died never arrived.

Fixes #42

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@itz4blitz

Copy link
Copy Markdown
Owner Author

@vbmithr this one fixes the queue-loss case you reproduced in #42.

What changed:

  • gateway.rs: run now calls SendQueue::reconnected(resuming) instead of clear(). When the connection resumes the existing session, member subscriptions that were never written to the socket stay queued, including a guild unsubscribe. Only commands are dropped. A connection that identifies a new session still clears everything, as before.
  • members.rs / ready.rs: on RESUMED, a new resubscribe() (subscribe(true)) sends the current viewport once more. That covers a frame written just as the socket died that never arrived. It costs one frame per resume.

Tests:

  • member_viewport_changed_while_offline_is_delivered_after_resume in crates/chatd/tests/discord.rs. It runs in the normal suite, not ignored, and follows your steps: subscribe to channel A, drop the socket, wait for Offline, select channel B while offline, let the session resume. It then asserts that B's subscription arrives after the resume frame and that its member cache fills. It fails without the source change.
  • Unit test a_resumed_connection_keeps_unwritten_subscriptions_and_a_new_session_does_not.

Not covered: a guild unsubscribe that was already written but lost with the socket isn't sent again. Only the current subscription is repeated on resume. Fixing that would mean tracking per guild what Discord actually received.

To try it:

git fetch origin pull/50/head:pr-50 && git checkout pr-50
cargo test -p chatd --test discord member_viewport_changed_while_offline
cargo test -p chatd adapters::discord::gateway
cargo test -p chatd                                        # full daemon suite

If you still have your local audit test, running it against this branch would be a useful second check. So would a real suspend/resume with the member pane open.

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.

Reconcile queued Discord member subscriptions after gateway session resume

1 participant