Problem
tend-mention starts a full billable session every time the bot leaves a non-empty-body review on a same-repo PR someone else authored. The session reads the PR, recognises the trigger as its own review, and exits silently. It is a guaranteed no-op: the bot is the reviewer, the findings are addressed to the PR's author, and the bot pushing unbidden to another author's branch is barred by running-in-ci's conduct rules — so there is no author role for it to act in.
This is the sibling leg of #747. That issue fixed the empty-body APPROVED case; the gate it added stops at body == "", on the reasoning max-sixty gave in the thread — "if it's an approval with nits then we may want to make follow-up changes?". That reasoning holds on a PR the bot authored (which is exactly what #166 and #761 protect), but not on someone else's PR, where the nits are instructions for the human author and the bot has nothing to follow up.
Distinct from #866 / #849, which target the synthetic empty-body COMMENTED container GitHub creates for an inline reply. These are real bot reviews with real bodies (455–1920 chars).
Evidence
Five occurrences in one 24h window on PRQL/prql (bot login prql-bot), covering every non-empty-body bot review left on a same-repo human PR in that window — there were no counter-examples:
$1.79 booked plus one run that never got to bill, out of a $31.52 window — and each also costs a relay run and a verify run.
The sessions say so themselves. From 31297736617: "The triggering review (ID 4890641190) was authored by prql-bot itself on eitsupi's PR #6172 — I'm the reviewer, not the author, and there are no replies or other comments on the thread. Responding to my own review would be a self-conversation loop, so I'm exiting silently without posting anything." From 31298590569: "I'm the reviewer, @eitsupi is the author, and the review's findings are addressed to them. … No comment, review, or push made."
The waste is not free. 31281535113 is the one that failed rather than exiting: its session JSONL ends on "error":"rate_limit", "apiErrorStatus":429, You've hit your session limit · resets 12am (UTC). The account's Claude session budget was exhausted at that moment, and one of the requests that had been drawing it down was this class of session. A no-op that can't run is still a no-op, but a no-op that consumes a scarce slot and then files a tend-outage issue is worse than one that just bills $0.45.
Root cause
For a pull_request_review event the gate falls through to the participation heuristic at mention.yaml.j2#L307-L313, which counts the triggering review itself, so BOT_REVIEWS is always non-empty and should_run=true is unconditional. The only skip on that path is the three-clause gate at L248-L254, whose approved + empty-body clauses exclude every review in the table above.
Proposed fix
The discriminator that's missing is the PR's author, and it's already resolved one line earlier. Insert between the PR_AUTHOR check (L301-L305) and the BOT_REVIEWS heuristic:
# A review the bot leaves on someone else's PR has no author role to
# follow up in — the findings are addressed to that PR's author, and
# pushing unbidden to another author's branch is barred by
# running-in-ci's conduct rules. The BOT_REVIEWS heuristic below
# counts this very review, so without this the session always starts
# and always exits silently (#747 fixed only the empty-body APPROVED
# leg of the same shape). Placed *after* the PR_AUTHOR check above,
# so #166/#761's actionable self-reviews on the bot's own PRs still
# fire, and after the body/inline @-mention scans, so an explicit
# summons still wins.
if [ "$KIND" = "pull_request_review" ] \
&& [ "$REVIEW_AUTHOR" = "<<cfg.bot_name>>" ]; then
echo "should_run=false" >> "$GITHUB_OUTPUT"; exit 0
fi
At that point PR_AUTHOR != <bot> is already established, because the preceding block exit 0s when they're equal — so this cannot swallow the bot-authored-PR case. Everything the earlier gates admit is unaffected: a body @-mention returns at L220-L224, an inline-comment @-mention at L260-L267, and a human's review never reaches here with REVIEW_AUTHOR set to the bot.
Scope note: only same-repo PRs are affected. The relay job's head.repo.full_name == github.repository condition means bot reviews on fork PRs never dispatch — verified against the same window, where three non-empty-body bot reviews on @kgutwin's fork PRs (#6155, #6156, #6162) started no session at all.
Worth confirming against the control in the same window: 31300648824, triggered by the bot's own COMMENTED review on #6173 — a bot-authored PR — is the case that must keep firing, and the proposed gate leaves it firing.
How the occurrences were enumerated
Every bot review submitted in the window was listed with state and body length, cross-referenced against the PR's head.repo.full_name and author, then matched to the repository_dispatch run it produced; each run's session artifact was downloaded and its final assistant turn read to confirm the exit was a no-op and that no comment, review, or push was made. The two empty-body APPROVED reviews in the same window (4890133352 on #6170, 4889810454 on #6168) correctly produced no session — #747's gate working — which is what makes the body-length clause the load-bearing one.
Problem
tend-mentionstarts a full billable session every time the bot leaves a non-empty-body review on a same-repo PR someone else authored. The session reads the PR, recognises the trigger as its own review, and exits silently. It is a guaranteed no-op: the bot is the reviewer, the findings are addressed to the PR's author, and the bot pushing unbidden to another author's branch is barred byrunning-in-ci's conduct rules — so there is no author role for it to act in.This is the sibling leg of #747. That issue fixed the empty-body
APPROVEDcase; the gate it added stops atbody == "", on the reasoning max-sixty gave in the thread — "if it's an approval with nits then we may want to make follow-up changes?". That reasoning holds on a PR the bot authored (which is exactly what #166 and #761 protect), but not on someone else's PR, where the nits are instructions for the human author and the bot has nothing to follow up.Distinct from #866 / #849, which target the synthetic empty-body
COMMENTEDcontainer GitHub creates for an inline reply. These are real bot reviews with real bodies (455–1920 chars).Evidence
Five occurrences in one 24h window on PRQL/prql (bot login
prql-bot), covering every non-empty-body bot review left on a same-repo human PR in that window — there were no counter-examples:APPROVED, 1567 charsCOMMENTED, 1453 charsCOMMENTED, 1503 charsCOMMENTED, 1920 charsAPPROVED, 455 chars$1.79 booked plus one run that never got to bill, out of a $31.52 window — and each also costs a
relayrun and averifyrun.The sessions say so themselves. From 31297736617: "The triggering review (ID 4890641190) was authored by prql-bot itself on eitsupi's PR #6172 — I'm the reviewer, not the author, and there are no replies or other comments on the thread. Responding to my own review would be a self-conversation loop, so I'm exiting silently without posting anything." From 31298590569: "I'm the reviewer, @eitsupi is the author, and the review's findings are addressed to them. … No comment, review, or push made."
The waste is not free. 31281535113 is the one that failed rather than exiting: its session JSONL ends on
"error":"rate_limit","apiErrorStatus":429,You've hit your session limit · resets 12am (UTC). The account's Claude session budget was exhausted at that moment, and one of the requests that had been drawing it down was this class of session. A no-op that can't run is still a no-op, but a no-op that consumes a scarce slot and then files atend-outageissue is worse than one that just bills $0.45.Root cause
For a
pull_request_reviewevent the gate falls through to the participation heuristic atmention.yaml.j2#L307-L313, which counts the triggering review itself, soBOT_REVIEWSis always non-empty andshould_run=trueis unconditional. The only skip on that path is the three-clause gate at L248-L254, whoseapproved+ empty-body clauses exclude every review in the table above.Proposed fix
The discriminator that's missing is the PR's author, and it's already resolved one line earlier. Insert between the
PR_AUTHORcheck (L301-L305) and theBOT_REVIEWSheuristic:At that point
PR_AUTHOR != <bot>is already established, because the preceding blockexit 0s when they're equal — so this cannot swallow the bot-authored-PR case. Everything the earlier gates admit is unaffected: a body@-mention returns at L220-L224, an inline-comment@-mention at L260-L267, and a human's review never reaches here withREVIEW_AUTHORset to the bot.Scope note: only same-repo PRs are affected. The
relayjob'shead.repo.full_name == github.repositorycondition means bot reviews on fork PRs never dispatch — verified against the same window, where three non-empty-body bot reviews on @kgutwin's fork PRs (#6155, #6156, #6162) started no session at all.Worth confirming against the control in the same window: 31300648824, triggered by the bot's own
COMMENTEDreview on #6173 — a bot-authored PR — is the case that must keep firing, and the proposed gate leaves it firing.How the occurrences were enumerated
Every bot review submitted in the window was listed with
stateandbodylength, cross-referenced against the PR'shead.repo.full_nameand author, then matched to therepository_dispatchrun it produced; each run's session artifact was downloaded and its final assistant turn read to confirm the exit was a no-op and that no comment, review, or push was made. The two empty-bodyAPPROVEDreviews in the same window (4890133352 on #6170, 4889810454 on #6168) correctly produced no session — #747's gate working — which is what makes the body-length clause the load-bearing one.