Skip to content

feat(rail): labeled sections + starred workspaces in the workspace rail#116

Merged
juacker merged 1 commit into
mainfrom
feat/workspace-rail-sections
Jul 23, 2026
Merged

feat(rail): labeled sections + starred workspaces in the workspace rail#116
juacker merged 1 commit into
mainfrom
feat/workspace-rail-sections

Conversation

@juacker

@juacker juacker commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Redesigns the left workspace rail around explicit, labeled sections (Claude Desktop-style) and adds a star workspace feature.

Problem

The rail merged three invisible sort keys — attention → scheduled → recency — into one flat, unlabeled list. Scheduled workspaces always floated to the top with nothing explaining why, and long lists were hard to scan (juacker feedback, 2026-07-23).

Design (mirrors claude.ai's Starred / Recents sidebar)

  1. Needs attention — pending approvals, failed or blocked tasks (only rendered when non-empty; outranks the star)
  2. ★ Starred — user-pinned workspaces (new)
  3. Recent — everything else, most-recently-updated first
  • Scheduled workspaces no longer jump the queue: the cadence (every 5m / cron) stays as a per-row label — an attribute, not a position.
  • Headers appear only when grouping is in effect; a plain recency list stays headerless. The collapsed rail separates sections with thin dividers.
  • Star via hover/focus star button or the ⋯ menu; row actions are now keyboard-reachable (:focus-within).

Implementation

  • WorkspaceConfig.starred_at: i64 (#[serde(default)], 0 = unstarred; timestamp for future "recently starred" sorts), mirrored on WorkspaceLocator, exposed as starred: bool on WorkspaceListEntry.
  • New workspace_set_starred command via the atomic update_workspace_config_for_id helper. Deliberately does not bump updated_at, so starring never reorders the recency sort. Forks start unstarred.
  • FE: setWorkspaceStarred client fn, optimistic toggle with revert in FleetLayout, section partition + headers in WorkspaceRail.

Tests

  • Rust: starred_at legacy-config default + atomic-update roundtrip (cargo test --lib: 803 pass).
  • FE: 8 new WorkspaceRail.test.tsx cases — headerless plain list, scheduled no longer floats, section ordering, attention-outranks-star, hover + menu star toggles, cross-section filter, collapsed headers, lone-Starred header (vitest: 179 pass).
  • tsc / eslint / clippy clean.

Review

3 independent reviews (Codex, Minimax, Claude): all production_quality, zero blocker/major. Applied minors: :focus-within keyboard access, lone-Starred-header test. Accepted (documented) minors: optimistic-toggle vs 5s-poll flicker (pre-existing pattern shared with pause toggle); rapid-toggle last-write-converges. Pre-existing (out of scope, logged in TODO): bare load→save writers in permissions.rs can clobber concurrent config writes; empty-workspaceId fallback in resolve_workspace_id.

The rail previously merged three invisible sort keys (attention, then
scheduled, then recency) into one flat list — scheduled workspaces
always floated to the top with nothing explaining why, and long lists
were hard to scan.

Redesign it around explicit, labeled sections (Claude-style):

  1. Needs attention — pending approvals, failed or blocked tasks
  2. Starred — user-pinned workspaces (new)
  3. Recent — everything else, most-recently-updated first

Scheduled workspaces no longer jump the queue: their cadence renders as
a per-row label (an attribute, not a position). Headers only appear
when grouping is in effect; a bare recency list stays headerless, and
the collapsed rail separates sections with thin dividers instead.

Starring: hover star toggle + overflow-menu item per row, persisted as
WorkspaceConfig.starred_at (timestamp, 0 = unstarred) via the new
workspace_set_starred command. Starring deliberately does not bump
updated_at, so it never reorders the recency sort. Forks start
unstarred. The flag is mirrored onto WorkspaceLocator so
workspace_list derives it without re-reading configs.
@juacker
juacker marked this pull request as ready for review July 23, 2026 08:14
@juacker
juacker merged commit 3c451ea into main Jul 23, 2026
2 checks passed
juacker added a commit that referenced this pull request Jul 23, 2026
…concurrent config writes (#118)

persist_decisions_to_agent wrote the workspace config via a bare
load->mutate->save, bypassing the process-wide writer lock in
workspace_config::update(). A permission decision racing another config
write (starring a workspace, re-anchoring a schedule) could silently
discard that write and refresh the workspace index from the stale
snapshot — the same lost-update class already fixed for mark_opened in
commands/workspace.rs.

- Run the read-modify-write inside workspace_config::update() and refresh
  the index from the config returned under the lock.
- Fast path: skip the lock + disk round-trip entirely when decisions
  contain nothing durable (Once-only, or Always with blank prefixes).
- Extract the list surgery into apply_decisions_to_shell_policy(), a pure
  function over WorkspaceAgent, and unit-test it: allow/deny list moves,
  idempotency (no change reported on re-apply, no duplicates), trimming,
  and no-op decisions.

Found by Codex during the PR #116 review.
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.

1 participant