Skip to content

Settings: a read-only window still draws the Projects write controls - #2463

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/3-projects-readonly-writes
Nov 4, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/3-projects-readonly-writes

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2305
Owner: Dev-3
Reviewer: Birfy
Written-by: FlyBot Dev-3
Claim-Id: faf8973b62ac49a9a448bd905c808bff
Branch: dev/3-projects-readonly-writes
Tip: 8672ed4
Tree: d8b32d7889282f0380f450f1a4200ff50b0cb5b2
Base: 8c5dbd3
Diff: 8 files, +131/−18
Tier: T1
why: [] — under the live risk-rules.json at this base core/src/client/**, core/test/**, core/client.js, patches/ds-bot/** and changelog.d/** are T1 and docs/** is T0, and 61 hand-written lines in 4 files stay under every size ceiling, so the file rule alone decides it.

What was wrong

Settings → Projects is the one settings page whose write half was still drawn in a window that holds the team read-only. ProjectsPage always drew the New Project form — the name field and the folder picker — and ProjectsList always drew a Bind to {name} choice on every Project row and an Unbind button on every binding. The host refuses project-create and project-bind in that window (run in settings.js), so each press answered with the refusal line instead of doing what the control promised: the page offered four kinds of write the host was never going to accept.

The rest of the client already follows this rule, and the fix reuses the same helper rather than inventing a second read of the lock: lockedWindow(roster) (schedules.js) is what SettingsDialog prints the host's own sentence from (#2199) and what the details drawer and its overview use to draw plain text instead of a refused control (#2385, #2387, #2391, and #2458 for the overview's values).

What changed

  • core/src/client/settings.js (+8/−4): ProjectsPage and ProjectsList read lockedWindow(roster) once and, when the window is locked, draw no New Project section, no Bind to {name} select and no Unbind. The Project rows with their names and working folders, the members already bound (still listed with their kind hint), and Open board stay readable — the window refuses the writes, not the reading. A writable window draws exactly the tree it drew before.
  • core/test/project-settings-page.test.js (+47): one case with three readings on a read-only roster — the page draws no write control and keeps the rows, the folder fact, Open board and the bound member; the list section draws no Bind select and no Unbind; and the writable control in the same file still counts four controls and one name field, one select, one Unbind, so the new gate cannot pass by drawing nothing.
  • docs/subsystems/core-client.md (+1): one paragraph for the Projects page beside the other read-only surfaces, naming lockedWindow and the two refused writes.
  • changelog.d/2305-projects-readonly-writes.fixed.md (+1): the user-visible change.

core/client.js and the four patches/ds-bot/ paths in the diff are generated: core/ is the source of truth and was rebuilt with npm run build in core/, then node scripts/ds-bot-patches.mjs generate rewrote the bundle and the carried test file. Nothing under core/client.js or patches/ was hand-edited.

What I ran

Red first, clean 4a165ba8 with only the new case in the worktree and core/src/client/settings.js reverted to the base copy:

cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/project-settings-page.test.js

5 tests, 4 pass, 1 fail — not ok 4 - a read-only window draws no write on the Projects page, message a read-only window offers no Projects write, the actual list holding input[aria-label="Project name"] … select[aria-label="Bind to Site"] against [], at assert.deepEqual(writeControls(nodes), [], 'a read-only window offers no Projects write'). The writable control in the same file passes in that run, so the failure is the read-only window drawing writes, not the test finding no page.

After the fix the same command is 5/5.

At the pushed tip 8672ed4f, in a clean worktree of this pull request's head, with the base the queue will merge onto:

TZ=UTC ARCH_DOCS_BASE=8c5dbd34 npm run verify

exit 0 — 4876 tests across 8 suites, every suite # fail 0 (core 2866, social 577, tree 137, timeline 141, workflow 421, .github + policy 671, interop 37, live-e2e 26); arch-docs: OK (8 changed file(s)); 10 patches apply cleanly in order; core/ equals ds-bot/ + patches/ds-bot/; patches/ds-bot/ is what generate writes (generate is a no-op); lint 0 errors (the 78 core, 36 social, 9 timeline, 2 tree, 16 workflow warnings are unchanged and pre-existing). generate was run twice at this tip and the second run left git status --porcelain empty, so the branch is exactly what generate writes on this base.

The same red-first reading holds on the new base: clean 8c5dbd34 with only the new case in the worktree and the source reverted is 5 tests / 4 pass / 1 fail, the same not ok 4 - a read-only window draws no write on the Projects page at the same assertion, and the pushed tip is 5/5.

The base moved while the branch was finishing. Live main went 4a165ba8 → 8c5dbd34, and that commit is this Bot's own #2460 (plugins/workflow/src/service.js, its test, docs/subsystems/workflow.md, its changelog) — no file this branch touches. The push therefore overlaid these 8 paths onto the new main, which is why Base and Tree above differ from the local commit's tree; Tip is that overlaid commit.

Not run: the smoke test (dev/smoke.mjs) — this change touches no core/index.js or core/src/host/ file, and the bench needs a DSH checkout this worktree does not have.

Checkpoint

{
  "pr": 2463,
  "claim": "Dev-3",
  "issue": 2305,
  "summary": "Settings → Projects drew its write half — a name field, New Project, a Bind to {name} choice on every Project row and Unbind on every binding — in a window that holds the team read-only, where the host refuses project-create and project-bind, so every press answered with a refusal line. The page and the list now read the window lock once and draw no write it would be refused. The Project rows, their names and working folders, the members already bound and Open board stay readable, and a writable window is unchanged. Tests: core/test/project-settings-page.test.js.",
  "files": [
    "core/src/client/settings.js",
    "core/test/project-settings-page.test.js",
    "docs/subsystems/core-client.md",
    "changelog.d/2305-projects-readonly-writes.fixed.md"
  ],
  "checks": {
    "ran": "TZ=UTC ARCH_DOCS_BASE=8c5dbd34 npm run verify at this tip; cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/project-settings-page.test.js; node scripts/ds-bot-patches.mjs generate twice (second run a no-op)",
    "result": "green",
    "at": "8672ed4fb0f3cbb7cadeb43cec5fda11adea9c07"
  },
  "next": "none — ready for review (T1: tests-ok green at the tip is the whole gate)"
}

Settings -> Projects drew a name field, New Project, a Bind to {name}
choice and Unbind in a window that holds the team read-only, and the
host refuses project-create and project-bind there, so every one of
those controls answered a press with a refusal line. The page now reads
the window lock once (lockedWindow, the same helper the details drawer
uses) and draws no write it would be refused; the Project rows, their
names and working folders, the members already bound and Open board
stay readable, and a writable window is unchanged.

Tests: core/test/project-settings-page.test.js.
@Birfy
Birfy added this pull request to the merge queue Nov 4, 2026
Merged via the queue into ThinkFlowLab:main with commit bdd0260 Nov 4, 2026
15 checks passed
@Birfy
Birfy deleted the dev/3-projects-readonly-writes branch November 4, 2026 20:06
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.

Settings: a read-only window still draws the Projects write controls

2 participants