Follow-up from #160.
#plan-viewers is rendered inside #plan-header, but two independent mechanisms replace overlapping DOM:
- Plan broadcasts (
Broadcaster.replace_to(plan, target: "plan-header", ...)) replace the whole header, including the viewers list.
- The presence channel (
plan_presence_channel.rb) independently replaces #plan-viewers on its ~15s cadence.
Problems this creates:
- Out-of-order replacement: a header broadcast and a presence broadcast can arrive in either order; whichever lands last wins. The presence fingerprint cache can then suppress a correcting update until membership actually changes.
- Lost viewer-relative styling: header broadcasts render with no
current_user, so the "you" styling on the viewer's own avatar disappears after any header replacement, until the next presence tick repaints it.
Suggested direction: move the presence mount outside the header replacement boundary (a sibling of #plan-header rather than a child), so each Turbo target has exactly one owner. The header's meta row can reserve the slot; presence stays viewer-relative and self-managed.
Works correctly today in practice (presence self-heals within one tick) — this is about removing the ownership overlap before it grows another symptom.
Follow-up from #160.
#plan-viewersis rendered inside#plan-header, but two independent mechanisms replace overlapping DOM:Broadcaster.replace_to(plan, target: "plan-header", ...)) replace the whole header, including the viewers list.plan_presence_channel.rb) independently replaces#plan-viewerson its ~15s cadence.Problems this creates:
current_user, so the "you" styling on the viewer's own avatar disappears after any header replacement, until the next presence tick repaints it.Suggested direction: move the presence mount outside the header replacement boundary (a sibling of
#plan-headerrather than a child), so each Turbo target has exactly one owner. The header's meta row can reserve the slot; presence stays viewer-relative and self-managed.Works correctly today in practice (presence self-heals within one tick) — this is about removing the ownership overlap before it grows another symptom.