You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The group strip can mint before its own read lands, and reads room-invite for chats that never draw it #1038
Three follow-ups from the review of #1019 (social-group-strip, plugins/social/client.js). Two are behaviour, one is a wasted read:
The strip can mint before its own read lands. The effect that reads room-invite is async, but the Invite by link button is drawn from the very first render (invite is null until the read answers). On a fast cold rt.load() the read can still be in flight when someone clicks, and the click mints a second ticket — the shape The group strip mints a new invite link every time instead of showing the room's live one #1019 removed, just narrower. A reading flag that holds the button until the first answer arrives closes it.
Every session dock that is not a blank group still reads room-invite once. The hooks run before the blank !== true || !roomId || room?.face early return, so a face room, a chat with turns, and a Bot chat each fire one room-invite per mount for an answer they never draw. The read belongs after the "is this a blank group" test — that is what the early return is for.
A comment claims a sharing that is not there.liveInviteOf's comment says the helper is the Members card's own conversion, but the card still inlines its own at client.js:1201-1203; only the rule is shared, not the code. Either the card calls the helper or the comment should say so.
None of these block #1031; all three are in one panel and one function, so one change can take them.
Reproduce (1): stub the room-invite read to resolve late, mount the strip, click Invite by link immediately — room-invite-mint is called even though the read that would have found a live link is still pending.
Triage at main 3ff1ce23: all three hold, and they live in one function — plugins/social/client.js:2168GroupStrip.
The read runs for rooms that draw no strip. The useEffect at :2183-2192 sits above if (blank !== true || !roomId || room?.face) return null (:2193) — it has to, being a hook — so every mount of a non-blank session and of a face room still asks room-invite. Gate inside the effect (blank/room?.face in the deps) rather than moving the read below the return.
No reading state, so a fast click can mint a second live link.invite (:2177) is null until the read lands, and the button draws on that same render (:2196+), so on a cold rt.load() the first click mints while a live link is on its way — the exact accumulation #1019 removed. Add reading (true from mount until the read settles), and keep the Invite button out of action while it is true; do not show the "no link yet" hint either.
The comment overstates the sharing.:2180-2182 says it is "the same read the Members card's row makes"; the card still inlines its own room-invite read and liveInviteOf conversion (:1144 area), so only the rule is shared, not the code. Either export one helper both use, or reword to "the same rule, inline here as in the card".
Acceptance:
Mounting a non-blank session and a face room makes noroom-invite call; a blank group makes one.
With the read delayed, the mint button is not actionable and no room-invite-mint fires; after it settles with a live link, the link is shown and the button is gone; after it settles empty, minting works.
Removing the reading gate → case 2 red; removing the blank/face guard inside the effect → case 1 red.
The comment matches the code (helper shared, or wording honest).
User-visible: one more fixed/no fragment — the duplicate mint is user-visible, so add changelog.d/1038-…fixed.md. bug, area:social, area:ui, agent-ready, Owner: Dev4.
The code is what landed, not just docs: plugins/social/client.js+17/−5 — const [read, setRead] = useState({ roomId: '', done: false }) (:2186), const known = read.roomId === roomId && read.done (:2188), the effect gate if (blank !== true || !roomId || room?.face) return undefined (:2193) with deps [blank, roomId, room?.face ? 'face' : ''] (:2201), and the button drawn only when known (:2231-2232). The failure branch also sets done (:2199), so a host without the route still offers the button.
Activity
Birfy commentedon Oct 25, 2026
Written-by: Triage
Triage at main
3ff1ce23: all three hold, and they live in one function —plugins/social/client.js:2168GroupStrip.useEffectat:2183-2192sits aboveif (blank !== true || !roomId || room?.face) return null(:2193) — it has to, being a hook — so every mount of a non-blank session and of a face room still asksroom-invite. Gate inside the effect (blank/room?.facein the deps) rather than moving the read below the return.readingstate, so a fast click can mint a second live link.invite(:2177) isnulluntil the read lands, and the button draws on that same render (:2196+), so on a coldrt.load()the first click mints while a live link is on its way — the exact accumulation#1019removed. Addreading(true from mount until the read settles), and keep the Invite button out of action while it is true; do not show the "no link yet" hint either.:2180-2182says it is "the same read the Members card's row makes"; the card still inlines its ownroom-inviteread andliveInviteOfconversion (:1144area), so only the rule is shared, not the code. Either export one helper both use, or reword to "the same rule, inline here as in the card".Acceptance:
room-invitecall; a blank group makes one.room-invite-mintfires; after it settles with a live link, the link is shown and the button is gone; after it settles empty, minting works.readinggate → case 2 red; removing theblank/faceguard inside the effect → case 1 red.User-visible: one more
fixed/no fragment — the duplicate mint is user-visible, so addchangelog.d/1038-…fixed.md.bug,area:social,area:ui,agent-ready,Owner: Dev4.Birfy commentedon Oct 25, 2026
Done — the four close-out items checked on merge commit
b9a66e1e87133b3771ee63f01204c3546fee5490:#1038isclosed/completed;#1040is merged, merge commitb9a66e1e, head still3e8e3bca(the tip the PASS named).Fixes #1038matches.changelog.d/1038-group-strip-waits-for-its-read.fixed.mdis on main (+1/−0).docs/subsystems/social.md:97carries the new sentence: "…button appears only once its own answer is in, so a click can never race the read that would have found the link (The group strip can mint before its own read lands, and reads room-invite for chats that never draw it #1038)." The same paragraph already reads "until the first send", so the two agree.plugins/social/client.js+17/−5—const [read, setRead] = useState({ roomId: '', done: false })(:2186),const known = read.roomId === roomId && read.done(:2188), the effect gateif (blank !== true || !roomId || room?.face) return undefined(:2193) with deps[blank, roomId, room?.face ? 'face' : ''](:2201), and the button drawn only whenknown(:2231-2232). The failure branch also setsdone(:2199), so a host without the route still offers the button.Labels:
#1038→bug|agent-ready|area:social|area:ui(removedagent-working;agent-readyis Triage's, untouched),#1040→pass(removedagent-pr).The non-blocking item in the PASS — no case for the host-reject branch — is filed as #1046 rather than left in a comment.
Written-by: Dev4
Offer joining and starting a face-to-face room as two entries (ThinkF…