From 576ff7768ab9fa5ea9c07253f07a11a7a963f730 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 9 Jun 2026 14:31:59 -0700 Subject: [PATCH] fix(FN-6131): stop failing on foreign branch attribution Treat foreign task-attributed commits in branch ranges as informational instead of blocking worktree acquisition or branch authority checks. Fusion-Task-Id: FN-6131 --- ...-foreign-branch-attribution-nonblocking.md | 5 + .../src/__tests__/branch-conflicts.test.ts | 92 +------------------ packages/engine/src/branch-conflicts.ts | 82 ++--------------- 3 files changed, 18 insertions(+), 161 deletions(-) create mode 100644 .changeset/fn-6131-foreign-branch-attribution-nonblocking.md diff --git a/.changeset/fn-6131-foreign-branch-attribution-nonblocking.md b/.changeset/fn-6131-foreign-branch-attribution-nonblocking.md new file mode 100644 index 0000000000..191d2915d8 --- /dev/null +++ b/.changeset/fn-6131-foreign-branch-attribution-nonblocking.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Stop failing task worktree acquisition and branch authority checks when a task branch contains foreign task-attributed commits. diff --git a/packages/engine/src/__tests__/branch-conflicts.test.ts b/packages/engine/src/__tests__/branch-conflicts.test.ts index 33598c4f83..6bb9d2e2c0 100644 --- a/packages/engine/src/__tests__/branch-conflicts.test.ts +++ b/packages/engine/src/__tests__/branch-conflicts.test.ts @@ -51,7 +51,6 @@ import { execSync } from "node:child_process"; import { existsSync } from "node:fs"; import { BranchConflictError, - BranchCrossContaminationError, assertCleanBranchAtBase, inspectBranchConflict, listUniqueBranchCommits, @@ -443,100 +442,19 @@ describe("branch-conflicts", () => { it.each([ { name: "passes when attribution comes from subject token", - log: "aaa111\u001ffeat(FN-4068): own\u001f\n", - expectForeign: false, }, { name: "passes when attribution comes from trailer token", - log: "aaa111\u001ffeat: own\u001fFusion-Task-Id: FN-4068\n", - expectForeign: false, }, { - name: "throws when commit is attributed to a foreign task", - log: "bbb222\u001ffeat(FN-4386): foreign\u001fFusion-Task-Id: FN-4386\n", - expectForeign: true, + name: "passes when commit is attributed to a foreign task", }, - ])("assertCleanBranchAtBase $name", async ({ log, expectForeign }) => { - mockedExecSync.mockImplementation((cmd: string | string[]) => { - const command = typeof cmd === "string" ? cmd : cmd[0]; - if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) { - return Buffer.from(log); - } - throw new Error(`Unexpected command: ${command}`); + ])("assertCleanBranchAtBase $name", async () => { + mockedExecSync.mockImplementation(() => { + throw new Error("assertCleanBranchAtBase should not inspect commit attribution"); }); - const assertion = assertCleanBranchAtBase("/tmp/repo", "fusion/fn-4068", "main", "FN-4068"); - if (expectForeign) { - await expect(assertion).rejects.toBeInstanceOf(BranchCrossContaminationError); - return; - } - await expect(assertion).resolves.toBeUndefined(); - }); - - // FN-5475 / option-2 promotion check: a commit attributed to another - // task that's already reachable from the integration target was integrated - // via fast-forward and shouldn't be treated as contamination on a - // downstream branch that briefly inherited it. - it("assertCleanBranchAtBase treats foreign-attributed commits that are ancestors of main as promoted", async () => { - mockedExecSync.mockImplementation((cmd: string | string[]) => { - const command = typeof cmd === "string" ? cmd : cmd[0]; - if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) { - return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n"); - } - if (command.includes("git merge-base --is-ancestor 'bbb222' 'main'")) { - // Simulate the FN-5475 case: foreign commit is already on local main. - return Buffer.from(""); - } - throw new Error(`Unexpected command: ${command}`); - }); - - await expect( - assertCleanBranchAtBase("/tmp/repo", "fusion/fn-4068", "main", "FN-4068"), - ).resolves.toBeUndefined(); - }); - - it("assertCleanBranchAtBase treats foreign-attributed commits that are only ancestors of origin/main as promoted", async () => { - mockedExecSync.mockImplementation((cmd: string | string[]) => { - const command = typeof cmd === "string" ? cmd : cmd[0]; - if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) { - return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n"); - } - if (command.includes("git merge-base --is-ancestor 'bbb222' 'main'")) { - // Local main is stale and does not yet contain the promoted dependency. - const err = new Error("not an ancestor") as Error & { stderr?: string }; - err.stderr = ""; - throw err; - } - if (command.includes("git merge-base --is-ancestor 'bbb222' 'origin/main'")) { - // Remote-tracking integration branch already contains it. - return Buffer.from(""); - } - throw new Error(`Unexpected command: ${command}`); - }); - - await expect( - assertCleanBranchAtBase("/tmp/repo", "fusion/fn-4068", "main", "FN-4068"), - ).resolves.toBeUndefined(); - }); - - it("assertCleanBranchAtBase still throws when foreign-attributed commits are NOT on any integration ref", async () => { - mockedExecSync.mockImplementation((cmd: string | string[]) => { - const command = typeof cmd === "string" ? cmd : cmd[0]; - if (command.includes("git log --format=%H%x1f%s%x1f%b 'main..fusion/fn-4068'")) { - return Buffer.from("bbb222feat(FN-4386): foreignFusion-Task-Id: FN-4386\n"); - } - if (command.includes("git merge-base --is-ancestor")) { - // Not on any integration ref — exits non-zero. - const err = new Error("not an ancestor") as Error & { stderr?: string }; - err.stderr = ""; - throw err; - } - throw new Error(`Unexpected command: ${command}`); - }); - - await expect( - assertCleanBranchAtBase("/tmp/repo", "fusion/fn-4068", "main", "FN-4068"), - ).rejects.toBeInstanceOf(BranchCrossContaminationError); + await expect(assertCleanBranchAtBase("/tmp/repo", "fusion/fn-4068", "main", "FN-4068")).resolves.toBeUndefined(); }); describe("reportBranchAttribution", () => { diff --git a/packages/engine/src/branch-conflicts.ts b/packages/engine/src/branch-conflicts.ts index 2a24a32f33..adea60350c 100644 --- a/packages/engine/src/branch-conflicts.ts +++ b/packages/engine/src/branch-conflicts.ts @@ -367,8 +367,7 @@ export async function branchTipCarriesTaskIdTrailer( /** * Whole-branch authority check: the branch ref exists, its tip carries the - * task's Fusion-Task-Id trailer, and (when a base is supplied) the range - * `base..branch` has no foreign FN-attributed commits. + * task's Fusion-Task-Id trailer. * * Returns `{ ok: true }` when safe to treat the branch ref as authoritative * for `taskId`. On failure, returns `{ ok: false, reason }` so callers can @@ -389,87 +388,22 @@ export async function isBranchAuthoritativeForTask( if (!tipCarriesTrailer) { return { ok: false, reason: "tip-missing-task-trailer" }; } - if (baseSha) { - try { - await assertCleanBranchAtBase(repoDir, branch, baseSha, taskId); - } catch (err) { - const reason = err instanceof BranchCrossContaminationError ? "foreign-contamination" : "clean-branch-check-failed"; - return { ok: false, reason }; - } - } return { ok: true }; } -/** - * Cheap ancestry check: is `commitSha` reachable from `ref`? - * - * Used to recognize "promoted" commits during contamination audits: when - * the engine fast-forwards local `main` with a sibling task's commit, that - * commit's `Fusion-Task-Id` trailer still points at the sibling, but the - * commit itself is now integrated. Treating it as foreign contamination - * for downstream tasks branched from the same main tip is incorrect — the - * commit is, by definition, ancestral on the integration target. - * - * Returns `false` on any git error (missing ref, repo unreadable, etc.) - * so the caller falls back to the conservative trailer-only judgement. - */ -async function isAncestorOf(repoDir: string, commitSha: string, ref: string): Promise { - try { - await runGit(repoDir, `git merge-base --is-ancestor ${quoteShellArg(commitSha)} ${quoteShellArg(ref)}`); - return true; - } catch { - return false; - } -} - export async function assertCleanBranchAtBase( repoDir: string, branchName: string, baseSha: string, taskId: string, ): Promise { - const output = await runGit(repoDir, `git log --format=%H%x1f%s%x1f%b ${quoteShellArg(`${baseSha}..${branchName}`)}`) - .catch(() => ""); - if (!output) return; - - const subjectPattern = /^(feat|fix|test|chore|docs|refactor|perf|build)\((FN-\d+)\):/i; - const trailerPattern = /(?:^|\n)Fusion-Task-Id:\s*(FN-\d+)\s*(?:\n|$)/i; - const candidateForeign: BranchCrossContaminationCommit[] = []; - for (const line of output.split("\n").map((entry) => entry.trim()).filter(Boolean)) { - const [sha, subject, body] = line.split("\u001f"); - const subjectMatch = (subject ?? "").match(subjectPattern); - const trailerMatch = (body ?? "").match(trailerPattern); - const attributedTaskId = (trailerMatch?.[1] ?? subjectMatch?.[2] ?? "").toUpperCase(); - if (attributedTaskId && attributedTaskId !== taskId.toUpperCase()) { - candidateForeign.push({ sha, subject: subject ?? "", foreignTaskId: attributedTaskId }); - } - } - - if (candidateForeign.length === 0) return; - - // FN-5475/FN-219: a commit attributed to another task that's already - // reachable from the integration target was promoted through integration. - // Treat it as ancestral, not contamination. Check both local `main` and - // `origin/main`: long-running dashboards can have stale local main while - // the remote-tracking branch already contains the promoted dependency, and - // using only local main produces false branch-cross-contamination loops. - const integratedRefs = ["main", "origin/main"]; - const foreignCommits: BranchCrossContaminationCommit[] = []; - for (const commit of candidateForeign) { - let alreadyIntegrated = false; - for (const ref of integratedRefs) { - if (await isAncestorOf(repoDir, commit.sha, ref)) { - alreadyIntegrated = true; - break; - } - } - if (alreadyIntegrated) continue; - foreignCommits.push(commit); - } - - if (foreignCommits.length > 0) { - throw new BranchCrossContaminationError({ branchName, baseSha, taskId, foreignCommits }); - } + // Foreign task attribution in a branch range is informational only. Stacked + // task branches and cherry-equivalent commits are handled by merge/display + // attribution, not by failing worktree acquisition or branch authority. + void repoDir; + void branchName; + void baseSha; + void taskId; } export interface ClassifyBootstrapMisbindingInput {