|
| 1 | +--- |
| 2 | +name: Worktree Management Surface Design |
| 3 | +description: Replace the dormant worktree button with a visible Git-panel worktree summary and manager across desktop and mobile |
| 4 | +type: project |
| 5 | +--- |
| 6 | + |
| 7 | +# Worktree Management Surface Design |
| 8 | + |
| 9 | +## Overview |
| 10 | + |
| 11 | +The repository already has most of the hard worktree plumbing: server commands for `list/status/diff/tree/create/remove`, a front-end `worktreeListAtomFamily`, live refresh wiring in `providers.tsx`, and a detail viewer (`WorktreeModal`). The problem is discoverability and placement. The original header button was both redundant and badly placed, so it was removed. That left the capability effectively hidden even though the data and modal still exist. |
| 12 | + |
| 13 | +This spec replaces the orphaned header button with a visible but scoped worktree management surface inside the existing Git panel. The first visible layer should show basic facts at a glance; the second layer should support viewing details and managing worktrees. |
| 14 | + |
| 15 | +## Goals |
| 16 | + |
| 17 | +1. Make worktree information visible in the Git workflow on both desktop and mobile. |
| 18 | +2. Show basic worktree state at a glance: total count, dirty count, and current worktree. |
| 19 | +3. Provide a management flow for list, details, create, and delete. |
| 20 | +4. Reuse the existing worktree commands, atoms, and detail-loading logic instead of creating a parallel subsystem. |
| 21 | +5. Remove the dormant `WorktreeListButton` entry path and its tests once the replacement surface exists. |
| 22 | + |
| 23 | +## Non-Goals |
| 24 | + |
| 25 | +- Folding worktree management into the branch quick-pick main flow. |
| 26 | +- Opening another worktree as the current workspace in this first version. |
| 27 | +- Worktree rename, prune, repair, or advanced git maintenance actions. |
| 28 | +- Editing files or staging changes from the worktree detail viewer. |
| 29 | + |
| 30 | +## Product Decision |
| 31 | + |
| 32 | +### Why not the branch switcher |
| 33 | + |
| 34 | +The branch switcher has a narrow job: change branches quickly. The requested capability is broader: inspect multiple worktrees and manage their lifecycle. Mixing those concerns would make the branch surface heavier, harder to scan, and harder to reason about. The entry point should live near Git management, not near quick branch selection. |
| 35 | + |
| 36 | +### Chosen location |
| 37 | + |
| 38 | +Place the worktree entry at the top of `GitPanel`, above the commit box and file change groups. This has three advantages: |
| 39 | + |
| 40 | +1. It is visible without crowding the global Git status bar. |
| 41 | +2. `GitPanel` is already shared by desktop and mobile, so one placement decision serves both layouts. |
| 42 | +3. The feature stays inside the Git context, which matches the user’s mental model for “view/manage multiple worktrees.” |
| 43 | + |
| 44 | +## User Experience |
| 45 | + |
| 46 | +### 1. Summary card in `GitPanel` |
| 47 | + |
| 48 | +Add a `WorktreesSummaryCard` as the first section inside `GitPanel`. |
| 49 | + |
| 50 | +Displayed information: |
| 51 | + |
| 52 | +- `Worktrees`: total number of worktrees in the repo. |
| 53 | +- `Dirty`: number of worktrees whose status is not clean. |
| 54 | +- `Current`: the worktree whose `path` matches the active workspace path. |
| 55 | + |
| 56 | +Primary actions: |
| 57 | + |
| 58 | +- `Manage`: opens the full worktree manager. |
| 59 | +- `New`: opens the create-worktree flow directly. |
| 60 | + |
| 61 | +The card should stay compact. It is a summary and launch surface, not a full list. If loading fails, show a small inline error state and keep `Manage` available for retry. |
| 62 | + |
| 63 | +### 2. Manager surface |
| 64 | + |
| 65 | +Open a dedicated manager overlay from `Manage`. |
| 66 | + |
| 67 | +- Desktop: modal card. |
| 68 | +- Mobile: sheet. |
| 69 | + |
| 70 | +The manager should have two internal views instead of stacking multiple overlays: |
| 71 | + |
| 72 | +- `list` view |
| 73 | +- `detail` view |
| 74 | + |
| 75 | +This avoids modal-on-modal behavior and keeps navigation predictable on mobile. |
| 76 | + |
| 77 | +`list` view contents: |
| 78 | + |
| 79 | +- Header with title and close/back control. |
| 80 | +- Primary `New` action. |
| 81 | +- Worktree rows showing: |
| 82 | + - `name` |
| 83 | + - `branch` |
| 84 | + - `path` |
| 85 | + - `clean/dirty` |
| 86 | + - `current` badge when the row matches the active workspace path |
| 87 | +- Row tap/click opens `detail` view for that worktree. |
| 88 | +- Row-level delete affordance for non-current worktrees. |
| 89 | + |
| 90 | +### 3. Detail view |
| 91 | + |
| 92 | +The current `WorktreeModal` already supports `status / diff / tree`. That capability should remain, but the content should be reused inside the new manager instead of only being reachable from the old button flow. |
| 93 | + |
| 94 | +Detail view should show: |
| 95 | + |
| 96 | +- path |
| 97 | +- branch |
| 98 | +- latest commit sha/subject |
| 99 | +- clean/dirty state |
| 100 | +- tabbed content: |
| 101 | + - `Status` |
| 102 | + - `Diff` |
| 103 | + - `Tree` |
| 104 | + |
| 105 | +If the selected worktree disappears externally while detail view is open, return to the list view and show a transient error or empty-state message. |
| 106 | + |
| 107 | +### 4. Create flow |
| 108 | + |
| 109 | +The first version must support creation, but it should stay simple. |
| 110 | + |
| 111 | +Fields: |
| 112 | + |
| 113 | +- `Branch` (required) |
| 114 | +- `Path` (required, prefilled with a suggested absolute path, editable) |
| 115 | + |
| 116 | +Path behavior: |
| 117 | + |
| 118 | +- Use the active workspace path to derive a default sibling path. |
| 119 | +- Suggestion format: `<workspace-parent>/<workspace-name>-<slugified-branch>`. |
| 120 | +- The user may edit the path before submission. |
| 121 | + |
| 122 | +Validation: |
| 123 | + |
| 124 | +- branch is required |
| 125 | +- path is required |
| 126 | +- path should be absolute before enabling submit |
| 127 | + |
| 128 | +On success: |
| 129 | + |
| 130 | +- close the create dialog/sheet |
| 131 | +- refresh the worktree list immediately |
| 132 | +- return the manager to list mode |
| 133 | +- surface a success toast |
| 134 | + |
| 135 | +### 5. Delete flow |
| 136 | + |
| 137 | +Delete must be guarded. |
| 138 | + |
| 139 | +Rules: |
| 140 | + |
| 141 | +- The current worktree cannot be deleted from this UI. |
| 142 | +- Clean, non-current worktrees require a standard destructive confirmation. |
| 143 | +- Dirty, non-current worktrees require an explicit `Force remove` confirmation path. |
| 144 | + |
| 145 | +On success: |
| 146 | + |
| 147 | +- refresh the worktree list immediately |
| 148 | +- if the deleted row was open in detail view, return to the list view |
| 149 | +- surface a success toast |
| 150 | + |
| 151 | +## Architecture |
| 152 | + |
| 153 | +### Web reuse |
| 154 | + |
| 155 | +Keep and reuse: |
| 156 | + |
| 157 | +- `worktreeListAtomFamily` in `packages/web/src/features/workspace/atoms/git.ts` |
| 158 | +- provider-driven refresh logic in `packages/web/src/app/providers.tsx` |
| 159 | +- `useWorktreeActions` data loading for `status/diff/tree` |
| 160 | +- existing worktree commands exposed by the server |
| 161 | + |
| 162 | +Remove as an entry point: |
| 163 | + |
| 164 | +- `packages/web/src/features/workspace/views/shared/worktree-list-button.tsx` |
| 165 | +- its tests once the replacement surface is covered |
| 166 | + |
| 167 | +### Web component structure |
| 168 | + |
| 169 | +Recommended structure: |
| 170 | + |
| 171 | +- `GitPanel` |
| 172 | + - renders `WorktreesSummaryCard` |
| 173 | + - renders existing commit + change UI |
| 174 | +- `WorktreesSummaryCard` |
| 175 | + - loads `worktree.list` on first visible use if atom is empty/stale |
| 176 | + - computes summary metrics |
| 177 | + - opens manager or create flow |
| 178 | +- `WorktreeManagerSurface` |
| 179 | + - owns `list/detail/create/delete-confirm` local view state |
| 180 | + - adapts to desktop modal vs mobile sheet chrome |
| 181 | +- `WorktreeDetailPanel` |
| 182 | + - extracted from `WorktreeModal` so detail content can be embedded in the manager |
| 183 | + |
| 184 | +`WorktreeModal` should either be removed entirely or reduced to a very thin wrapper around `WorktreeDetailPanel` if any caller still needs it during migration. The important part is that detail rendering must stop depending on the deleted header button flow. |
| 185 | + |
| 186 | +### Current worktree detection |
| 187 | + |
| 188 | +No core type change is required for the first version. |
| 189 | + |
| 190 | +The UI can determine the current worktree by comparing: |
| 191 | + |
| 192 | +- active workspace `path` |
| 193 | +- `WorktreeInfo.path` |
| 194 | + |
| 195 | +This keeps the change localized to the web layer. |
| 196 | + |
| 197 | +## Server-side adjustments |
| 198 | + |
| 199 | +The existing server commands are almost sufficient, but create/remove should integrate with the same refresh loop as the rest of the workspace Git state. |
| 200 | + |
| 201 | +Required change: |
| 202 | + |
| 203 | +- `worktree.create` should emit `git.state.changed { worktreeChanged: true }` |
| 204 | +- `worktree.remove` should emit `git.state.changed { worktreeChanged: true }` |
| 205 | + |
| 206 | +Why: |
| 207 | + |
| 208 | +- external `git worktree add/remove` is already picked up by metadata refresh |
| 209 | +- UI-triggered create/remove should be equally observable without requiring ad hoc reload logic everywhere |
| 210 | + |
| 211 | +Even with the event emission, the web manager should still refresh the list immediately after a successful create/remove so the user sees deterministic feedback without waiting for the debounce window. |
| 212 | + |
| 213 | +## Data Flow |
| 214 | + |
| 215 | +### Initial load |
| 216 | + |
| 217 | +1. User opens the Git tab. |
| 218 | +2. `GitPanel` renders `WorktreesSummaryCard`. |
| 219 | +3. If `worktreeListAtomFamily(workspaceId)` has never loaded, dispatch `worktree.list`. |
| 220 | +4. The card renders loading, then summary metrics. |
| 221 | + |
| 222 | +### Ongoing refresh |
| 223 | + |
| 224 | +After initial load: |
| 225 | + |
| 226 | +- provider refresh keeps the list up to date when `git_metadata` or `worktreeChanged` events arrive |
| 227 | +- create/remove success paths also trigger immediate local reload |
| 228 | + |
| 229 | +### Detail loading |
| 230 | + |
| 231 | +Selecting a row in the manager: |
| 232 | + |
| 233 | +- sets the selected `WorktreeInfo` |
| 234 | +- reuses `useWorktreeActions` to load `status/diff/tree` |
| 235 | +- refetches when the selected tab changes or the selected worktree changes |
| 236 | + |
| 237 | +## Error Handling |
| 238 | + |
| 239 | +- `worktree.list` failure: show inline error in summary card and full manager retry state. |
| 240 | +- `worktree.status/diff/tree` failure: show inline error in detail view without closing the manager. |
| 241 | +- `worktree.create` failure: keep the create form open and show the error inline. |
| 242 | +- `worktree.remove` failure: keep the confirmation open and show the error inline. |
| 243 | +- stale selection after external removal: bounce from detail view back to list with a notice. |
| 244 | + |
| 245 | +## Testing Strategy |
| 246 | + |
| 247 | +### Server |
| 248 | + |
| 249 | +- extend worktree command tests to assert that `worktree.create/remove` emit `worktreeChanged` |
| 250 | + |
| 251 | +### Web |
| 252 | + |
| 253 | +- summary card loads and displays total/dirty/current |
| 254 | +- summary card appears inside `GitPanel` on both desktop and mobile |
| 255 | +- manager list shows row metadata and current badge |
| 256 | +- manager opens detail view and reuses `status/diff/tree` |
| 257 | +- create success refreshes the list and shows the new row |
| 258 | +- delete is disabled/hidden for the current worktree |
| 259 | +- dirty delete requires force confirmation |
| 260 | +- old desktop header entry does not return |
| 261 | + |
| 262 | +## Migration Notes |
| 263 | + |
| 264 | +This is a replacement, not an additive entry point. The final state should have exactly one visible worktree management surface: |
| 265 | + |
| 266 | +- visible summary in `GitPanel` |
| 267 | +- manager overlay behind that summary |
| 268 | +- no duplicate status-bar chip |
| 269 | +- no duplicate branch-header button |
| 270 | + |
| 271 | +That keeps the feature discoverable without repeating the same action in multiple places. |
0 commit comments