fix(FN-7335): retry active merge pause aborts
This commit is contained in:
7
.changeset/merge-reviewing-pause-abort.md
Normal file
7
.changeset/merge-reviewing-pause-abort.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Retry workflow merge-node pause aborts while merge review is active.
|
||||||
|
category: fix
|
||||||
|
dev: Treats transient in-review statuses such as reviewing/merging as safe for bounded merge retry.
|
||||||
@@ -127,6 +127,22 @@ describe("merge-node paused-abort retry classification (FN-6735)", () => {
|
|||||||
expect(store.updateTask).not.toHaveBeenCalledWith(task.id, expect.objectContaining({ status: "failed" }), undefined);
|
expect(store.updateTask).not.toHaveBeenCalledWith(task.id, expect.objectContaining({ status: "failed" }), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("retries FN-7335-style merge pause aborts while AI merge review status is active", async () => {
|
||||||
|
const { store, task, executor, mergeRequester } = makeHarness({ status: "reviewing" });
|
||||||
|
|
||||||
|
await invokeGraphFailure(executor, task, "merge");
|
||||||
|
|
||||||
|
expect(mergeRequester).toHaveBeenCalledWith(task.id);
|
||||||
|
const messages = logText(store);
|
||||||
|
expect(messages).toContain("Workflow graph merge failure at node 'merge' routed to bounded auto-merge retry after benign pause/resume abort");
|
||||||
|
expect(messages).not.toContain("operator action required");
|
||||||
|
expect(store.updateTask).not.toHaveBeenCalledWith(
|
||||||
|
task.id,
|
||||||
|
expect.objectContaining({ status: "failed" }),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("parks genuine merge conflicts as terminal instead of retrying forever", async () => {
|
it("parks genuine merge conflicts as terminal instead of retrying forever", async () => {
|
||||||
const { store, task, executor, mergeRequester } = makeHarness();
|
const { store, task, executor, mergeRequester } = makeHarness();
|
||||||
|
|
||||||
|
|||||||
@@ -7592,6 +7592,14 @@ export class TaskExecutor {
|
|||||||
|| normalized.includes("max retries");
|
|| normalized.includes("max retries");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isRetryableMergePauseAbortStatus(status: string | null | undefined): boolean {
|
||||||
|
/*
|
||||||
|
FNXC:WorkflowMerge 2026-07-01-22:05:
|
||||||
|
FN-7335 surfaced a merge-node pause/resume abort while the row was legitimately `in-review` with status="reviewing" from the AI merge reviewer. That status is merge activity, not a pre-existing terminal failure; keep the retry classifier strict on real errors while allowing transient merge/review statuses to re-enter bounded merge retry.
|
||||||
|
*/
|
||||||
|
return status == null || status === "reviewing" || status === "merging" || status === "merging-pr";
|
||||||
|
}
|
||||||
|
|
||||||
private async isRetryableBenignMergePauseAbort(
|
private async isRetryableBenignMergePauseAbort(
|
||||||
live: TaskDetail,
|
live: TaskDetail,
|
||||||
result: WorkflowGraphTaskRunResult,
|
result: WorkflowGraphTaskRunResult,
|
||||||
@@ -7605,7 +7613,7 @@ export class TaskExecutor {
|
|||||||
if (!pausedAborted) return false;
|
if (!pausedAborted) return false;
|
||||||
if (abortProvenance === "global-pause" || live.userPaused === true) return false;
|
if (abortProvenance === "global-pause" || live.userPaused === true) return false;
|
||||||
if (abortProvenance === "completion-finalize") return false;
|
if (abortProvenance === "completion-finalize") return false;
|
||||||
if (live.column !== "in-review" || live.status != null || live.error != null) return false;
|
if (live.column !== "in-review" || !this.isRetryableMergePauseAbortStatus(live.status) || live.error != null) return false;
|
||||||
if (live.mergeDetails?.mergeConfirmed === true) return false;
|
if (live.mergeDetails?.mergeConfirmed === true) return false;
|
||||||
if (this.isTerminalMergeGraphFailureValue(this.graphFailureValue(result))) return false;
|
if (this.isTerminalMergeGraphFailureValue(this.graphFailureValue(result))) return false;
|
||||||
const failedNode = result.visitedNodeIds[result.visitedNodeIds.length - 1];
|
const failedNode = result.visitedNodeIds[result.visitedNodeIds.length - 1];
|
||||||
|
|||||||
Reference in New Issue
Block a user