feat(desktop): rework Workspaces page into a status-grouped triage list

Merged
KI
3mo ago
+1072-605

Summary

  • Rebuilds the /v2-workspaces list 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.getDiffStatsByWorkspaces host-service query (one request per host) so every row can show real +/− churn without per-workspace getStatus fan-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: deriveBoardColumn promoted from the board's private utils to v2-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 shared isWithinArchivedWindow.
  • Data: lastAgentEventAt and agentIds are derived from the existing host-wide terminalAgents.list query (no new requests); diff totals come from the new batch procedure, which reuses the exact limiter-admitted snapshot path as getStatus (extracted runStatusSnapshot) 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 BoardColumnIcon for 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 via usePresetIcon, 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: sortMode in the filter store (Last activity default / Created / Diff size / Name) applied inside every section in both views via shared compareWorkspaces; 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 useV2ProjectLocalMetaStore with status:<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 typecheck
  • biome check on the touched trees
  • bun test — deriveBoardColumn suite 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 createdAt for workspaces where no agent ever ran (host workspace.list doesn't expose updatedAt yet).
  • Raw status colors (amber-500 etc.) follow the app's existing convention; a semantic-token sweep is tracked separately as design debt.

Follow-ups

  • Surface updatedAt from host workspace.list for 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 deriveBoardColumn and compareWorkspaces across 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 shared runStatusSnapshot at background priority; derive lastAgentEventAt and agentIds from existing terminalAgents.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 via status:<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.

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.