fix(maintainer-gate): bind info-status exemptions to the Bot author and body prefix

Merged
DO
3d ago
+230-49

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 as review_request and opted_out. It clears a comment only when all of these hold:

      • author.__typename == "Bot"
      • the login, with any [bot] suffix removed, has a registered info_status_pattern
      • the body matches that pattern
      • the body has no finding-bearing section

      A missing __typename is not Bot, so the comment stays blocked (fail closed).

    • One definition for both callers. check_maintainer_comments and maintainer_gate_open_comment_ids both use info_status_clear, so they cannot drift apart.

    • Author type lookup. maintainer_gate_merge_edit_times now also reads author{__typename} in its existing GraphQL pass and merges it into the snapshot. gh pr view --json comments does not expose the author type. The value from the API overrides anything already in the snapshot.

    • Warning. check_maintainer_comments logs 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: the sonarqubecloud pattern 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 on main since #2444. #2444 switched the feature-ideation redispatch job to GH_TOKEN: ${{ github.token }}, which can start workflow_dispatch runs, and GH_PAT_WORKFLOWS is being retired (#1326). The test still required secrets.GH_PAT_WORKFLOWS. It now asserts that the redispatch job uses github.token and that the workflow no longer references GH_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:

CallerWhere the author type comes fromGuard
pr-review, scripts/review-one-pr.shThe 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.

CaseExpected
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 Botblocks
Codex review summary with a finding, from the Botblocks
Same Sonar or Codex text from a User with the bot loginblocks
Same text from a different bot, or the two notices swapped between botsblocks
Notice with no author typeblocks and logs the warning
Sonar passed summary that does not open the bodyblocks
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_gate and test_maintainer_resolve_comment: 172 of 172 pass under bash 5
  • The full CI bats list, run on this branch and on main under bash 5 on macOS, shows no new failures. A path-with-spaces artifact in test_merge_queue_dequeue passes 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 Rollout with command=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 disposition on 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