From 2868f85c3e44e95848aed5c051a302c2de61fc9b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 20:51:28 +0000
Subject: [PATCH 1/4] Initial plan


From f96455aa8fd57c6c048ffe03a3499ae8ca74cc53 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 21:16:15 +0000
Subject: [PATCH 2/4] =?UTF-8?q?fix:=20downgrade=20REQUEST=5FCHANGES?=
 =?UTF-8?q?=E2=86=92COMMENT=20on=20self-authored=20PRs=20in=20safe-outputs?=
 =?UTF-8?q?=20PR=20review?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixes 422 "Can not request changes on your own pull request" failures that
occur when the PR Code Quality Reviewer (or any reviewer workflow) runs
against a PR authored by the same bot identity (e.g. linter-miner branches).

Three complementary defences in pr_review_buffer.cjs:

1. Proactive check: before building requestParams, compare pullRequest.user.login
   against GITHUB_ACTOR. If they match, downgrade REQUEST_CHANGES/APPROVE → COMMENT
   immediately, avoiding the 422 entirely.

2. Own-PR COMMENT retry → body-only COMMENT fallback: when the own-PR COMMENT retry
   (reactive path) also fails with "Line could not be resolved" or "Path could not be
   resolved", fall back to a body-only COMMENT review instead of hard-failing.

3. Body-only fallback own-PR guard: when the "Line could not be resolved" body-only
   fallback itself gets a 422 "Can not request changes on your own pull request",
   downgrade the body-only event to COMMENT and retry — this is the exact scenario
   that caused the 4/4 safe-output failures in runs 29946005059 and 29910168345.

Six new tests cover all three paths.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/pr_review_buffer.cjs      |  61 ++++++-
 actions/setup/js/pr_review_buffer.test.cjs | 203 +++++++++++++++++++++
 2 files changed, 257 insertions(+), 7 deletions(-)

diff --git a/actions/setup/js/pr_review_buffer.cjs b/actions/setup/js/pr_review_buffer.cjs
index 1417445f2ab..ef15dc2e33c 100644
--- a/actions/setup/js/pr_review_buffer.cjs
+++ b/actions/setup/js/pr_review_buffer.cjs
@@ -302,6 +302,18 @@ function createReviewBuffer() {
     let event = reviewMetadata ? reviewMetadata.event : "COMMENT";
     let body = reviewMetadata ? reviewMetadata.body : "";
 
+    // Proactively downgrade REQUEST_CHANGES/APPROVE → COMMENT on self-authored PRs.
+    // GITHUB_TOKEN lacks read:user scope for reliable identity pre-flight via the API, so
+    // compare the PR author login against GITHUB_ACTOR as a best-effort guard.
+    // The reactive 422 retry in the catch block is retained as a safety net for cases this misses.
+    if (event !== "COMMENT" && pullRequest.user?.login) {
+      const actor = (process.env.GITHUB_ACTOR || "").trim();
+      if (actor && pullRequest.user.login === actor) {
+        core.warning(`PR #${pullRequestNumber} is authored by the workflow actor ('${actor}'). ` + `Downgrading '${event}' → 'COMMENT' to avoid 422 "Can not request changes on your own pull request".`);
+        event = "COMMENT";
+      }
+    }
+
     // Determine if we should add footer based on footer mode
     let shouldAddFooter = footerMode === "always";
     if (footerMode === "if-body") {
@@ -620,10 +632,29 @@ function createReviewBuffer() {
           core.info(`Created PR review #${review.id}: ${review.html_url}`);
           return buildReviewSuccessResult(review, "COMMENT", comments.length, afterState);
         } catch (retryError) {
-          core.error(`Failed to submit PR review on retry: ${getErrorMessage(retryError)}`);
+          const retryErrorMsg = getErrorMessage(retryError);
+          // If the COMMENT retry still fails due to unresolvable line(s), fall back to body-only COMMENT.
+          if (retryErrorMsg.includes("Line could not be resolved") || retryErrorMsg.includes("Path could not be resolved")) {
+            core.warning(`COMMENT retry on own PR failed with unresolvable line(s): ${retryErrorMsg}. Falling back to body-only COMMENT.`);
+            try {
+              const ownPrBodyOnlyParams = { ...requestParams };
+              delete ownPrBodyOnlyParams.comments;
+              ownPrBodyOnlyParams.event = "COMMENT";
+              ownPrBodyOnlyParams.body = appendUnanchoredCommentsSection(typeof requestParams.body === "string" ? requestParams.body : "", comments);
+              const { data: review } = await createReviewWithRetry(ownPrBodyOnlyParams);
+              await maybeSupersedeOlderReviews(review.id);
+              const afterState = await fetchAfterStateIfAvailable();
+              core.info(`Created PR review #${review.id} (own-PR body-only COMMENT): ${review.html_url}`);
+              return buildReviewSuccessResult(review, "COMMENT", 0, afterState);
+            } catch (bodyOnlyError) {
+              core.error(`Failed to submit body-only COMMENT review: ${getErrorMessage(bodyOnlyError)}`);
+              return { success: false, error: getErrorMessage(bodyOnlyError) };
+            }
+          }
+          core.error(`Failed to submit PR review on retry: ${retryErrorMsg}`);
           return {
             success: false,
-            error: getErrorMessage(retryError),
+            error: retryErrorMsg,
           };
         }
       }
@@ -691,20 +722,36 @@ function createReviewBuffer() {
         }
 
         core.warning(`PR review submission failed due to unresolvable comment line(s): ${errorMessage}. Retrying as body-only review.`);
+        const bodyOnlyParams = { ...requestParams };
+        delete bodyOnlyParams.comments;
+        bodyOnlyParams.body = appendUnanchoredCommentsSection(typeof requestParams.body === "string" ? requestParams.body : "", comments);
         try {
-          const bodyOnlyParams = { ...requestParams };
-          delete bodyOnlyParams.comments;
-          bodyOnlyParams.body = appendUnanchoredCommentsSection(typeof requestParams.body === "string" ? requestParams.body : "", comments);
           const { data: review } = await createReviewWithRetry(bodyOnlyParams);
           await maybeSupersedeOlderReviews(review.id);
           const afterState = await fetchAfterStateIfAvailable();
           core.info(`Created PR review #${review.id} (body-only fallback): ${review.html_url}`);
           return buildReviewSuccessResult(review, event, 0, afterState);
         } catch (retryError) {
-          core.error(`Failed to submit body-only PR review: ${getErrorMessage(retryError)}`);
+          const retryErrorMsg = getErrorMessage(retryError);
+          // If body-only also fails because it's a self-authored PR, retry as body-only COMMENT.
+          if (bodyOnlyParams.event !== "COMMENT" && ownPrMessages.some(msg => retryErrorMsg.includes(msg))) {
+            core.warning(`Body-only ${bodyOnlyParams.event} review rejected on own PR. Retrying as body-only COMMENT.`);
+            try {
+              bodyOnlyParams.event = "COMMENT";
+              const { data: review } = await createReviewWithRetry(bodyOnlyParams);
+              await maybeSupersedeOlderReviews(review.id);
+              const afterState = await fetchAfterStateIfAvailable();
+              core.info(`Created PR review #${review.id} (body-only COMMENT fallback): ${review.html_url}`);
+              return buildReviewSuccessResult(review, "COMMENT", 0, afterState);
+            } catch (ownPrRetryError) {
+              core.error(`Failed to submit body-only COMMENT review: ${getErrorMessage(ownPrRetryError)}`);
+              return { success: false, error: getErrorMessage(ownPrRetryError) };
+            }
+          }
+          core.error(`Failed to submit body-only PR review: ${retryErrorMsg}`);
           return {
             success: false,
-            error: getErrorMessage(retryError),
+            error: retryErrorMsg,
           };
         }
       }
diff --git a/actions/setup/js/pr_review_buffer.test.cjs b/actions/setup/js/pr_review_buffer.test.cjs
index 8cf7c8a2126..3b0a6430047 100644
--- a/actions/setup/js/pr_review_buffer.test.cjs
+++ b/actions/setup/js/pr_review_buffer.test.cjs
@@ -896,6 +896,117 @@ describe("pr_review_buffer (factory pattern)", () => {
       expect(callArgs.event).toBe("APPROVE");
     });
 
+    it("should proactively downgrade REQUEST_CHANGES → COMMENT when PR author matches GITHUB_ACTOR", async () => {
+      const previousActor = process.env.GITHUB_ACTOR;
+      process.env.GITHUB_ACTOR = "linter-miner[bot]";
+      try {
+        buffer.addComment({ path: "test.js", line: 1, body: "comment" });
+        buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
+        buffer.setReviewContext({
+          repo: "owner/repo",
+          repoParts: { owner: "owner", repo: "repo" },
+          pullRequestNumber: 42,
+          pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
+        });
+
+        mockGithub.rest.pulls.createReview.mockResolvedValueOnce({
+          data: {
+            id: 703,
+            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-703",
+          },
+        });
+
+        const result = await buffer.submitReview();
+
+        expect(result.success).toBe(true);
+        expect(result.event).toBe("COMMENT");
+        // Only one call — no 422 because we downgraded proactively
+        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
+        const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
+        expect(callArgs.event).toBe("COMMENT");
+        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Downgrading 'REQUEST_CHANGES' → 'COMMENT'"));
+      } finally {
+        if (previousActor === undefined) {
+          delete process.env.GITHUB_ACTOR;
+        } else {
+          process.env.GITHUB_ACTOR = previousActor;
+        }
+      }
+    });
+
+    it("should proactively downgrade APPROVE → COMMENT when PR author matches GITHUB_ACTOR", async () => {
+      const previousActor = process.env.GITHUB_ACTOR;
+      process.env.GITHUB_ACTOR = "bot-user";
+      try {
+        buffer.addComment({ path: "test.js", line: 1, body: "comment" });
+        buffer.setReviewMetadata("LGTM", "APPROVE");
+        buffer.setReviewContext({
+          repo: "owner/repo",
+          repoParts: { owner: "owner", repo: "repo" },
+          pullRequestNumber: 42,
+          pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
+        });
+
+        mockGithub.rest.pulls.createReview.mockResolvedValueOnce({
+          data: {
+            id: 704,
+            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-704",
+          },
+        });
+
+        const result = await buffer.submitReview();
+
+        expect(result.success).toBe(true);
+        expect(result.event).toBe("COMMENT");
+        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
+        const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
+        expect(callArgs.event).toBe("COMMENT");
+        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Downgrading 'APPROVE' → 'COMMENT'"));
+      } finally {
+        if (previousActor === undefined) {
+          delete process.env.GITHUB_ACTOR;
+        } else {
+          process.env.GITHUB_ACTOR = previousActor;
+        }
+      }
+    });
+
+    it("should not downgrade when PR author does not match GITHUB_ACTOR", async () => {
+      const previousActor = process.env.GITHUB_ACTOR;
+      process.env.GITHUB_ACTOR = "reviewer-bot";
+      try {
+        buffer.addComment({ path: "test.js", line: 1, body: "comment" });
+        buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
+        buffer.setReviewContext({
+          repo: "owner/repo",
+          repoParts: { owner: "owner", repo: "repo" },
+          pullRequestNumber: 42,
+          pullRequest: { head: { sha: "abc123" }, user: { login: "pr-author" } },
+        });
+
+        mockGithub.rest.pulls.createReview.mockResolvedValueOnce({
+          data: {
+            id: 705,
+            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-705",
+          },
+        });
+
+        const result = await buffer.submitReview();
+
+        expect(result.success).toBe(true);
+        expect(result.event).toBe("REQUEST_CHANGES");
+        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
+        const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
+        expect(callArgs.event).toBe("REQUEST_CHANGES");
+      } finally {
+        if (previousActor === undefined) {
+          delete process.env.GITHUB_ACTOR;
+        } else {
+          process.env.GITHUB_ACTOR = previousActor;
+        }
+      }
+    });
+
     it("should not retry with COMMENT when event is already COMMENT and API error occurs", async () => {
       buffer.addComment({ path: "test.js", line: 1, body: "comment" });
       buffer.setReviewMetadata("Some feedback", "COMMENT");
@@ -933,6 +1044,41 @@ describe("pr_review_buffer (factory pattern)", () => {
       expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(2);
     });
 
+    it("should fall back to body-only COMMENT when own-PR COMMENT retry also fails with line-resolution error", async () => {
+      buffer.addComment({ path: "file.go", line: 10, body: "Inline comment" });
+      buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 42,
+        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
+      });
+
+      mockGithub.rest.pulls.createReview
+        // Initial attempt — own-PR 422
+        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+        // COMMENT retry with inline comments — line unresolvable
+        .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
+        // Body-only COMMENT fallback — success
+        .mockResolvedValueOnce({
+          data: {
+            id: 800,
+            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-800",
+          },
+        });
+
+      const result = await buffer.submitReview();
+
+      expect(result.success).toBe(true);
+      expect(result.event).toBe("COMMENT");
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+      const bodyOnlyArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
+      expect(bodyOnlyArgs.event).toBe("COMMENT");
+      expect(bodyOnlyArgs.comments).toBeUndefined();
+      expect(bodyOnlyArgs.body).toContain("Inline comment");
+      expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("COMMENT retry on own PR failed with unresolvable line(s)"));
+    });
+
     it("should skip (success:true, skipped:true) when PR is permanently locked after all retries", async () => {
       const setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation(handler => {
         if (typeof handler === "function") {
@@ -1417,6 +1563,63 @@ describe("pr_review_buffer (factory pattern)", () => {
       expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(2);
     });
 
+    it("should retry body-only as COMMENT when body-only REQUEST_CHANGES is rejected on own PR after line-resolution failure", async () => {
+      buffer.addComment({ path: "file.go", line: 5, body: "Nil dereference on this line" });
+      buffer.setReviewMetadata("Fix the blocking issues.", "REQUEST_CHANGES");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 47375,
+        pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
+      });
+
+      mockGithub.rest.pulls.createReview
+        // Initial attempt — line resolution fails (this triggers the body-only path)
+        .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
+        // Body-only REQUEST_CHANGES — own-PR 422
+        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+        // Body-only COMMENT retry — success
+        .mockResolvedValueOnce({
+          data: {
+            id: 900,
+            html_url: "https://github.com/owner/repo/pull/47375#pullrequestreview-900",
+          },
+        });
+
+      const result = await buffer.submitReview();
+
+      expect(result.success).toBe(true);
+      expect(result.event).toBe("COMMENT");
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+      const bodyOnlyCommentArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
+      expect(bodyOnlyCommentArgs.event).toBe("COMMENT");
+      expect(bodyOnlyCommentArgs.comments).toBeUndefined();
+      expect(bodyOnlyCommentArgs.body).toContain("Nil dereference on this line");
+      expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Body-only REQUEST_CHANGES review rejected on own PR"));
+    });
+
+    it("should return failure when body-only COMMENT retry also fails on own PR", async () => {
+      buffer.addComment({ path: "file.go", line: 5, body: "Review comment" });
+      buffer.setReviewMetadata("Feedback.", "REQUEST_CHANGES");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 42,
+        pullRequest: { head: { sha: "abc123" } },
+      });
+
+      mockGithub.rest.pulls.createReview
+        .mockRejectedValueOnce(new Error("Line could not be resolved"))
+        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+        .mockRejectedValueOnce(new Error("Unexpected server error"));
+
+      const result = await buffer.submitReview();
+
+      expect(result.success).toBe(false);
+      expect(result.error).toContain("Unexpected server error");
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+    });
+
     it("should escape HTML-sensitive characters in fallback summary and body", async () => {
       buffer.addComment({
         path: "src/<unsafe>&\"'.js",

From 8f84f653303d6fc848a4d4433d6f69c8fabd357d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 22:19:49 +0000
Subject: [PATCH 3/4] fix: remove self-author actor heuristic in PR review
 buffer

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
 actions/setup/js/pr_review_buffer.cjs      |  12 -
 actions/setup/js/pr_review_buffer.test.cjs | 243 +++++++--------------
 2 files changed, 81 insertions(+), 174 deletions(-)

diff --git a/actions/setup/js/pr_review_buffer.cjs b/actions/setup/js/pr_review_buffer.cjs
index ef15dc2e33c..3a3e3cf2a51 100644
--- a/actions/setup/js/pr_review_buffer.cjs
+++ b/actions/setup/js/pr_review_buffer.cjs
@@ -302,18 +302,6 @@ function createReviewBuffer() {
     let event = reviewMetadata ? reviewMetadata.event : "COMMENT";
     let body = reviewMetadata ? reviewMetadata.body : "";
 
-    // Proactively downgrade REQUEST_CHANGES/APPROVE → COMMENT on self-authored PRs.
-    // GITHUB_TOKEN lacks read:user scope for reliable identity pre-flight via the API, so
-    // compare the PR author login against GITHUB_ACTOR as a best-effort guard.
-    // The reactive 422 retry in the catch block is retained as a safety net for cases this misses.
-    if (event !== "COMMENT" && pullRequest.user?.login) {
-      const actor = (process.env.GITHUB_ACTOR || "").trim();
-      if (actor && pullRequest.user.login === actor) {
-        core.warning(`PR #${pullRequestNumber} is authored by the workflow actor ('${actor}'). ` + `Downgrading '${event}' → 'COMMENT' to avoid 422 "Can not request changes on your own pull request".`);
-        event = "COMMENT";
-      }
-    }
-
     // Determine if we should add footer based on footer mode
     let shouldAddFooter = footerMode === "always";
     if (footerMode === "if-body") {
diff --git a/actions/setup/js/pr_review_buffer.test.cjs b/actions/setup/js/pr_review_buffer.test.cjs
index 3b0a6430047..850d4754ad4 100644
--- a/actions/setup/js/pr_review_buffer.test.cjs
+++ b/actions/setup/js/pr_review_buffer.test.cjs
@@ -896,47 +896,27 @@ describe("pr_review_buffer (factory pattern)", () => {
       expect(callArgs.event).toBe("APPROVE");
     });
 
-    it("should proactively downgrade REQUEST_CHANGES → COMMENT when PR author matches GITHUB_ACTOR", async () => {
-      const previousActor = process.env.GITHUB_ACTOR;
-      process.env.GITHUB_ACTOR = "linter-miner[bot]";
-      try {
-        buffer.addComment({ path: "test.js", line: 1, body: "comment" });
-        buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
-        buffer.setReviewContext({
-          repo: "owner/repo",
-          repoParts: { owner: "owner", repo: "repo" },
-          pullRequestNumber: 42,
-          pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
-        });
+    it("should not retry with COMMENT when event is already COMMENT and API error occurs", async () => {
+      buffer.addComment({ path: "test.js", line: 1, body: "comment" });
+      buffer.setReviewMetadata("Some feedback", "COMMENT");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 42,
+        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
+      });
 
-        mockGithub.rest.pulls.createReview.mockResolvedValueOnce({
-          data: {
-            id: 703,
-            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-703",
-          },
-        });
+      mockGithub.rest.pulls.createReview.mockRejectedValue(new Error("Validation Failed"));
 
-        const result = await buffer.submitReview();
+      const result = await buffer.submitReview();
 
-        expect(result.success).toBe(true);
-        expect(result.event).toBe("COMMENT");
-        // Only one call — no 422 because we downgraded proactively
-        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
-        const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
-        expect(callArgs.event).toBe("COMMENT");
-        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Downgrading 'REQUEST_CHANGES' → 'COMMENT'"));
-      } finally {
-        if (previousActor === undefined) {
-          delete process.env.GITHUB_ACTOR;
-        } else {
-          process.env.GITHUB_ACTOR = previousActor;
-        }
-      }
+      expect(result.success).toBe(false);
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
     });
 
-    it("should proactively downgrade APPROVE → COMMENT when PR author matches GITHUB_ACTOR", async () => {
+    it("should return failure when retry also fails", async () => {
       const previousActor = process.env.GITHUB_ACTOR;
-      process.env.GITHUB_ACTOR = "bot-user";
+      process.env.GITHUB_ACTOR = "someone-else";
       try {
         buffer.addComment({ path: "test.js", line: 1, body: "comment" });
         buffer.setReviewMetadata("LGTM", "APPROVE");
@@ -947,21 +927,13 @@ describe("pr_review_buffer (factory pattern)", () => {
           pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
         });
 
-        mockGithub.rest.pulls.createReview.mockResolvedValueOnce({
-          data: {
-            id: 704,
-            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-704",
-          },
-        });
+        mockGithub.rest.pulls.createReview.mockRejectedValueOnce(new Error("Can not approve your own pull request")).mockRejectedValueOnce(new Error("Some other error"));
 
         const result = await buffer.submitReview();
 
-        expect(result.success).toBe(true);
-        expect(result.event).toBe("COMMENT");
-        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
-        const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
-        expect(callArgs.event).toBe("COMMENT");
-        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Downgrading 'APPROVE' → 'COMMENT'"));
+        expect(result.success).toBe(false);
+        expect(result.error).toContain("Some other error");
+        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(2);
       } finally {
         if (previousActor === undefined) {
           delete process.env.GITHUB_ACTOR;
@@ -971,33 +943,42 @@ describe("pr_review_buffer (factory pattern)", () => {
       }
     });
 
-    it("should not downgrade when PR author does not match GITHUB_ACTOR", async () => {
+    it("should fall back to body-only COMMENT when own-PR COMMENT retry also fails with line-resolution error", async () => {
       const previousActor = process.env.GITHUB_ACTOR;
-      process.env.GITHUB_ACTOR = "reviewer-bot";
+      process.env.GITHUB_ACTOR = "someone-else";
       try {
-        buffer.addComment({ path: "test.js", line: 1, body: "comment" });
+        buffer.addComment({ path: "file.go", line: 10, body: "Inline comment" });
         buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
         buffer.setReviewContext({
           repo: "owner/repo",
           repoParts: { owner: "owner", repo: "repo" },
           pullRequestNumber: 42,
-          pullRequest: { head: { sha: "abc123" }, user: { login: "pr-author" } },
+          pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
         });
 
-        mockGithub.rest.pulls.createReview.mockResolvedValueOnce({
-          data: {
-            id: 705,
-            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-705",
-          },
-        });
+        mockGithub.rest.pulls.createReview
+          // Initial attempt — own-PR 422
+          .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+          // COMMENT retry with inline comments — line unresolvable
+          .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
+          // Body-only COMMENT fallback — success
+          .mockResolvedValueOnce({
+            data: {
+              id: 800,
+              html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-800",
+            },
+          });
 
         const result = await buffer.submitReview();
 
         expect(result.success).toBe(true);
-        expect(result.event).toBe("REQUEST_CHANGES");
-        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
-        const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
-        expect(callArgs.event).toBe("REQUEST_CHANGES");
+        expect(result.event).toBe("COMMENT");
+        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+        const bodyOnlyArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
+        expect(bodyOnlyArgs.event).toBe("COMMENT");
+        expect(bodyOnlyArgs.comments).toBeUndefined();
+        expect(bodyOnlyArgs.body).toContain("Inline comment");
+        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("COMMENT retry on own PR failed with unresolvable line(s)"));
       } finally {
         if (previousActor === undefined) {
           delete process.env.GITHUB_ACTOR;
@@ -1007,78 +988,6 @@ describe("pr_review_buffer (factory pattern)", () => {
       }
     });
 
-    it("should not retry with COMMENT when event is already COMMENT and API error occurs", async () => {
-      buffer.addComment({ path: "test.js", line: 1, body: "comment" });
-      buffer.setReviewMetadata("Some feedback", "COMMENT");
-      buffer.setReviewContext({
-        repo: "owner/repo",
-        repoParts: { owner: "owner", repo: "repo" },
-        pullRequestNumber: 42,
-        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
-      });
-
-      mockGithub.rest.pulls.createReview.mockRejectedValue(new Error("Validation Failed"));
-
-      const result = await buffer.submitReview();
-
-      expect(result.success).toBe(false);
-      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(1);
-    });
-
-    it("should return failure when retry also fails", async () => {
-      buffer.addComment({ path: "test.js", line: 1, body: "comment" });
-      buffer.setReviewMetadata("LGTM", "APPROVE");
-      buffer.setReviewContext({
-        repo: "owner/repo",
-        repoParts: { owner: "owner", repo: "repo" },
-        pullRequestNumber: 42,
-        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
-      });
-
-      mockGithub.rest.pulls.createReview.mockRejectedValueOnce(new Error("Can not approve your own pull request")).mockRejectedValueOnce(new Error("Some other error"));
-
-      const result = await buffer.submitReview();
-
-      expect(result.success).toBe(false);
-      expect(result.error).toContain("Some other error");
-      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(2);
-    });
-
-    it("should fall back to body-only COMMENT when own-PR COMMENT retry also fails with line-resolution error", async () => {
-      buffer.addComment({ path: "file.go", line: 10, body: "Inline comment" });
-      buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
-      buffer.setReviewContext({
-        repo: "owner/repo",
-        repoParts: { owner: "owner", repo: "repo" },
-        pullRequestNumber: 42,
-        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
-      });
-
-      mockGithub.rest.pulls.createReview
-        // Initial attempt — own-PR 422
-        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
-        // COMMENT retry with inline comments — line unresolvable
-        .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
-        // Body-only COMMENT fallback — success
-        .mockResolvedValueOnce({
-          data: {
-            id: 800,
-            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-800",
-          },
-        });
-
-      const result = await buffer.submitReview();
-
-      expect(result.success).toBe(true);
-      expect(result.event).toBe("COMMENT");
-      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
-      const bodyOnlyArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
-      expect(bodyOnlyArgs.event).toBe("COMMENT");
-      expect(bodyOnlyArgs.comments).toBeUndefined();
-      expect(bodyOnlyArgs.body).toContain("Inline comment");
-      expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("COMMENT retry on own PR failed with unresolvable line(s)"));
-    });
-
     it("should skip (success:true, skipped:true) when PR is permanently locked after all retries", async () => {
       const setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation(handler => {
         if (typeof handler === "function") {
@@ -1564,38 +1473,48 @@ describe("pr_review_buffer (factory pattern)", () => {
     });
 
     it("should retry body-only as COMMENT when body-only REQUEST_CHANGES is rejected on own PR after line-resolution failure", async () => {
-      buffer.addComment({ path: "file.go", line: 5, body: "Nil dereference on this line" });
-      buffer.setReviewMetadata("Fix the blocking issues.", "REQUEST_CHANGES");
-      buffer.setReviewContext({
-        repo: "owner/repo",
-        repoParts: { owner: "owner", repo: "repo" },
-        pullRequestNumber: 47375,
-        pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
-      });
-
-      mockGithub.rest.pulls.createReview
-        // Initial attempt — line resolution fails (this triggers the body-only path)
-        .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
-        // Body-only REQUEST_CHANGES — own-PR 422
-        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
-        // Body-only COMMENT retry — success
-        .mockResolvedValueOnce({
-          data: {
-            id: 900,
-            html_url: "https://github.com/owner/repo/pull/47375#pullrequestreview-900",
-          },
+      const previousActor = process.env.GITHUB_ACTOR;
+      process.env.GITHUB_ACTOR = "someone-else";
+      try {
+        buffer.addComment({ path: "file.go", line: 5, body: "Nil dereference on this line" });
+        buffer.setReviewMetadata("Fix the blocking issues.", "REQUEST_CHANGES");
+        buffer.setReviewContext({
+          repo: "owner/repo",
+          repoParts: { owner: "owner", repo: "repo" },
+          pullRequestNumber: 47375,
+          pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
         });
 
-      const result = await buffer.submitReview();
+        mockGithub.rest.pulls.createReview
+          // Initial attempt — line resolution fails (this triggers the body-only path)
+          .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
+          // Body-only REQUEST_CHANGES — own-PR 422
+          .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+          // Body-only COMMENT retry — success
+          .mockResolvedValueOnce({
+            data: {
+              id: 900,
+              html_url: "https://github.com/owner/repo/pull/47375#pullrequestreview-900",
+            },
+          });
 
-      expect(result.success).toBe(true);
-      expect(result.event).toBe("COMMENT");
-      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
-      const bodyOnlyCommentArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
-      expect(bodyOnlyCommentArgs.event).toBe("COMMENT");
-      expect(bodyOnlyCommentArgs.comments).toBeUndefined();
-      expect(bodyOnlyCommentArgs.body).toContain("Nil dereference on this line");
-      expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Body-only REQUEST_CHANGES review rejected on own PR"));
+        const result = await buffer.submitReview();
+
+        expect(result.success).toBe(true);
+        expect(result.event).toBe("COMMENT");
+        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+        const bodyOnlyCommentArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
+        expect(bodyOnlyCommentArgs.event).toBe("COMMENT");
+        expect(bodyOnlyCommentArgs.comments).toBeUndefined();
+        expect(bodyOnlyCommentArgs.body).toContain("Nil dereference on this line");
+        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Body-only REQUEST_CHANGES review rejected on own PR"));
+      } finally {
+        if (previousActor === undefined) {
+          delete process.env.GITHUB_ACTOR;
+        } else {
+          process.env.GITHUB_ACTOR = previousActor;
+        }
+      }
     });
 
     it("should return failure when body-only COMMENT retry also fails on own PR", async () => {

From bff72c705da71e6c02216f617d42d88f17c255f7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 22 Jul 2026 22:23:52 +0000
Subject: [PATCH 4/4] test: remove redundant actor isolation from PR review
 tests

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
 actions/setup/js/pr_review_buffer.test.cjs | 172 +++++++++------------
 1 file changed, 71 insertions(+), 101 deletions(-)

diff --git a/actions/setup/js/pr_review_buffer.test.cjs b/actions/setup/js/pr_review_buffer.test.cjs
index 850d4754ad4..31079debc65 100644
--- a/actions/setup/js/pr_review_buffer.test.cjs
+++ b/actions/setup/js/pr_review_buffer.test.cjs
@@ -915,77 +915,57 @@ describe("pr_review_buffer (factory pattern)", () => {
     });
 
     it("should return failure when retry also fails", async () => {
-      const previousActor = process.env.GITHUB_ACTOR;
-      process.env.GITHUB_ACTOR = "someone-else";
-      try {
-        buffer.addComment({ path: "test.js", line: 1, body: "comment" });
-        buffer.setReviewMetadata("LGTM", "APPROVE");
-        buffer.setReviewContext({
-          repo: "owner/repo",
-          repoParts: { owner: "owner", repo: "repo" },
-          pullRequestNumber: 42,
-          pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
-        });
+      buffer.addComment({ path: "test.js", line: 1, body: "comment" });
+      buffer.setReviewMetadata("LGTM", "APPROVE");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 42,
+        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
+      });
 
-        mockGithub.rest.pulls.createReview.mockRejectedValueOnce(new Error("Can not approve your own pull request")).mockRejectedValueOnce(new Error("Some other error"));
+      mockGithub.rest.pulls.createReview.mockRejectedValueOnce(new Error("Can not approve your own pull request")).mockRejectedValueOnce(new Error("Some other error"));
 
-        const result = await buffer.submitReview();
+      const result = await buffer.submitReview();
 
-        expect(result.success).toBe(false);
-        expect(result.error).toContain("Some other error");
-        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(2);
-      } finally {
-        if (previousActor === undefined) {
-          delete process.env.GITHUB_ACTOR;
-        } else {
-          process.env.GITHUB_ACTOR = previousActor;
-        }
-      }
+      expect(result.success).toBe(false);
+      expect(result.error).toContain("Some other error");
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(2);
     });
 
     it("should fall back to body-only COMMENT when own-PR COMMENT retry also fails with line-resolution error", async () => {
-      const previousActor = process.env.GITHUB_ACTOR;
-      process.env.GITHUB_ACTOR = "someone-else";
-      try {
-        buffer.addComment({ path: "file.go", line: 10, body: "Inline comment" });
-        buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
-        buffer.setReviewContext({
-          repo: "owner/repo",
-          repoParts: { owner: "owner", repo: "repo" },
-          pullRequestNumber: 42,
-          pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
-        });
+      buffer.addComment({ path: "file.go", line: 10, body: "Inline comment" });
+      buffer.setReviewMetadata("Fix this", "REQUEST_CHANGES");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 42,
+        pullRequest: { head: { sha: "abc123" }, user: { login: "bot-user" } },
+      });
 
-        mockGithub.rest.pulls.createReview
-          // Initial attempt — own-PR 422
-          .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
-          // COMMENT retry with inline comments — line unresolvable
-          .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
-          // Body-only COMMENT fallback — success
-          .mockResolvedValueOnce({
-            data: {
-              id: 800,
-              html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-800",
-            },
-          });
+      mockGithub.rest.pulls.createReview
+        // Initial attempt — own-PR 422
+        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+        // COMMENT retry with inline comments — line unresolvable
+        .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
+        // Body-only COMMENT fallback — success
+        .mockResolvedValueOnce({
+          data: {
+            id: 800,
+            html_url: "https://github.com/owner/repo/pull/42#pullrequestreview-800",
+          },
+        });
 
-        const result = await buffer.submitReview();
+      const result = await buffer.submitReview();
 
-        expect(result.success).toBe(true);
-        expect(result.event).toBe("COMMENT");
-        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
-        const bodyOnlyArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
-        expect(bodyOnlyArgs.event).toBe("COMMENT");
-        expect(bodyOnlyArgs.comments).toBeUndefined();
-        expect(bodyOnlyArgs.body).toContain("Inline comment");
-        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("COMMENT retry on own PR failed with unresolvable line(s)"));
-      } finally {
-        if (previousActor === undefined) {
-          delete process.env.GITHUB_ACTOR;
-        } else {
-          process.env.GITHUB_ACTOR = previousActor;
-        }
-      }
+      expect(result.success).toBe(true);
+      expect(result.event).toBe("COMMENT");
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+      const bodyOnlyArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
+      expect(bodyOnlyArgs.event).toBe("COMMENT");
+      expect(bodyOnlyArgs.comments).toBeUndefined();
+      expect(bodyOnlyArgs.body).toContain("Inline comment");
+      expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("COMMENT retry on own PR failed with unresolvable line(s)"));
     });
 
     it("should skip (success:true, skipped:true) when PR is permanently locked after all retries", async () => {
@@ -1473,48 +1453,38 @@ describe("pr_review_buffer (factory pattern)", () => {
     });
 
     it("should retry body-only as COMMENT when body-only REQUEST_CHANGES is rejected on own PR after line-resolution failure", async () => {
-      const previousActor = process.env.GITHUB_ACTOR;
-      process.env.GITHUB_ACTOR = "someone-else";
-      try {
-        buffer.addComment({ path: "file.go", line: 5, body: "Nil dereference on this line" });
-        buffer.setReviewMetadata("Fix the blocking issues.", "REQUEST_CHANGES");
-        buffer.setReviewContext({
-          repo: "owner/repo",
-          repoParts: { owner: "owner", repo: "repo" },
-          pullRequestNumber: 47375,
-          pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
-        });
+      buffer.addComment({ path: "file.go", line: 5, body: "Nil dereference on this line" });
+      buffer.setReviewMetadata("Fix the blocking issues.", "REQUEST_CHANGES");
+      buffer.setReviewContext({
+        repo: "owner/repo",
+        repoParts: { owner: "owner", repo: "repo" },
+        pullRequestNumber: 47375,
+        pullRequest: { head: { sha: "abc123" }, user: { login: "linter-miner[bot]" } },
+      });
 
-        mockGithub.rest.pulls.createReview
-          // Initial attempt — line resolution fails (this triggers the body-only path)
-          .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
-          // Body-only REQUEST_CHANGES — own-PR 422
-          .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
-          // Body-only COMMENT retry — success
-          .mockResolvedValueOnce({
-            data: {
-              id: 900,
-              html_url: "https://github.com/owner/repo/pull/47375#pullrequestreview-900",
-            },
-          });
+      mockGithub.rest.pulls.createReview
+        // Initial attempt — line resolution fails (this triggers the body-only path)
+        .mockRejectedValueOnce(new Error('Unprocessable Entity: "Line could not be resolved"'))
+        // Body-only REQUEST_CHANGES — own-PR 422
+        .mockRejectedValueOnce(new Error("Can not request changes on your own pull request"))
+        // Body-only COMMENT retry — success
+        .mockResolvedValueOnce({
+          data: {
+            id: 900,
+            html_url: "https://github.com/owner/repo/pull/47375#pullrequestreview-900",
+          },
+        });
 
-        const result = await buffer.submitReview();
+      const result = await buffer.submitReview();
 
-        expect(result.success).toBe(true);
-        expect(result.event).toBe("COMMENT");
-        expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
-        const bodyOnlyCommentArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
-        expect(bodyOnlyCommentArgs.event).toBe("COMMENT");
-        expect(bodyOnlyCommentArgs.comments).toBeUndefined();
-        expect(bodyOnlyCommentArgs.body).toContain("Nil dereference on this line");
-        expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Body-only REQUEST_CHANGES review rejected on own PR"));
-      } finally {
-        if (previousActor === undefined) {
-          delete process.env.GITHUB_ACTOR;
-        } else {
-          process.env.GITHUB_ACTOR = previousActor;
-        }
-      }
+      expect(result.success).toBe(true);
+      expect(result.event).toBe("COMMENT");
+      expect(mockGithub.rest.pulls.createReview).toHaveBeenCalledTimes(3);
+      const bodyOnlyCommentArgs = mockGithub.rest.pulls.createReview.mock.calls[2][0];
+      expect(bodyOnlyCommentArgs.event).toBe("COMMENT");
+      expect(bodyOnlyCommentArgs.comments).toBeUndefined();
+      expect(bodyOnlyCommentArgs.body).toContain("Nil dereference on this line");
+      expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Body-only REQUEST_CHANGES review rejected on own PR"));
     });
 
     it("should return failure when body-only COMMENT retry also fails on own PR", async () => {
