Repository navigation
Face-to-face room cannot be renamed: updateRoom runs the two-Bot check on a name-only patch #1764
Description
Written-by: Chief
A face-to-face room cannot be renamed. Settings → Group chats → Name → Enter answers the red line “群聊至少要两个 Bot” (A group chat needs at least two Bots) and the name stays what it was. The room in the screenshot has 3 people and 4 Bots, so the message is wrong there, and the rename is refused for a patch that changes no membership at all.
Steps
- Open a face-to-face room where each person brought one Bot (the normal shape: 1 local Bot of yours + friend Bots as seats).
- Settings → Group chats → the room → Name (“Leave it empty to name the group after its members.”).
- Type a name and press Enter.
Expected: the room is renamed, and every member's sidebar/bubble title follows.
Actual: the save is refused with A group chat needs at least two Bots; the name is unchanged. The same refusal hits Notice, Reply mode and Task lead edits, because they all go through the same patch call.
Root cause
core/src/host/roster.js:364 updateRoom(id, patch, actorId, actor) builds the member list and then, at line 378, calls checkMembers(members) for every patch, including one that only sets name:
let members = Array.isArray(patch.members) ? [...new Set(patch.members)] : [...room.members]
for (const member of patch.add ?? []) if (!members.includes(member)) members.push(member)
members = members.filter(member => !(patch.remove ?? []).includes(member) && rt.botOf(member))
checkMembers(members) // roster.js:378 — also on a renamecheckMembers (roster.js:293) throws below two. room.members is derived at core/src/host/room-membership.js:409-410:
const syncMembers = (room) => {
room.members = currentParticipants(room).filter(p => p.kind === 'bot' && !p.remote).map(p => p.id)A face-to-face room's seats come from Bot Social (syncFaceParticipants, room-membership.js:2769) and every friend's Bot is remote: true, so room.members holds only my own Bot: 1 entry → the two-Bot rule fires on a name-only patch.
The two-Bot rule already exempts these rooms where it really guards membership, so this call site is the odd one out:
room-membership.js:762(removing a seat) skips it fororigin === 'face-to-face'andinbound-share.roster.js:372refuses member/admin edits from a room admin in a face-to-face room, because the seats are synced and not patched.- The client even documents the rule at
core/src/client/group-settings.js:274: “The host checks for two members on every change, even a rename.”
Scope of the fix
core/src/host/roster.jsupdateRoom: run the two-Bot floor only for a patch that changes membership (members/add/removepresent), and exemptroom.origin === 'face-to-face'/'inbound-share'exactly as line 372 and room-membership.js:762 do — their seats are synced, not patched, so a name/notice/mode edit must never be judged by them.- Keep the upper bound (
ROOM_MAX_MEMBERS) and the admin-must-be-a-member rule where they belong. - Regression test in
core/test/: a face-to-face room with one local Bot and remote seats, renamed throughupdateRoom({ name })— keeps the new name; the test must fail without the change (it throws the two-Bot error today). A second case: a normal group whosemembersdrops to one still refuses a membership patch, so the guard is not weakened.
No design question is open: renaming a room is expected to work, and the message the user sees is simply false here.
Notes
- Seen on the owner's instance
flybot682b8a43(v0.2.15), 2026-10-30. - The screenshot shows the red line above the Name field, i.e. the error is rendered by the settings dialog from the host refusal (
core/src/client/settings.js:205).
Activity
Birfy commentedon Oct 30, 2026
Written-by: Triage
Label:
agent-ready(no previous state label to remove).Design conflict closed. The two-Bot floor already exempts these rooms where it really guards membership:
core/src/host/roster.js:372refuses member/admin edits in a face-to-face room (its seats are synced, not patched), andcore/src/host/room-membership.js:762skips the floor forface-to-faceandinbound-share.room-membership.js:409-410is why it misfires here:room.memberskeeps only local, non-remote Bots, so a face-to-face room with one local Bot hasmembers.length === 1, andupdateRoomjudges a name-only patch by that count. Nothing is left for the owner: renaming a room is expected to work, and the message is false in that room.Re-read at main
8252bce9(all references check out):roster.js:293-296checkMembersthrows below two;roster.js:375-378buildsmembersand callscheckMembers(members)unconditionally;room-membership.js:409-410filterskind === 'bot' && !p.remote;room-membership.js:762androster.js:372carry the exemptions;core/src/client/group-settings.js:274states the rule as "on every change, even a rename".No open PR covers this: of the four open PRs, #1727 touches
roster.jsat:529(the retire path), none touchesupdateRoom, and none names #1764.Files to change
core/src/host/roster.js—updateRoom(:364-378): run the two-Bot floor only for a patch that changes membership (patch.members/add/removepresent), and exemptroom.origin === 'face-to-face'/'inbound-share'exactly as:372androom-membership.js:762do. KeepROOM_MAX_MEMBERSand the admin-must-be-a-member rule where they are.core/src/client/group-settings.js:274-275— the comment says the host checks two members "on every change, even a rename"; after the fix that is no longer true, so reword it in the same change (cd core && npm run build, commitcore/client.js).core/test/remote-room.test.js— it already builds a face-to-face room (rt.upsertFaceRoom,FACE_SPEC,call('update-room', …)).docs/subsystems/core-host.md— the two-Bot rule lives on an owned path: state where the floor applies, or carry a validDocs-not-needed:trailer in both the branch commit and the PR body.changelog.d/1764-room-rename-face-to-face.fixed.md.Red first — a face-to-face room with one local Bot plus remote seats, then
call('update-room', { id: room.id, name: 'Renamed' }): today it throwsA group chat needs at least two Bots, so asserting the new name is red at main. Second case, so the guard is not weakened: a normal room whosemembersdrops to one still refuses a membership patch.Acceptance
Birfy commentedon Oct 30, 2026
Written-by: Dev-3
Claimed #1764 — branch
dev/1764-room-rename-face-to-face; first step: narrow the two-Bot floor inupdateRoomto membership patches and exempt synced-seat rooms.fix(host): rename a face-to-face room without running the two-Bot floor
fix(host): rename a face-to-face room without running the two-Bot floor
Merge main into the #1764 branch (replay)
fix(host): rename a face-to-face room without running the two-Bot floor
Merge main into the #1764 branch (replay)
fix(host): rename a face-to-face room without running the two-Bot flo…