feat(FN-3857): auto-recover merge deadlocks by recovering already merged ca

Adds auto-recovery for merge deadlocks when candidates have already been merged on main (FN-3857), including tests, documentation updates in AGENTS.md, and a patch changeset for the `@runfusion/fusion` package.

Fusion-Task-Id: FN-3857
This commit is contained in:
Fusion
2026-05-10 09:22:02 -07:00
committed by gsxdsm
parent 981c976efe
commit eb148124ba
4 changed files with 15 additions and 3 deletions

View File

@@ -2600,7 +2600,7 @@ describe("SelfHealingManager", () => {
managerWithRecovery.stop();
});
it("filters out non-candidates", async () => {
it("filters out non-candidates but still evaluates paused failed candidates", async () => {
const managerWithRecovery = new SelfHealingManager(store, {
rootDir: "/tmp/test-project",
getExecutingTaskIds: () => new Set(["FN-executing"]),
@@ -2613,12 +2613,17 @@ describe("SelfHealingManager", () => {
{ id: "FN-executing", column: "in-review", paused: false, status: "failed", mergeRetries: 3, mergeDetails: undefined, log: [] },
{ id: "FN-confirmed", column: "in-review", paused: false, status: "failed", mergeRetries: 3, mergeDetails: { mergeConfirmed: true }, log: [] },
]);
mockedExecSync.mockImplementation(() => "" as any);
const result = await managerWithRecovery.recoverAlreadyMergedReviewTasks();
expect(result).toBe(0);
expect(store.updateTask).not.toHaveBeenCalled();
expect(store.moveTask).not.toHaveBeenCalled();
expect(mockedExecSync).toHaveBeenCalledWith(
expect.stringContaining("Fusion-Task-Id: FN-paused"),
expect.any(Object),
);
managerWithRecovery.stop();
});

View File

@@ -1807,7 +1807,6 @@ export class SelfHealingManager {
*
* Candidate filter:
* - `column === "in-review"`
* - not paused
* - `status === "failed"`
* - `(mergeRetries ?? 0) >= MAX_AUTO_MERGE_RETRIES`
* - `mergeDetails.mergeConfirmed !== true`
@@ -1830,7 +1829,6 @@ export class SelfHealingManager {
const tasks = await this.store.listTasks({ column: "in-review", slim: true });
const candidates = tasks.filter((task) =>
task.column === "in-review" &&
!task.paused &&
task.status === "failed" &&
(task.mergeRetries ?? 0) >= MAX_AUTO_MERGE_RETRIES &&
task.mergeDetails?.mergeConfirmed !== true &&