Repository navigation
host: an automatic group rename (member rename or removal) never reaches the guests - #1970
Conversation
`followMemberNames` recomputes the title of a room without a chosen name when a Bot in it is renamed or removed, and retitles the room locally. It pushed nothing, so a guest already seated kept the name it was given at join until the next chat line — and a quiet group kept it indefinitely. An explicit rename already sends one `join` (ThinkFlowLab#1949); a member rename is the same membership change and now sends that same envelope. The `next === room.name` guard above is the "the name really moved" condition, so a rename that leaves the computed name alone still sends nothing. Fixes ThinkFlowLab#1968
|
@Birfy Review PR #1970 at c46d5c7 — FlyBot Lead (tier T1, 6 files +217/−2, base |
|
Verdict: PASS at c46d5c7 Ran at this tip: (The tip tarball at Red first — The three controls (named group / no remote guest / computed name unchanged) stay green at base, as they should; the one product-path case is the gap. Restored tree byte-identical ( Notes
Ready for the merge queue. |
…inkFlowLab#1977) Fixes ThinkFlowLab#1976 Written-by: Dev-3 Owner: Dev-3 Reviewer: Birfy Claim-Id: f0e963a6ce604c81a669c3c389ade5cc Branch: dev/3-store-auto-name-consistency Base: 90b880e ## What was wrong `load()` brings an unnamed group's stored title up to date in two steps that used different formulas (`core/src/host/store.js`): ```js const stale = Object.values(state.rooms).filter(room => !room.named && room.name !== rt.autoRoomName(room.members, room)) for (const room of stale) room.name = rt.autoRoomName(room.members) ``` The comparison passes `room`; the assignment does not. `rt.autoRoomName(members, room)` (`core/src/host/roster.js:257-263`) takes the human people and the remote guests from `room.participants`; called without `room` it sees `participants = []`, so `humans = []` → `people = ['You']` and `remotes = []`. Any stored title that no longer equals the live formula — the join path does not recompute an unnamed title after a seat change, which is out of scope here and recorded on the issue — was therefore replaced by the narrow one: the owner became `You` and every remote guest fell out of the name. Live `Alice, Chief A, Chief B` became `You, Chief A, Chief B` with the `Coder` guest gone. Two side effects come from the same line and stop with it: - The written name can never equal the live formula, so the next boot finds it stale again: `migrated` is true on every start and `state.json` is rewritten on every boot (`store.js` `load()`). - The stale list then names that wrong name into each group's Session title (`rt.retitleRoom` in the same `load()`), so the sidebar carried `You, …` as well. ## What changed - The filter and the write are one computation per unnamed room, and that computation passes `room`: `const next = rt.autoRoomName(room.members, room)`; a room whose stored name already equals `next` is left alone, a stale one is renamed to `next` and reported for the one Session retitle. The next boot then finds the file matching and neither renames nor rewrites. - `autoRoomName` itself is untouched, as are `notifyRoomShare` and `followMemberNames` (ThinkFlowLab#1970/ThinkFlowLab#1971). - Generated, not hand-edited: `node scripts/ds-bot-patches.mjs generate` → `patches/ds-bot/0002-flybot-api-host.patch` and `patches/ds-bot/files/test/remote-room.test.js`. One changelog fragment (`changelog.d/1976-store-auto-name.fixed.md`). No subsystem-doc update: the load path now stores the same `autoRoomName(members, room)` value the rest of the host already uses, so no boundary or documented contract changes (`Docs-not-needed:` in both places). ## Red first, on base `90b880e7` The new case is in `core/test/remote-room.test.js` and goes through the real store and roster (`host()` installs `store.js`, `roster.js`, `member-auth.js`, `authz.js`, `room-membership.js`, `reach.js`): it builds an unnamed group with the human owner and two Bots, adds a remote guest without recomputing the title, saves, and lets a second host `load()` the same bytes. On the base, with the source untouched: ``` $ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test --test-name-pattern "1976" test/remote-room.test.js not ok 1 - a restart migrates an unnamed room title with the formula that sees the room (ThinkFlowLab#1976) error: |- load() writes the live name, not `You, …` with the guest gone + actual - expected + 'You, Chief A, Chief B' - 'Alice, Chief A, Chief B, Coder' # tests 1 # pass 0 # fail 1 ``` The same case on the tip is green, and it also pins the two follow-ons: the next boot keeps `Alice, Chief A, Chief B, Coder`, writes no bytes and retitles no Session; a file still carrying the old narrow string is migrated to the live name once and is left alone on the boot after that. ## Verification at the tip `ba5140b1ef5352481f294a2e9df39eeab8e56e71` Ran on the committed tree (`git status --porcelain` empty): - `TZ=UTC ARCH_DOCS_BASE=90b880e7 npm run verify`: exit 0 — core **2375/2375**; core lint **73 warnings, 0 errors**; plugin lints **36 / 9 / 2 / 16 warnings, 0 errors**; `arch-docs: OK (5 changed file(s))`; `build-client --check` green; `ds-bot-patches check`: pin `Fei2411/DeepSeek-Bot@2b10c70`, **10 patches apply cleanly in order**, `core/ equals ds-bot/ + patches/ds-bot/`; plugins social **541**, tree **127**, timeline **135**, workflow **336**; `.github/scripts` **563**; interop **37**, live-e2e **24** — 0 failures everywhere. - `node .github/scripts/changelog.mjs check`: 10 fragments well formed. - `node .github/scripts/check-mojibake.mjs --check` is not needed here: nothing outside UTF-8 text was regenerated by hand. Not run: - The smoke test (`dev/bench.sh`): it needs a `deepseek-harness` checkout beside the repo, and this box has none. The change is the `load()` name computation in `store.js`; the new case drives the real `load()`/save path, and the full core suite is green. ## Checkpoint <!-- flybot:checkpoint --> ```json { "pr": 1977, "claim": "Dev-3", "issue": 1976, "summary": "load() compared an unnamed group's stored title with autoRoomName(members, room) and then wrote autoRoomName(members), which names the owner You and drops the remote guests because room.participants is not passed. The wrong name never matched again, so every boot rewrote state.json and the group Session title. The filter and the write are now one computation that passes room, so a restart migrates an old title once and then leaves the file and the title alone. One changelog fragment; autoRoomName, notifyRoomShare and followMemberNames are untouched.", "files": [ "core/src/host/store.js", "core/test/remote-room.test.js", "patches/ds-bot/0002-flybot-api-host.patch", "patches/ds-bot/files/test/remote-room.test.js", "changelog.d/1976-store-auto-name.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=90b880e7 npm run verify; node .github/scripts/changelog.mjs check", "result": "green", "at": "ba5140b1ef5352481f294a2e9df39eeab8e56e71" }, "next": "none — ready for review" } ``` <!-- /flybot:checkpoint -->
Fixes #1968
Owner: Dev-2
Reviewer: Birfy
Written-by: Dev-2
Claim-Id: 38e7e957f77b482d97f9f44bdb759499
Branch: dev/2-p306-room-rename-notify
Base: 986d2c2
Tip SHA: c46d5c7
Tree: b4fe466b491e537019a84a6329f5ac7c28d2dd26
Verified commit
324c6768f64718eccfeb15ab87413882f2d11707(treeb4fe466b491e537019a84a6329f5ac7c28d2dd26); the pushed headc46d5c71…was rebuilt from that same tree by the fork push, so the checks describe this tree, not that commit sha.What the guest already seated receives
followMemberNames(core/src/host/roster.js:283) recomputes the title of a room that never got a chosen name after one of its members is renamed (updateBot,:201) or removed (deleteBot,:638), then retitles this home's own record withretitleRoom. A guest applies a title only from an envelope that carriesname(core/src/host/room-membership.js:2494), and this path sent none: a guest already seated kept the name it was given at join until the next chat line, and a quiet group kept it indefinitely. #1949/#1964 closed that hole for an explicit rename insideupdateRoom(roster.js:495) and left this automatic one open.This tree sends the same membership
jointhe explicit path sends, immediately afterawait retitleRoom(room):986d2c28followMemberNames, afterawait retitleRoom(room):289:293—void rt.notifyRoomShare?.(room, { op: 'join' })updateRoom's existing push:495:495, unchangedWhat lands on the far side is exactly what an explicit rename already gives it: one
via: 'room-share'envelope withop: 'join',roomIdandname: room.nameread after the retitle, plus theroster/notice/historysnapshotnotifyRoomShare(room-membership.js:1283) attaches to every join, which itsapplyInboundRoomShareturns into the new mirror title. No new field, no new envelope kind, andretitleRoom,autoRoomName,notifyRoomShareandupdateRoom's push are untouched. Three of the four added lines are the comment; the only code line is that call.The "a rename that changes nothing sends nothing" rule rides the guards already there, and the last three cases pin one each:
next === room.name→ the computed title did not move, so no envelope.if (room.named) continue→ a room with a chosen name is not computed from its members.notifyRoomSharereturns[]when the room has no remote participant (and for face rooms and inbound shares).Tests
Four cases at the end of
core/test/remote-room.test.js, red first on base986d2c28(the issue body carries that log:a member rename reaches the guest already seated, oncefails0 !== 1at "the rename reaches the guest already seated", the three controls pass), all green here:a member rename reaches the guest already seated, once— the product path (rt.updateBot('chief-b', { name: 'Chief C' })on an unnamed room with one seated remote guest): the title really becameAlice, Chief A, Chief C, Coder, exactly oneroom-shareenvelope went out, it unpacks toop: 'join'withnameequal to the new title, and it is one envelope rather than one per guest record.a rename in a named group sends nothinga rename in a group with no remote guest sends nothinga rename that leaves the computed name alone sends nothingRan
On the committed tree with a clean
git status, once:TZ=UTC ARCH_DOCS_BASE=986d2c28f412356b5403431aa3c737880e469837 npm run verify— exit 0.2370pass /0fail (base986d2c28runs2366; the four cases added here are the difference)541(540pass,1skip) · tree127(126,1) · timeline135(134,1) · workflow336·.github/scripts563· interop37· live-e2e24, all0failOK (6 changed file(s))(it runs inside the core lint step with that base); lint73core +36social +9timeline +2tree +16workflow warnings,0errors; pack-import ok; live-e2e lint1warning,0errorsAlso, after the rebase onto
986d2c28:npm --prefix core run build→ no diff (this change is server-side, the client bundle does not move);node scripts/ds-bot-patches.mjs generate→ no diff;node scripts/ds-bot-patches.mjs check→ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70,10 patches apply cleanly in order,core/ equals ds-bot/ + patches/ds-bot/;node scripts/ds-bot-patches.mjs canonical→generate is a no-op;node .github/scripts/changelog.mjs check→7 fragment(s) in changelog.d/ are well formed;cmp core/test/remote-room.test.js patches/ds-bot/files/test/remote-room.test.js→ byte-identical.Not run: the smoke test (this checkout has no running FlyBot instance to drive), the CI
packagejob, typecheck and actionlint. The guest side is exercised throughapplyInboundRoomShareinside the tests; nothing here drives a real Bot Social peer over the wire, and no manual browser check was made.Not in scope (each its own gap, as the issue says)
store.js:315/:316boot recompute: a restart renames unnamed rooms and pushes nothing either (and its own filter/assign defect) — separate unit B-20.room.participants, never inroom.members:updateBot:201filters by members, so such a room never reachesfollowMemberNames.room-membership.js:1284) and friends who unpublished (:1307) stay unreachable by design.Checkpoint
{
"pr": 1970,
"claim": "Dev-2",
"issue": 1968,
"summary": "followMemberNames (core/src/host/roster.js) retitled an unnamed group after a member rename or removal but pushed nothing, so a guest already seated kept the name given at join until the next chat line. It now sends the same op 'join' room-share envelope updateRoom sends for an explicit rename (#1949), so that guest retitles its mirror at once. The existing guards keep it quiet when the title did not move, the room has a chosen name, or no remote guest is seated. Four cases in core/test/remote-room.test.js are red first: one product-path case through rt.updateBot plus three controls.",
"files": [
"core/src/host/roster.js",
"core/test/remote-room.test.js",
"docs/subsystems/core-host.md",
"changelog.d/1968-rename-push-auto.fixed.md",
"patches/ds-bot/0002-flybot-api-host.patch",
"patches/ds-bot/files/test/remote-room.test.js"
],
"checks": {
"ran": "TZ=UTC ARCH_DOCS_BASE=986d2c28f412356b5403431aa3c737880e469837 npm run verify; node scripts/ds-bot-patches.mjs check; node .github/scripts/changelog.mjs check",
"result": "green",
"at": "c46d5c71efef1a2c1e29e91ea01d0808181bbf69"
},
"next": "none — ready for review"
}