feat(desktop): rework Workspaces page into a status-grouped triage list
Summary
- Rebuilds the
/v2-workspaceslist from a project-grouped table into a Linear-style, status-grouped triage list: Needs attention → Working → Needs review → Idle → Merged, with per-row state glyphs, diff stats, PR checks progress, agent attribution, and sort modes shared with the board view. - Adds a batched
git.getDiffStatsByWorkspaceshost-service query (one request per host) so every row can show real +/− churn without per-workspacegetStatusfan-out. - Fixes the review bucket's biggest lie: a finished agent alone only counts as "Needs review" on worktree workspaces, so automation session runs no longer bury actual review candidates (15 → 6 rows on real data).
Why / Context
Design research against Cursor Cloud Agents, Linear, and Vercel (plus a code audit of fetched-but-never-rendered data) showed the old table answered "what exists" but not "what needs me": a Host column repeating one constant value (the page is single-host-scoped by construction), a dead default sort on that constant, creator rendered as the literal string "unknown", agent status fetched for filtering but never shown on rows, and no diff/churn signal anywhere. A subagent design-critique pass on the first iteration drove the review-bucket fix, muted color discipline, and header/row distinction.
How It Works
- Grouping:
deriveBoardColumnpromoted from the board's private utils tov2-workspaces/utils/and shared by both views; the list walks the same buckets urgency-first. Archived tombstones ride along in both views, scoped by the (now list-enabled) archived-window control via sharedisWithinArchivedWindow. - Data:
lastAgentEventAtandagentIdsare derived from the existing host-wideterminalAgents.listquery (no new requests); diff totals come from the new batch procedure, which reuses the exact limiter-admitted snapshot path asgetStatus(extractedrunStatusSnapshot) at background priority with a 4-way local cap, skipping missing worktrees instead of failing the batch. - Rows: state glyph (pulse for live agents, app-standard green review dot, shared
BoardColumnIconfor idle/merged/deleted), run-stamp suffix on session rows to disambiguate identical automation names, PR pill whose checks indicator becomes a partial-fill ring while CI runs (completed/relevant, skipped/cancelled excluded), agent logos viausePresetIcon, and fixed-width metadata slots (diff, branch, project, age) so columns align without table markup. Pin/delete are hover-only with reserved width (no layout shift). - Headers: sticky tinted bands with uppercase micro-labels (same convention as the sidebar's SESSIONS/PROJECTS), Linear-style workflow-pie icons (a deliberately different shape family from row glyphs), and the collapse chevron in the left padding gutter so the status icons stay in one column.
- Sorting:
sortModein the filter store (Last activity default / Created / Diff size / Name) applied inside every section in both views via sharedcompareWorkspaces; the visible age column shows activity time with creation + last-agent-event in the tooltip so it always matches the default order. - Collapse state reuses the registered
useV2ProjectLocalMetaStorewithstatus:<column>keys — a fixed six-key set, so the persisted map stays bounded per the localStorage policy.
Manual QA Checklist
Verified end-to-end on the dev desktop over CDP with real seeded workspace data (~54 workspaces, live agents, open PRs), at 1728 / 1100 / 760 px widths:
- Sections render urgency-first with correct counts; empty sections are skipped
- Working rows pulse; review rows show the same green dot as the sidebar; idle/merged use the shared icons
- Review section contains only PR-backed or worktree-review rows; automation sessions land in Idle
- Diff stats populate from the batch query (incl. compact "+13.6k" formatting); rows without churn stay blank
- Sort selector reorders live in both list and board (verified Diff size and Last activity)
- Session run-stamps render on duplicate automation rows
- Checks ring appears on a PR with running checks; success/failure keep check/X icons
- Section collapse persists; current workspace's section can't hide it; header focus ring correct
- Board view unchanged in behavior (columns, cards, archived window) with shared icons/sort applied
- Responsive: project name drops at <lg (icon stays), branch drops at <md; no hover layout shift
- Zero renderer console errors on fresh reload; no orphaned dev processes after testing
Testing
bunx tsc --noEmit(desktop) and host-service typecheckbiome checkon the touched treesbun test—deriveBoardColumnsuite extended for the new review rules (9 pass); host-service git router suite (86 pass)
Design Decisions
- Status grouping over project grouping: the sidebar already groups by project; a fleet page's job is triage. The board's existing bucket logic is reused so the two views can never disagree.
- Batch endpoint over per-row queries: the page deliberately refuses per-workspace fan-outs (existing pattern for PRs/agents); totals-only keeps the payload trivial and the numbers match the Changes tab exactly because the snapshot path is shared.
- Host column removed: with device scoping, every row shares one host; the device filter in the header already names it.
Known Limitations
- "Last activity" falls back to
createdAtfor workspaces where no agent ever ran (hostworkspace.listdoesn't exposeupdatedAtyet). - Raw status colors (
amber-500etc.) follow the app's existing convention; a semantic-token sweep is tracked separately as design debt.
Follow-ups
- Surface
updatedAtfrom hostworkspace.listfor true last-activity on agent-less workspaces - Collapse the filter row into a popover below
lg(three-row wrap at ~760px) - Extend the row treatment to board cards (remove their redundant host line)
Summary by cubic
Reworks the Workspaces page into a status‑grouped triage list and adds a batched diff‑stats query to remove per‑workspace getStatus fan‑out. This surfaces what needs attention first and keeps list and board in sync.
- Details
- Grouping and ordering: share
deriveBoardColumnandcompareWorkspacesacross list and board; list walks Attention → Working → Review → Idle → Merged → Deleted with "Last activity" as default. A finished agent alone counts as Review only on worktrees; open/draft/queued PRs always count. - Data and performance: add host‑service
git.getDiffStatsByWorkspaces(one request per host, 500‑ID cap server‑side; client slices), using sharedrunStatusSnapshotat background priority; derivelastAgentEventAtandagentIdsfrom existingterminalAgents.list. Archived‑window filtering is shared and now applies to both views. - UI and accessibility: section headers use
BoardColumnIcon; rows show state glyphs, PR pill with a partial‑fill checks ring (with accessible labels), agent logos, compact diff totals, and fixed‑width metadata. Section collapse persists viastatus:<column>keys and is disabled for the current workspace’s section; empty state keys off surviving sections and treats a non‑default archive window as an active filter.
- Grouping and ordering: share
Summary by CodeRabbit
New Features
- Added a redesigned workspace list organized by status, with collapsible sections, counts, sticky headers, and richer workspace details.
- Added sorting by activity, creation date, code churn, or name.
- Added archived-workspace filtering across board and list views.
- Added clearer status, agent, pull request, check-progress, and diff-stat indicators.
- Improved board column icons and workspace state presentation.
Bug Fixes
- Corrected review-state classification for main and session workspaces.
- Improved handling of pending pull-request checks and archived workspaces.