fix(desktop,host): say small panels are reading, or refused, rather than empty
Merged
DA
16d ago+639+639+012345678901234567890123456789-51-51-01234567890123456789
PR 5 of 9 in the loading-states rollout (Loading states audit, plan Fix Inbox loading state, "Loading states rollout"). It covers the small panels that said there was nothing, or drew nothing, before their data arrived.
Summary
- Side panel, Code tab. While the task's code is unread, the tab shows a
SpinnerRegion("Loading code"). It says "No code yet." only when the code is read as[]. To make that distinction real,compose-tasks.ts'sreadWorknow writesshownCode: []when the read finds no code. Before, it left the field absent, soundefinedstood for both "unread" and "none".withoutActivitystill clears it when you switch tasks.- With that change,
taskHoldsreads a task with no code as holding none (false) rather than unknown. A first snippet shown later now arrives at the strip's end, like any other kind read as empty.use-side-panel.ts'sarrived()comment no longer says code is left absent. sidebar-pinned-tab.tsx'spinGonetreats[]the way it treated the old absent value. A file pinned on a task Luke showed no code on was browsed to, not shown, so it isn't dimmed. Its behaviour is unchanged.
- With that change,
- Automation editor, MCP servers row. While
useIntegrationStatuseshasn't answered, the row shows a skeleton line for its description and a skeleton chip, inside aSkeletonRegion("Reading your connected servers…"). It no longer claims "None connected yet." or shows a "None" chip. A refused read shows anErrorStatewith Retry. - ⌘K. When the list is empty, the palette now says why instead of "Nothing matches.":
- Tasks: "Reading your tasks…" until
listRead, or a failure line with Retry (onRetryList) whenlistFailed. - People: "Reading the workspace's members…" until the members answer, or a failure line with Retry when the read is refused.
- All: the first kind still reading, else the first one refused. A query under All no longer says "Nothing matches." while the repository's files are being read.
- To support People,
use-workspace-members.tsgainsuseWorkspaceMembersRead, which returns{ members, refused, retry }.useWorkspaceMembersis now a thin wrapper over it, so its other callers are unchanged.
- Tasks: "Reading your tasks…" until
- Terminal tab, Shells list. While the first list read is out, the list shows skeleton rows shaped like a shell's row ("Reading the shells…"). A refused read, which leaves the tab unreachable, shows an
ErrorState("The shells could not be read."). It has no Retry of its own because the screen's bar beside it already offers one. A sandbox that stopped before the list answered says "No shells.".
Evidence
- Platform-independent checks:
pnpm check:affectedgreen locally (lint, knip, discarded-effect, the host and desktop vitest suites). - Each new test fails with its source change reverted (checked one by one):
desktop/desktop-tasks.test.ts: the Code tab waits while unread and says "No code yet." only for[].packages/host/src/compose-tasks.test.ts: code read as none is held as[], and a work read that didn't answer leaves it unread.desktop/automations-page.test.ts: the MCP row claims nothing while reading, and a refused read says so, then Retry reads it again.desktop/command-palette.test.ts: All with a query while the files are read, People reading, refused, and retried, and Tasks reading and refused with Retry.tasks/terminal-tab.test.ts: skeleton rows while the shells are read, and a refused read said in the list.
Screenshots (dark, from the jsdom suites' markup over the built stylesheet) follow in a comment.
Invariants
-
./scripts/check.shgreen. (CI) - JSON Schema goldens unchanged (
LUKE_UPDATE_FIXTURESnot run). - Every behavior change has, in this PR, the test that fails without it; no change-detector test added.
- Gateway envelope goldens unchanged. (CI)
- No new
astype assertion outside the allowlisted files. (CI via anti-slop) - No
Effect.runPromise/runSync/runForkoutside the runtime edges. - No new
setTimeout/setInterval/new Promise/AbortController/fs.watchoutside the recorded files. The newnew Promisecalls are in test files, which hold answers open. - No Effect, Stream, or Layer written as a statement and dropped.
- Test count delta: +10 (automations-page 2, command-palette 4, terminal-tab 2, desktop-tasks 1, compose-tasks 1).
- No hand-rolled file replaced.
- No
CLAUDE.md/AGENTS.mdsentence names a changed part. -
PRIVACY.mdunchanged. - Package deps unchanged.
- Barrel/door rule untouched.
Notes
- Follow-up: a refused work read keeps whatever was held, and
TasksViewcarries no failure marker for it. So on a refused read the Code tab keeps spinning until the next refresh or open reads the work again. Saying the read was refused would need a failure field in theTasksViewwire schema. That's left out of this PR, as agreed with the orchestrator. compose-tasks.ts,use-workspace-members.ts,use-side-panel.ts(a comment) andsidebar-pinned-tab.tsxare outside the files this PR was first given. The orchestrator approved the first two, and no other PR in the rollout owns any of them.
Made with Luke
Summary by cubic
Stops four small panels from claiming they are empty before their data arrives. Each now shows a reading state while a request is in flight, and says a read was refused instead of drawing nothing.
- The Code tab's spinner now distinguishes unread from "No code yet.": the host writes a task's shown code as
[]when the read finds none, soundefinedalone means unread. - The automation editor's MCP servers row shows a skeleton while reading and an ErrorState with Retry when refused.
- ⌘K's Tasks and People filters say they are reading until their lists answer, and offer Retry when refused; under All, the first still-reading or refused kind explains an empty list.
- The Terminal tab's Shells list shows skeleton rows while reading and an ErrorState when refused.