Skip to content

Commit aed4904

Browse files
committed
🤖 Block workspace rename during active streaming
Prevents race conditions when renaming while AI stream is active: - Bash tool processes would have stale cwd references - System message would contain incorrect workspace path - Git worktree move could conflict with active file operations Changes: - Check isStreaming() before allowing rename - Return clear error message to user - Add integration test verifying blocking behavior Rename succeeds immediately after stream completes. _Generated with `cmux`_
1 parent 10e9fb5 commit aed4904

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

src/services/ipcMain.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@ export class IpcMain {
266266
IPC_CHANNELS.WORKSPACE_RENAME,
267267
(_event, workspaceId: string, newName: string) => {
268268
try {
269+
// Block rename during active streaming to prevent race conditions
270+
// (bash processes would have stale cwd, system message would be wrong)
271+
if (this.aiService.isStreaming(workspaceId)) {
272+
return Err("Cannot rename workspace while AI stream is active. Please wait for the stream to complete.");
273+
}
274+
269275
// Validate workspace name
270276
const validation = validateWorkspaceName(newName);
271277
if (!validation.valid) {

tests/ipcMain/renameWorkspace.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,4 +434,56 @@ describeIntegration("IpcMain rename workspace integration tests", () => {
434434
},
435435
30000
436436
);
437+
438+
test.concurrent(
439+
"should fail to rename if workspace is currently streaming",
440+
async () => {
441+
const { env, workspaceId, tempGitRepo, branchName, cleanup } = await setupWorkspace("anthropic");
442+
try {
443+
// Add project and workspace to config via IPC
444+
await env.mockIpcRenderer.invoke(IPC_CHANNELS.PROJECT_CREATE, tempGitRepo);
445+
const projectsConfig = env.config.loadConfigOrDefault();
446+
const projectConfig = projectsConfig.projects.get(tempGitRepo);
447+
if (projectConfig) {
448+
const workspacePath = env.config.getWorkspacePath(tempGitRepo, branchName);
449+
projectConfig.workspaces.push({
450+
path: workspacePath,
451+
id: workspaceId,
452+
name: branchName
453+
});
454+
env.config.saveConfig(projectsConfig);
455+
}
456+
457+
// Start a stream (don't await - we want it running)
458+
sendMessageWithModel(
459+
env.mockIpcRenderer,
460+
workspaceId,
461+
"What is 2+2?" // Simple query that should complete quickly
462+
);
463+
464+
// Wait for stream to actually start
465+
const collector = createEventCollector(env.sentEvents, workspaceId);
466+
await collector.waitForEvent("stream-start", 5000);
467+
468+
// Attempt to rename while streaming - should fail
469+
const newName = "renamed-during-stream";
470+
const renameResult = await env.mockIpcRenderer.invoke(
471+
IPC_CHANNELS.WORKSPACE_RENAME,
472+
workspaceId,
473+
newName
474+
);
475+
476+
// Verify rename was blocked due to active stream
477+
expect(renameResult.success).toBe(false);
478+
expect(renameResult.error).toContain("stream is active");
479+
480+
// Wait for stream to complete
481+
await collector.waitForEvent("stream-end", 10000);
482+
} finally {
483+
await cleanup();
484+
}
485+
},
486+
20000
487+
);
488+
437489
});

0 commit comments

Comments
 (0)