Skip to content

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

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 31a96e5f509143ffb9fed52d37277a00
Branch: dev/1-readonly-reads-assert
Base: 910f630
At: 2026-11-03T06:33:21Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead

What is wrong

core/test/readonly-window-reads.test.js (added in #2269) is the only guard against a read name
falling out of channel.js's READS whitelist, but it asserts only "not refused with
bot/read-only". Any answer that is past the gate and dry still passes:

  • 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,"reason":"…not listed…"}} — the "nothing listed" half
  • project-of / project-board / folders → ok:true with empty data (no binding, no working folder)

So on a tree where a read-only window draws those screens empty, the suite is still green.

Reproduction (live main 910f6302)

$ node --test core/test/readonly-window-reads.test.js     # the file as it is on main
# tests 3 / # pass 3 / # fail 0        <- false green, on the dry data above

The same tree with each row asserting its own key (fixtures unchanged) is red in two of three
tests, and the red names the dry answers, not a crash.

Expected behaviour

Each row of READS carries [endpoint, payload, expect] and the test fails unless the answer
carries the data its screen draws (11 rows: tasks, projects, project-read, project-of,
project-board, project-team, project-decision, project-memory, folders, folder-list,
connector-app-status). Refusals and dry answers are reported as two separate lists, because they
are two different defects (whitelist lost a name vs the fixture never made the data).

Fixtures the rows need, all inside the test file (no source change):

  • fakeContext({ services: { directoryPicker: { capability: () => ({ kind: 'browse', list }) } } })
    — folders.js reads it through ctx.get('directoryPicker');
  • a seeded Composio session plus a stubbed https://backend.composio.dev/ fetch, so
    connector-app-status answers gmail: 'ready';
  • a Project with a workDir holding decisions/2026-01-01-x.md and a Bot bound to it;
    project-decision opens the first name the board itself listed (Project board decision rows can be listed but never opened #2041), and fails loudly with
    the board listed no decision file to open when the board listed none.

Reverse evidence: deleting any one name from READS in channel.js must turn the test red naming
that name (verified for folder-list, connector-app-status, project-decision: pass 2 / fail 1).

Scope

  • One file: core/test/readonly-window-reads.test.js (+84/−33). Test-only: no core/src change,
    no generated artifact, no changelog.
  • Out of scope: adding new reads to the whitelist, or touching the handlers whose fixtures were
    seeded here.

Duplicate check

Searched open and closed issues for readonly-window-reads: #2269 and #2267 (closed) are the
feature tickets whose test this hardens; no issue asks for the assertion strength itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions