mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix(EXT-696): prevent parent task deletion when API history file missing
This fixes the issue where parent tasks disappear from the UI in orchestrator mode after delegating to a child task via new_task tool. Root cause: In getTaskWithId(), if the historyItem existed in taskHistory but the apiConversationHistory.json file did not exist (due to race conditions during delegation), the method would incorrectly delete the task from state. Fix: When historyItem exists but the API history file does not, return the task with an empty apiConversationHistory array instead of deleting it. This preserves the task metadata during delegation and prevents the parent task from disappearing from the UI. Added tests to verify: - Task is preserved with empty API history when file is missing - Task is properly deleted when historyItem does not exist
This commit is contained in:
parent
4647d0f3c5
commit
5f46f32dab
2 changed files with 70 additions and 2 deletions
|
|
@ -1684,10 +1684,24 @@ export class ClineProvider
|
|||
apiConversationHistory,
|
||||
}
|
||||
}
|
||||
|
||||
// EXT-696: historyItem exists but API conversation file doesn't.
|
||||
// This can happen during race conditions (e.g., delegation before file is flushed).
|
||||
// Return with empty apiConversationHistory instead of deleting the task.
|
||||
// The task's metadata is still valid and should be preserved.
|
||||
this.log(
|
||||
`[getTaskWithId] Task ${id} exists in history but API conversation file not found. Returning with empty history.`,
|
||||
)
|
||||
return {
|
||||
historyItem,
|
||||
taskDirPath,
|
||||
apiConversationHistoryFilePath,
|
||||
uiMessagesFilePath,
|
||||
apiConversationHistory: [],
|
||||
}
|
||||
}
|
||||
|
||||
// if we tried to get a task that doesn't exist, remove it from state
|
||||
// FIXME: this seems to happen sometimes when the json file doesnt save to disk for some reason
|
||||
// Only delete from state if historyItem truly doesn't exist
|
||||
await this.deleteTaskFromState(id)
|
||||
throw new Error("Task not found")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -592,4 +592,58 @@ describe("ClineProvider Task History Synchronization", () => {
|
|||
expect(state.taskHistory.some((item: HistoryItem) => item.workspace === "/different/workspace")).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getTaskWithId", () => {
|
||||
it("returns task with empty apiConversationHistory when file does not exist but historyItem exists (EXT-696 fix)", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
provider.isViewLaunched = true
|
||||
|
||||
const historyItem = createHistoryItem({
|
||||
id: "delegated-parent-task",
|
||||
task: "Parent task that was delegated",
|
||||
})
|
||||
|
||||
// Add the task to history
|
||||
await provider.updateTaskHistory(historyItem)
|
||||
|
||||
// Mock fileExistsAtPath to return false (simulating race condition during delegation)
|
||||
const fsUtils = await import("../../../utils/fs")
|
||||
const fileExistsSpy = vi.spyOn(fsUtils, "fileExistsAtPath").mockResolvedValue(false)
|
||||
|
||||
// Track if deleteTaskFromState was called
|
||||
const deleteTaskSpy = vi.spyOn(provider, "deleteTaskFromState")
|
||||
|
||||
// Call getTaskWithId
|
||||
const result = await provider.getTaskWithId("delegated-parent-task")
|
||||
|
||||
// Should return the task with empty API conversation history, NOT delete it
|
||||
expect(result.historyItem.id).toBe("delegated-parent-task")
|
||||
expect(result.historyItem.task).toBe("Parent task that was delegated")
|
||||
expect(result.apiConversationHistory).toEqual([])
|
||||
|
||||
// Should NOT have called deleteTaskFromState
|
||||
expect(deleteTaskSpy).not.toHaveBeenCalled()
|
||||
|
||||
// Clean up
|
||||
fileExistsSpy.mockRestore()
|
||||
deleteTaskSpy.mockRestore()
|
||||
})
|
||||
|
||||
it("throws error and deletes task when historyItem does not exist", async () => {
|
||||
await provider.resolveWebviewView(mockWebviewView)
|
||||
provider.isViewLaunched = true
|
||||
|
||||
// Track if deleteTaskFromState was called
|
||||
const deleteTaskSpy = vi.spyOn(provider, "deleteTaskFromState").mockResolvedValue()
|
||||
|
||||
// Call getTaskWithId with a task that doesn't exist
|
||||
await expect(provider.getTaskWithId("non-existent-task")).rejects.toThrow("Task not found")
|
||||
|
||||
// Should have called deleteTaskFromState
|
||||
expect(deleteTaskSpy).toHaveBeenCalledWith("non-existent-task")
|
||||
|
||||
// Clean up
|
||||
deleteTaskSpy.mockRestore()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue