Skip to content

core: the read-only reads test passes on a dry answer — assert each row's own key - #2280

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-readonly-reads-assert
Nov 3, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-readonly-reads-assert

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2278

What was wrong

core/test/readonly-window-reads.test.js (#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, 项目看板:7 个指标卡 + 在跑任务表 #1829), the people (project-team: a Chief row with botId === 'a', not rows: [] or [null, null], Project board shows each Project Bot's dossier (persona, memory, skills), and the owner opens their agent from their own chat #1864), the memory entry (project-memory: a decision whose text is A decision, not an empty record, Stage 2.5: the Project Assistant, minimal version (private memory + daily 09:00 digest) #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 (Project board decision rows can be listed but never opened #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 (#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 core: a read-only window's create entries no longer open the scheduled-task form #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.

@HugoXK
HugoXK marked this pull request as draft November 3, 2026 06:36
@HugoXK
HugoXK force-pushed the dev/1-readonly-reads-assert branch 2 times, most recently from 66211ac to 23da818 Compare November 3, 2026 06:44
…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.
@HugoXK
HugoXK force-pushed the dev/1-readonly-reads-assert branch from 23da818 to e6bb416 Compare November 3, 2026 07:07
@HugoXK
HugoXK marked this pull request as ready for review November 3, 2026 07:28
@HugoXK

HugoXK commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy — #2280 is ready at this tip e6bb416cc52c0583348469d8a94b656f4e5ab8d1 (base b451e3b3, 2 files, mcm true, mergeable_state clean).

enqueue-guard is green at this exact tip (enqueue #2280: no hold and the gate passes), and this is T1 (core/test/** plus its generated mirror), so no approval is needed — the tip may enter the queue as it stands.

What it does: the read-only reads test now checks the values each screen draws instead of the shape of the answer. Before this change a dry answer passed: the guard only asked "not refused with bot/read-only". The four rows that used to pass on a dry answer now fail on it — project-team (the dossier's own Chief row), project-memory (the seeded decision text), project-board (counts/metrics, including the 0-vs-null shape from #1829) and folders (picker === 'browse') — and Fixes #2278 is in the body.

Self-test: the four rows go red when their seeded data is removed, and again when the readers are made to answer dry; deleting a name from the channel's read list reddens the row that names it (folders → 2 pass / 1 fail, the failure names folders); the writable cases stay green under every one of those mutations. verify on the tip: 4603/4603/0, arch-docs OK, 10 patches clean, generate no-op.

@Birfy
Birfy added this pull request to the merge queue Nov 3, 2026
Merged via the queue into ThinkFlowLab:main with commit 8912545 Nov 3, 2026
16 checks passed
@Birfy
Birfy deleted the dev/1-readonly-reads-assert branch November 3, 2026 07:41
HugoXK added a commit to HugoXK/FlyBot that referenced this pull request Nov 3, 2026
…ays nothing about why (ThinkFlowLab#2277)

Fixes ThinkFlowLab#2276
Fixes ThinkFlowLab#2279
Fixes ThinkFlowLab#2283

Three read-only slices on one branch. They all end in the same client
bundle and the same generated patch split, so they share one build and
one `generate`: the alternative was three PRs each rewriting
`0008-client-bundle.patch` and fighting over `core/client.js`.

## What each one fixes

- **ThinkFlowLab#2276 — the Memory panel** (`core/src/client/memory-panel.js`,
+9/−5): a read-only window no longer draws **Remove** or the ask/update
form, both of which the host refuses (`memory-forget`, `memory-ask`),
and says why in the host's own sentence from `roster.readOnly`. The
entries themselves and **Regenerate summary** (a read) keep working.
Test: `core/test/memory-panel-readonly.test.js` (3 tests).
- **ThinkFlowLab#2279 — the details drawer's Settings view**
(`core/src/client/details-drawer.js`, +5/−3): it draws the host's
read-only sentence in the form's place instead of mounting
`BotSettingsForm`, reading the same window lock (`lockedWindow`) the
settings page reads. A writable window keeps the form unchanged. The
same file hands the roster back to `ScheduleSummary`: without it the
details overview drew its "No scheduled tasks yet. Add one" entry in a
read-only window, and that entry opens the create form a locked Settings
page does not draw (ThinkFlowLab#2232/P-457). The suite for this change now renders
the overview path, because `core/test/schedules-client.test.js` calls
`ScheduleSummary` with a roster of its own and cannot see a call site
that forgets one. Test:
`core/test/details-bot-settings-readonly.test.js` (3 tests).
- **ThinkFlowLab#2283 — the ask_user card** (`core/src/client/questions.js` +38/−24,
`core/src/client/cells.js` +1/−1): a read-only window draws no answer
control on the card, and the answers it already holds stay readable.
Test: `core/test/questions-readonly.test.js` (6 tests).

`docs/subsystems/core-client.md` carries all three bullets, and each
change has its own fragment under `changelog.d/` (ThinkFlowLab#2276, ThinkFlowLab#2279, ThinkFlowLab#2283).

## Files

21 files, `+17621/−16580`. The semantic change is six files plus three
new tests, three changelog fragments and the one-line `topics.json`
mapping; everything else is the generated split: `core/client.js` and
its `0008-client-bundle.patch` (a module-order relocation of
`questions.js` and `schedules.js` inside the bundle — the bulk of the
line count, not new code), `0001-core-fixes.patch`,
`0003-flybot-api-client.patch`, `0012-room-membership.patch`, and the
three whole-file mirrors under `patches/ds-bot/files/test/`.
`patches/ds-bot/topics.json` is in the diff for one reason:
`src/client/questions.js` is an upstream file FlyBot edits, so
`generate` refuses the split until that file has a topic
(`0003-flybot-api-client`), and without that one line `core/` cannot be
rebuilt.

## Red first, then green

- The overview half of ThinkFlowLab#2279 has its own red: with the drawer not
passing `roster` to `ScheduleSummary`,
`core/test/details-bot-settings-readonly.test.js` was `# tests 3 / #
pass 2 / # fail 1`, `no create entry is drawn in a read-only window` —
actual `['No scheduled tasks yet. Add one']`, expected `[]`. With the
roster passed again it is `3/3/0`.
- The other two suites were written against the behaviour they lock (a
read-only window draws no write entry, a writable one keeps it), so each
asserts both sides: the Memory panel `3/3/0` and the ask_user card
`6/6/0` on this tip.

## Evidence, measured on this tip's tree

- Suites, each run with `TZ=UTC node --import ./test/harness-resolve.mjs
--test <file>`:
- `memory-panel-readonly` `3/3/0`, `details-bot-settings-readonly`
`3/3/0`, `questions-readonly` `6/6/0` (tests/pass/fail).
- Regressions in one run, `# tests 173 / # pass 173 / # fail 0`:
`memory` `97/97/0`, `memory-empty` `6/6/0`, `memory-search` `4/4/0`,
`schedules-client` `52/52/0`, `questions` `2/2/0`,
`settings-readonly-head` `3/3/0`, `sidebar-readonly` `4/4/0`,
`composer-locked` `2/2/0`, `readonly-window-reads` `3/3/0`.
- `TZ=UTC npm run verify` at the repo root — exit 0 on this tree (core
lint + `arch-docs`, plugin lint, `build-client.mjs --check`,
`ds-bot-patches.mjs check`, the core suite and both plugin suites).
- `ARCH_DOCS_BASE=89125458 npm run lint` — `Found 74 warnings and 0
errors` (warnings pre-existing), `arch-docs: OK (21 changed file(s))`.
- `node scripts/ds-bot-patches.mjs check`:
  ```
  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)`: `generate` was run twice on
this tree and the second pass wrote nothing.
- `npm run build` (in `core/`) reproduces the committed `core/client.js`
byte-for-byte: a second build leaves the tree clean.
- The branch was rebased from `19e206ec` onto `89125458` after ThinkFlowLab#2280
merged, and `build`, `generate`, `check`, `canonical` and `verify` were
all re-run there.

## What I did not run

- The bench smoke test — this branch touches no `core/index.js` and no
`core/src/host/` file.

Verified head: `bb5a863229103b45a50baec3ca71d3d4e295e4a8` (tree
`e43d9a1538ed12b01adad475f2c856ab3fe9418b`, single parent
`891254589abb9aa6ac42bc1bb64874c716648876`).
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.

core: the read-only reads test passes on a dry answer — assert each row's own key

2 participants