fix(ws): always echo a message to its author, even when unsubscribed (LC-397) #412
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/lc-397-author-echo-unconditional"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes LC-397. The optimistic-echo "Sending..." sticking issue (LC-382) happens in enclave rooms but not DMs - your exact observation is what pinned it.
Root cause
The composer posts with
hx-swap="none", so the canonical message render arrives only via the author's own WebSocket echo - which carries theclient_idthat removes the optimistic placeholder. Butrender_new_message_or_bumponly sent that echo when the author's connection was already in the per-connectionsubscribedroom set:When the author was a broadcast recipient but their room subscription hadn't registered for that connection, they fell to that
return Noneand got nothing, so the placeholder was never reconciled until the LC-382 watchdog/refresh. DMs dodged it because DM members are always inroom_membersand subscribe via that path; enclave rooms hit it.Fix
The author posted the message and has a placeholder waiting, so they must always receive their own echo regardless of subscription. Handle the author before the subscription check and render the echo (with the
client_id) unconditionally. The echo is an id-keyed OOB swap into#messages, so it self-limits to the tab actually viewing the room (other tabs drop it harmlessly) - the same self-limiting-OOB principle the codebase already relies on. Non-author delivery (subscribed -> mark-read + render; else sidebar bump) is unchanged.Testing
author_gets_echo_even_when_not_subscribed: posts as the author, then rendersrender_new_message_or_bumpwith an empty subscription set and asserts the echo still carriesdata-lc-client-id. It would have failed before the fix (unsubscribed author returnedNone).render_new_message_or_bumpis nowpub, re-exported viaroutes(mirroringmaybe_coyote_ban).just check,just test,just test-saasall green (exit 0).This is the real root cause behind the LC-382 watchdog being needed in enclave rooms - the watchdog stays as a backstop, but the author's placeholder should now reconcile instantly from the echo.
The optimistic-echo "Sending..." stuck issue (LC-382) hit enclave rooms but not DMs. The composer posts with hx-swap="none", so the canonical render arrives only via the author's own WebSocket echo, which carries the client_id that removes the optimistic placeholder. But render_new_message_or_bump only sent that echo when the author's connection was already in the per-connection `subscribed` room set; an unsubscribed author fell to `if message.user_id == viewer.id { return None }` and got no echo, so the placeholder was never reconciled (until the LC-382 watchdog/refresh). DMs avoided it because DM members are always in room_members and subscribe via that path. The author posted the message and has a placeholder waiting, so they must always receive their own echo. Handle the author before the subscription check and render the echo (with the client_id) unconditionally. The echo is an id-keyed OOB swap into #messages, so it self-limits to the tab actually viewing the room; other tabs drop it harmlessly. Non-author delivery (subscribed -> mark-read + render; else sidebar bump) is unchanged. render_new_message_or_bump is now pub (re-exported via routes, mirroring maybe_coyote_ban) so a regression test asserts the author's echo carries the client_id with an empty subscription set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>