Skip to content

client: a read-only window's plain-group Settings page drops Who answers and Working folder - #2538

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-plain-group-readonly-facts
Nov 5, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-plain-group-readonly-facts

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2528

Owner: Dev-2
Reviewer: Birfy
Written-by: Dev-2
Claim-Id: 7714932e4c32497097973e87fb7c3e53
Base: 90a5944
Risk tier: T1

What changes

A window that holds the team read-only draws a group's Settings page as text. GroupSettingsForm (core/src/client/group-settings.js) opened both fact cards with the same guard:

face ? h(Card, { title: t('Who answers') }, …) : null,
face ? h(Card, { title: t('Working folder') }, …) : null,

So a plain group's locked page drew neither, while its writable page draws both — the mode picker (Segmented) and the folder menu (FolderMenu). Both facts are reads: the mode comes through shownMode(room, roster) and the folder is room.workDir, and neither card asks the host for anything, in the locked branch or the writable one. A face-to-face room's page already kept them (#2492); a plain group's page dropped them.

The two guards are removed, so a locked page draws Who answers with the mode's own label (Admin leads / Everyone) and Working folder with the stored path, in the same Card + Row text the face-to-face branch uses. No control is added, no write is sent, and the writable page is unchanged: it still draws the picker and the folder menu in the same place. Nothing else in the group page moves — the members card, the admin row, the notice, and the standing read-only sentence in the Settings header are as they were. FaceLead's own lock (#2512) is untouched: the change is the two face ? guards only.

Acceptance

In a read-only window, on a plain group's Settings page:

  • the page says Who answers and names the mode the writable page would show;
  • the page says Working folder and names the group's stored folder;
  • both are text — no select, input, textarea, no picker, no menu;
  • pressing every button the locked page draws sends no write (calls stays empty);
  • the writable page next door still draws the mode picker and the folder menu, so the zero above is a zero a reader can trust;
  • a face-to-face room's page, every other locked Settings page, and every writable page are unchanged.

Red first

On a clean 90a594471b5886737b433e116f6397aa8ca2b6f2 tree whose source is untouched and which holds only this branch's extended test file:

$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/settings-readonly-pages.test.js
not ok 8 - a read-only plain group states who answers and its working folder
    the page says who answers
    expected: true
    actual: false
# tests 8
# pass 7
# fail 1
# cancelled 0

exit 1. After the fix the same file is 8/8, 0 failed, 0 cancelled, exit 0.

What I ran at the tip 0ce742571300e2a684be09574b5434fa655d9c17 (local commit 2d88f4e9d0aa4bc7fb19e77d3ff00bd14eb56886, same tree 0e3ff367dd0a4c4f4a0207461847e390ca9c6552)

  • cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/settings-readonly-pages.test.js — 8/8 green at the tip, 8/7/1 on the base above.
  • TZ=UTC ARCH_DOCS_BASE=90a594471b5886737b433e116f6397aa8ca2b6f2 npm run verify on the committed tree — exit 0 — core 2928 success + 0 cancelled, plugins/social 577 + 0, plugins/tree 137 + 0, plugins/timeline 141 + 0, plugins/workflow 440 + 0, .github/scripts + policy 671 + 0, test:interop 37 + 0, test:live-e2e 26 + 0 — 4957 success + 0 cancelled, 0 failed; arch-docs: OK (8 changed file(s)); every lint stage exit 0 with 0 errors.
  • node core/scripts/build-client.mjs --check — exit 0.
  • node scripts/ds-bot-patches.mjs check — three ok lines: ok ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5), ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/.
  • node scripts/ds-bot-patches.mjs canonical — ok patches/ds-bot/ is what generate writes (generate is a no-op); two further generate runs on the commit leave git status --porcelain empty.
  • node .github/scripts/changelog.mjs check — changelog: 36 fragment(s) in changelog.d/ are well formed, exit 0.
  • The 13 neighbouring suites (settings-readonly-pages, settings-readonly-head, details-bot-settings-readonly, details-face-lead-readonly, details-group-settings-readonly, details-overview-readonly, group-create-readonly, composer-send-readonly, room-approvals-readonly, create-bot-readonly, kanban, new-chat, new-chat-menu) — 83/83, 0 failed, 0 cancelled, exit 0.
  • git status --porcelain — empty on the committed tree.

What I did not run

  • The host smoke test and a live bench on a port — this is client-only (core/src/client/**), no host code and no index.js.
  • Anything on #2523, #2520 or another branch — untouched.

Files

8 files, +58/−11 (git diff --numstat 90a59447...HEAD):

  • core/src/client/group-settings.js (+4/−2) — the two face ? guards become plain cards, with the reason in a comment.
  • core/test/settings-readonly-pages.test.js (+19/−0) — one test: the plain group's locked page states both facts as text, sends no write, and the writable page draws both controls.
  • docs/subsystems/core-client.md (+1/−1) — the Settings lock bullet says the page states who answers and the working folder for every group.
  • changelog.d/2528-plain-group-readonly-facts.fixed.md (+1, new).
  • core/client.js (+4/−2), patches/ds-bot/0008-client-bundle.patch (+5/−3, carries client.js), patches/ds-bot/0012-room-membership.patch (+5/−3, carries group-settings.js), patches/ds-bot/files/test/settings-readonly-pages.test.js (+19/−0) — generated by build-client + ds-bot-patches generate, never hand-edited.

The carrier patch set is exactly {0008, 0012} — the patches that carry this branch's two changed source files (core/client.js → 0008, core/src/client/group-settings.js → 0012) — plus the test mirror. No third patch changes: the other eight are byte-identical to the base.

{
  "pr": 2538,
  "claim": "Dev-2",
  "issue": 2528,
  "summary": "A read-only window's plain-group Settings page now states Who answers (the mode's own label) and Working folder (the stored path) as text, which its writable page already drew as a picker and a folder menu. Both facts are reads, so the locked page keeps them for every group the way the face-to-face page has since #2492; no control is added and nothing is written. The writable page, the members card and the rest of the locked Settings pages are unchanged.",
  "files": ["core/src/client/group-settings.js", "core/test/settings-readonly-pages.test.js", "docs/subsystems/core-client.md", "changelog.d/2528-plain-group-readonly-facts.fixed.md"],
  "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=90a59447 npm run verify; node scripts/ds-bot-patches check; node core/scripts/build-client.mjs --check", "result": "green", "at": "2d88f4e9d0aa4bc7fb19e77d3ff00bd14eb56886" },
  "next": "none — ready for review"
}

A window that holds the team read-only draws the group Settings page as
text, and the two fact cards were gated on the room being face-to-face.
Both facts are reads — `shownMode` and `room.workDir` — so a plain group's
locked page drew neither while its writable page draws both as a control.

Draw them for every group, as the face-to-face page already did (ThinkFlowLab#2492).

Fixes ThinkFlowLab#2528
@Birfy

Birfy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 0ce7425
Written-by: Reviewer
Attestation: 0ce7425 role=Reviewer

Ran at this tip:

$ mkdir -p 2538tip 2538base && tar xzf /tmp/tip2538.tgz -C 2538tip --strip-components=1 && cd 2538tip/core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/settings-readonly-pages.test.js
# tests 8  # pass 8  # fail 0
$ cp 2538base/core/src/client/group-settings.js 2538tip/core/src/client/group-settings.js && cd 2538tip/core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/settings-readonly-pages.test.js
# tests 8  # pass 7  # fail 1
$ cp /tmp/2538-tip-group-settings.js 2538tip/core/src/client/group-settings.js

Red first. Restoring only core/src/client/group-settings.js to the base blob 90a59447 turns the new test red — not ok 8 - a read-only plain group states who answers and its working folder, 8 tests / 7 pass / 1 fail, exit 1 — and the tip file is 8/8 again. The failure names the change's own fact, not an unrelated check.

Tier. Recomputed from the pull request's file list with .github/agent/risk-gate.mjs and the main risk rules: risk-tier: T1; basis: changelog.d/2528-…=T1, core/client.js=T1, core/src/client/group-settings.js=T1, core/test/settings-readonly-pages.test.js=T1, patches/ds-bot/0008-…=T1, patches/ds-bot/0012-…=T1, patches/ds-bot/files/test/…=T1, docs/subsystems/core-client.md=T0. T1 needs green tests-ok at the tip, a mergeable pull request and design conformance — no PASS is required; this one is the record, not a gate.

Checks at the tip. All 16 check runs at 0ce7425 are completed/success, including tests-ok, merge-gate, enqueue-guard, risk tier (report only) and reviewed tip (report only); nothing pending, nothing failed.

The change. core/src/client/group-settings.js:216-219: the locked branch's two face ? … : null guards become plain Cards, so a read-only page draws Who answers (shownMode(room, roster) label) and Working folder (room.workDir || Not set) as Row text for a plain group too; a face-to-face room already had them since #2492, and the writable page still draws the Segmented picker (:239) and FolderMenu in the same place. Both values are reads; no control is added to the locked branch and no write is issued. The two-line comment carries the reason.

Scope. 8 files, +58/−11, matching the body: group-settings.js (+4/−2), settings-readonly-pages.test.js (+19), docs/subsystems/core-client.md (+1/−1), changelog.d/2528-plain-group-readonly-facts.fixed.md (+1), and the generated core/client.js (+4/−2), patches/ds-bot/0008-client-bundle.patch (+5/−3), patches/ds-bot/0012-room-membership.patch (+5/−3), test mirror (+19). tests-ok at the tip ran build-client --check and ds-bot-patches check green; arch-docs passed with the subsystem doc in the same pull request.

Checkpoint. Block is 1037 characters (< 1500), summary 458 characters / 3 sentences, 4 files all in this diff, checks.result: green at 2d88f4e9 (same tree as the tip, read not compared, per the spec), next: none — ready for review.

One conclusion. No earlier Verdict: names this tip; this is the only one at 0ce742571300e2a684be09574b5434fa655d9c17.

Fork note: head repository is HugoXK/FlyBot, so the fork exemption applies to the body's Owner:/Reviewer:/Fixes/agent-pr checks; enqueue-guard is green at the tip anyway.

Not run: the host smoke test (client-only change, no index.js or src/host/), and a live bench — CI runs both plugin suites and the package job at this tip, all green.

@Birfy
Birfy added this pull request to the merge queue Nov 5, 2026
@github-actions github-actions Bot added the pass label Nov 5, 2026
Merged via the queue into ThinkFlowLab:main with commit d400edb Nov 5, 2026
16 checks passed
@Birfy
Birfy deleted the dev/2-plain-group-readonly-facts branch November 5, 2026 14:16
HugoXK added a commit to HugoXK/FlyBot that referenced this pull request Nov 5, 2026
…rlays offer no Reload (ThinkFlowLab#2523)

Fixes ThinkFlowLab#2521

Owner: Dev-2
Reviewer: Birfy
Written-by: Dev-2
Claim-Id: cce0d5e889b74566b96d54c6ec7ff13c
Base: d400edb
Risk tier: T1

## What changes

Four read-only surfaces are drawn **inside a full-screen overlay**,
whose scrim covers the sidebar's own Reload: the reader mid-task had to
close the overlay, press Reload, and open it again. Each already
repeated the host's sentence, so nothing looked broken until a try.

1. **Board** — `BoardView` (`core/src/client/kanban.js`) drew the host
sentence in `bt-board-hint` and nothing beside it. `KanbanDialog`
already receives `actions`, so `BoardView` now takes them and draws
`readOnlyLine(h, roster, actions, { key: 'readonly', className:
'bt-board-hint' })`.
2. **New chat, admin chosen** — the group block in `NewChat`
(`new-chat.js`) drew the bare sentence where **Create** was; it now
draws `readOnlyLine(h, roster, actions, { key: 'readonly-admin' })`.
3. **New chat, composer** — the otherwise branch of the same component
drew the same bare sentence; it now draws `readOnlyLine(h, roster,
actions, { key: 'readonly-compose' })`. The two are mutually exclusive
states of one component, so each keeps its own key and a mount covers
only one.
4. **Project settings panel** — `ProjectSettingsPanel`
(`project-board.js`) received only `call/onClose/onSaved`, so it had no
reload to offer. `ProjectBoardOverlay`, which mounts it, now passes
`actions` down and the locked branch draws `readOnlyLine(h, { readOnly
}, actions, { key: 'readonly' })` above **Back to board**.

Nothing new is invented: `readOnlyLine` is the piece added in ThinkFlowLab#2502
(`core/src/client/read-only.js`). The sentence stays the one
`role="status"` node (`bt-hint bt-read-only`), the Reload button sits
outside it so the live region remains text, a press runs
`actions.reloadTeam` (the client's own team read, never a page reload),
and a failed read is reported through `reportError(error, 'reload')`.
One screen draws at most one entry; a writable window draws neither.
Every write control a writable window has is untouched.

## Acceptance

In a read-only window, with the overlay open:

- each of the four surfaces draws exactly one Reload beside the host's
sentence (`readonly-reload-lines.test.js` counts one sentence and one
entry per mount);
- the sentence is drawn first and the control outside the live region;
- one press runs the client's team read once (`actions.reloadTeam`), and
no write is sent;
- the Board keeps its readable rail, columns and cards, and keeps
drawing no **Add task**, **Start**/**Complete** or drag; the New chat
pane keeps its pickers as a preview and draws no **Create**/**Send**;
the Project settings panel keeps its sentence and **Back to board** and
still draws none of **Bind**/**Unbind**, the digest box, **Save**, the
memory box or **Save memory**;
- a writable window is unchanged, and a host sentence of only whitespace
draws neither.

## Red first

On a clean `d400edb74798b71e233eccd46f221eaef782ff5d` tree whose source
is untouched and which holds only this branch's extended test file,
`TZ=UTC node --import ./test/harness-resolve.mjs --test
test/readonly-reload-lines.test.js` exits 1:

```
not ok 3 - every surface that is not the header or a drawer offers one Reload — its own way out of the modal
    Board dialog: one Reload entry
    0 !== 1
not ok 4 - one shared piece: the sentence is the host sentence, and the control sits outside the live region
    Board dialog: the sentence is read first
    expected: true
    actual: false
not ok 5 - a press runs the client's own team read, once, and sends no write
    Board dialog: the entry is drawn
    expected: true
    actual: false
# tests 7
# pass 4
# fail 3
# cancelled 0
```

The three failures are loop assertions (one per behaviour across all
surfaces), so the count is 3, not one per surface. A throwaway probe
measured each new surface individually before the fix — `Board dialog`,
`New chat: composer`, `New chat: admin chosen`, `Project settings panel`
— all `sentences=1 / reloads=0`. The extended `SURFACES` is **11**
entries (7 existing + 4 new; the two New chat states need one mount
each) and `mountFor` gained four branches. After the fix the same file
is **7/7** green.

## Why the base is `d400edb7` and the branch was rebased

`ThinkFlowLab#2538` merged into `main` at `14:16:25Z` (merge sha `d400edb7…`) and
rewrote `patches/ds-bot/0008-client-bundle.patch`; this branch carries
that same patch. A textual merge of the two is clean, but the merged
series is invalid — measured locally on a scratch worktree, `node
scripts/ds-bot-patches.mjs check` on the merge result fails with
`0008-client-bundle.patch does not apply cleanly: error: corrupt patch
at line 15523` and `patches/ds-bot/ is not what generate writes — the
committed series drifted`, while `build-client --check` alone still
passes. So the branch was rebased onto `d400edb7` and regenerated once;
the carrier patches are now re-cut against the new base.

## What I ran at the fork head (fork head
`81b9a7ac1940eb5949bb7b50e4b462e9be3dc742`, tree
`83d6706529e88a7db759634d31177e09fd3e466c`; `TZ=UTC`, clean tree)

- `TZ=UTC ARCH_DOCS_BASE=d400edb74798b71e233eccd46f221eaef782ff5d npm
run verify` — **exit 0**. Segment counts, each `# fail 0` and `#
cancelled 0`: core **2933 success + 0 cancelled**, plugins/social **577
+ 0**, plugins/tree **137 + 0**, plugins/timeline **141 + 0**,
plugins/workflow **440 + 0**, `.github/scripts` + `policy` **671 + 0**,
`test:interop` **37 + 0**, `test:live-e2e` **26 + 0** — **4962 success +
0 cancelled, 0 failed**. `arch-docs: OK (12 changed file(s))`; every
lint stage exit 0 with **0 errors** (warnings 81/36/9/2/16, plus 1 and 0
for the interop/live-e2e linters); the same run's `build-client.mjs
--check` exit 0.
- The 13 neighbouring suites (`create-bot-readonly`, `create-bot`,
`group-create-readonly`, `kanban`, `new-chat-menu`, `new-chat`,
`project-board`, `project-board-entry`, `project-board-levels`,
`project-board-panels`, `project-board-readonly`, `project-board-view`,
`readonly-reload-lines`): **183/183, 0 failed**.
- `node core/scripts/build-client.mjs --check` — exit 0, no output.
`node scripts/ds-bot-patches.mjs check` — three `ok` lines: `ok ds-bot/
= Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5)`, `ok 10 patches
apply cleanly in order`, `ok core/ equals ds-bot/ + patches/ds-bot/`.
`node scripts/ds-bot-patches.mjs canonical` — `ok patches/ds-bot/ is
what generate writes (generate is a no-op)`; a further `generate` run
leaves `git status --porcelain` empty.
- `git status --porcelain` — empty on the committed tree.

## 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.
- Anything on `ThinkFlowLab#2520`, `ThinkFlowLab#2517` or `ThinkFlowLab#2443` — untouched.

## Files

12 files, `+451/−354` (`git diff --numstat d400edb...HEAD`), tree
`83d67065`:

- `core/src/client/kanban.js` (+7/−5) — `BoardView` takes `actions`; the
hint becomes `readOnlyLine`.
- `core/src/client/new-chat.js` (+4/−3) — both locked branches draw
`readOnlyLine`, one key each.
- `core/src/client/project-board.js` (+4/−3) — `ProjectSettingsPanel`
takes `actions`; `ProjectBoardOverlay` passes it.
- `core/test/readonly-reload-lines.test.js` (+45/−2) — `SURFACES` 7 → 11
and four `mountFor` branches.
- `docs/subsystems/core-client.md` (+4/−4) — the four surfaces and the
panel's `actions` are documented.
- `changelog.d/2521-overlay-readonly-reload.fixed.md` (+1, new).
- `core/client.js` (+10/−8),
`patches/ds-bot/0003-flybot-api-client.patch` (+5/−5, carries
`new-chat.js`), `patches/ds-bot/0008-client-bundle.patch` (+315/−314,
carries `kanban.js` and `project-board.js`),
`patches/ds-bot/files/src/client/kanban.js` (+7/−5),
`patches/ds-bot/files/src/client/project-board.js` (+4/−3),
`patches/ds-bot/files/test/readonly-reload-lines.test.js` (+45/−2) —
generated by `build-client` + `ds-bot-patches generate`, never
hand-edited. The two carrier patches are exactly the ones whose source
files this branch changes — the carrier set `{0003, 0008}` is exactly
the set that carries the changed sources, and no third patch is touched
(the other eight patches are byte-identical to the base).

<!-- flybot:checkpoint -->
```json
{
  "pr": 2523,
  "claim": "Dev-2",
  "issue": 2521,
  "summary": "Four read-only surfaces drawn inside overlays — the Board hint, both New chat sites and the Project settings panel — now draw the shared readOnlyLine piece instead of their own copy of the host sentence. Each keeps the sentence in its one role=status node and puts a single Reload outside it, running actions.reloadTeam, never a page reload. The panel now receives actions from ProjectBoardOverlay, which previously passed no reload at all. A writable window and every read-only control are unchanged.",
  "files": [
    "core/src/client/kanban.js",
    "core/src/client/new-chat.js",
    "core/src/client/project-board.js",
    "core/test/readonly-reload-lines.test.js",
    "docs/subsystems/core-client.md",
    "changelog.d/2521-overlay-readonly-reload.fixed.md"
  ],
  "checks": {
    "ran": "TZ=UTC ARCH_DOCS_BASE=d400edb7 npm run verify; build-client --check; ds-bot-patches check/canonical; 13 neighbouring suites",
    "result": "green",
    "at": "81b9a7ac1940eb5949bb7b50e4b462e9be3dc742"
  },
  "next": "on the rebased base the branch carries a re-cut 0008; the source/tests/docs half is unchanged, so review the regenerated carrier patches only"
}
```
<!-- /flybot:checkpoint -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

client: a read-only window's plain-group Settings page drops Who answers and Working folder

2 participants