Commit a95d027
authored
🤖 Add stable workspace IDs (#259)
## Summary
Workspaces now use stable, unique IDs (10 hex chars) instead of deriving
IDs from paths. This simplifies workspace renames from 150 lines of
complex migration logic to ~60 lines of metadata updates.
## Changes
**Core Implementation:**
- Generate stable IDs at workspace creation using
`crypto.randomBytes(5).toString('hex')`
- Separate `id` (stable, immutable) from `name` (mutable, user-facing)
- Add symlinks for UX: `~/.cmux/src/<project>/<name>` → `<id>`
- Automatic migration for legacy workspaces on startup
**Workspace Rename Simplified:**
- Before: Move session dir, migrate message IDs, move worktree, complex
rollback (150 lines)
- After: Update metadata name field and symlink (60 lines)
- Workspace ID never changes during rename
**File Structure:**
```
# New workspace
~/.cmux/src/cmux/a1b2c3d4e5/ # Worktree (stable ID)
~/.cmux/src/cmux/feature-branch → a1b2c3d4e5 # Symlink
~/.cmux/sessions/a1b2c3d4e5/ # Session data
# Legacy workspace (unchanged)
~/.cmux/src/cmux/stable-ids/ # Worktree
~/.cmux/sessions/cmux-stable-ids/ # Session data
```
## Implementation Details
**Type Changes:**
- Added `WorkspaceMetadata.name` field (separate from `id`)
- Added `WorkspaceMetadata.createdAt` timestamp (optional for backward
compat)
**Config Module:**
- `generateStableId()`: Create 10-char hex IDs
- `createWorkspaceSymlink()`, `updateWorkspaceSymlink()`,
`removeWorkspaceSymlink()`: Manage symlinks
- `getAllWorkspaceMetadata()`: Eager migration for legacy workspaces
**Git Operations:**
- Added `workspaceId` parameter to `CreateWorktreeOptions`
- Use stable ID for worktree directory name
**IPC Handlers:**
- Workspace creation generates stable ID before creating worktree
- Rename updates metadata + symlink only (ID unchanged)
- Remove cleans up symlinks
**Frontend:**
- Pass `metadata.name` to WorkspaceListItem for display
- Removed path parsing logic
## Testing
- ✅ **511 unit tests** pass
- ✅ **8 rename integration tests** pass
- ✅ **5 remove integration tests** pass (including new symlink cleanup
test)
- ✅ **9 config unit tests** (new)
## Simplifications Applied
1. **Removed unused `WorkspaceMetadataUI` type alias** (-3 lines)
2. **Simplified `updateWorkspaceSymlink`** by reusing existing methods
(-16 lines)
3. **Improved `removeWorkspaceSymlink`** robustness:
- Fixed TOCTOU vulnerability (atomic check + operation)
- Use `lstat` to avoid following symlinks
- Handle ENOENT gracefully
4. **Added symlink cleanup on workspace removal** (+10 lines, bug fix)
**Net change:** +368 lines total (+536 additions, -168 deletions), +150
product code
## Benefits
- **Instant renames**: No file moves, just metadata update
- **Simpler code**: Removed 90 lines of complex migration/rollback logic
- **Better UX**: Symlinks let users navigate by readable names
- **Stable references**: Chat history, config stay valid across renames
- **Future-proof**: Enables workspace aliases, templates, cross-project
refs
_Generated with `cmux`_1 parent 03b3e18 commit a95d027
File tree
35 files changed
+1205
-772
lines changed- src
- components
- hooks
- services
- stores
- types
- utils/commands
- tests
- e2e/utils
- ipcMain
35 files changed
+1205
-772
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
329 | 329 | | |
330 | 330 | | |
331 | 331 | | |
| 332 | + | |
332 | 333 | | |
333 | 334 | | |
334 | 335 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
172 | 173 | | |
173 | 174 | | |
174 | 175 | | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | | - | |
179 | | - | |
180 | | - | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
181 | 187 | | |
182 | 188 | | |
183 | 189 | | |
| |||
215 | 221 | | |
216 | 222 | | |
217 | 223 | | |
218 | | - | |
219 | | - | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
220 | 228 | | |
221 | 229 | | |
222 | 230 | | |
| |||
225 | 233 | | |
226 | 234 | | |
227 | 235 | | |
228 | | - | |
| 236 | + | |
229 | 237 | | |
230 | 238 | | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
231 | 242 | | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
232 | 249 | | |
233 | 250 | | |
234 | 251 | | |
235 | 252 | | |
236 | 253 | | |
237 | | - | |
| 254 | + | |
238 | 255 | | |
239 | 256 | | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
254 | 286 | | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
255 | 296 | | |
256 | 297 | | |
257 | | - | |
258 | | - | |
259 | | - | |
| 298 | + | |
260 | 299 | | |
261 | | - | |
262 | | - | |
263 | | - | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
264 | 310 | | |
265 | 311 | | |
266 | 312 | | |
| |||
364 | 410 | | |
365 | 411 | | |
366 | 412 | | |
| 413 | + | |
367 | 414 | | |
368 | 415 | | |
369 | 416 | | |
370 | | - | |
| 417 | + | |
371 | 418 | | |
372 | | - | |
373 | | - | |
374 | | - | |
375 | | - | |
376 | | - | |
377 | | - | |
378 | | - | |
379 | | - | |
380 | | - | |
381 | | - | |
382 | | - | |
383 | | - | |
384 | | - | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
385 | 432 | | |
386 | 433 | | |
387 | 434 | | |
388 | 435 | | |
389 | | - | |
| 436 | + | |
390 | 437 | | |
391 | 438 | | |
392 | 439 | | |
393 | | - | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
394 | 446 | | |
395 | 447 | | |
396 | 448 | | |
| |||
410 | 462 | | |
411 | 463 | | |
412 | 464 | | |
413 | | - | |
| 465 | + | |
414 | 466 | | |
415 | 467 | | |
416 | 468 | | |
| |||
422 | 474 | | |
423 | 475 | | |
424 | 476 | | |
425 | | - | |
426 | | - | |
427 | | - | |
428 | | - | |
429 | | - | |
| 477 | + | |
| 478 | + | |
430 | 479 | | |
431 | 480 | | |
432 | 481 | | |
433 | 482 | | |
434 | | - | |
435 | | - | |
| 483 | + | |
| 484 | + | |
436 | 485 | | |
437 | 486 | | |
438 | | - | |
| 487 | + | |
439 | 488 | | |
440 | 489 | | |
441 | 490 | | |
| |||
534 | 583 | | |
535 | 584 | | |
536 | 585 | | |
537 | | - | |
538 | | - | |
539 | | - | |
540 | | - | |
541 | | - | |
542 | | - | |
| 586 | + | |
543 | 587 | | |
544 | 588 | | |
545 | 589 | | |
| |||
679 | 723 | | |
680 | 724 | | |
681 | 725 | | |
682 | | - | |
| 726 | + | |
683 | 727 | | |
684 | | - | |
| 728 | + | |
685 | 729 | | |
686 | 730 | | |
687 | 731 | | |
688 | 732 | | |
689 | 733 | | |
690 | 734 | | |
691 | | - | |
692 | | - | |
693 | | - | |
| 735 | + | |
| 736 | + | |
694 | 737 | | |
695 | | - | |
| 738 | + | |
696 | 739 | | |
697 | 740 | | |
698 | 741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
196 | | - | |
| 196 | + | |
197 | 197 | | |
198 | 198 | | |
199 | 199 | | |
200 | 200 | | |
201 | 201 | | |
202 | 202 | | |
203 | 203 | | |
204 | | - | |
| 204 | + | |
205 | 205 | | |
206 | 206 | | |
207 | 207 | | |
| |||
311 | 311 | | |
312 | 312 | | |
313 | 313 | | |
314 | | - | |
315 | | - | |
| 314 | + | |
| 315 | + | |
316 | 316 | | |
317 | 317 | | |
318 | 318 | | |
| |||
443 | 443 | | |
444 | 444 | | |
445 | 445 | | |
446 | | - | |
| 446 | + | |
447 | 447 | | |
448 | 448 | | |
449 | 449 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| |||
0 commit comments