Skip to content

social: a read-only window still offers Bot Social's write entries, and reopening a past room writes a local mirror #2219

Closed

Description

@HugoXK

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.js loads the real plugins/social/client.js with the repository's hook renderer and mounts FriendsBots with roster.readOnly = '<host sentence>', social/status returning 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.js factory), seed settings.pastFaceRooms with one row whose local room is absent, then call('social/face-open', { faceRoomId }) ⇒ ok: true and flybot.rooms.list() grows by one.

Expected behaviour

First issue (this one) — the shell-visible entries. With a read-only roster:

  1. FriendsBots:2899 draws no write control: no accept-notice-dismiss (:2971), no accept-notice-ask (:2974-2977), no share-request-dismiss (:2993), and no face-leave (ConfirmLeave:559, used by FaceRoomCard:2036); share-request-open (:2996-3000) may stay (navigation), and the past-room row (Open, :2014-2022) stays as the face-open read half. The section still reads — social/status (:2923) keeps polling, so the list is not blank.
  2. The three +-menu rows for Bot Social (:3919/:3927/:3935) are not registered in a read-only window.
  3. ChatApprovalDock (:2397) and HeldReplyCard (:2535) draw no decision control.
  4. 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).

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 — FriendsBots and the components it mounts, the three new-chat.action rows, ChatApprovalDock, HeldReplyCard; signal is roster.readOnly (the shell already passes roster to every slot: core/src/client/flybot-slots.js:6/20/26).
  • plugins/social/src/social.js — openFaceRoom (:4597): keep the hosted early return, skip upsertFace and return the stored view when flybot.readOnly?.() is true.
  • Tests: plugins/social/test/approval-dock.test.js-style client test (add a read-only case), plugins/social/test/social.test.js for the host half; changelog.d/<this issue>-social-readonly.fixed.md; plugins/social/README.md if 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):

  1. 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); labels Approve / Reject / Always auto-send count = 0 (today 3).
  2. social new-chat.action rows registered = 0 (today 3).
  3. Counter-proof, writable roster + same fixtures: both counts > 0, and clicking the first still sends the same call as today.
  4. Read-only still reads: social/status called 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 —

  1. social/face-open returns a viewable result (room name + members present) and flybot.rooms.list() length is unchanged (today +1); with a writable host the same call grows the list by one (today's behaviour pinned).

Acceptance

  1. All five assertions fail before and pass after.
  2. plugins/social suite green, npm run verify green, arch-docs: OK.
  3. 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

  1. added a commit that references this issue on Nov 2, 2026
    59c79a5
  2. HugoXK commented on Nov 2, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 5316e2cbd9434eac813aa5497da0cb2c
    Reason: merged via PR #2221 (19b5e9b)
    At: 2026-11-02T19:37:20Z

  3. added 2 commits that reference this issue on Nov 2, 2026
    19b5e9b
    d608f2c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions