* 重试任务 * * 1. 停止当前 ACTIVE Workspace 的所有 Session * 2. 将 ACTIVE Workspace 标记为 ABANDONED(保留 worktree 供参考) * 3. 重置 Task 状态为 TODO * 4. 通知前端 — 用户可重新派发 Agent(会创建新 Worktree)
(id: string)
| 766 | } |
| 767 | ensureProjectIsMutable(task.project, 'change task status'); |
| 768 | |
| 769 | const currentStatus = task.status as TaskStatus; |
| 770 | |
| 771 | // 如果状态没有变化,直接返回 |
| 772 | if (currentStatus === status) { |
| 773 | return withProjectGitMetadata(task); |
| 774 | } |
| 775 | |
| 776 | // 校验状态流转是否合法 |
| 777 | const allowedTransitions = VALID_TRANSITIONS[currentStatus]; |
| 778 | if (!allowedTransitions || !allowedTransitions.includes(status)) { |
| 779 | throw new InvalidStateTransitionError(currentStatus, status); |
| 780 | } |
| 781 | |
| 782 | // 切换状态时自动计算新列的 position |
| 783 | const maxPosition = await prisma.task.aggregate({ |
| 784 | where: { projectId: task.projectId, status, deletedAt: null }, |
| 785 | _max: { position: true }, |
| 786 | }); |
| 787 | |
| 788 | const updated = await prisma.task.update({ |
| 789 | where: { id }, |
| 790 | data: { |
| 791 | status, |
| 792 | position: (maxPosition._max.position ?? 0) + 1, |
| 793 | }, |
| 794 | }); |
| 795 | |
| 796 | // 通知前端 |
| 797 | this.emitTaskUpdated(id, task.projectId, status); |
| 798 | |
| 799 | return { |
| 800 | ...updated, |
| 801 | project: { |
| 802 | ...task.project, |
| 803 | ...buildProjectGitMetadata(task.project), |
| 804 | }, |
| 805 | }; |
| 806 | } |
| 807 | |
| 808 | /** |
| 809 | * 更新任务位置(用于拖拽排序) |
| 810 | * 如果同时传了 status,会进行状态流转并通知前端 |
| 811 | */ |
| 812 | async updatePosition(id: string, position: number, status?: TaskStatus) { |
| 813 | const task = await prisma.task.findFirst({ |
| 814 | where: { id, deletedAt: null }, |
| 815 | include: { project: true }, |
| 816 | }); |
| 817 | if (!task) { |
| 818 | throw new NotFoundError('Task', id); |
| 819 | } |
| 820 | ensureProjectIsMutable(task.project, 'reorder tasks'); |
| 821 |
no test coverage detected