-
Notifications
You must be signed in to change notification settings - Fork 2.5k
fix(ui): guarantee orchestrator thread/chat pane restores after subtask completion #7720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
c8911ca
5d886b2
d37bc8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import { describe, it, expect, vi, beforeEach } from "vitest" | ||
| import { Task } from "../Task" | ||
| import { ClineProvider } from "../../webview/ClineProvider" | ||
| import { TodoItem } from "@roo-code/types" | ||
|
|
||
| describe("Task subtask mode restoration", () => { | ||
| let parentTask: Task | ||
| let mockProvider: any | ||
|
|
||
| beforeEach(() => { | ||
| mockProvider = { | ||
| handleModeSwitch: vi.fn().mockResolvedValue(undefined), | ||
| log: vi.fn(), | ||
| deref: vi.fn().mockReturnValue({ | ||
| handleModeSwitch: vi.fn().mockResolvedValue(undefined), | ||
| }), | ||
| } | ||
| }) | ||
|
|
||
| it("should restore parent task mode when subtask completes", async () => { | ||
| // Create parent task with orchestrator mode | ||
| parentTask = new Task({ | ||
| provider: mockProvider as any, | ||
| apiConfiguration: {} as any, | ||
| task: "Parent task", | ||
| }) | ||
|
|
||
| // Set parent task to orchestrator mode | ||
| parentTask.pausedModeSlug = "orchestrator" | ||
|
|
||
| // Mock the provider reference | ||
| parentTask.providerRef = { | ||
| deref: () => mockProvider.deref(), | ||
| } as any | ||
|
|
||
| // Complete the subtask | ||
| await parentTask.completeSubtask("Subtask completed") | ||
|
|
||
| // Verify handleModeSwitch was called with the pausedModeSlug | ||
| expect(mockProvider.deref().handleModeSwitch).toHaveBeenCalledWith("orchestrator") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great test coverage for the core functionality! Consider adding a test case that verifies the order of operations - specifically that |
||
|
|
||
| // Verify task is unpaused | ||
| expect(parentTask.isPaused).toBe(false) | ||
|
|
||
| // Verify childTaskId is cleared | ||
| expect(parentTask.childTaskId).toBeUndefined() | ||
| }) | ||
|
|
||
| it("should handle missing provider gracefully", async () => { | ||
| // Create parent task | ||
| parentTask = new Task({ | ||
| provider: mockProvider as any, | ||
| apiConfiguration: {} as any, | ||
| task: "Parent task", | ||
| }) | ||
|
|
||
| // Set parent task to orchestrator mode | ||
| parentTask.pausedModeSlug = "orchestrator" | ||
|
|
||
| // Mock provider as unavailable | ||
| parentTask.providerRef = { | ||
| deref: () => undefined, | ||
| } as any | ||
|
|
||
| // Complete the subtask - should not throw | ||
| await expect(parentTask.completeSubtask("Subtask completed")).resolves.not.toThrow() | ||
|
|
||
| // Verify task is still unpaused | ||
| expect(parentTask.isPaused).toBe(false) | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -439,6 +439,9 @@ export class ClineProvider | |
| // Resume the last cline instance in the stack (if it exists - this is | ||
| // the 'parent' calling task). | ||
| await this.getCurrentTask()?.completeSubtask(lastMessage) | ||
|
||
|
|
||
| // Explicitly trigger UI state update so that webview/pane transitions back to the parent thread. | ||
| await this.postStateToWebview() | ||
|
||
| } | ||
|
|
||
| /* | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good defensive programming with the null check! However, I noticed that
handleModeSwitch()in ClineProvider already callspostStateToWebview()internally. SincefinishSubTask()also callshandleModeSwitch()and then explicitly callspostStateToWebview()again (line 444 in ClineProvider), could this cause duplicate UI updates? Would it be cleaner to rely on just one of these calls?