Repository navigation
A read-only window's composer still writes: Send to a Bot, and a multi-pick Send creates a room - #2501
Merged
Conversation
Pressing Send in a locked window reached the host with `send` (one pick) or `create-room` + `send` (several), and pressing a picker row with a line typed sent it — each answered `bot/read-only` only after the write. The composer now draws the host's own sentence where the Send button was, and both paths return before the write; opening a session stays a read.
HugoXK
marked this pull request as draft
November 5, 2026 04:40
HugoXK
marked this pull request as ready for review
November 5, 2026 04:53
Contributor
Author
|
Queue request — please take this tip to the merge queue.
|
HugoXK
added a commit
to HugoXK/FlyBot
that referenced
this pull request
Nov 5, 2026
…row that silently does nothing once an admin is chosen (ThinkFlowLab#2504) Fixes ThinkFlowLab#2503 Fixes ThinkFlowLab#2506 Owner: Dev-2 Reviewer: Birfy Written-by: FlyBot Dev-2 Claim-Id: 7a28ae9f7eb744ac9719db7351b3e7e2 Risk tier: T1 ## What changes Two read-only shapes still drew a control whose press could only earn the host's `bot/read-only`. They sit on the same screen and the same bundle lane, so one PR closes both; each has its own case and its own acceptance below. `ThinkFlowLab#2503` — a new group chat that already picked its admin. `ThinkFlowLab#2499`/`ThinkFlowLab#2501` stopped the writes, but the composer ternary excluded exactly the `group && admin !== null` shape from the host's sentence (that sentence is drawn on the To: line, where `Create group chat` would be), so the composer fell through to a live Send button in a window where `sendMessage` returns at `new-chat.js:146` before any branch. In a locked window the composer now draws no Send in any shape: where the button was it shows the host's own sentence (`hostText(roster.readOnly)`, `.bt-hint.bt-read-only`, `role="status"`), and in the group-with-admin shape that sentence already stands on the To: line, so the composer draws nothing and the view carries it exactly once. `ThinkFlowLab#2506` — the New… menu's **Add coding agent** row. `addAgent` is `add-agent`, a write the host refuses (`core/src/host/channel.js:310-312`, `READS` does not carry it), and the row's `if ((roster.agents ?? []).length === 0)` never read `lockedWindow` even though the same function computes `locked` at `:42`. A locked menu now does not draw the row at all — it is not replaced by a sentence, the menu is a preview — while the other rows (New chat, New Bot, New group chat, a plugin row) stay drawn, and a writable window keeps the row. Scope is those two guards, the two regression cases in one test file, the `ThinkFlowLab#2501` docs sentence made true per shape plus the menu's own item, and two changelog fragments. The send path, `makeGroup`'s own `locked` guard, the To:-row sentence, the Create paths `ThinkFlowLab#2496` already locked, `service.js` and `core/src/host/**` are untouched. ## Acceptance — ThinkFlowLab#2503 In a read-only window, New… → New group chat → choose the admin → pick one more Bot → type a line: - the composer draws no Send control at all — the case asserts `sendButton(nodes) === undefined`, not merely that a drawn button is disabled; - Enter in the composer reaches the host with nothing: `calls` is empty, so no `send` and no `create-room`; - the host's own sentence appears exactly once, in one `.bt-hint.bt-read-only` whose text is `hostText(roster.readOnly)`, on the To: line where Create was; - the writable control next door is unchanged: Send is drawn and enabled, one press is `[['send', 'scout', 'hello there'], ['open-session', 'scout']]`, several picks are `create-room` then `send` then `open-session`, and no read-only line is drawn. ## Acceptance — ThinkFlowLab#2506 In a read-only window, the New… menu (`mode: 'new'`, no To: row, no composer): - the menu itself is drawn (`New group chat` is there) and the `Add coding agent` row is not: the case asserts the row is absent, not that a drawn row does nothing; - with the row absent there is no press to make, and drawing or pressing nothing writes nothing: `calls` stays empty; no `add-agent` reaches the host, so no `bot/read-only` comes back; - the writable control next door is unchanged: the row is drawn and exactly one press sends exactly one `['add-agent']`. ## Red first Both cases fail on the clean base `550fb3ac` with the base source in place and only the test file from this branch: ``` not ok 4 - a read-only window draws no Send for a group that still has its admin chosen the composer draws no live Send control - undefined + { type: 'button', className: 'bt-nc-send', disabled: false, onClick: [Function: sendMessage] } not ok 6 - a read-only New… menu draws no Add coding agent row no Add coding agent row in a read-only window true !== false # tests 7 # pass 5 # fail 2 ``` They are independent: with both fixes in place, putting back only the composer ternary (`locked && !(group && admin !== null)`) leaves `# pass 6 / # fail 1` with case 4 the only red, and dropping only `!locked` from the Add coding agent guard leaves `# pass 6 / # fail 1` with case 6 the only red. With both in place the file is 7/7 green. ## What I ran at `4a3798c0ef4f2d2a8f18b23b4e69eb7892a97b92` (`TZ=UTC`, clean tree, tree `bf57d007`) - `npm run build` (core), then `node scripts/ds-bot-patches.mjs generate` twice and a third time: the working diff is byte-identical between rounds (`git diff | sha256sum` equal), so `generate` is idempotent here. - `node scripts/ds-bot-patches.mjs check` — `ok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70`, `ok 10 patches apply cleanly in order`, `ok core/ equals ds-bot/ + patches/ds-bot/`. - `TZ=UTC ARCH_DOCS_BASE=550fb3ac7d8eb74b749c1accf7586b0eb6d8a321 npm run verify` — exit 0. Segment counts, each `# fail 0`: core **2915 passed**, plugins/social **577**, plugins/tree **137**, plugins/timeline **141**, plugins/workflow **440**, `.github/scripts` + `policy` **671**, `test:interop` **37**, `test:live-e2e` **26** — 4944 tests, 0 failed. Lint: 0 errors (78/36/9/2/16 warnings, none in the files this PR touches); `build-client.mjs --check` and `ds-bot-patches check`/`canonical` green in the same run. ## What I did not run - The host smoke test and a live service on a bench port — this change is client-only (`core/src/client/**`), no host code. ## Files 9 files, +123/−21 (`git diff --numstat 550fb3a...4a3798c`), tree `bf57d007`: - `core/src/client/new-chat.js` (+7/−4) — the composer ternary and the menu row's guard, hand-written. - `core/test/composer-send-readonly.test.js` (+48/−2) — both regression cases plus the two writable controls, hand-written. - `docs/subsystems/core-client.md` (+1/−1) — the `ThinkFlowLab#2499` bullet now says the sentence stands on the To: line for this shape and the composer draws nothing, and the New… menu's item joins it. - `changelog.d/2503-composer-group-admin-locked.fixed.md` (+1), `changelog.d/2506-newchat-add-agent-locked.fixed.md` (+1) — new fragments. - `core/client.js` (+2/−2), `patches/ds-bot/0003-flybot-api-client.patch` (+11/−7), `patches/ds-bot/0008-client-bundle.patch` (+4/−3), `patches/ds-bot/files/test/composer-send-readonly.test.js` (+48/−2) — generated by build + `generate`, never hand-edited. <!-- flybot:checkpoint --> ```json { "pr": 2504, "claim": "Dev-2", "issue": 2503, "summary": "Two read-only shapes that drew a control the host refuses are gone: a group composer whose admin is already chosen no longer draws a Send arrow that does nothing (the host's sentence stays once, on the To: line), and the New… menu no longer draws Add coding agent, whose add-agent the host answers with bot/read-only. Each has its own failing case and its own writable control, and this PR closes ThinkFlowLab#2503 and ThinkFlowLab#2506.", "files": [ "core/src/client/new-chat.js", "core/test/composer-send-readonly.test.js", "docs/subsystems/core-client.md", "changelog.d/2506-newchat-add-agent-locked.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=550fb3ac npm run verify (plus npm run build and ds-bot-patches generate/check)", "result": "green", "at": "4a3798c0ef4f2d2a8f18b23b4e69eb7892a97b92" }, "next": "none — ready for review" } ``` <!-- /flybot:checkpoint -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2499
Owner: Dev-2
Reviewer: Birfy
Written-by: FlyBot Dev-2
Claim-Id: 6d33e6a7139c42a98d694733c9a2bbc4
Risk tier: T1
What changes
In a window that holds the team read-only, the new-chat composer no longer reaches the host with a write.
sendMessage(new-chat.js) returns beforesend(one pick) and beforecreate-room+send(several picks, no admin), so neither the Send button nor Enter writes;openBotkeeps the session open — a read — and drops theset-flagsunhide and the typed line, so pressing a picker row writes nothing. The composer puts the host's own sentence (hostText(roster.readOnly),.bt-hint.bt-read-only,role="status") where the Send button was, drawn once: when the group form already shows that line for its own Create, the composer does not repeat it. The pickers stay a preview, and a writable window sends exactly as before.openBot's send path is included because the issue's acceptance asks for it ("openBot's send path is covered by the same lock; opening a session may stay possible (it is a read)"). The group-creation paths from #2496,service.jsandcore/src/host/**are untouched.Red first
core/test/composer-send-readonly.test.js(new, 223 lines), committed alone at9872270ffon the clean base7f724a07:Case 4 is the control: the writable window sends once, then opens the session; several picks make the room and then send the line. After the fix the same file is 4/4.
What I ran at
240f0154(TZ=UTC, clean tree, tree363abfa443)npm run build(core) thennode scripts/ds-bot-patches.mjs generate— a secondgenerateleftgit status --porcelainempty.node scripts/ds-bot-patches.mjs check—ok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70,ok 10 patches apply cleanly in order,ok core/ equals ds-bot/ + patches/ds-bot/.TZ=UTC ARCH_DOCS_BASE=7f724a07c97955f893963cd1fa329c9c4c97f3fd npm run verify— exit 0. Segment counts, each# fail 0: core 2910 passed, plugins/social 577, plugins/tree 137, plugins/timeline 141, plugins/workflow 440,.github/scripts+policy671,test:interop37,test:live-e2e26 — 4939 tests, 0 failed. Lint: 0 errors (warnings only, none in the files this PR touches);build-client.mjs --checkandds-bot-patches check/canonicalgreen in the same run.What I did not run
core/src/client/**), no host code.Files
8 files, +2986/−2466 (
git diff --numstat 7f724a07...240f0154), tree363abfa443:core/src/client/new-chat.js(+11/−5) — the send path and the composer's read-only line, hand-written.core/test/composer-send-readonly.test.js(+223) — new, hand-written.docs/subsystems/core-client.md(+1) — the bullet after Settings and the New… menu still offer group creation in a read-only window #2496's.changelog.d/2499-composer-send-readonly.fixed.md(+1) — new fragment.core/client.js(+6/−3),patches/ds-bot/0003-flybot-api-client.patch(+33/−3),patches/ds-bot/0008-client-bundle.patch(+2488/−2455),patches/ds-bot/files/test/composer-send-readonly.test.js(+223) — generated by build +generate, never hand-edited.patches/ds-bot/0012-room-membership.patchdoes not change: this file's mirror lands in 0008.{ "pr": 2501, "claim": "Dev-2", "issue": 2499, "summary": "In a read-only window the new-chat composer no longer writes: sendMessage returns before send and create-room, openBot keeps the session open but drops the unhide and the typed line, and the composer draws the host's own sentence where the Send button was, once. The pickers stay a preview and a writable window sends exactly as before.", "files": [ "core/src/client/new-chat.js", "core/test/composer-send-readonly.test.js", "docs/subsystems/core-client.md", "changelog.d/2499-composer-send-readonly.fixed.md" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=7f724a07 npm run verify (plus npm run build and ds-bot-patches generate/check)", "result": "green", "at": "240f015465478008da1cfed2fc4e0d6c29d7f3a5" }, "next": "none — ready for review" }