Skip to content

Settings and the New… menu still offer group creation in a read-only window #2496

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: 4811bfdf3f1e43e9b087b5699dca427c
Branch: dev/2-group-create-readonly
Base: e809c43
At: 2026-11-05T03:11:30Z

What happens

In a window that holds the team read-only (roster.readOnly set by the host), both group-creation paths are still drawn and still try to write. The host refuses afterwards, so the user fills a form and only then gets bot/read-only.

  • Settings → Group chats draws Create group chat; pressing it mounts the full NewGroupForm (Name, admin, members, Create). Create really calls create-room.
  • Sidebar New… draws New group chat; choosing it opens the same NewChat in group mode, whose Create group chat calls create-room through a second code path.

The lock predicate already exists in this file family — lockedWindow(roster) (core/src/client/schedules.js:141, non-empty roster.readOnly after trim) — and create-bot.js, details-drawer.js, schedules.js and the Projects pages already use it. These two forms do not: zero references in either file on the base.

Reproduction (live main 80bc66cc)

Two write paths, one mechanism behind each:

  • core/src/client/settings.js:179 — GroupsPage draws Create group chat (setCreating(true) at :174) with no lock check; it mounts NewGroupForm at :177 (core/src/client/group-settings.js:320).
  • core/src/client/group-settings.js:327 create() guards only busy || admin === null || members.length === 0, then calls actions.createRoom at :331; the footer Create sits just below it.
  • core/src/client/index.js:600 — createRoom is after(call('create-room', …)).
  • Sidebar: core/src/client/sidebar.js:404/407 openNewChat('new') → core/src/client/new-chat.js:165 New group chat → group mode → createGroup at :244-247 → makeGroup() at :124, which calls actions.createRoom at :132. The palette entry (:88) only opens the same NewChat('group'). Line numbers are the ones on 924f6235; re-check at branch time.
  • Host: core/src/host/channel.js:312 answers every endpoint outside READS with fail(readOnly, 'bot/read-only'); create-room is not in READS (:237-239).

User path: second window → Settings → Group chats → Create group chat → fill → Create → toast bot/read-only, form stays. Same from New… → New group chat.

Red first (run before the fix, on a clean 80bc66cc)

New file core/test/group-create-readonly.test.js (199 lines), cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/group-create-readonly.test.js:

not ok 1 - P-570: Settings → Group chats creates no group in a read-only window
not ok 2 - P-570: the New… menu creates no group in a read-only window
ok     3 - P-570: a writable window still creates a group
# tests 3  # pass 1  # fail 2

Both failures carry the same sentence — no write action reaches the host: expected 0, actual 1 with create-room as the action — on 924f6235 as well as on the older base (80bc66cc), where the same file is 3 tests / 1 pass / 2 fail. Reverting only the Settings half reddens case 1; only the new-chat half reddens case 2; both halves in place gives 3/3.

One detail for this base: group-settings.js:8 already imports lockedWindow (#2479), so the fix reuses that import instead of adding its own — a second import would break the build.

Expected

The door goes in the two forms that can write, not on the entries and not in the action layer. The entries stay as preview entries — the same shape #2307 chose for Create Bot, where the form itself is already read-only.

  • Locked, NewGroupForm (Settings path): the footer Create is replaced by the host's own sentence — hostText(roster.readOnly) inside a .bt-hint.bt-read-only with role="status" — and create() returns before createRoom. If the lock arrives while the form is open, the footer swaps to the same state.
  • Locked, NewChat group mode (sidebar path): the same footer swap, and makeGroup() returns before touching actions.createRoom.
  • Writable window: both forms render exactly as today, byte for byte.
  • The Settings dialog header keeps its existing sentence (settings.js:236-238, #2199); the footer line is the form's own state, the same shape already shipped for the inline Create Bot.

Scope

Hand-written: core/src/client/group-settings.js (+11/−3), core/src/client/new-chat.js (+18/−6), the new test file, one line in docs/subsystems/core-client.md (or a Docs-not-needed: trailer), one changelog fragment.
Generated, never hand-edited: core/client.js, patches/ds-bot/** (bundle + test mirror) via npm --prefix core run build + node scripts/ds-bot-patches.mjs generate; a second generate run must leave git status --porcelain empty.
Not touched: core/src/host/**, core/src/client/index.js (no action-layer gate — it would leave dead controls and component tests cannot reach it), existing tests, i18n keys (the sentence is the host's own string).
Out of scope, separate issue (same file, lands after this one): the plain Send in a locked NewChat still writes — new-chat.js:142 sendMessage() → actions.send (:147), and with no admin chosen its createRoom at :157 runs first, so a locked multi-select Send writes twice. That path is reachable (group mode, pick admin + two members, drop the admin, press the arrow) and is covered by the follow-up, not by this one.

Dedup

Open at filing: "Create group chat" → 1, #2474 (the seven Settings pages; it gates the Group chats detail page, not this list's Create entry, and closes with its own PR); create-room → 1, #51 (cross-owner agent routes); NewGroupForm → 0. Closed and different: #998 (blank draft after creating a group), #2228 (read-only room overview still draws Leave), #2279/#2387 (drawer mount points), #2307 (Create Bot form).

Tier: T1 (core/src/client/**, core/test/**, docs/**, patches/ds-bot/**, changelog.d/**). Single client lane: core/client.js + patches/ds-bot/0008 — one PR at a time on this lane.

Base

Branch from live main as it stands when the branch is cut. #2491 (P-565) has merged as e809c43a and it touched core/src/client/settings.js, core/src/client/i18n.js and the generated core/client.js + patches/ds-bot/{0001,0008,0014}; the line numbers quoted above are from 924f6235/80bc66cc, so re-read the anchors at branch time. group-settings.js:8 already imports lockedWindow (#2479) — reuse that import, do not add a second one.

Out of scope here, and carried by the follow-up issue: the ordinary Send in NewChat while the window is read-only, and the multi-select Send that goes through create-room when no admin is picked.

Activity

  1. HugoXK commented on Nov 5, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 4811bfdf3f1e43e9b087b5699dca427c
    Reason: merged 7f724a0 (#2498)
    At: 2026-11-05T04:25:27Z

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