fix(maintainer-gate): bind info-status exemptions to the Bot author and body prefix
Problem
The maintainer comment gate lets a registered bot's no-finding notice through with no disposition (#1918, #1995). Two examples are SonarQube Cloud's "Quality Gate passed" summary and Codex's usage-limit notice. The gate tied that exemption to the bare login only. It never checked that the author was the bot.
GitHub has an ordinary User account named sonarqubecloud. That account could post the passed-gate text on any PR and clear the gate. The Sonar pattern was also unanchored, so it could match the passed-gate text anywhere in a body.
Note on combsmith/.github#1421
I replayed the gate against the live #1421 comments at the channel commit that dismissed the approval (cd02e25, run 41829975366). Both notices were already exempt, minimized or not:
IC_kwDORyesfc8AAAABipnEHw(Sonar)IC_kwDORyesfc8AAAABgQo7fQ(Codex)
The blocker I can reproduce is CodeRabbit's summary IC_kwDORyesfc8AAAABgQo8PA. It was edited at 2026-11-04T01:45:48Z, after its latest covering disposition, so #2008 re-opened it as designed. This PR does not change that path.
Change
scripts/lib/maintainer-comment-gate.sh-
New check. A shared jq definition,
info_status_clear, follows the same style asreview_requestandopted_out. It clears a comment only when all of these hold:author.__typename == "Bot"- the login, with any
[bot]suffix removed, has a registeredinfo_status_pattern - the body matches that pattern
- the body has no finding-bearing section
A missing
__typenameis notBot, so the comment stays blocked (fail closed). -
One definition for both callers.
check_maintainer_commentsandmaintainer_gate_open_comment_idsboth useinfo_status_clear, so they cannot drift apart. -
Author type lookup.
maintainer_gate_merge_edit_timesnow also readsauthor{__typename}in its existing GraphQL pass and merges it into the snapshot.gh pr view --json commentsdoes not expose the author type. The value from the API overrides anything already in the snapshot. -
Warning.
check_maintainer_commentslogs a warning when a comment from a registered info-status login has no author type. That makes a stall on clean notices easy to diagnose.
-
scripts/lib/reviewer-sources.tsv: thesonarqubecloudpattern is now anchored to the passed-badge prefix every SonarQube Cloud gate comment opens with. I checked 43 Sonar comments across 40 org PRs, and all start with it. The zero-issue and zero-hotspot requirements are unchanged. The Codex pattern was already anchored at the body start.- Docs: the gate doc and ADR-0012 state the Bot-author requirement.
tests/test_feature_ideation.bats(separate commit): fixes a stale test that has failed onmainsince #2444. #2444 switched the feature-ideation redispatch job toGH_TOKEN: ${{ github.token }}, which can startworkflow_dispatchruns, andGH_PAT_WORKFLOWSis being retired (#1326). The test still requiredsecrets.GH_PAT_WORKFLOWS. It now asserts that the redispatch job usesgithub.tokenand that the workflow no longer referencesGH_PAT_WORKFLOWS.
No other exemption path changes. maintainer-resolve-comment.sh already required a Bot actor for its bot path.
Call sites
Both callers already supply the author type:
| Caller | Where the author type comes from | Guard |
|---|---|---|
pr-review, scripts/review-one-pr.sh | The gate runs on the output of maintainer_gate_merge_edit_times, which now selects author{__typename} | Existing wiring test (#2008) checks the merge runs before the gate. The merge test's gh shim now answers only a query that selects author{__typename}, so dropping it fails the test. |
dev-lead, scripts/dev-lead-fix-reviews.sh (resolve_dispositioned_comments) | Its issue-comment query already selects author{login __typename} | New wiring test |
Risk
The risk is medium. Every change makes the gate stricter, and every failure mode fails closed.
The worst case is that clean Sonar or Codex notices block again on a PR. That would happen if the author-type lookup fails, or if a vendor changes its badge URL. The fix then is the old manual step: minimize the notice as RESOLVED, or add <!-- maintainer:not-a-finding -->. No path clears a comment it did not clear before.
This library reaches consumers through the dev-lead and pr-review reusables.
Test plan
New cases in tests/dev-lead/unit/test_maintainer_comment_gate.bats use the verbatim #1421 bodies. Each case checks both the gate verdict and dev-lead's candidate list.
| Case | Expected |
|---|---|
Sonar passed summary from the sonarqubecloud Bot, with and without [bot] | clear |
Codex usage-limit notice from the chatgpt-codex-connector Bot, with and without [bot] | clear |
| Failed Sonar quality gate from the Bot | blocks |
| Codex review summary with a finding, from the Bot | blocks |
| Same Sonar or Codex text from a User with the bot login | blocks |
| Same text from a different bot, or the two notices swapped between bots | blocks |
| Notice with no author type | blocks and logs the warning |
| Sonar passed summary that does not open the body | blocks |
maintainer_gate_merge_edit_times merges author.__typename (Bot, User, null author, id not returned) | merged as given |
Existing fixtures now carry __typename: "Bot" and the real badge URL.
Local results:
- shellcheck: clean
check-bats-negation: clean- markdownlint: clean
test_maintainer_comment_gateandtest_maintainer_resolve_comment: 172 of 172 pass under bash 5- The full CI bats list, run on this branch and on
mainunder bash 5 on macOS, shows no new failures. A path-with-spaces artifact intest_merge_queue_dequeuepasses from a space-free path.
Linux CI is the authoritative check.
Rollback
Revert this PR. If a release containing it has already been promoted, move the channel back to the previous immutable tag:
- dispatch
Canary Rolloutwithcommand=rollback agent=<agent> ring=<tier> to=vX.Y.Z, or - run
cut-release.sh <agent> <X.Y.Z> --promote --channel <tier> --push.
Monitoring
After promotion, watch pr-review logs for these two lines:
carry no author type, the new warning[maintainer-gate] WARNING: N PR issue comment(s) lack a verified dispositionon PRs whose only open comments are clean Sonar or Codex notices
Either line means clean notices are blocking again. dev-lead fix-bot-comment dispatches against a clean Sonar or Codex notice are the same signal.
🤖 Generated with Claude Code