From cea1d1a0f1a7139e749e220c88e1654ba9f2cd73 Mon Sep 17 00:00:00 2001 From: ChiragArora31 Date: Mon, 20 Apr 2026 11:14:00 +0530 Subject: [PATCH 1/3] fix(scm-github): avoid conflict flag on blocked draft PRs Compute noConflicts from explicit CONFLICTING mergeability only so draft/blocked/unknown states do not trigger merge-conflict reactions. Made-with: Cursor --- packages/plugins/scm-github/src/index.ts | 2 +- packages/plugins/scm-github/test/index.test.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/plugins/scm-github/src/index.ts b/packages/plugins/scm-github/src/index.ts index 5c2ed2f901..dff07cbd13 100644 --- a/packages/plugins/scm-github/src/index.ts +++ b/packages/plugins/scm-github/src/index.ts @@ -1205,7 +1205,7 @@ function createGitHubSCM(): SCM { // Conflicts / merge state const mergeable = (data.mergeable ?? "").toUpperCase(); const mergeState = (data.mergeStateStatus ?? "").toUpperCase(); - const noConflicts = mergeable === "MERGEABLE"; + const noConflicts = mergeable !== "CONFLICTING"; if (mergeable === "CONFLICTING") { blockers.push("Merge conflicts"); } else if (mergeable === "UNKNOWN" || mergeable === "") { diff --git a/packages/plugins/scm-github/test/index.test.ts b/packages/plugins/scm-github/test/index.test.ts index 8f6b2d1f52..32e4ac25f1 100644 --- a/packages/plugins/scm-github/test/index.test.ts +++ b/packages/plugins/scm-github/test/index.test.ts @@ -1038,6 +1038,7 @@ describe("scm-github plugin", () => { const result = await scm.getMergeability(pr); expect(result.blockers).toContain("Review required"); + expect(result.noConflicts).toBe(true); }); it("reports merge conflicts as blockers", async () => { @@ -1055,7 +1056,7 @@ describe("scm-github plugin", () => { expect(result.blockers).toContain("Merge conflicts"); }); - it("reports UNKNOWN mergeable as noConflicts false", async () => { + it("treats UNKNOWN mergeable as non-conflicting", async () => { mockGh({ state: "OPEN" }); // getPRState mockGh({ mergeable: "UNKNOWN", @@ -1066,7 +1067,7 @@ describe("scm-github plugin", () => { mockGh([{ name: "build", state: "SUCCESS" }]); const result = await scm.getMergeability(pr); - expect(result.noConflicts).toBe(false); + expect(result.noConflicts).toBe(true); expect(result.blockers).toContain("Merge status unknown (GitHub is computing)"); expect(result.mergeable).toBe(false); }); From b1121a4cd38877dfc9b92bfee8f100854a0d853c Mon Sep 17 00:00:00 2001 From: ChiragArora31 Date: Tue, 21 Apr 2026 07:17:16 +0530 Subject: [PATCH 2/3] test(scm-github): add #1314 draft mergeability regression and document noConflicts - Clarify that noConflicts follows graphql-batch semantics (not conflicting unless mergeable is CONFLICTING). - Add draft PR with MERGEABLE + BLOCKED merge state asserting noConflicts true while draft/branch-protection blockers keep mergeable false. Made-with: Cursor --- packages/plugins/scm-github/src/index.ts | 4 +++- packages/plugins/scm-github/test/index.test.ts | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/plugins/scm-github/src/index.ts b/packages/plugins/scm-github/src/index.ts index dff07cbd13..c4a4f1fb0b 100644 --- a/packages/plugins/scm-github/src/index.ts +++ b/packages/plugins/scm-github/src/index.ts @@ -1202,7 +1202,9 @@ function createGitHubSCM(): SCM { blockers.push("Review required"); } - // Conflicts / merge state + // Conflicts / merge state — `noConflicts` means "not explicitly CONFLICTING" + // (matches graphql-batch `hasConflicts`), so draft / BLOCKED / UNKNOWN do not + // look like merge conflicts to lifecycle conflict reactions. const mergeable = (data.mergeable ?? "").toUpperCase(); const mergeState = (data.mergeStateStatus ?? "").toUpperCase(); const noConflicts = mergeable !== "CONFLICTING"; diff --git a/packages/plugins/scm-github/test/index.test.ts b/packages/plugins/scm-github/test/index.test.ts index 32e4ac25f1..0a963c0919 100644 --- a/packages/plugins/scm-github/test/index.test.ts +++ b/packages/plugins/scm-github/test/index.test.ts @@ -1087,6 +1087,23 @@ describe("scm-github plugin", () => { expect(result.mergeable).toBe(false); }); + it("draft PR with MERGEABLE + BLOCKED merge state is not treated as conflicting (#1314)", async () => { + mockGh({ state: "OPEN" }); // getPRState + mockGh({ + mergeable: "MERGEABLE", + reviewDecision: "APPROVED", + mergeStateStatus: "BLOCKED", + isDraft: true, + }); + mockGh([{ name: "build", state: "SUCCESS" }]); + + const result = await scm.getMergeability(pr); + expect(result.noConflicts).toBe(true); + expect(result.mergeable).toBe(false); + expect(result.blockers).toContain("PR is still a draft"); + expect(result.blockers).toContain("Merge is blocked by branch protection"); + }); + it("reports multiple blockers simultaneously", async () => { mockGh({ state: "OPEN" }); // getPRState mockGh({ From 05d43a7af03eda3d4ac0dd2dc50d1819a2e3360b Mon Sep 17 00:00:00 2001 From: ChiragArora31 Date: Sun, 17 May 2026 05:18:00 +0530 Subject: [PATCH 3/3] test(scm-github): cover unknown draft mergeability Co-authored-by: Cursor --- packages/plugins/scm-github/test/index.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/plugins/scm-github/test/index.test.ts b/packages/plugins/scm-github/test/index.test.ts index fe09aef0be..118aa4b73e 100644 --- a/packages/plugins/scm-github/test/index.test.ts +++ b/packages/plugins/scm-github/test/index.test.ts @@ -1177,10 +1177,10 @@ describe("scm-github plugin", () => { expect(result.mergeable).toBe(false); }); - it("draft PR with MERGEABLE + BLOCKED merge state is not treated as conflicting (#1314)", async () => { + it("draft PR with UNKNOWN + BLOCKED merge state is not treated as conflicting (#1314)", async () => { mockGh({ state: "OPEN" }); // getPRState mockGh({ - mergeable: "MERGEABLE", + mergeable: "UNKNOWN", reviewDecision: "APPROVED", mergeStateStatus: "BLOCKED", isDraft: true, @@ -1191,6 +1191,7 @@ describe("scm-github plugin", () => { expect(result.noConflicts).toBe(true); expect(result.mergeable).toBe(false); expect(result.blockers).toContain("PR is still a draft"); + expect(result.blockers).toContain("Merge status unknown (GitHub is computing)"); expect(result.blockers).toContain("Merge is blocked by branch protection"); });