fix: prevent bot-triggered PR review loops#209
Conversation
Guards previously relied on a single bare-string literal ('docker-agent')
and/or the sender.type == 'Bot' heuristic. PAT-based bots have type 'User',
so those checks were silently missed. Additionally several workflow branches
had no actor check at all.
Changes:
- self-review-pr-trigger.yml: extend save-context job guard to also reject
'docker-agent[bot]', any user.type == 'Bot', and comments bearing the
<!-- cagent-review --> or <!-- cagent-review-reply --> HTML markers.
The marker check is self-healing across identity changes.
- self-review-pr.yml: add actor + marker guard to the issue_comment branch
of the review job (previously had no actor check at all).
- review-pr.yml (review job, issue_comment branch): add the same actor +
marker guard. The bot's own comments can no longer queue a review run.
- review-pr.yml (review job, pull_request non-review_requested branch):
tighten sender check from type != 'Bot' to also reject
sender.login == 'docker-agent' and 'docker-agent[bot]', covering PAT
machine-user accounts whose type is 'User'.
- review-pr.yml (reply-to-feedback artifact path): add missing
comment-author-type != 'Bot' check to match the parity of the direct
path and reply-to-mention's artifact branch.
- review-pr/README.md: add the same job-level if: guard to the
pr-review-trigger.yml consumer template so copy-paste deployments are
protected out of the box.
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The multi-identity + body-marker guard approach is sound and correctly handles all identified bot identity variants. The logic has been reviewed across all four changed files:
.github/workflows/self-review-pr-trigger.yml — The expanded if: condition correctly guards against pull_request_review_comment events from bots. For pull_request events, github.event.comment is null, causing all conditions to evaluate true — this is intentional and documented behavior; those events are meant to pass through.
.github/workflows/self-review-pr.yml — The previously unguarded issue_comment branch now has the full multi-identity + marker check. The workflow_run.conclusion == 'success' branch correctly relies on upstream filtering in self-review-pr-trigger.yml.
.github/workflows/review-pr.yml — Three sub-fixes applied correctly: (a) issue_comment branch guard added; (b) pull_request non-review_requested branch now also checks sender login for both docker-agent and docker-agent[bot]; (c) artifact path for reply-to-feedback gains the missing comment-author-type != 'Bot' check. The direct pull_request_review_comment path on reply-to-feedback (unchanged) already relies on user.type != 'Bot' to cover GitHub App identities, since docker-agent[bot] always has type == 'Bot'.
review-pr/README.md — Consumer template now ships with the correct guard, eliminating the copy-paste vulnerability.
No bugs found in the changed code.
derekmisler
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The bot-loop prevention guards are logically correct. Core behaviours verified:
pull_requestevent path preserved: Forpull_requestevents,github.event.commentis an empty context object. All guard conditions ('' != 'docker-agent',!contains('', '...')) correctly evaluate totrue, so legitimatepull_requesttriggers are unblocked. ✅review_requestedpath unchanged: Thereview_requestedbranch (github.event.requested_reviewer.login == 'docker-agent') is untouched. ✅workflow_runbranch inself-review-pr.ymlunaffected: Thegithub.event.workflow_run.conclusion == 'success'branch is preserved and unguarded — correct. ✅- reply-to-feedback trigger-path gets
comment-author-typeguard: The previously unguarded trigger path now checkscomment-author-type != 'Bot'. ✅
Additional note (not in diff): The README's consumer pr-review.yml caller template (review-pr/README.md line ~106) still has an unguarded issue_comment branch:
if: |
github.event_name == 'issue_comment' ||
github.event.workflow_run.conclusion == 'success'Bot comments from docker-agent will still launch the consumer workflow, only to be filtered internally by the reusable review-pr.yml. The internal self-review-pr.yml was updated (✅) but the public README template was not. Low priority — no incorrect reviews are posted — but worth updating for consistency and to avoid wasted CI minutes.
One inline comment below for a minor consistency gap.
- review-pr.yml: reply-to-feedback artifact path now also rejects comment-author == 'docker-agent[bot]', matching the parity pattern used in all other multi-identity guards throughout the file. - review-pr/README.md: the consumer pr-review.yml caller template's review job if: condition now mirrors self-review-pr.yml — the bare 'issue_comment' branch is replaced with a 6-condition guard that rejects both docker-agent login variants, type==Bot accounts, and any comment bearing the <!-- cagent-review --> or <!-- cagent-review-reply --> HTML markers.
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The bot-loop prevention changes look correct and complete. All four files consistently apply the same layered guard: login checks for both docker-agent and docker-agent[bot], a user.type != 'Bot' check, and HTML-marker checks for both <!-- cagent-review --> and <!-- cagent-review-reply -->.
Key observations:
contains()calls are scoped only toissue_commentbranches wherecomment.bodyis always non-null — null-safety is preserved.pull_requestevents (null comment) are unaffected: theissue_commentbranch is skipped entirely, and thepull_requestbranches do not referencecomment.body.- The
review_requestedpath correctly remains unguarded (bots requesting a review should still trigger reviews). - The previously asymmetric
reply-to-feedbackartifact path now hascomment-author-type != 'Bot'andcomment-author != 'docker-agent[bot]'on parity with the direct path. - The README consumer template is updated consistently with the live workflow guards.
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
This PR correctly addresses the bot-triggered PR review loop problem through a multi-layered guard strategy. The changes are well-reasoned and the implementation is sound.
Changes reviewed:
.github/workflows/review-pr.yml— Added full actor + marker guard toissue_commentbranch ofreviewjob; tightenedpull_requestbranch withsender.loginchecks; addedcomment-author-type != 'Bot'parity toreply-to-feedbackartifact path.github/workflows/self-review-pr-trigger.yml— Extendedsave-contextguard withdocker-agent[bot],user.type != 'Bot', and both HTML marker checks.github/workflows/self-review-pr.yml— Added actor + marker guard to previously unguardedissue_commentbranchreview-pr/README.md— Consumer template updated with matching guard
Key correctness observations:
- ✅ Null-safety on
pull_requestevents: Theself-review-pr-trigger.ymlfires onpull_requestevents (no comment payload). Accessinggithub.event.comment.*on these events returns''— so'' != 'docker-agent'and!contains('', '<!-- cagent-review -->')both evaluatetrue, correctly allowing thesave-contextjob to run for real PR events while blocking bot review comments. - ✅
reply-to-feedbackparity: The artifact path (line 381) now matches the direct path's defense posture. The direct path already haduser.type != 'Bot'which coversdocker-agent[bot](whosetype == 'Bot'), so no gap exists. - ✅
workflow_runbranch: Theself-review-pr.ymlworkflow_run.conclusion == 'success'branch relies on the upstream trigger workflow's guard (now patched), which is the correct architectural pattern — no regression. - ✅ Body-marker defense: Using
<!-- cagent-review -->/<!-- cagent-review-reply -->markers as the primary self-healing guard is robust across identity changes (login renames, App → PAT switches).
No bugs found in the changed code.
Problem
When docker-agent posts a review comment on a PR it triggers a second PR review run, creating an infinite loop. The root causes are:
'docker-agent'and/orsender.type != 'Bot'. PAT-based bots haveuser.type == 'User', so those checks silently missed them (documented in AGENTS.md as a known identity gap).self-review-pr.ymlissue_commentbranch,review-pr.ymlissue_commentbranch).reply-to-feedbackartifact path was missing thecomment-author-type != 'Bot'check present on the direct path.pr-review-trigger.ymlexample in the README shipped with zero bot-skip guard, so copy-paste adopters were unprotected.Fix
Replace brittle login-literal checks with a multi-identity + body-marker guard. The bot always injects
<!-- cagent-review -->or<!-- cagent-review-reply -->into its comments; checking for those markers makes the guard self-healing across identity changes (login renames, App → PAT switches, etc.).Changed files
.github/workflows/self-review-pr-trigger.ymlsave-contextjob guard: addsdocker-agent[bot],user.type != 'Bot', and both HTML marker checks alongside the existing login check.pull_requestevents (wherecommentis null) still pass correctly —null != 'docker-agent'evaluates TRUE..github/workflows/self-review-pr.ymlissue_commentbranch of thereviewjob (previously had no actor check at all)..github/workflows/review-pr.ymlreviewjobissue_commentbranch; (b) tightenedpull_requestnon-review_requestedbranch fromsender.type != 'Bot'to also rejectsender.login == 'docker-agent'/'docker-agent[bot]'; (c) added missingcomment-author-type != 'Bot'toreply-to-feedbackartifact path for parity with the direct path.review-pr/README.mdif:guard to thesave-contextjob in thepr-review-trigger.ymlconsumer template.Behaviour preserved
pull_requestevents (no comment payload) are unaffected — the null-safe comparisons evaluate TRUE and the jobs still run.review_requestedevents targetingdocker-agentstill trigger the review./reviewand@docker-agentmention paths are unchanged.Refs: investigation artifact
c1da342d-cc21-49a0-bca6-364151e22183