mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Completed desktop task bars were only being dismissed locally, which let the persisted task list resurface on refresh or bleed into the next user turn. This wires the existing server-side reset path into the desktop stores and session flow so a finished task cycle is summarized once, cleared locally, and removed remotely before the next round starts. Constraint: Existing task persistence already lives behind the server task-list API and must stay compatible with persisted JSON task files Rejected: Only hide the completed task bar in UI state | left stale persisted tasks behind and reintroduced them on reload Rejected: Clear desktop state without a task summary | dropped useful completion context from the chat transcript Confidence: high Scope-risk: moderate Reversibility: clean Directive: Keep desktop task dismissal and task-list persistence behavior aligned; do not reintroduce local-only clearing without covering reload and next-turn flows Tested: `cd desktop && bun run lint` Tested: `cd desktop && bun run test src/stores/cliTaskStore.test.ts src/stores/chatStore.test.ts src/components/chat/SessionTaskBar.test.tsx src/components/chat/ComputerUsePermissionModal.test.tsx` Tested: `bun test src/server/__tests__/e2e/business-flow.test.ts --test-name-pattern "Task Lists API"` Not-tested: Full `bun test src/server/__tests__/e2e/business-flow.test.ts` suite still has unrelated pre-existing failures in Models and Sessions sections
110 lines
3.1 KiB
TypeScript
110 lines
3.1 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { cliTasksApi } from '../api/cliTasks'
|
|
import type { CLITask, TaskStatus } from '../types/cliTask'
|
|
import { useCLITaskStore } from './cliTaskStore'
|
|
|
|
vi.mock('../api/cliTasks', () => ({
|
|
cliTasksApi: {
|
|
getTasksForList: vi.fn(),
|
|
resetTaskList: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
function makeTask(taskListId: string, status: TaskStatus = 'in_progress'): CLITask {
|
|
return {
|
|
id: '1',
|
|
subject: 'Keep current session isolated',
|
|
description: '',
|
|
status,
|
|
blocks: [],
|
|
blockedBy: [],
|
|
taskListId,
|
|
}
|
|
}
|
|
|
|
describe('cliTaskStore', () => {
|
|
beforeEach(() => {
|
|
useCLITaskStore.getState().clearTasks()
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
afterEach(() => {
|
|
useCLITaskStore.getState().clearTasks()
|
|
})
|
|
|
|
it('clears stale tasks immediately when switching tracked sessions', async () => {
|
|
let resolveRequest: ((value: { tasks: ReturnType<typeof makeTask>[] }) => void) | null = null
|
|
|
|
vi.mocked(cliTasksApi.getTasksForList).mockImplementation(
|
|
(sessionId: string) =>
|
|
new Promise<{ tasks: ReturnType<typeof makeTask>[] }>((resolve) => {
|
|
if (sessionId === 'session-2') resolveRequest = resolve
|
|
}),
|
|
)
|
|
|
|
useCLITaskStore.setState({
|
|
sessionId: 'session-1',
|
|
tasks: [makeTask('session-1')],
|
|
expanded: true,
|
|
completedAndDismissed: true,
|
|
dismissedCompletionKey: 'session-1::done',
|
|
})
|
|
|
|
const fetchPromise = useCLITaskStore.getState().fetchSessionTasks('session-2')
|
|
|
|
expect(useCLITaskStore.getState()).toMatchObject({
|
|
sessionId: 'session-2',
|
|
tasks: [],
|
|
expanded: false,
|
|
completedAndDismissed: false,
|
|
dismissedCompletionKey: null,
|
|
})
|
|
|
|
expect(resolveRequest).not.toBeNull()
|
|
resolveRequest!({ tasks: [makeTask('session-2', 'completed')] })
|
|
await fetchPromise
|
|
|
|
expect(useCLITaskStore.getState().tasks).toMatchObject([
|
|
{ taskListId: 'session-2', status: 'completed' },
|
|
])
|
|
})
|
|
|
|
it('resets a completed task list locally before clearing it remotely', async () => {
|
|
let resolveReset: ((value: { ok: true }) => void) | null = null
|
|
|
|
vi.mocked(cliTasksApi.resetTaskList).mockImplementation(
|
|
() => new Promise<{ ok: true }>((resolve) => {
|
|
resolveReset = resolve
|
|
}),
|
|
)
|
|
|
|
useCLITaskStore.setState({
|
|
sessionId: 'session-1',
|
|
tasks: [
|
|
makeTask('session-1', 'completed'),
|
|
{ ...makeTask('session-1', 'completed'), id: '2', subject: 'Second completed task' },
|
|
],
|
|
expanded: true,
|
|
completedAndDismissed: true,
|
|
dismissedCompletionKey: 'session-1::done',
|
|
})
|
|
|
|
const resetPromise = useCLITaskStore.getState().resetCompletedTasks()
|
|
|
|
expect(vi.mocked(cliTasksApi.resetTaskList)).toHaveBeenCalledWith('session-1')
|
|
expect(useCLITaskStore.getState()).toMatchObject({
|
|
tasks: [],
|
|
resetting: true,
|
|
completedAndDismissed: false,
|
|
dismissedCompletionKey: null,
|
|
expanded: false,
|
|
})
|
|
|
|
expect(resolveReset).not.toBeNull()
|
|
resolveReset!({ ok: true })
|
|
await resetPromise
|
|
|
|
expect(useCLITaskStore.getState().resetting).toBe(false)
|
|
})
|
|
})
|