feat(FN-4885): complete Step 6 — add deadlock disposition dashboard copy

Fusion-Task-Id: FN-4885
Fusion-Task-Lineage: 31110eed-5ca3-4d67-8f11-581185056dc6
This commit is contained in:
Fusion (runfusion.ai)
2026-05-17 09:15:03 -07:00
committed by gsxdsm
parent ddfc927abd
commit 5793c4bcd7
2 changed files with 37 additions and 2 deletions

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { getInReviewStallCopy, shouldShowInReviewStallBadge } from "../inReviewStallCopy";
import { getInReviewStallCopy, getInReviewStallDeadlockCopy, shouldShowInReviewStallBadge } from "../inReviewStallCopy";
describe("inReviewStallCopy", () => {
it.each([
@@ -121,4 +121,16 @@ describe("inReviewStallCopy", () => {
expect(copy.suggestedAction).toBe("Open the activity log for details.");
expect(copy.badgeLabel).toBe("In-review stall");
});
it("returns deadlock disposition copy when paused reason indicates auto-dispose", () => {
const copy = getInReviewStallDeadlockCopy({
pausedReason: "in-review-stall-deadlock",
log: [],
} as any);
expect(copy).toMatchObject({
headline: "In-review deadlock auto-disposed",
});
expect(copy?.nextAction).toContain("unpause");
});
});

View File

@@ -1,4 +1,4 @@
import type { InReviewStallCode, InReviewStallSignal, Task } from "@fusion/core";
import { IN_REVIEW_STALL_DEADLOCK_LOG_PREFIX, type InReviewStallCode, type InReviewStallSignal, type Task } from "@fusion/core";
import { MAX_AUTO_MERGE_RETRIES } from "../hooks/useBlockerFanout";
@@ -11,6 +11,12 @@ export interface InReviewStallCopy {
code: InReviewStallCode;
}
export interface InReviewStallDeadlockCopy {
headline: string;
description: string;
nextAction: string;
}
const BADGE_LABEL_BY_CODE: Record<InReviewStallCode, string> = {
"merge-blocker": "Merge blocked",
"transient-merge-status-no-owner": "Merge stalled",
@@ -85,6 +91,23 @@ export function getInReviewStallCopy(
const ACTIVE_MERGE_STATUSES: ReadonlySet<Task["status"]> = new Set(["merging", "merging-pr", "merging-fix"]);
const IN_REVIEW_STALL_DEADLOCK_COPY: InReviewStallDeadlockCopy = {
headline: "In-review deadlock auto-disposed",
description:
"Self-healing paused this in-review task after the same stall repeated without progress. This prevents infinite merge-blocker churn.",
nextAction:
"Inspect the merge blocker/branch conflict, recover manually, then unpause to retry. If recovery needs extra implementation, create a follow-up with fn_task_refine.",
};
export function getInReviewStallDeadlockCopy(task: Pick<Task, "pausedReason" | "log">): InReviewStallDeadlockCopy | undefined {
if (task.pausedReason === "in-review-stall-deadlock") {
return IN_REVIEW_STALL_DEADLOCK_COPY;
}
const hasDeadlockLog = task.log?.some((entry) => entry.action.startsWith(IN_REVIEW_STALL_DEADLOCK_LOG_PREFIX)) ?? false;
return hasDeadlockLog ? IN_REVIEW_STALL_DEADLOCK_COPY : undefined;
}
export function shouldShowInReviewStallBadge(task: Pick<Task, "column" | "paused" | "inReviewStall" | "status">): boolean {
if (task.column !== "in-review" || task.paused === true || task.inReviewStall == null) {
return false;