Commit 8ad31d8
committed
Add stable workspace IDs
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.
**Stable ID Generation:**
- Generate at workspace creation: `crypto.randomBytes(5).toString('hex')`
- Separate `id` (stable, immutable) from `name` (mutable, user-facing)
- Add symlinks for UX: `~/.cmux/src/<project>/<name>` → `<id>`
**Type System:**
- `WorkspaceMetadata`: Backend type with stable ID, no path field
- `WorkspaceMetadataWithPaths`: Frontend type with computed paths
- IPC layer enriches metadata with `stableWorkspacePath` and `namedWorkspacePath`
**Workspace Operations:**
- Create: Generate stable ID before creating worktree
- Rename: Update metadata + symlink only (ID unchanged, ~60 lines)
- Remove: Clean up worktree, session data, and symlinks
**Frontend Integration:**
- Build `pathToMetadata` map for lookups (handles both stable and legacy)
- Use map lookups instead of parsing workspace IDs from paths
- Support both new stable-ID workspaces and legacy name-based workspaces
```
~/.cmux/src/cmux/a1b2c3d4e5/ # Worktree (stable ID)
~/.cmux/src/cmux/feature-branch → a1b2c3d4e5 # Symlink
~/.cmux/sessions/a1b2c3d4e5/ # Session data
~/.cmux/src/cmux/stable-ids/ # Worktree
~/.cmux/sessions/cmux-stable-ids/ # Session data
```
- **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
- ✅ 511 unit tests pass
- ✅ 8 rename integration tests pass
- ✅ 5 remove integration tests pass
- ✅ 13 E2E tests pass
- ✅ 9 new config unit tests
Fix stuck loading state for deleted/invalid workspaces
When a workspace is deleted or doesn't exist:
- Previously: selectedWorkspace persisted in localStorage, causing eternal "Loading workspace..."
- Now: Validate workspace exists on mount and clear invalid selection
Added validation effect that:
- Checks if selected workspace ID exists in workspaceMetadata
- Clears selection if workspace was deleted
- Also clears URL hash to prevent re-selection on reload
Fixes edge cases:
- Workspace deleted while app was closed
- URL with invalid workspace ID (#workspace=invalid-id)
- Workspace removed from another instance
Remove rename blocking during streaming
With stable IDs, workspace rename no longer requires moving files or
changing workspace ID. Rename only updates:
- metadata.name (display name)
- Symlink (~/.cmux/src/project/name → workspaceId)
Session directory (~/.cmux/sessions/workspaceId) remains unchanged,
so active streams can continue writing safely.
Changes:
- Remove isStreaming check from WORKSPACE_RENAME handler
- Remove "should block rename during active stream" test
- Simplifies UX: no more "Press Esc first" error
Benefits:
- Users can organize workspaces without interrupting work
- One less artificial limitation
- Cleaner, simpler code (-38 lines)
Fix integration test race condition with AI SDK dynamic imports
Integration tests were failing in CI with:
"Failed to create model: ReferenceError: You are trying to `import` a
file outside of the scope of the test code."
This only occurred when multiple tests ran concurrently in CI, not locally.
AI SDK providers use dynamic imports for lazy loading (to optimize startup
time from 6-13s → 3-6s). Under high concurrency in CI (8 workers × 11 test
files × concurrent tests within files), Jest/Bun's module resolution has a
race condition where multiple simultaneous dynamic imports of the same
module can fail.
Preload AI SDK providers once during test setup, similar to how we preload
tokenizer modules. This ensures subsequent dynamic imports hit the module
cache instead of racing.
- Added `preloadAISDKProviders()` function to aiService.ts
- Called during `setupWorkspace()` alongside `loadTokenizerModules()`
- Preserves lazy loading in production (startup optimization)
- Eliminates race condition in concurrent test environment
- ✅ Tests pass locally with concurrent execution
- ✅ No impact on production startup time (preload only in tests)
- ✅ No changes to test behavior, only timing/reliability
Fixes the flaky integration test failures in PR #259.
Fix formatting
Refactor: eliminate pathToMetadata code smell
1. Rename type: WorkspaceMetadataWithPaths → FrontendWorkspaceMetadata
2. sortedWorkspacesByProject returns metadata arrays directly
3. Removed duplicate pathToMetadata maps from App.tsx and ProjectSidebar
4. WorkspaceListItem accepts metadata object (6 props → 1)
5. Updated keyboard navigation to work with metadata
- Net: -23 lines (removed duplicate logic)
- Clearer data flow: pass data, not lookup maps
- Simpler component API: metadata object vs 6 props
16 files changed, 124 insertions(+), 147 deletions(-)
Fix lint errors: remove unused imports and params1 parent 03b3e18 commit 8ad31d8
File tree
29 files changed
+970
-558
lines changed- src
- components
- hooks
- services
- stores
- types
- utils/commands
- tests
- e2e/utils
- ipcMain
29 files changed
+970
-558
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
148 | 149 | | |
149 | 150 | | |
150 | 151 | | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | 152 | | |
159 | 153 | | |
160 | 154 | | |
| |||
237 | 231 | | |
238 | 232 | | |
239 | 233 | | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
255 | 241 | | |
256 | 242 | | |
257 | 243 | | |
258 | 244 | | |
259 | 245 | | |
260 | 246 | | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
261 | 264 | | |
262 | 265 | | |
263 | 266 | | |
| |||
272 | 275 | | |
273 | 276 | | |
274 | 277 | | |
275 | | - | |
276 | | - | |
277 | | - | |
278 | | - | |
| 278 | + | |
279 | 279 | | |
280 | | - | |
281 | | - | |
282 | | - | |
283 | | - | |
284 | 280 | | |
285 | | - | |
286 | | - | |
287 | | - | |
288 | | - | |
289 | | - | |
290 | | - | |
291 | | - | |
292 | | - | |
293 | | - | |
294 | | - | |
295 | | - | |
296 | | - | |
297 | | - | |
298 | | - | |
299 | | - | |
300 | | - | |
301 | | - | |
302 | | - | |
303 | | - | |
304 | | - | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
314 | 281 | | |
315 | 282 | | |
316 | 283 | | |
| |||
364 | 331 | | |
365 | 332 | | |
366 | 333 | | |
| 334 | + | |
367 | 335 | | |
368 | 336 | | |
369 | 337 | | |
370 | | - | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
371 | 346 | | |
372 | | - | |
373 | | - | |
374 | | - | |
375 | | - | |
376 | | - | |
377 | | - | |
378 | | - | |
379 | | - | |
380 | | - | |
381 | | - | |
382 | | - | |
383 | | - | |
384 | | - | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
385 | 360 | | |
386 | 361 | | |
387 | 362 | | |
| |||
390 | 365 | | |
391 | 366 | | |
392 | 367 | | |
393 | | - | |
| 368 | + | |
394 | 369 | | |
395 | 370 | | |
396 | 371 | | |
| |||
410 | 385 | | |
411 | 386 | | |
412 | 387 | | |
413 | | - | |
| 388 | + | |
414 | 389 | | |
415 | 390 | | |
416 | 391 | | |
| |||
422 | 397 | | |
423 | 398 | | |
424 | 399 | | |
425 | | - | |
426 | | - | |
427 | | - | |
428 | | - | |
429 | | - | |
| 400 | + | |
| 401 | + | |
430 | 402 | | |
431 | 403 | | |
432 | 404 | | |
433 | 405 | | |
434 | | - | |
435 | | - | |
| 406 | + | |
| 407 | + | |
436 | 408 | | |
437 | 409 | | |
438 | | - | |
| 410 | + | |
439 | 411 | | |
440 | 412 | | |
441 | 413 | | |
| |||
495 | 467 | | |
496 | 468 | | |
497 | 469 | | |
498 | | - | |
| 470 | + | |
499 | 471 | | |
500 | 472 | | |
501 | 473 | | |
| |||
679 | 651 | | |
680 | 652 | | |
681 | 653 | | |
682 | | - | |
| 654 | + | |
683 | 655 | | |
684 | | - | |
| 656 | + | |
685 | 657 | | |
686 | 658 | | |
687 | 659 | | |
688 | 660 | | |
689 | 661 | | |
690 | | - | |
691 | | - | |
692 | | - | |
693 | | - | |
694 | | - | |
| 662 | + | |
695 | 663 | | |
696 | 664 | | |
697 | 665 | | |
| |||
712 | 680 | | |
713 | 681 | | |
714 | 682 | | |
715 | | - | |
716 | | - | |
717 | | - | |
718 | | - | |
| 683 | + | |
719 | 684 | | |
720 | | - | |
721 | 685 | | |
722 | 686 | | |
723 | | - | |
724 | | - | |
725 | | - | |
726 | | - | |
727 | 687 | | |
728 | 688 | | |
729 | 689 | | |
| |||
| 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 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
| 5 | + | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
471 | 471 | | |
472 | 472 | | |
473 | 473 | | |
474 | | - | |
| 474 | + | |
475 | 475 | | |
476 | 476 | | |
477 | 477 | | |
| |||
491 | 491 | | |
492 | 492 | | |
493 | 493 | | |
494 | | - | |
| 494 | + | |
495 | 495 | | |
496 | 496 | | |
497 | 497 | | |
498 | 498 | | |
499 | | - | |
500 | 499 | | |
501 | 500 | | |
502 | 501 | | |
| |||
820 | 819 | | |
821 | 820 | | |
822 | 821 | | |
823 | | - | |
824 | | - | |
825 | | - | |
826 | | - | |
827 | | - | |
828 | | - | |
829 | | - | |
830 | | - | |
831 | | - | |
832 | | - | |
833 | | - | |
| 822 | + | |
| 823 | + | |
834 | 824 | | |
835 | 825 | | |
836 | 826 | | |
837 | | - | |
838 | | - | |
839 | | - | |
| 827 | + | |
| 828 | + | |
840 | 829 | | |
841 | 830 | | |
842 | 831 | | |
| |||
0 commit comments