Skip to content

submit-pull-request-review: add supersede-older-reviews option to dismiss stale bot reviews on re-review #27655

Description

@PureWeen

Problem

When an agent workflow posts a REQUEST_CHANGES review via submit-pull-request-review, the blocking review persists even after the author fixes all findings and triggers a re-review. The new review run cannot dismiss the stale blocking review because:

  1. gh-aw enforces pull-requests: readpull-requests: write is rejected by the compiler (correctly, for security)
  2. There is no dismiss-pull-request-review safe output type
  3. The hide-older-comments: true option on add-comment has no equivalent for reviews

Net result: A stale CHANGES_REQUESTED review from github-actions[bot] blocks the PR indefinitely until a human manually dismisses it.

Real-World Impact

  • PureWeen/PolyPilot#619: A 3-model adversarial code review posted REQUEST_CHANGES on commit 1. The author fixed all 4 findings in commits 2-6. Re-running /review posted a new review but the old blocking review persisted, preventing merge.
  • dotnet/maui#35027: The MAUI repo adopted allowed-events: [COMMENT] as a workaround after discovering the same issue.
  • gh-aw#25869: The design-decision-gate workflow independently removed submit-pull-request-review entirely to avoid stale blocking reviews — same workaround pattern.
  • tailspin-toys-workshop: The gh-aw workshop teaches allowed-events: [COMMENT, REQUEST_CHANGES] as the recommended pattern, which would hit this problem if students use /review re-runs on their workflows.

Current Workaround

Use allowed-events: [COMMENT] to prevent the agent from posting blocking reviews. The review body still communicates severity through findings, but this loses:

  • GitHub-native "Changes requested" badge and merge-blocking semantics
  • PR list discoverability (no blocking indicator at a glance)
  • Branch protection integration (bot reviews cannot gate merge)

Proposed Solution

Add a supersede-older-reviews: true option to submit-pull-request-review, analogous to hide-older-comments: true on add-comment:

safe-outputs:
  submit-pull-request-review:
    max: 1
    allowed-events: [COMMENT, REQUEST_CHANGES]
    supersede-older-reviews: true  # dismiss prior bot reviews from same workflow

Behavior

When posting a new review, auto-dismiss previous REQUEST_CHANGES reviews from the same workflow (identified by GITHUB_WORKFLOW / workflow-id marker) on the same PR. The dismissal message would be "Superseded by updated review from same workflow."

Why not a standalone dismiss-pull-request-review safe output?

A standalone dismiss primitive has a wider attack surface — an agent could be prompt-injected into dismissing legitimate human reviews. supersede-older-reviews is structurally safer because:

  • Scoped to the same workflow's own prior reviews (never human reviews)
  • Atomic with posting a new review (cannot dismiss without replacing)
  • Matches the existing hide-older-comments pattern exactly
  • No new permissions exposure — the pull-requests: write needed for dismissal stays inside platform infrastructure

Security Analysis (4-model adversarial consensus)

This proposal was reviewed by 4 independent AI models with adversarial consensus. All 4 unanimously agreed the problem is real. However, on the proposed solution, 2/3 disagreed — raising important concerns that should inform the design:

Threat Risk
Prompt injection → dismiss human reviews Impossible — scoped to same-workflow bot reviews only
Prompt injection → dismiss to unblock merge Cannot dismiss without simultaneously posting a replacement review
Branch protection bypass Marginal — the new review replaces the old one, so net review state is fresh, not absent
Scope creep Cannot target other bots/humans — scoped by workflow identity

⚠️ Counter-Arguments (from adversarial review)

We ran a 3-model adversarial review of this issue itself. Two of three reviewers disagreed with the proposed supersede-older-reviews design, raising valid concerns:

Atomicity is false

The hide-older-comments analogy breaks down because dismiss + post is two separate GitHub API calls, not a transaction. If dismiss succeeds but the new review POST fails (network error, rate limit, model timeout), the PR is left unblocked with no review — a security regression.

Auto-merge race condition

If a repo has auto-merge enabled: dismiss fires → PR satisfies all merge conditions → GitHub auto-merges → new review arrives after merge. The PR merges without the replacement review ever being evaluated.

Stale REQUEST_CHANGES as a security ratchet

A stale blocking review requires human judgment to dismiss. This is arguably a security feature, not a bug — it prevents a prompt-injected "clean" re-review from automatically unblocking a PR that had legitimate findings. The human dismissal step is a trust boundary.

"Same workflow" identity is underspecified

How does the platform determine which previous reviews belong to "this workflow"? All gh-aw workflows share github-actions[bot] as the actor. Workflow file path, run ID lineage, or a platform metadata store? Each has different security properties.

Alternative Approaches

Given these concerns, the gh-aw team may prefer one of these alternatives:

Option A: Codify [COMMENT] as the recommended pattern (lowest risk)

Update the docs to explicitly recommend allowed-events: [COMMENT] for review workflows. This is already what every team in this issue converged on. Bot reviews inform; humans control merge-blocking.

Option B: Explicit dismiss-pull-request-review safe output (higher visibility)

A separate safe output (not bundled as a side effect of posting) with:

  • scope: same-workflow — only reviews posted by same workflow identity
  • require-replacement: true — must be paired with a new review in the same run
  • max: 1 — rate limited
    This makes the permission grant visible in the workflow definition and auditable.

Option C: create-check-run for bot blocking

Status checks (required_status_checks) are purpose-built for bot blocking signals — independent of the human review flow, easy to re-run and replace, no dismiss semantics needed.

Precedent

add-comment already supports hide-older-comments: true with similar lifecycle semantics. However, as noted in the counter-arguments, hiding a comment is cosmetic (content still accessible), while dismissing a REQUEST_CHANGES review is a branch protection state change — categorically different operations.

The current submit-pull-request-review docs (reference) confirm no lifecycle management option exists today. The only options are: max, target, target-repo, allowed-repos, allowed-events, footer.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions