Repository navigation
Face-room invite preview renders the room lead as "object Object" #846
Description
What is wrong
previewInvite treats Bot Social's lead field as a string, but the server sends an object. The invite preview card therefore reads Lead: object Object where it should name the person.
plugins/social/src/face-rooms.js:686-693—previewInvite(ticket)returnslead: body?.lead ? cleanFaceName(body.lead) : ''.plugins/social/src/face-rooms.js:62—cleanFaceNamestarts withString(value ?? ''), so an object becomes[object Object]and the bracket strip at:65turns it intoobject Object.- Bot Social
GET /v1/face-rooms/invites/{ticket}answersleadas{ handle, display_name, owner_name }.
Raw server answer, measured on social.botawaken.com on main cac79e10 (unchanged in previewInvite on that tip):
{
"room_id": "frm_01M5M3D2Q58JHTB9WHBBA2BYMN",
"name": null,
"status": "open",
"lead": { "handle": "@chief.selftesta2.home", "display_name": "Chief", "owner_name": "SelfTest A2" },
"member_count": 2,
"max_members": 8,
"expires_at": "2026-10-23T10:14:14.970Z"
}What social/face-invite-preview gives the client for the same ticket:
{"roomId":"frm_01M5M3D2Q58JHTB9WHBBA2BYMN","name":"","lead":"object Object","members":2,"maxMembers":8,"expiresAt":"2026-10-23T10:14:14.970Z"}Reproduce (real UI, two isolated homes)
- Home A: sidebar
+→ Face-to-face room, type a 6-digit number nobody uses → Start a room → Enter chat. - Open the room's details panel → Invite by link → Create link; the card shows
https://…/r/<ticket>. - Home B: sidebar
+→ Face-to-face room, paste that link into the invite field, press Join with link. - The preview card appears before joining and reads:
A room with no name
Lead: object Object
1 / 8 in the room
9:53 left to join
Expected (the label rule the rest of the plugin already uses, faceMemberLabel / faceOwnerName in face-rooms.js:236-264):
Lead: Chief (SelfTest A2)
Actual: Lead: object Object.
Scope
plugins/social/src/face-rooms.js:689— read the object (display_name+owner_name, i.e.faceMemberLabel(body.lead)) instead ofString()ing it. A server that sends a string (older build, or a test fixture) should keep working.plugins/social/test/social.test.js— the#798preview test passes a stringlead, which is why this never failed. A test with the server's real object shape would catch it.docs/subsystems/social.md:111already promises the card draws "the name, the lead, …", so no doc change is needed once the code matches it.
Related, non-blocking
The same card reads A room with no name because Bot Social sends name: null for a room that was never given a name (the room's title — Face-to-face · SelfTest A2, SelfTest B2 · 988036 — is generated by FlyBot, not stored on the server). The person still cannot tell which room the link opens; naming it from the lead/members would be an enhancement, not a bug.
Evidence
- Screenshot of the card:
reports/shots/g-b-dialog.pngin the self-test workspace (the DOM text above was read from that page). - Full E2E round: 19/19 checks passed (open room → mint link → preview → join the same
faceRoomId→ message both ways), so this is the only defect this round.
Activity
Birfy commentedon Oct 23, 2026
Triage
Verified on
c0c866da(read the code, not only the report):plugins/social/src/face-rooms.js:689—lead: body?.lead ? cleanFaceName(body.lead) : ''. Bot Social answersleadas{ handle, display_name, owner_name }, so an object goes into a function that starts withString(value ?? '')at:62; the bracket strip at:64then turns[object Object]intoobject Object.plugins/social/client.js:922runscleanRoomName(seen.lead), which strips brackets but keeps the words, so the card still readsLead: object Object.#839/#822::689is byte-identical on2c86927c(before that merge), and theString()coercion at:62is older still. What changed is only the visible text ([object Object]→object Object). This is the older defect, now easier to read.Duplicate check: searched
object Object,invite preview leadand openface-roomsissues — no duplicate.#164/#165were the same class of bug (an object labelled as Object) on a different surface and are closed;#773/#798built this preview card, which is why it only has a string-shaped fixture.Expected behaviour
For the ticket in the report,
social/face-invite-previewmust answerlead: "Chief (SelfTest A2)"— exactly whatfaceMemberLabel(:258-263) already produces for{ handle: '@chief.selftesta2.home', display_name: 'Chief', owner_name: 'SelfTest A2' }. The card then readsLead: Chief (SelfTest A2).Scope
plugins/social/src/face-rooms.js:689only. Branch on the shape: a string keepscleanFaceName(value)(older servers and the existing fixture), an object usesfaceMemberLabel(value).faceMemberLabelalready covers the missing-field cases: nodisplay_name→ the handle's name; noowner_name→ the name alone; an auto owner likeFlyBot 682b8a43→ the username.plugins/social/test/face-rooms.test.js— the#773preview test at:589sendslead: 'Coder · Bob'and asserts it comes back unchanged; that case must stay green. Add the server's real object shape beside it.docs/subsystems/social.md:111already promises the card names the lead, so the document is right and the code is wrong. The usual changelog fragment is enough.Acceptance
previewInvitewithlead: { handle, display_name: 'Chief', owner_name: 'SelfTest A2' }returns'Chief (SelfTest A2)'; withowner_namemissing it returns just the name; withdisplay_namemissing it returns the handle-derived name.lead: 'Coder · Bob'still comes back as'Coder · Bob'.cleanFaceName(body.lead)back in place of the branch turns the new object case red and leaves the string case green — otherwise the new test is not holding the fix.Lead: Chief (SelfTest A2)on the preview card before joining, following the same four steps the report gives.Out of scope, kept as reported
name: null→A room with no nameis a separate enhancement: the room title is generated locally and never stored server-side. The issue body already marks it non-blocking, and it must not be folded into this fix.Labels:
bug,area:social,area:ui,agent-ready— the visible surface is the social panel's invite card.Birfy commentedon Oct 23, 2026
Claiming this one for Dev3.
fix/846-invite-preview-lead-object, cut from mainc0c866da.previewInvitereadsleadby its shape — a member object ({ handle, display_name, owner_name }) goes through the same label rule the member list already uses (faceMemberLabel), a string keeps the oldcleanFaceNamepath, and an object that names nobody shows no lead line instead ofobject Object.Lead: Chief (SelfTest A2)and thatobject Objectnever reaches the card.name: nullhalf stays out of this PR, as scoped in the issue.Birfy commentedon Oct 23, 2026
Investigation (not a Verdict): is the client's invite-preview card a second entry for the same
leadobject?Chain, read on
7936fdf7:plugins/social/client.js:1382(previewLink) → host bridgeplugins/social/src/social.js:3550(social/face-invite-preview) →plugins/social/src/face-rooms.js:689(lead: body?.lead ? cleanFaceName(body.lead) : '') →plugins/social/client.js:919-930(faceInvitePreview→cleanRoomName(seen.lead)) →plugins/social/client.js:1502(Lead: ${preview.lead}).Findings
cleanRoomName(client.js:901) doesString(value)first, so an object becomes[object Object], then its own[[\]"<>@]strip leavesobject Object. Rendered with the real client test harness (dialogWithState, bridge mocked) an object lead printsLead: object Object`.previewInvite, which already stringifies the object before the client sees it. So there is no second user-visible entry and no separate issue — the visibleobject Objectstarts atface-rooms.js:689.cleanRoomNamestays object-blind. Optional hardening, onetypeofguard, and the client harness can already send an object, so it can be red-first.lead: 'Chief (SelfTest A2)'the card rendersLead: Chief (SelfTest A2)unchanged (parentheses survivecleanRoomName).face-rooms.js:688isname: body?.name ? String(body.name) : ''; an object name printsobject Objectas the card title. Out of scope here (thename: nullhalf is tracked separately) — noted, not fixed.Other
leadsurfaces are already object-safe (noobject Objectthere): member rows (viewFaceMember→ cleanedlabel), the joined-room lead (viewFaceLead→{handle}, rendered from seats), and the lead-change notice (platformLeadText→faceMemberLabel, covered bytest/face-rooms.test.js:530Room lead: Coder (Bob)).Shape check for the fix:
faceMemberLabel({ handle: '@chief.selftest.home', display_name: 'Chief', owner_name: 'SelfTest A2' })→Chief (SelfTest A2), which is the already-established format.faceMemberLabel('Coder · Bob')→Bot, so the fix must branch ontypeof body.lead === 'object'; replacing the string path withfaceMemberLabelunconditionally breaks the#773string case (test/face-rooms.test.js:589).Repro used: extracted
7936fdf7tree,plugins/social,node --import ./test/resolve.mjs --test test/probe846.test.js(throwaway probe on the copy, nothing pushed).