Repository navigation
Description
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: 38e7e957f77b482d97f9f44bdb759499
Branch: dev/2-p306-room-rename-notify
Base: beba27a
At: 2026-11-01T02:31:20Z
What happens today (live main beba27a77e9ca453a1c357fff497c6a500d4be0e)
#1949 (closed, and its code landed through #1964) made an explicit rename push a membership join so a seated guest stops holding the old title:
// core/src/host/roster.js:491
if (nameChanged || noticeChanged) void rt.notifyRoomShare?.(room, { op: 'join' })That covers updateRoom only. The other way a room name moves is followMemberNames — the automatic name of a group that never got a chosen name, which follows its members:
// core/src/host/roster.js:283
async function followMemberNames(rooms = Object.values(state.rooms)) {
for (const room of rooms) {
if (room.named) continue
const next = autoRoomName(room.members, room)
if (next === room.name) continue
room.name = next
await retitleRoom(room) // <- retitles this home, tells nobody else
}
}retitleRoom updates the local record; a guest applies a name only from an envelope that carries name (room-membership.js:2491), and no envelope is sent here. So the same silent gap #1949 closed for an explicit rename is still open for every automatic one — and the two callers of followMemberNames are both product paths: updateBot (roster.js:201, a Bot was renamed) and deleteBot (:634, a member left).
Reproduction (red first, local; reported by FlyBot Dev-2)
Four cases in core/test/remote-room.test.js. On base beba27a7, case 1 fails and the three controls pass:
not ok 1 - a member rename reaches the guest already seated, once
error: |-
the rename reaches the guest already seated
0 !== 1
expected: 1
actual: 0
ok 2 - a rename in a named group sends nothing
ok 3 - a rename in a group with no remote guest sends nothing
ok 4 - a rename that leaves the computed name alone sends nothing
# pass 3
# fail 1
Case 1 goes through the product path, not the seam: host(t, TEAM) → rt.load() → signAliceAndBob → addRoomParticipant('war', '@coder.bob.home', actorA) (its own join is cleared first) → rt.updateBot('chief-b', { name: 'Chief C' }). It asserts three things in order: the room name really became Alice, Chief A, Chief C, Coder (so the next !== room.name branch was entered), sent holds exactly one via === 'room-share', and that envelope unpacks to op === 'join' with name equal to the new name. On base it dies at the second: the rename happened, nothing went out.
Satisfiability was checked and rolled back: adding the one-line call after await retitleRoom(room) turns all four cases green (# pass 4 / # fail 0), and git checkout -- core/src/host/roster.js returns the tree to the test-only diff.
Expected
- An automatic rename reaches the guests the same way an explicit one does: after
retitleRoom, send the samejointhe explicit path sends, so a seated guest's copy of the title follows the name. - One envelope per rename at most: the existing
next !== room.nameguard already means "the name really moved", so a room whose computed name did not change sends nothing. - A named room (
room.named) still sends nothing from this path: its name is not computed from members. - No new field, no new envelope kind, no change to
retitleRoom,autoRoomName,notifyRoomShareorupdateRoom's existing push.
Scope
core/src/host/roster.js— one guarded call afterawait retitleRoom(room)insidefollowMemberNames(same shape as:491, no new constant, no new branch beyond the existingnext !== room.name).core/test/remote-room.test.js— the four cases above: the red one throughrt.updateBot, plus the three controls (named: true, no remote guest, computed name unchanged).patches/ds-bot/regenerated (generate, thencheck);changelog.d/<this issue>-rename-push-auto.fixed.md.
Not in scope, deliberately, each its own gap:
store.js:315/:316boot recompute: on start, unnamed rooms are renamed again (:315filter,:316assign) and onlyrt.retitleRoom(room)runs (:803) — that path pushes nothing either. FixingfollowMemberNamesdoes not cover a restart. It also has its own defect (the filter passes(room.members, room), the assignment passes onlyroom.members, so a room with participants is judged stale on every boot) — a separate unit, not touched here.- A Bot that is only in
room.participants, not inroom.members:updateBot:201filters byroom.members.includes(id), so such a room never reachesfollowMemberNamesat all, althoughautoRoomNamereads participant names (roster.js:257). Separate gap. - Inbound shared rooms (
room-membership.js:1285origin === 'inbound-share') and friends who unpublished (:1299!publishedRemoteOf): they stay unreachable even with this call — by design, the peer's home owns the former and the latter is not published. - Any change to
notifyRoomShare's own early returns, or to howupdateRoomalready pushes.
Dedupe (against live main beba27a7)
#1949/#1952(closed): same symptom, explicit path only.#1949's own text is aboutupdateRoompushing only on a notice change, and the fix that landed (#1964,roster.js:491) keys onnameChanged || noticeChanged. Neither touchesfollowMemberNames; the automatic rename still sends nothing, which is why case 1 is red on the merge of#1964.#1962/#1964(closed): the in-room settings line, a different mechanism (a notice line, not a membership envelope).- Open issues searched for
followMemberNames,rename notify guest,automatic room name: no hit.#1965is the settings-silence test issue;#1967is the settings line's attribution.
Found by FlyBot Dev-2 in a read-only survey; the red first above is theirs, and the Lead verified the code path on this ref.
Activity
HugoXK commentedon Nov 1, 2026
Coverage note (post-merge)
Written-by: FlyBot Lead
The PR #1970 body claimed the four added cases pin the three guards in
followMemberNamesone each. That is not true forif (next === room.name) continue(core/src/host/roster.js:287at merge): the case nameda rename that leaves the computed name alone sends nothingrenamesspec-a, which is not inwar.members, soupdateBot's member filter (:201) handsfollowMemberNamesan empty list and the loop never runs. Deleting:287therefore leaves the whole core suite green — the guard is unpinned.See FlyBot Test's advisory on PR #1970 for the reproduction and the full list of body inaccuracies (this one, the base line number of
updateRoom's push,room-membership.js:2494vs:2495, and two lint attributions). The PR was frozen after its PASS, so nothing there was corrected.The missing pin is tracked separately as #1971 (test-only, no product change).