Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 5316e2cbd9434eac813aa5497da0cb2c
Branch: dev/4-social-readonly-shell
Base: 75e3c18
At: 2026-11-02T18:33:06Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
Owner: FlyBot Dev-4
Branch: dev/4-social-readonly-shell
Credit: audit and draft by FlyBot Product.
Kind: bug (read-only surface)
Source: FlyBot Product audit P-437 on live main 75e3c18d; Lead decisions 2026-11-02 (first issue = shell-visible entries; the page-level surfaces are the second issue, sequenced not a gap; social/face-open split into a read half and a write half).
Dedupe (searched before opening)
type:issue searches: social read-only, Bot Social locked, face-open, readOnly + social: open hits are #1175 (hold policy), #2215/#2202 (workflow plugin). Closed: #14 (2026-07 read-only host, social timer), #197 (social UI nits), and the closed PR #211 ("read-only face rooms hide the composer") — that one predates the current lock and left no gate in plugins/social/client.js (today: one readOnly occurrence, an <input readOnly> at :3024). No open or closed issue covers the Bot Social client's read-only gate or the face-open mirror write.
What happens today
A. The Bot Social client has no read-only gate at all. The only readOnly in plugins/social/client.js is an <input readOnly> on the invite link (:3024); no component reads roster.readOnly / flybot.readOnly(). Measured in the evidence tree (75e3c18d, probe-social-readonly.test.js): a read-only roster still draws the sidebar section's write buttons —
SOCIAL_RO_BUTTONS ["accept-notice-dismiss","accept-notice-ask","share-request-dismiss","share-request-open"]
click Dismiss -> ["social/status","social/share-request-dismiss"]
SOCIAL_WRITABLE_BUTTONS (identical)
share-request-open is navigation (opens the Bot's Sharing); the other three are writes, and the host refuses them with bot/read-only.
B. The shell-visible entry points that open more write surfaces. plugins/social/client.js:3919 (Join a face-to-face room), :3927 (Start a face-to-face room), :3935 (Add friend) are rows in the sidebar + menu; each opens a panel full of writes (AddFriend:3034 — social/create-invite:3097, social/join-invite:3109, social/respond:3145; FaceToFaceGroup:1404 — social/face-join:1505, social/face-start:1548, social/face-leave:1563/:1570, social/face-join-invite:1603). Hiding buttons inside the panels but leaving the rows changes nothing.
C. Sessions still draw the social write controls. ChatApprovalDock:2397 (social/decide-approval:2472, social/face-auto:2466) and HeldReplyCard:2535 (Approve/Reject/Always auto-send, :2553-2555; the decide call is the core decide write). FaceRoomStatus:2326's Close is navigation and stays.
D. social/face-open is registered as a read but writes a local mirror. plugins/social/src/social.js:4636 declares it { read: true }, so the read-only gate lets it through. openFaceRoom (:4597) returns early for a room the host still has (:4599 — a genuine read, no write), but for a past room whose local mirror is gone it calls flybot.rooms.upsertFace (:4606-4618) and returns the new room id. So in a read-only window, reopening an old room succeeds at a write, while the same window refuses every other write.
Reproduction (measured on 75e3c18d)
- Client:
plugins/social/test/probe-social-readonly.test.jsloads the realplugins/social/client.jswith the repository's hook renderer and mountsFriendsBotswithroster.readOnly = '<host sentence>',social/statusreturning one share request and one accept notice — output above. The writable run is identical. - Host: on a read-only host (
core/test/home-lock.test.jsfactory), seedsettings.pastFaceRoomswith one row whose local room is absent, thencall('social/face-open', { faceRoomId })⇒ok: trueandflybot.rooms.list()grows by one.
Expected behaviour
First issue (this one) — the shell-visible entries. With a read-only roster:
FriendsBots:2899draws no write control: noaccept-notice-dismiss(:2971), noaccept-notice-ask(:2974-2977), noshare-request-dismiss(:2993), and noface-leave(ConfirmLeave:559, used byFaceRoomCard:2036);share-request-open(:2996-3000) may stay (navigation), and the past-room row (Open,:2014-2022) stays as theface-openread half. The section still reads —social/status(:2923) keeps polling, so the list is not blank.- The three
+-menu rows for Bot Social (:3919/:3927/:3935) are not registered in a read-only window. ChatApprovalDock(:2397) andHeldReplyCard(:2535) draw no decision control.social/face-open(social.js:4636) keeps its read half and loses the write half:- a room the host still has ⇒ unchanged early return (
:4599), no write (works today; pin it); - a past room whose local mirror is gone ⇒ no
rooms.upsertFace; the host returns the stored past view (name, status, members,readOnly: true) so the reader can still see the room, and the client shows that view instead of opening a session; - a writable window keeps today's behaviour exactly (mirror created, session opened).
- a room the host still has ⇒ unchanged early return (
Second issue (not here). Settings → Bot Social (SocialServerSettings:3681: social/server-set:3745, social/server-check:3740), Bot details (BotSocialDetails:3445: social/create-invite:3474, social/share-with-friend:3543), room overview (FaceRoomOverview:2765: social/face-start:2837, social/face-leave:2831/:2867), room members (LocalInviteRow:1166: social/face-invite:1104, social/face-invite-revoke:1115), FriendChatSettings:3366 (social/auto-accept:3386, social/hide-remote:3414). These need a page opened first; they are sequenced, not a gap — the issue that lands this one must say so in its body.
Scope
plugins/social/client.js—FriendsBotsand the components it mounts, the threenew-chat.actionrows,ChatApprovalDock,HeldReplyCard; signal isroster.readOnly(the shell already passesrosterto every slot:core/src/client/flybot-slots.js:6/20/26).plugins/social/src/social.js—openFaceRoom(:4597): keep thehostedearly return, skipupsertFaceand return the stored view whenflybot.readOnly?.()is true.- Tests:
plugins/social/test/approval-dock.test.js-style client test (add a read-only case),plugins/social/test/social.test.jsfor the host half;changelog.d/<this issue>-social-readonly.fixed.md;plugins/social/README.mdif it documents the window.
Not in scope: the page-level surfaces above (second issue), social/server's read flag, a new primitive, the host's refusal behaviour.
Red first (counts; whole tree, no findAll(dialog, …))
New client test, mounted with roster.readOnly + fixtures (one share request, one accept notice, one pending hold, one live room):
data-flybot ∈ {accept-notice-dismiss, accept-notice-ask, share-request-dismiss, face-leave}button count = 0 (today 3 without a live room, 4 with one); labelsApprove/Reject/Always auto-sendcount = 0 (today 3).- social
new-chat.actionrows registered = 0 (today 3). - Counter-proof, writable roster + same fixtures: both counts > 0, and clicking the first still sends the same call as today.
- Read-only still reads:
social/statuscalled once on mount and the section renders its rows.
Host test (read-only fixture from social.test.js): with one past room whose local mirror is absent —
social/face-openreturns a viewable result (room name + members present) andflybot.rooms.list()length is unchanged (today +1); with a writable host the same call grows the list by one (today's behaviour pinned).
Acceptance
- All five assertions fail before and pass after.
plugins/socialsuite green,npm run verifygreen,arch-docs: OK.- A read-only window shows the Bot Social section and its reads, offers no Bot Social write entry, and reopening a past room never creates a room; a writable window behaves exactly as today.
Activity
Bot Social: a read-only window offers no write entry and reopening a …
HugoXK commentedon Nov 2, 2026
Release
Written-by: FlyBot Lead
Released: 5316e2cbd9434eac813aa5497da0cb2c
Reason: merged via PR #2221 (19b5e9b)
At: 2026-11-02T19:37:20Z
social: offer no write entry in a read-only window, and reopen a past…
social: a read-only window draws no Sharing or members invite write (T…