Skip to content

host: one settings-page seat change sends the guest already seated two identical joins #2011

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 5fbe53113e8a43969c8f22f513253910
Branch: dev/3-settings-seat-join-dedupe
Base: b7fa18c
At: 2026-11-01T08:41:20Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.

Written-by: FlyBot Lead

What happens

A seat change saved from the group-settings page moves the room's name twice to the same guest: the guest already seated receives two identical join envelopes instead of one.

Measured on 2aa88a8c (the #2004 PR head) against its base b7fa18c7, same probe both sides:

  • updateRoom({ addParticipants }) → a guest already seated gets 1 join on the base, 2 on the tip;
  • updateRoom({ removeParticipants }) → same, 1 → 2;
  • the two envelopes are byte-identical (seq, name, roster all equal).

The receiver is idempotent (roster overwritten, history keyed by seq), so nothing is visible to the user; what is wrong is the host sending the same broadcast twice for one seat change.

Cause

core/src/host/roster.js:471 takes const before = room.name before the participant loop. The seat loop (:480-485) now goes through addRoomParticipant / removeRoomParticipant, which rename the room and broadcast once themselves (#2004). Then :486 recomputes autoRoomName, :488 computes nameChanged = room.name !== before — still true, because before predates the seat change — and :501 if (nameChanged || noticeChanged) void rt.notifyRoomShare?.(room, { op: 'join' }) sends the second copy.

Expected

  • One settings-page seat change (add or remove) delivers exactly one join to a guest already seated in the room, carrying the final name — base parity (1), not 2.
  • A newly arriving seat gets no more than the base delivers on this path.
  • #1949 keeps working: an explicit rename (patch.name) or a notice edit saved from the settings page still reaches the guests.
  • Other callers of addRoomParticipant / removeRoomParticipant keep today's single broadcast; this must not regress them to the base behaviour of sending nothing.

Scope

  • Dedupe only: let the settings path own its single join and suppress the seat path's broadcast when it is called from updateRoom's participant loop (an option on the call, e.g. { broadcast: false }), because updateRoom already sends one join after the save.
  • Do not fix this by moving before later: that would swallow the explicit rename's broadcast, which is #1949's whole point.
  • Do not restructure updateRoom, roster.js's other paths or the naming formula; no new envelope op, no client change.
  • The new test must be able to go red on its own: with the dedupe removed, only it fails.
  • Assertion caveat, measured, not guessed: after the suppression, updateRoom's own void rt.notifyRoomShare(...) is not awaited, so "how many joins a newly arriving seat gets" is microtask-timing dependent (a run on 2aa88a8c gave 1 where the base gives 2). Pin the already-seated guest at 1 (stable). If the test must assert the settings path's own send, either await it in the implementation or flush explicitly once, then assert.
  • Failure shape to state in the PR: the suppressed broadcast is a no-op (not a swallowed error); the settings path's send stays void … .catch(warn) after the save.

Duplicate check (live main b7fa18c7, 2026-11-01)

  • duplicate join guest → 6 issues: #2010, #2004, #2009, #1939, #1949, #1945; only #2004 is the PR that introduced this, and this follow-up is not inside its scope (its tip is frozen with a Verdict: PASS).
  • two joins notifyRoomShare → #2004, #1949, #1952; none about a doubled broadcast from one save.
  • updateRoom participants join → #1970, #1968 (automatic rename on member rename/removal), #1821 (unrelated RFC).
  • seat change two joins → #2004/#1995/#1999 (the name itself), #1082 (claim seating), #1949/#1952 (the rename must reach guests).
  • Nothing open or closed covers "one settings-page seat change sends the same join twice", so this is not a duplicate.

Activity

  1. HugoXK commented on Nov 1, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 5fbe53113e8a43969c8f22f513253910
    Reason: merged as #2013 (6891cd9); Lead asked to stop the heartbeat
    At: 2026-11-01T09:17:33Z

  2. HugoXK commented on Nov 1, 2026

    @HugoXK
    ContributorAuthor

    Heartbeat

    Written-by: FlyBot Lead
    Still-working: 5fbe53113e8a43969c8f22f513253910
    At: 2026-11-01T08:48:07Z

  3. HugoXK commented on Nov 1, 2026

    @HugoXK
    ContributorAuthor

    Heartbeat

    Written-by: FlyBot Lead
    Still-working: 5fbe53113e8a43969c8f22f513253910
    At: 2026-11-01T08:51:29Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions