claude.yml: don't self-trigger on bot-authored @claude comments#914
Conversation
The job `if:` fired on any comment/review/issue whose body contains `@claude`, with no guard on the author. That let the agent re-trigger itself: a comment posted by claude[bot] (the agent's own GitHub App identity) that merely contains `@claude` — e.g. quoting a prior `@claude ...` command or a "posted by @claude" signature — re-fired this workflow, which posted another such comment, which re-fired it again, in an unbounded loop (observed on PR #900 on 2026-06-16). The existing `Acknowledge @claude mention` ack avoids the loop only because it is posted via GITHUB_TOKEN (github-actions[bot]), whose events cannot trigger workflows. claude[bot] comments, however, are posted with the Claude GitHub App token, which DOES fire `issue_comment` — so the token-kind protection does not cover them. Gate the job on `github.event.sender.type != 'Bot'` so only a human @claude mention invokes the agent. `github.event.sender` is the actor that raised the event for all four trigger types, and this mirrors the `sender.type != 'Bot'` filter already used in claude-code-review.yml.
|
I'll analyze this and get back to you. |
|
Claude finished @d-morrison's task in 1m 39s —— View job Code Review — PR #914Verdict: Approve. The fix is correct, well-reasoned, and consistent with the described root cause. One minor suggestion below.
Finding: partial mirror of
|
Address review feedback on PR #914: the job `if:` guard mirrored only the first half of claude-code-review.yml's bot filter. Add the `!endsWith(github.actor, '[bot]')` belt-and-suspenders clause so the two workflows use an identical double guard. This also blocks a bot actor whose `sender.type` is somehow not reported as 'Bot' (old/misconfigured integrations). No downside: human actor logins never end in '[bot]', so intended human invocations are unaffected.
|
Claude finished @d-morrison's task in 1m 15s —— View job Claude finished @d-morrison's task —— View job Code Review — PR #914 (updated after commit
|
|
Claude finished @d-morrison's task in 1m 13s —— View job Code Review — PR #914Verdict: Approve. The fix is correct and complete. All prior findings resolved.
SummaryThe double guard on line 68 of github.event.sender.type != 'Bot' && !endsWith(github.actor, '[bot]') && (This is an exact idiomatic match for the expression in (github.event.sender.type != 'Bot' && !endsWith(github.actor, '[bot]'))The finding from the first review round (missing Everything checks out:
No new issues found. |
|
Claude finished @d-morrison's task in 1m 57s —— View job Code Review — PR #914Verdict: Approve. The fix is correct, complete, and ready to merge.
SummaryThe only change is to the if: |
github.event.sender.type != 'Bot' && !endsWith(github.actor, '[bot]') && (
(github.event_name == 'issue_comment' && ...) ||
...
)Is correct. Parentheses are balanced. Counting across lines 67–73: 11 opens, 11 closes — net 0. ✓ All four trigger conditions are retained unchanged. Only the wrapping and guard were added. ✓
The The inline comment block (lines 49–66) accurately explains the No issues found. The prior review's only finding (missing |

What
Guard the Claude Code agent workflow (
.github/workflows/claude.yml) so it only runs when a human mentions@claude— not when a bot-authored comment merely contains the string@claude.Why
The job
if:triggered on any comment/review/issue whose body contains@claude, with no guard on the author:That let the agent re-trigger itself. A comment posted by
claude[bot](the agent's own GitHub App identity) that merely contains@claude— e.g. quoting a prior`@claude fix nits`command, or aposted by @claudesignature — satisfies theif:and fires a fresh run, which posts another such comment, which fires it again: an unbounded self-trigger loop.This was observed live on #900 on 2026-06-16: a status comment containing the literal
@claude fix nitsdispatched a run; that run'sclaude[bot]comment echoed@claudewhile explaining the false-positive; that re-dispatched the workflow; and so on (~12–15 min per iteration). See runs 27646413589 → 27646992663.Why the existing protection didn't cover it
The inline comment on the
Acknowledge @claude mentionstep notes that the ack can't self-loop. That's true — but only because the ack is posted viaGITHUB_TOKEN(asgithub-actions[bot]), andGITHUB_TOKEN-authored events cannot trigger workflows.claude[bot]comments, however, are posted with the Claude GitHub App token, which does fireissue_comment. So the token-kind protection never applied to the agent's own App-authored comments.Change
Gate the job on the comment/review/issue author not being a bot:
github.event.senderis the actor that raised the event (the comment/review author or issue opener) for all four trigger types, so a single top-level guard covers them uniformly. This mirrors the fullsender.type != 'Bot' && !endsWith(github.actor, '[bot]')double guard already used inclaude-code-review.yml, keeping the two workflows idiomatically identical. (sender.type != 'Bot'alone is sufficient for the observedclaude[bot]loop; the!endsWith(actor, '[bot]')clause is belt-and-suspenders for a bot actor whosesender.typeis somehow not reported asBot— added in response to review feedback.)Intended human invocations (OWNER/MEMBER typing
@claude) are unaffected — human actor logins never end in[bot]. If a specific automation should ever be allowed to invoke the agent, add an explicit allow-clause (the same pattern #900 proposed for letting Claude pushes through the review filter).Verification
python3parses the workflow; thejobs.claude.ifexpression has balanced parentheses (12/12), retains all four trigger conditions, and includes thesender.type != 'Bot' && !endsWith(github.actor, '[bot]')double guard.claudejob), so no mirrored expression to keep in sync (concurrency.cancel-in-progresshere is the literalfalse).main(which addedactions: writeto this job) cleanly.Per CLAUDE.md, this is a
.github/CI-only change in its own dedicated PR (no book content touched).🤖 Generated with Claude Code
https://claude.ai/code/session_01QGCFAvjMVUtwCpRgSFr81G