import type { Prisma, TaskStatus } from '@prisma/client' import { prisma } from '@/lib/prisma' export type StoryStatusChange = 'promoted' | 'demoted' | null export interface UpdateTaskStatusResult { task: { id: string title: string status: TaskStatus story_id: string implementation_plan: string | null } storyStatusChange: StoryStatusChange storyId: string } // Update task.status atomically and auto-promote/demote the parent story: // - All sibling tasks DONE → story.status = DONE // - Story was DONE and a task moves out of DONE → story.status = IN_SPRINT // Demote target is IN_SPRINT (not OPEN): OPEN means "back in product backlog", // which is a sprint-management action, not a status side-effect. export async function updateTaskStatusWithStoryPromotion( taskId: string, newStatus: TaskStatus, client?: Prisma.TransactionClient, ): Promise { const run = async (tx: Prisma.TransactionClient): Promise => { const task = await tx.task.update({ where: { id: taskId }, data: { status: newStatus }, select: { id: true, title: true, status: true, story_id: true, implementation_plan: true, }, }) const siblings = await tx.task.findMany({ where: { story_id: task.story_id }, select: { status: true }, }) const allDone = siblings.every((s) => s.status === 'DONE') const story = await tx.story.findUniqueOrThrow({ where: { id: task.story_id }, select: { status: true }, }) let storyStatusChange: StoryStatusChange = null if (newStatus === 'DONE' && allDone && story.status !== 'DONE') { await tx.story.update({ where: { id: task.story_id }, data: { status: 'DONE' }, }) storyStatusChange = 'promoted' } else if (newStatus !== 'DONE' && story.status === 'DONE') { await tx.story.update({ where: { id: task.story_id }, data: { status: 'IN_SPRINT' }, }) storyStatusChange = 'demoted' } return { task, storyStatusChange, storyId: task.story_id } } if (client) return run(client) return prisma.$transaction(run) }