- Add phase field to workflow step definitions (pre-merge vs post-merge) with persistence and API - Execute pre-merge steps in executor before merge; post-merge steps in merger after successful merge - Pre-merge failures block merge and keep task in in-review; post-merge failures are logged only - Expose phase controls and phase-aware results in the dashboard UI - Add changeset for the published @gsxdsm/fusion package
70 lines
2.0 KiB
TypeScript
70 lines
2.0 KiB
TypeScript
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<WorkflowStepResult["status"]>([
|
|
"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<Task, "column" | "paused" | "status" | "error" | "steps" | "workflowStepResults">,
|
|
): 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<Task, "column" | "paused" | "status" | "error" | "steps" | "workflowStepResults">,
|
|
): boolean {
|
|
return getTaskMergeBlocker(task) === undefined;
|
|
}
|