Skip to content

A read-only window's composer still writes: Send to a Bot, and a multi-pick Send creates a room - #2501

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-newchat-send-readonly
Nov 5, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-newchat-send-readonly

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

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 before send (one pick) and before create-room + send (several picks, no admin), so neither the Send button nor Enter writes; openBot keeps the session open — a read — and drops the set-flags unhide 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.js and core/src/host/** are untouched.

Red first

core/test/composer-send-readonly.test.js (new, 223 lines), committed alone at 9872270ff on the clean base 7f724a07:

not ok 1 - a read-only window sends nothing when one Bot is picked and Send is pressed
    no write action reaches the host
  + 0: 'send', 'scout', 'hello there'
not ok 2 - a read-only window creates no room when several Bots are picked and Enter is pressed
    no create-room and no send
  + 0: 'create-room', ['scout', 'third']; 1: 'send', 'r-new', 'hello there'
not ok 3 - a read-only window sends nothing when a picker row is pressed with a line typed
    the picker row writes nothing
  + 0: 'send', 'chief', 'hello there'
ok 4 - a writable window still sends once from the composer
# tests 4
# pass 1
# fail 3

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, tree 363abfa443)

  • npm run build (core) then node scripts/ds-bot-patches.mjs generate — a second generate left git status --porcelain empty.
  • 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 + policy 671, test:interop 37, test:live-e2e 26 — 4939 tests, 0 failed. Lint: 0 errors (warnings only, 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 port 8787 — this change is client-only (core/src/client/**), no host code.

Files

8 files, +2986/−2466 (git diff --numstat 7f724a07...240f0154), tree 363abfa443:

  • 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.patch does 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"
}

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
HugoXK marked this pull request as draft November 5, 2026 04:40
@HugoXK
HugoXK marked this pull request as ready for review November 5, 2026 04:53
@HugoXK

HugoXK commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

Queue request — please take this tip to the merge queue.

  • Head 240f015465478008da1cfed2fc4e0d6c29d7f3a5, base 7f724a07c97955f893963cd1fa329c9c4c97f3fd, Fixes #2499.
  • enqueue-guard on this tip: enqueue #2501: no "…" and the gate passes — decision enqueue A read-only window's composer still writes: Send to a Bot, and a multi-pick Send creates a room #2501, and it records no PASS needed: computed tier T1.
  • Tip state: non-draft, mergeable: true / clean, maintainer_can_modify: true, 18 check runs = 18 success + 0 cancelled, 0 failure.
  • tests-ok is green on this exact tip.
  • FlyBot Test's independent advisory is PASS at the same tip (240f0154): verify 4939 passed / 0 failed, red-first 4/1/3 on base 7f724a07, and each of the three write entries reddens alone when its own half is reverted. Advisory only, as usual for T1.
  • FlyBot Dev-2 is standing by and will not push a new tip; the branch is dev/2-newchat-send-readonly in HugoXK/FlyBot.

@Birfy
Birfy added this pull request to the merge queue Nov 5, 2026
Merged via the queue into ThinkFlowLab:main with commit 00b2fb0 Nov 5, 2026
19 checks passed
@Birfy
Birfy deleted the dev/2-newchat-send-readonly branch November 5, 2026 05:09
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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A read-only window's composer still writes: Send to a Bot, and a multi-pick Send creates a room

2 participants