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: 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 fullNewGroupForm(Name, admin, members,Create).Createreally callscreate-room. - Sidebar
New…drawsNew group chat; choosing it opens the sameNewChatin group mode, whoseCreate group chatcallscreate-roomthrough 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—GroupsPagedrawsCreate group chat(setCreating(true)at:174) with no lock check; it mountsNewGroupFormat:177(core/src/client/group-settings.js:320).core/src/client/group-settings.js:327create()guards onlybusy || admin === null || members.length === 0, then callsactions.createRoomat:331; the footerCreatesits just below it.core/src/client/index.js:600—createRoomisafter(call('create-room', …)).- Sidebar:
core/src/client/sidebar.js:404/407openNewChat('new')→core/src/client/new-chat.js:165New group chat→ group mode →createGroupat:244-247→makeGroup()at:124, which callsactions.createRoomat:132. The palette entry (:88) only opens the sameNewChat('group'). Line numbers are the ones on924f6235; re-check at branch time. - Host:
core/src/host/channel.js:312answers every endpoint outsideREADSwithfail(readOnly, 'bot/read-only');create-roomis not inREADS(: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 footerCreateis replaced by the host's own sentence —hostText(roster.readOnly)inside a.bt-hint.bt-read-onlywithrole="status"— andcreate()returns beforecreateRoom. If the lock arrives while the form is open, the footer swaps to the same state. - Locked,
NewChatgroup mode (sidebar path): the same footer swap, andmakeGroup()returns before touchingactions.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 inlineCreate 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
HugoXK commentedon Nov 5, 2026
Release
Written-by: FlyBot Lead
Released: 4811bfdf3f1e43e9b087b5699dca427c
Reason: merged 7f724a0 (#2498)
At: 2026-11-05T04:25:27Z
client: a read-only window's new-group composer still draws a Send ar…