From 0c53f46afb406924d06cdee1b6048ebdd65cbc54 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 25 Jun 2026 01:43:59 -0700 Subject: [PATCH] FN-6983: remove branch reattachment warning Remove the stale branch reattachment affordance from Task Detail while preserving self-healing as the recovery path. - Remove unused rebind banner styles and mobile action-shell rules. - Drop stale recoverBranchBinding mocks from TaskDetailModal tests. - Add rendering coverage proving Task Detail and embedded content do not show reattachment affordances across missing, populated, workspace, and mobile branch states. - Add a patch changeset for the published Fusion package. Files changed: .changeset/fn-6983-task-detail-rebind-warning.md | 7 ++ .../dashboard/app/components/TaskDetailModal.css | 42 +----------- .../TaskDetailModal.create-pr-e2e.test.tsx | 1 - .../TaskDetailModal.create-pr-integration.test.tsx | 1 - .../__tests__/TaskDetailModal.rendering.test.tsx | 80 ++++++++++++++++++++++ .../__tests__/TaskDetailModal.test-helpers.ts | 1 - 6 files changed, 88 insertions(+), 44 deletions(-) Fusion-Task-Id: FN-6983 Fusion-Task-Lineage: 1a45c38e-f7d0-4df7-94cf-7d1309758e0d --- .../fn-6983-task-detail-rebind-warning.md | 7 ++ .../app/components/TaskDetailModal.css | 42 +--------- .../TaskDetailModal.create-pr-e2e.test.tsx | 1 - ...DetailModal.create-pr-integration.test.tsx | 1 - .../TaskDetailModal.rendering.test.tsx | 80 +++++++++++++++++++ .../__tests__/TaskDetailModal.test-helpers.ts | 1 - 6 files changed, 88 insertions(+), 44 deletions(-) create mode 100644 .changeset/fn-6983-task-detail-rebind-warning.md diff --git a/.changeset/fn-6983-task-detail-rebind-warning.md b/.changeset/fn-6983-task-detail-rebind-warning.md new file mode 100644 index 0000000000..e4fbbae433 --- /dev/null +++ b/.changeset/fn-6983-task-detail-rebind-warning.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Stop showing branch reattachment warnings in Task Detail. +category: fix +dev: Removes stale TaskDetailModal rebind-banner CSS/mocks and covers missing-branch workspace shapes. diff --git a/packages/dashboard/app/components/TaskDetailModal.css b/packages/dashboard/app/components/TaskDetailModal.css index 1954edac2d..cffe2727c8 100644 --- a/packages/dashboard/app/components/TaskDetailModal.css +++ b/packages/dashboard/app/components/TaskDetailModal.css @@ -2271,48 +2271,8 @@ Narrow mobile task detail surfaces from both Board and List must allow horizonta gap: var(--space-sm); } -.rebind-banner { - display: flex; - flex-direction: column; - gap: var(--space-sm); - padding: var(--space-lg); - border-radius: var(--radius-md); - background: color-mix(in srgb, var(--color-warning) 8%, transparent); - border: 1px solid color-mix(in srgb, var(--color-warning) 30%, transparent); -} - -.rebind-banner-header { - display: flex; - align-items: center; - gap: var(--space-sm); -} - -.rebind-banner-headline { - font-weight: 600; - color: var(--text); -} - -.rebind-banner-copy { - margin: 0; - color: var(--text); -} - -.rebind-banner-result { - color: var(--text-muted); - display: flex; - flex-direction: column; - gap: var(--space-xs); -} - -.rebind-banner-actions { - display: flex; - align-items: center; - gap: var(--space-sm); -} - @media (max-width: 768px) { - .detail-near-duplicate-banner__actions, - .rebind-banner-actions { + .detail-near-duplicate-banner__actions { flex-direction: column; align-items: flex-start; } diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-e2e.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-e2e.test.tsx index bc7b77332e..2d15be3917 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-e2e.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-e2e.test.tsx @@ -31,7 +31,6 @@ vi.mock("../../api", async (importOriginal) => { updateGlobalSettings: vi.fn().mockResolvedValue({}), pauseTask: vi.fn().mockResolvedValue({}), unpauseTask: vi.fn().mockResolvedValue({}), - recoverBranchBinding: vi.fn(), fetchWorkflowResults: vi.fn().mockResolvedValue([]), fetchTaskReview: vi.fn().mockResolvedValue({ reviewState: { source: "reviewer-agent", items: [], addressing: [] }, automationStatus: null, emptyMessage: "No reviewer feedback yet" }), refreshTaskReview: vi.fn().mockResolvedValue({ reviewState: undefined, automationStatus: null }), diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-integration.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-integration.test.tsx index 5142cbe91b..5392bd3f9e 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-integration.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.create-pr-integration.test.tsx @@ -37,7 +37,6 @@ vi.mock("../../api", async (importOriginal) => { updateGlobalSettings: vi.fn().mockResolvedValue({}), pauseTask: vi.fn().mockResolvedValue({}), unpauseTask: vi.fn().mockResolvedValue({}), - recoverBranchBinding: vi.fn(), fetchWorkflowResults: vi.fn().mockResolvedValue([]), fetchTaskReview: vi.fn().mockResolvedValue({ reviewState: { source: "reviewer-agent", items: [], addressing: [] }, automationStatus: null, emptyMessage: "No reviewer feedback yet" }), refreshTaskReview: vi.fn().mockResolvedValue({ reviewState: undefined, automationStatus: null }), diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx b/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx index 048c63a0a7..f7ff29ffd8 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx @@ -742,6 +742,86 @@ describe("TaskDetailModal", () => { expect(onRequestClose).toHaveBeenCalledTimes(1); }); + function expectNoBranchReattachmentAffordance(container: HTMLElement): void { + // FN-6983: Task Detail must not ask users to manually reattach branches; self-healing owns recovery. + expect(screen.queryByText(/Branch needs reattachment/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/Branch binding lost/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/isn't currently attached to a fusion branch/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/Reattached branch/i)).not.toBeInTheDocument(); + expect(screen.queryByRole("button", { name: /Reattach branch/i })).not.toBeInTheDocument(); + expect(container.querySelector(".rebind-banner")).toBeNull(); + expect(container.querySelector(".rebind-banner-actions")).toBeNull(); + expect(container.querySelector(".rebind-banner-result")).toBeNull(); + } + + function renderTaskDetail(task: ReturnType, mobileHeaderMode?: "back") { + return render( + , + ); + } + + describe("branch reattachment affordance absence", () => { + it.each([ + ["null branch and worktree", makeTask({ column: "in-review", branch: null, worktree: null })], + ["undefined branch with missing worktree", makeTask({ column: "in-review", branch: undefined, worktree: null })], + ["populated branch control", makeTask({ column: "in-review", branch: "fusion/fn-099", worktree: "/tmp/fn-099" })], + ["non-in-review missing branch", makeTask({ column: "todo", branch: null, worktree: null })], + ] as const)("renders no reattachment banner for %s", (_label, task) => { + const { container } = renderTaskDetail(task); + + expectNoBranchReattachmentAffordance(container); + }); + + it("renders no reattachment banner for workspace in-review tasks with no singular branch", () => { + const task = makeTask({ + column: "in-review", + worktree: null, + workspaceWorktrees: { + "repo-a": { worktreePath: "/tmp/fn-099/repo-a", branch: "fusion/fn-099", baseCommitSha: "abc123" }, + "repo-b": { worktreePath: "/tmp/fn-099/repo-b", branch: "fusion/fn-099" }, + }, + }); + delete (task as { branch?: string | null }).branch; + + const { container } = renderTaskDetail(task); + + expectNoBranchReattachmentAffordance(container); + }); + + it("keeps the removed mobile rebind action shell absent in narrow task detail rendering", () => { + const { container } = renderTaskDetail(makeTask({ column: "in-review", branch: null, worktree: null }), "back"); + + expect(screen.getByRole("button", { name: "Back to task list" })).toBeInTheDocument(); + expectNoBranchReattachmentAffordance(container); + }); + + it("renders no reattachment banner from embedded TaskDetailContent", () => { + const { container } = render( + , + ); + + expectNoBranchReattachmentAffordance(container); + }); + }); + it("styles detail-body scrollbar rules", () => { const css = readDashboardStylesSource(); diff --git a/packages/dashboard/app/components/__tests__/TaskDetailModal.test-helpers.ts b/packages/dashboard/app/components/__tests__/TaskDetailModal.test-helpers.ts index d00536042d..1eeebc5a27 100644 --- a/packages/dashboard/app/components/__tests__/TaskDetailModal.test-helpers.ts +++ b/packages/dashboard/app/components/__tests__/TaskDetailModal.test-helpers.ts @@ -37,7 +37,6 @@ vi.mock("../../api", async (importOriginal) => { updateGlobalSettings: vi.fn().mockResolvedValue({}), pauseTask: vi.fn().mockResolvedValue({}), unpauseTask: vi.fn().mockResolvedValue({}), - recoverBranchBinding: vi.fn(), refreshPrStatus: vi.fn(), fetchWorkflowResults: vi.fn().mockResolvedValue([]), fetchTaskReview: vi.fn().mockResolvedValue({ reviewState: { source: "reviewer-agent", items: [], addressing: [] }, automationStatus: null, emptyMessage: "No reviewer feedback yet — this task has not produced reviewer-agent feedback in direct mode." }),