Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2009-human-seat-rename-broadcast.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding or removing a person in a group without a chosen name now tells the guests already seated the new name ([#2009](https://github.com/ThinkFlowLab/FlyBot/issues/2009)): before this a human seat moved the host's own name and Session title while the other homes kept the copy they arrived with, because only a Bot's seat change sent the one `join` a guest holds. A group with a chosen name, a mirror of another home's room and a face-to-face room still send nothing for a seat change.
8 changes: 8 additions & 0 deletions core/src/host/room-membership.js
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,10 @@ export function install(rt) {
// the room get the same name once, and the arriving seat keeps its one targeted join.
if (moved && broadcast) reshareRoster(room, { except: record.handle })
} else if (record?.kind === 'bot' && !record.remote && broadcast) reshareRoster(room)
// #2009: a human seat is this home's own, so it never changes the roster a guest holds — but it does
// change the name an unnamed group is called after, and a human seat has no envelope of its own to
// carry that name. Only `moved` sends, so a chosen name still sends nothing.
else if (record?.kind === 'human' && moved && broadcast) reshareRoster(room)
return room
})
}
Expand Down Expand Up @@ -822,6 +826,10 @@ export function install(rt) {
await finishTicketLeave(room).catch(error => rt.warn?.('ds-bot: leaving %s was not settled: %s', room.name, error))
} else if (kind === 'bot' && !me.remote && broadcast) {
reshareRoster(room)
} else if (kind === 'human' && moved && broadcast) {
// #2009: the same as an add — a human leave moves the name of an unnamed group, and the guests
// still seated have no other way to hear it.
reshareRoster(room)
}
// #2004: the leave moved the name, so the guests still in the room get the copy the host holds. The
// seat that left is already out of `currentParticipants`; a local Bot's leave sent its roster above.
Expand Down
81 changes: 81 additions & 0 deletions core/test/remote-room.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3772,3 +3772,84 @@ test('#2004: a claim that seats a Bot moves the name for the guest already in th
assert.equal(guest.copies.get('@chief.bob.home'), room.name, 'the claimed seat is told the name it is seated under')
assert.equal(joins.get('@chief.bob.home'), 1, 'the claimed seat is told once, not twice')
})

// #2009: a human seat is this home's own, so it never changes the roster a guest holds — but it does
// change the name an unnamed group is called after. That name only ever left on an envelope addressed
// to the seat that changed, and a human seat is not addressable: the guests already seated kept the
// copy they arrived with while the host moved on.
function seatBob(rt) {
rt.state.members['owner-2'] = { id: 'owner-2', createdAt: 2, displayName: 'Bob' }
}

test('#2009: adding a human seat moves the name for the guest already in the room', async (t) => {
const { rt, sent } = titledHost(t, TEAM)
await rt.load()
publish(rt)
await grantToFriend(rt, 'chief-a')
await grantToFriend(rt, 'chief-b')
seatBob(rt)
const room = rt.state.rooms.war
delete room.named
room.name = rt.autoRoomName(room.members, room)
const guest = guestSide(sent)

await rt.addRoomParticipant('war', { kind: 'bot', remote: true, handle: CODER.handle }, ALICE)
guest.reads()
assert.equal(room.name, 'Alice, Chief A, Chief B, Coder', 'the guest is in the name it arrived under')

await rt.addRoomParticipant('war', { kind: 'human', id: 'owner-2', displayName: 'Bob' }, ALICE)
const joins = guest.reads()
assert.equal(room.name, 'Alice, Bob, Chief A, Chief B, Coder', 'the human seat is in the name of an unnamed group')
assert.equal(guest.copies.get(CODER.handle), room.name, 'the guest already seated ends on the name the host holds')
assert.equal(joins.get(CODER.handle), 1, 'one human seat costs a seated guest one join, not none and not two')
})

test('#2009: removing a human seat moves the name for the guest still seated', async (t) => {
const { rt, sent } = titledHost(t, TEAM)
await rt.load()
publish(rt)
await grantToFriend(rt, 'chief-a')
await grantToFriend(rt, 'chief-b')
seatBob(rt)
const room = rt.state.rooms.war
delete room.named
room.name = rt.autoRoomName(room.members, room)
const guest = guestSide(sent)

await rt.addRoomParticipant('war', { kind: 'bot', remote: true, handle: CODER.handle }, ALICE)
await rt.addRoomParticipant('war', { kind: 'human', id: 'owner-2', displayName: 'Bob' }, ALICE)
guest.reads()
assert.equal(room.name, 'Alice, Bob, Chief A, Chief B, Coder')

await rt.removeRoomParticipant('war', { kind: 'human', id: 'owner-2' }, ALICE)
const joins = guest.reads()
assert.equal(room.name, 'Alice, Chief A, Chief B, Coder', 'the human seat is gone from the name too')
assert.equal(guest.copies.get(CODER.handle), room.name, 'the guest still seated ends on the name the host holds after a human leave')
assert.equal(joins.get(CODER.handle), 1, 'one human leave costs a seated guest one join, not none and not two')
})

test('#2009: a group with a chosen name sends its seated guests nothing for a human seat', async (t) => {
const { rt, titles, sent } = titledHost(t, TEAM)
await rt.load()
publish(rt)
await grantToFriend(rt, 'chief-a')
await grantToFriend(rt, 'chief-b')
seatBob(rt)
const room = rt.state.rooms.war
const guest = guestSide(sent)

await rt.addRoomParticipant('war', { kind: 'bot', remote: true, handle: CODER.handle }, ALICE)
guest.reads()
titles.length = 0

await rt.addRoomParticipant('war', { kind: 'human', id: 'owner-2', displayName: 'Bob' }, ALICE)
let joins = guest.reads()
assert.equal(room.name, 'War room', 'a chosen name does not follow the seats')
assert.equal(joins.get(CODER.handle) ?? 0, 0, 'a human seat sends nothing when the name did not move')

await rt.removeRoomParticipant('war', { kind: 'human', id: 'owner-2' }, ALICE)
joins = guest.reads()
assert.equal(room.name, 'War room')
assert.equal(joins.get(CODER.handle) ?? 0, 0, 'a human leave sends nothing either')
assert.equal(titles.length, 0, 'and no Session is renamed for a chosen name')
})
2 changes: 1 addition & 1 deletion docs/subsystems/core-host.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions patches/ds-bot/files/src/host/room-membership.js
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,10 @@ export function install(rt) {
// the room get the same name once, and the arriving seat keeps its one targeted join.
if (moved && broadcast) reshareRoster(room, { except: record.handle })
} else if (record?.kind === 'bot' && !record.remote && broadcast) reshareRoster(room)
// #2009: a human seat is this home's own, so it never changes the roster a guest holds — but it does
// change the name an unnamed group is called after, and a human seat has no envelope of its own to
// carry that name. Only `moved` sends, so a chosen name still sends nothing.
else if (record?.kind === 'human' && moved && broadcast) reshareRoster(room)
return room
})
}
Expand Down Expand Up @@ -822,6 +826,10 @@ export function install(rt) {
await finishTicketLeave(room).catch(error => rt.warn?.('ds-bot: leaving %s was not settled: %s', room.name, error))
} else if (kind === 'bot' && !me.remote && broadcast) {
reshareRoster(room)
} else if (kind === 'human' && moved && broadcast) {
// #2009: the same as an add — a human leave moves the name of an unnamed group, and the guests
// still seated have no other way to hear it.
reshareRoster(room)
}
// #2004: the leave moved the name, so the guests still in the room get the copy the host holds. The
// seat that left is already out of `currentParticipants`; a local Bot's leave sent its roster above.
Expand Down
81 changes: 81 additions & 0 deletions patches/ds-bot/files/test/remote-room.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3772,3 +3772,84 @@ test('#2004: a claim that seats a Bot moves the name for the guest already in th
assert.equal(guest.copies.get('@chief.bob.home'), room.name, 'the claimed seat is told the name it is seated under')
assert.equal(joins.get('@chief.bob.home'), 1, 'the claimed seat is told once, not twice')
})

// #2009: a human seat is this home's own, so it never changes the roster a guest holds — but it does
// change the name an unnamed group is called after. That name only ever left on an envelope addressed
// to the seat that changed, and a human seat is not addressable: the guests already seated kept the
// copy they arrived with while the host moved on.
function seatBob(rt) {
rt.state.members['owner-2'] = { id: 'owner-2', createdAt: 2, displayName: 'Bob' }
}

test('#2009: adding a human seat moves the name for the guest already in the room', async (t) => {
const { rt, sent } = titledHost(t, TEAM)
await rt.load()
publish(rt)
await grantToFriend(rt, 'chief-a')
await grantToFriend(rt, 'chief-b')
seatBob(rt)
const room = rt.state.rooms.war
delete room.named
room.name = rt.autoRoomName(room.members, room)
const guest = guestSide(sent)

await rt.addRoomParticipant('war', { kind: 'bot', remote: true, handle: CODER.handle }, ALICE)
guest.reads()
assert.equal(room.name, 'Alice, Chief A, Chief B, Coder', 'the guest is in the name it arrived under')

await rt.addRoomParticipant('war', { kind: 'human', id: 'owner-2', displayName: 'Bob' }, ALICE)
const joins = guest.reads()
assert.equal(room.name, 'Alice, Bob, Chief A, Chief B, Coder', 'the human seat is in the name of an unnamed group')
assert.equal(guest.copies.get(CODER.handle), room.name, 'the guest already seated ends on the name the host holds')
assert.equal(joins.get(CODER.handle), 1, 'one human seat costs a seated guest one join, not none and not two')
})

test('#2009: removing a human seat moves the name for the guest still seated', async (t) => {
const { rt, sent } = titledHost(t, TEAM)
await rt.load()
publish(rt)
await grantToFriend(rt, 'chief-a')
await grantToFriend(rt, 'chief-b')
seatBob(rt)
const room = rt.state.rooms.war
delete room.named
room.name = rt.autoRoomName(room.members, room)
const guest = guestSide(sent)

await rt.addRoomParticipant('war', { kind: 'bot', remote: true, handle: CODER.handle }, ALICE)
await rt.addRoomParticipant('war', { kind: 'human', id: 'owner-2', displayName: 'Bob' }, ALICE)
guest.reads()
assert.equal(room.name, 'Alice, Bob, Chief A, Chief B, Coder')

await rt.removeRoomParticipant('war', { kind: 'human', id: 'owner-2' }, ALICE)
const joins = guest.reads()
assert.equal(room.name, 'Alice, Chief A, Chief B, Coder', 'the human seat is gone from the name too')
assert.equal(guest.copies.get(CODER.handle), room.name, 'the guest still seated ends on the name the host holds after a human leave')
assert.equal(joins.get(CODER.handle), 1, 'one human leave costs a seated guest one join, not none and not two')
})

test('#2009: a group with a chosen name sends its seated guests nothing for a human seat', async (t) => {
const { rt, titles, sent } = titledHost(t, TEAM)
await rt.load()
publish(rt)
await grantToFriend(rt, 'chief-a')
await grantToFriend(rt, 'chief-b')
seatBob(rt)
const room = rt.state.rooms.war
const guest = guestSide(sent)

await rt.addRoomParticipant('war', { kind: 'bot', remote: true, handle: CODER.handle }, ALICE)
guest.reads()
titles.length = 0

await rt.addRoomParticipant('war', { kind: 'human', id: 'owner-2', displayName: 'Bob' }, ALICE)
let joins = guest.reads()
assert.equal(room.name, 'War room', 'a chosen name does not follow the seats')
assert.equal(joins.get(CODER.handle) ?? 0, 0, 'a human seat sends nothing when the name did not move')

await rt.removeRoomParticipant('war', { kind: 'human', id: 'owner-2' }, ALICE)
joins = guest.reads()
assert.equal(room.name, 'War room')
assert.equal(joins.get(CODER.handle) ?? 0, 0, 'a human leave sends nothing either')
assert.equal(titles.length, 0, 'and no Session is renamed for a chosen name')
})
Loading