From c97c9765c3e7ed1079f2981e196b3ababc61935c Mon Sep 17 00:00:00 2001 From: Vlad Mocanu Date: Fri, 27 Feb 2026 13:57:59 +0200 Subject: [PATCH] fix: allow Forgejo bot actor on non-comment events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The self-trigger guard blocked the claude bot on all events. Loop risk only exists for comment/review events (tag mode). PR, workflow_dispatch, and schedule events are safe — refined the guard to only block on issue_comment, issues, pull_request_review, and pull_request_review_comment events. Co-Authored-By: Claude Opus 4.6 --- src/github/validation/actor.ts | 27 ++++++++++++++++++++----- test/actor.test.ts | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/github/validation/actor.ts b/src/github/validation/actor.ts index 763d704..395d083 100644 --- a/src/github/validation/actor.ts +++ b/src/github/validation/actor.ts @@ -18,14 +18,31 @@ export async function checkHumanActor( // check but still guard against self-triggering (infinite loop prevention). if (detectPlatform() === "forgejo") { const botName = githubContext.inputs.botName.replace(/\[bot\]$/, ""); - if ( + const isBotActor = githubContext.actor === botName || - githubContext.actor === githubContext.inputs.botName - ) { - throw new Error( - `Blocking self-trigger on Forgejo: actor '${githubContext.actor}' matches bot_name '${githubContext.inputs.botName}'. This prevents infinite loops.`, + githubContext.actor === githubContext.inputs.botName; + + if (isBotActor) { + // Self-trigger loop risk only exists for comment/issue events (tag mode). + // PR and other events (agent mode, workflow_dispatch) are safe — the bot + // pushing a branch or being triggered by schedule can't cause loops. + const loopRiskEvents = [ + "issue_comment", + "issues", + "pull_request_review_comment", + "pull_request_review", + ]; + if (loopRiskEvents.includes(githubContext.eventName)) { + throw new Error( + `Blocking self-trigger on Forgejo: actor '${githubContext.actor}' matches bot_name '${githubContext.inputs.botName}'. This prevents infinite loops.`, + ); + } + console.log( + `Forgejo bot actor '${githubContext.actor}' allowed for event '${githubContext.eventName}' (no loop risk)`, ); + return; } + console.log( `Forgejo actor check passed for: ${githubContext.actor} (bot_name: ${githubContext.inputs.botName})`, ); diff --git a/test/actor.test.ts b/test/actor.test.ts index 79e75d6..0c3f486 100644 --- a/test/actor.test.ts +++ b/test/actor.test.ts @@ -205,5 +205,41 @@ describe("checkHumanActor", () => { "Blocking self-trigger on Forgejo", ); }); + + test("should allow bot actor on pull_request events (no loop risk)", async () => { + const mockOctokit = createMockOctokit("User"); + const context = createMockContext(); + context.actor = "claude"; + context.inputs.botName = "claude[bot]"; + (context as { eventName: string }).eventName = "pull_request"; + + await expect( + checkHumanActor(mockOctokit, context), + ).resolves.toBeUndefined(); + }); + + test("should allow bot actor on workflow_dispatch events (no loop risk)", async () => { + const mockOctokit = createMockOctokit("User"); + const context = createMockContext(); + context.actor = "claude"; + context.inputs.botName = "claude[bot]"; + (context as { eventName: string }).eventName = "workflow_dispatch"; + + await expect( + checkHumanActor(mockOctokit, context), + ).resolves.toBeUndefined(); + }); + + test("should block bot actor on issue_comment events (loop risk)", async () => { + const mockOctokit = createMockOctokit("User"); + const context = createMockContext(); + context.actor = "claude"; + context.inputs.botName = "claude[bot]"; + // eventName defaults to "issue_comment" from createMockContext + + await expect(checkHumanActor(mockOctokit, context)).rejects.toThrow( + "Blocking self-trigger on Forgejo", + ); + }); }); });