feat(FN-4084): recover tasks blocked by stalled merger self-healing

Merge starvation recovery was hardened across the engine: self-healing now detects and clears blocked-in-review tasks that starve the merger, the project engine gains defensive recovery hooks for stalled merges, and tests cover the new recovery paths.

Fusion-Task-Id: FN-4084
This commit is contained in:
Fusion
2026-05-12 07:30:46 -07:00
committed by gsxdsm
parent 266eafaecd
commit 4850fcb51f
7 changed files with 264 additions and 31 deletions

View File

@@ -2053,7 +2053,7 @@ describe("ProjectEngine stale mergeActive rescue (FN-3900)", () => {
await engine.stop();
});
it("internalEnqueueMerge warns and skips direct leaked mergeActive entries", async () => {
it("FN-4084: internalEnqueueMerge reconciles leaked mergeActive entries", async () => {
const mockStore = createMockStore({ ...baseSettings, autoMerge: true });
mocks.currentStore = mockStore.store;
@@ -2062,6 +2062,7 @@ describe("ProjectEngine stale mergeActive rescue (FN-3900)", () => {
mergeQueue: string[];
mergeActive: Set<string>;
activeMergeTaskId: string | null;
mergeRunning: boolean;
internalEnqueueMerge: (taskId: string) => void;
};
const warnSpy = vi.spyOn(runtimeLog, "warn").mockImplementation(() => {});
@@ -2071,12 +2072,13 @@ describe("ProjectEngine stale mergeActive rescue (FN-3900)", () => {
privateEngine.mergeActive = new Set(["FN-leaked2"]);
privateEngine.mergeQueue = [];
privateEngine.activeMergeTaskId = null;
privateEngine.mergeRunning = true;
privateEngine.internalEnqueueMerge("FN-leaked2");
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("mergeActive entry is leaked"));
expect(privateEngine.mergeQueue).toEqual([]);
expect(mocks.aiMergeTask).not.toHaveBeenCalled();
expect(privateEngine.mergeQueue).toEqual(["FN-leaked2"]);
expect(privateEngine.mergeActive.has("FN-leaked2")).toBe(true);
await engine.stop();
});