Repository navigation
core: a read-only window refuses the reads its own screens ask for - #2269
Conversation
The host's read whitelist named six of the screens a read-only window draws, so the Projects panel, a Bot's task list, the folder picker's list and the connector status line were refused with bot/read-only on open. Add the nine read endpoints (eleven names, since projects/project-read and folders/folder-list each pair a panel with its list) to READS; no write endpoint moves, and authorize() still runs after the read gate. Fixes ThinkFlowLab#2267
|
@Birfy — review request for PR #2269 at tip Tier T3, independent lane: host Red-first on clean The other two cases are counter-proofs that must stay green: the same window still refuses every write, and a writable window answers exactly as before. Fixed at this tip: the test file 3/3; Checks at this tip: 15 success + 1 cancelled ( T3 needs your PASS at this tip before the queue; please review |
|
Verdict: PASS at d4866ee Ran at this tip: Review points.
Verdict: PASS — T3 with the tool's own tier, red-first on the single new read case, write refusal unchanged. |
…wLab#2278) The test added with ThinkFlowLab#2269 was the only guard against a read name falling out of `channel.js`'s READS whitelist, but it asserted only "not refused with bot/read-only". An answer past the gate but dry passed: `folder-list` answered bot/refused with no browser, `connector-app-status` answered "Connect Composio first", `project-decision` answered the "nothing listed" half, and `project-of` / `project-board` / `folders` answered ok with empty data. Each of the 11 READS rows now carries the key its screen draws and checks it, and the fixtures make that data real: a `browse` directory picker for the folder rows, a signed-in Composio session with its host stubbed for the connector row, and a Project working folder holding the decision file the board itself lists (ThinkFlowLab#2041). A refusal as bot/read-only (the whitelist lost the name) and a dry answer (the fixture never made the data) are reported as two separate lists, so a red names the defect.
…wLab#2278) The test added with ThinkFlowLab#2269 was the only guard against a read name falling out of `channel.js`'s READS whitelist, but it asserted only "not refused with bot/read-only". An answer past the gate but dry passed: `folder-list` answered bot/refused with no browser, `connector-app-status` answered "Connect Composio first", `project-decision` answered the "nothing listed" half, and `project-of` / `project-board` / `folders` answered ok with empty data. Each of the 11 READS rows now carries the key its screen draws and checks it, and the fixtures make that data real: a `browse` directory picker for the folder rows, a signed-in Composio session with its host stubbed for the connector row, and a Project working folder holding the decision file the board itself lists (ThinkFlowLab#2041). A refusal as bot/read-only (the whitelist lost the name) and a dry answer (the fixture never made the data) are reported as two separate lists, so a red names the defect.
…owLab#2278) The test added with ThinkFlowLab#2269 was the only guard against a read name falling out of `channel.js`'s READS whitelist, but it asserted only "not refused with bot/read-only". An answer past the gate but dry passed: `folder-list` answered bot/refused with no browser, `connector-app-status` answered "Connect Composio first", `project-decision` answered the "nothing listed" half, and `project-of` / `project-board` / `folders` answered ok with empty data. Each of the 11 READS rows now checks the data its screen draws, and the fixtures make that data real: a `browse` directory picker for the folder rows, a signed-in Composio session with its host stubbed for the connector row, and a Project working folder holding the decision file the board itself lists (ThinkFlowLab#2041). Four rows that only checked a shape now check values: the board's `counts` and its credential-less `metrics` (ThinkFlowLab#1829), the dossier's Chief row (ThinkFlowLab#1864), the assistant memory entry (ThinkFlowLab#1712), and the `picker` kind the Browse entry needs. A refusal as bot/read-only (the whitelist lost the name) and a dry answer (the fixture never made the data) are reported as two separate lists, so a red names the defect.
…ow's own key (ThinkFlowLab#2280) Fixes ThinkFlowLab#2278 ## What was wrong `core/test/readonly-window-reads.test.js` (ThinkFlowLab#2269) is the only guard against a read name falling out of `channel.js`'s `READS` whitelist, but it asserted only "the answer is not refused with `bot/read-only`". An answer that is past the gate and dry still passed, so the guard was green on a tree where a read-only window draws those screens empty. Measured on the tree of this PR's base with the file as it shipped: | read | what it actually answered | | --- | --- | | `folder-list` | `{"ok":false,"error":{"code":"bot/refused","message":"This DeepSeek Harness cannot list folders"}}` | | `connector-app-status` | `{"ok":false,"error":{"code":"bot/refused","message":"Connect Composio first"}}` | | `project-decision` | `{"ok":true,"value":{"ok":true,…}}` — the "nothing listed" half | | `project-of` | `{"ok":true,"value":{"project":null}}` | | `project-board` | `…"reason":"no working folder: this Project names none"` | | `folders` | `{"ok":true,"value":{"team":null,"picker":null,"folders":[]}}` | Six reads were dry; the old file was `# tests 3 / # pass 3 / # fail 0` on that same tree. ## What this changes Test-only: no source change, no shared fixture. - `core/test/readonly-window-reads.test.js` — each of the 11 `READS` rows now checks the values its screen draws, not the shape of the answer, so a dry answer is red instead of passing for "not refused". What each row proves: the row itself (`tasks`, `projects`, `project-read`, `project-of`, `project-read`'s project, `project-decision`'s file text and the decisions the board listed), the count beside it and its numbers (`project-board`: `tasks` plus `counts.waiting === 1` / `inflight 0` / `blocked 0`; `metrics.ok === false` with reason `no-credentials` and every metric `null`, ThinkFlowLab#1829), the people (`project-team`: a Chief row with `botId === 'a'`, not `rows: []` or `[null, null]`, ThinkFlowLab#1864), the memory entry (`project-memory`: a decision whose text is `A decision`, not an empty record, ThinkFlowLab#1712), the folder rows and the chooser kind (`folders`: `picker === 'browse'` plus the working folder; `folder-list`: an entry named `decisions`), and the connector's app state (`connector-app-status`: `gmail === 'ready'`). A row's payload may be a function of the answers above it, used only by `project-decision`, which opens a name the board itself listed (ThinkFlowLab#2041) rather than one this file made up. A refusal as `bot/read-only` (the whitelist lost the name) and a dry answer (past the gate, no data) are reported as two separate lists, so a red names which defect it is. - Fixtures inside that file make the data real: a `directoryPicker` of the `browse` kind on the fake context for the folder rows; a signed-in Composio session (state seed + `COMPOSIO_API_KEY` in `secrets.json`, with only `https://backend.composio.dev/` stubbed and the real `fetch` restored on stop) for the connector row; a Project working folder holding `decisions/2026-01-01-x.md` for the decision and board rows. - `patches/ds-bot/files/test/readonly-window-reads.test.js` — the same bytes. `core/` is carried as `ds-bot/` + patches, and this test file is one of the whole-file overlay entries, so a change under `core/test/` is only consistent once the overlay copy is regenerated. `package` step `ds-bot/ matches the pin, patches apply, core/ is ds-bot/ + patches (ThinkFlowLab#34)` caught exactly that (`core/test/readonly-window-reads.test.js: differs from ds-bot/ + patches`) before this file was regenerated in. ## Red first, then green - Before: the file as shipped, same base tree — `# tests 3 / # pass 3 / # fail 0`. The six dry answers above went unnoticed (false green). - After: `# tests 3 / # pass 3 / # fail 0`, run twice with the same counts. - Counter-proof (whitelist): deleting one name from `channel.js`'s `READS` makes the read-only case red and names it — `'folder-list'` removed → `# tests 3 / # pass 2 / # fail 1`, `not ok 1 - … a read-only window still answers these reads: folder-list`. The same shape was reproduced for `connector-app-status` and `project-decision`. `channel.js` was restored and the tree of this commit (below) re-verified green afterwards. - Counter-proof (data): with the dossier's Chief, the memory record, the card's `status`/`queueRole` and the `browse` chooser stripped from the fixtures — i.e. exactly the four dry answers that shape-only checks used to accept — the same case is red and names those four rows: `project-board`, `project-team` (`{"rows":[null,null]}`), `project-memory` (`"decisions":[]`) and `folders` (`"picker":"native"`), plus `folder-list` because a `native` chooser has no listing: `# tests 3 / # pass 1 / # fail 2`. Restored, then green again. ## What I ran - `TZ=UTC node --import ./test/harness-resolve.mjs --test test/readonly-window-reads.test.js` — `# tests 3 / # pass 3 / # fail 0` (twice, and once more after the patch overlay was regenerated). - The counter-proof above, then `git checkout -- core/src/host/channel.js` and a green run on the restored tree. - `node scripts/ds-bot-patches.mjs generate` — 307 overlay files written, of which exactly one changed (`patches/ds-bot/files/test/readonly-window-reads.test.js`, +84/−33); no `.patch` file changed. - `node scripts/ds-bot-patches.mjs check` — `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)`. - Rebased onto the new `main` `b451e3b3` (this PR's base moved after ThinkFlowLab#2246 merged), then `node scripts/ds-bot-patches.mjs generate` again — it wrote nothing (no overlay or patch file changed), `check` and `canonical` still green, and the affected suite re-ran green on the rebased tree. - `ARCH_DOCS_BASE=b451e3b3 npm run lint` — `Found 74 warnings and 0 errors` (warnings pre-existing), `arch-docs: OK (2 changed file(s))`. - `git show --stat` — 2 files, +216/−68 (the test and its overlay copy, same 142-line diff each). ## What I did not run - `npm run verify` — not run in this worktree. The change is test-only with no source edit; the affected suite, the patch checks above and lint were run directly. - No build; no `changelog.d/` fragment and no subsystem doc (test-only, no user-visible change). - No other test file touched, and no source file touched. Verified head: `e6bb416cc52c0583348469d8a94b656f4e5ab8d1` (tree `b201349f4c5d170b4b9a1db48ed6207404d938a5`, single parent `b451e3b361771e85db156845f89a254c62ac275d`) — the pushed tip, whose tree is byte-for-byte the tree the run counts above were taken on.
Fixes #2267
Written-by: Dev
Owner: Dev
Reviewer: Birfy
Claim-Id: e41bbd6fb40f4902bfba93c12d3d4f17
Branch: dev/1-readonly-window-reads
Base: 03a1253
Tree: 3919f1983840f2a9a546d20ff74e248b02f2298d
Verified commit: 22d35d41b8885b08dc2f418baf6382ec5386c42f
Pushed head: d4866ee
Scope
A read-only window refuses reads with
bot/read-onlywhen the endpoint is not in the host's read list, and the window's own screens call reads that list did not name: a Bot's task list, the Projects panel and the reads its cards open, the folder picker's list and the connector status line. The host now names those reads as reads, so they answer in a read-only window; the authorization check still runs after the read gate, and no write endpoint is added —project-create,project-bind,task-create,folder-pick,connector-connect,set-prefs,create-botandworkflow-savekeep theirbot/read-onlyrefusal. A writable window behaves as before.The new test opens a real read-only window through the host's own lock and asserts three things: the reads answer, the writes are still refused, and a window that may write again is unchanged. Nothing is edited by hand in the generated trees:
client.jsand the0008bundle do not move, and the patch and itsfiles/mirror come fromgenerate.Files
core/src/host/channel.js— the read list grows; the refusal for writes stays.core/test/readonly-window-reads.test.js— new: reads answer, writes refused, writable window unchanged.docs/subsystems/core-host.md— one line on what the read list covers.changelog.d/2267-readonly-window-reads.fixed.md— one line.patches/ds-bot/0005-social-seams.patch,patches/ds-bot/files/test/readonly-window-reads.test.js— generated.Readings
Red first, on a clean
03a125306226158ae13c1b3a1185e4db30c7ca67with only the new test added —3 tests / 2 pass / 1 fail:The other two cases — every write still refused, a writable window unchanged — already pass at that base, so the change is what moves the first one.
At the tip:
3 tests / 3 pass / 0 fail.2681 tests / 2681 pass / 0 fail.TZ=UTC ARCH_DOCS_BASE=03a125306226158ae13c1b3a1185e4db30c7ca67 npm run verify: exit 0,4596 tests / 4596 pass / 0 fail(core 2681, social 570, tree 132, timeline 140, workflow 411, scripts 599, interop 37, live-e2e 26).arch-docs: OK (6 changed file(s));build-client --checkok;ds-bot-patches checkall three lines ok (pinFei2411/DeepSeek-Bot@2b10c70, ten patches apply in order,core/equalsds-bot/+ patches);canonicalok; a secondgeneratewrites nothing.6 files changed, 341 insertions(+), 4 deletions(-).Tier: T3— basis:core/src/host/channel.js=T3;changelog.d/2267-readonly-window-reads.fixed.md=T1;core/test/readonly-window-reads.test.js=T1;patches/ds-bot/0005-social-seams.patch=T1;patches/ds-bot/files/test/readonly-window-reads.test.js=T1;docs/subsystems/core-host.md=T0.Not run: the smoke test — this box has no
../deepseek-harnesscheckout, sodev/bench.shcannot start.{
"pr": 2269,
"claim": "Dev",
"issue": 2267,
"summary": "The host names the read endpoints a read-only window's own screens call, so the Projects panel, a Bot's task list, the folder picker and the connector status line answer instead of reporting bot/read-only. Only the read list grows: every write keeps its refusal and a window that may write is unchanged. A new test drives a real read-only window and asserts those three cases.",
"files": [
"core/src/host/channel.js",
"core/test/readonly-window-reads.test.js",
"docs/subsystems/core-host.md",
"changelog.d/2267-readonly-window-reads.fixed.md",
"patches/ds-bot/0005-social-seams.patch",
"patches/ds-bot/files/test/readonly-window-reads.test.js"
],
"checks": {
"ran": "TZ=UTC ARCH_DOCS_BASE=03a125306226158ae13c1b3a1185e4db30c7ca67 npm run verify",
"result": "green",
"at": "22d35d41b8885b08dc2f418baf6382ec5386c42f"
},
"next": "ready for review at this tip; the Lead posts the single pointer, and a T3 PASS must name this tip."
}
The readings above are from the verified commit
22d35d41; the tip has not moved since that run.