import type { Task, WorkflowStepResult } from "./types.js"; const BLOCKING_TASK_STATUSES = new Set([ "failed", "awaiting-inspection", ]); const NON_TERMINAL_STEP_STATUSES = new Set([ "pending", "in-progress", ]); const NON_TERMINAL_WORKFLOW_STATUSES = new Set([ "pending", ]); /** * Returns a human-readable reason when a task in review is not safe to finalize. * Undefined means the task is eligible to move from `in-review` to `done`. */ export function getTaskMergeBlocker( task: Pick, ): string | undefined { if (task.column !== "in-review") { return `task is in '${task.column}', must be in 'in-review'`; } if (task.paused) { return "task is paused"; } if (task.status && BLOCKING_TASK_STATUSES.has(task.status)) { return task.error ? `task is marked '${task.status}': ${task.error}` : `task is marked '${task.status}'`; } if (task.steps.length > 0 && task.steps.some((step) => NON_TERMINAL_STEP_STATUSES.has(step.status))) { return "task has incomplete steps"; } // Only pre-merge workflow step failures block merge. // Post-merge failures run after merge and do not block it. if ( task.workflowStepResults?.some((result) => { const phase = result.phase || "pre-merge"; return phase === "pre-merge" && NON_TERMINAL_WORKFLOW_STATUSES.has(result.status); }) ) { return "task has incomplete or failed pre-merge workflow steps"; } if ( task.workflowStepResults?.some((result) => { const phase = result.phase || "pre-merge"; return phase === "pre-merge" && result.status === "failed"; }) ) { return "task has failed pre-merge workflow steps"; } return undefined; } export function isTaskReadyForMerge( task: Pick, ): boolean { return getTaskMergeBlocker(task) === undefined; }