fix(FN-6648): treat completed in-review tasks as benign despite lingering non-user paused flag
The paused-after-completion graceful-exit path finalizes a fully completed task to in-review while leaving a non-user paused:true flag set (handoffToReview/applyInReviewEnterEffects clear status/blockedBy but not paused). handleGraphFailure's completion-finalized guards required paused!==true, so once the volatile completion markers were lost (execute() re-entry deletes completionFinalizedTaskIds; teardown overwrites provenance to hard-cancel) the trailing graph failure was misclassified as an operator-action pause abort and the completed task was parked status:failed (FN-6638 recurrence). Drop the paused!==true requirement from alreadyFinalizedToReview and suppressFinalizedCompletionAbort, and gate genuinePauseAbort's bare paused clause on the completion suppression. Genuine userPaused/global-pause/in-progress tasks are unaffected. Fusion-Task-Id: FN-6648
This commit is contained in:
5
.changeset/fn-6648-completion-finalize-paused-flag.md
Normal file
5
.changeset/fn-6648-completion-finalize-paused-flag.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Fix completed tasks being parked failed in in-review with a spurious "engine abort during pause/resume — operator action required" error (FN-6648; recurrence of FN-6478/FN-6568/FN-6625/FN-6644/FN-6647). The paused-after-completion graceful-exit path finalizes a fully completed task to in-review while leaving a non-user `paused` flag set; `handleGraphFailure`'s completion-finalized guards required `paused !== true`, so the trailing graph failure was misclassified as an operator-action pause abort once the volatile completion markers were lost. The classifier now recognizes finalized completions regardless of a lingering non-user pause flag, while genuine user/global pauses and in-progress tasks are unaffected.
|
||||
@@ -1612,6 +1612,40 @@ describe("TaskExecutor bounded recovery retries", () => {
|
||||
expectBenignAlreadyAdvanced(store, column);
|
||||
});
|
||||
|
||||
it("treats a completed in-review row as benign even with a lingering NON-user paused flag (FN-6648)", async () => {
|
||||
/*
|
||||
FNXC:WorkflowLifecycle 2026-06-18-16:25:
|
||||
FN-6648 (FN-6638 recurrence): the paused-after-completion graceful-exit
|
||||
path finalizes a fully completed task to in-review while leaving a
|
||||
NON-user `paused: true` flag set (handoffToReview/applyInReviewEnterEffects
|
||||
clear status/blockedBy but never `paused`). Worst case: the volatile
|
||||
completionFinalized marker is lost (execute re-entry) AND provenance is
|
||||
overwritten to hard-cancel by teardown — only persisted evidence remains.
|
||||
This must resolve benignly, NOT park the completed task as an
|
||||
operator-action "engine abort during pause/resume" failure.
|
||||
*/
|
||||
const store = createMockStore();
|
||||
const task = makeCompletedTask();
|
||||
store.getTask.mockResolvedValue({
|
||||
...task,
|
||||
column: "in-review",
|
||||
paused: true,
|
||||
userPaused: false,
|
||||
status: undefined,
|
||||
error: null,
|
||||
});
|
||||
const executor = new TaskExecutor(store, "/tmp/test", {});
|
||||
(executor as any).markPausedAborted("FN-001", "hard-cancel");
|
||||
|
||||
await (executor as any).handleGraphFailure(task, {
|
||||
disposition: "failed",
|
||||
outcome: "failure",
|
||||
visitedNodeIds: ["execute"],
|
||||
});
|
||||
|
||||
expectBenignAlreadyAdvanced(store);
|
||||
});
|
||||
|
||||
it("preserves explicit user-pause parking even when durable completion state exists", async () => {
|
||||
const store = createMockStore();
|
||||
const task = makeCompletedTask();
|
||||
|
||||
@@ -6468,7 +6468,18 @@ export class TaskExecutor {
|
||||
&& live.status == null
|
||||
&& live.error == null
|
||||
&& live.userPaused !== true
|
||||
&& live.paused !== true
|
||||
// FNXC:WorkflowLifecycle 2026-06-18-16:20:
|
||||
// FN-6648: do NOT require `paused !== true` here. The
|
||||
// paused-after-completion graceful-exit path (executor ~8748/8194)
|
||||
// finalizes a FULLY COMPLETED task to in-review while leaving a
|
||||
// NON-user `paused: true` flag set — handoffToReview /
|
||||
// applyInReviewEnterEffects clear status/blockedBy/overlapBlockedBy
|
||||
// but never `paused`. Requiring `paused !== true` made this clean
|
||||
// completion unrecognizable, so `genuinePauseAbort` parked it failed
|
||||
// with the spurious "engine abort during pause/resume" error
|
||||
// (FN-6638 recurrence). `userPaused`/global-pause are still excluded,
|
||||
// and `persistedCompletedProgress` + `persistedCompletionFinalizeLog`
|
||||
// + status/error == null keep this scoped to genuine completions.
|
||||
&& abortProvenance !== "global-pause"
|
||||
&& !mergeSeamAborted
|
||||
&& persistedCompletionFinalizeLog,
|
||||
@@ -6478,14 +6489,21 @@ export class TaskExecutor {
|
||||
completionFinalized
|
||||
&& live.column !== "in-progress"
|
||||
&& !live.userPaused
|
||||
&& live.paused !== true
|
||||
// FN-6648: `paused !== true` intentionally dropped here too — the
|
||||
// suppression is already gated on `completionFinalized` (completed
|
||||
// steps + finalize-to-review evidence) plus userPaused/global-pause
|
||||
// exclusions, so a lingering non-user post-completion pause flag must
|
||||
// not defeat it. See alreadyFinalizedToReview note above.
|
||||
&& abortProvenance !== "global-pause"
|
||||
&& !mergeSeamAborted,
|
||||
);
|
||||
const genuinePauseAbort = Boolean(
|
||||
live.userPaused
|
||||
|| abortProvenance === "global-pause"
|
||||
|| (live.paused && !mergeSeamAborted)
|
||||
// FN-6648: gate the bare `paused` clause on the completion-finalize
|
||||
// suppression so a completed task carrying a non-user post-completion
|
||||
// pause flag is not parked as an operator-action failure.
|
||||
|| (live.paused && !mergeSeamAborted && !suppressFinalizedCompletionAbort)
|
||||
|| (pausedAborted && !mergeSeamAborted && !completionFinalizeAborted && !suppressFinalizedCompletionAbort),
|
||||
);
|
||||
if (genuinePauseAbort) {
|
||||
|
||||
Reference in New Issue
Block a user