fix(pr-review): set FORCE_REVIEW only from an explicit dispatch flag (#2034)
Summary
FORCE_REVIEW (the break-glass that skips every review gate) is now set only when the dispatch carries an explicit client_payload.force_review: true. Before, any repository_dispatch set it. A dispatch without the flag now runs with every gate armed and gets only the narrow FORCE_RE_REVIEW same-SHA bypass, which applies only to an orphaned head marker.
Problem
Fixes #2034. The central mention listener (petry-projects/.github pr-review-mention-reusable.yml) sends the same {pr_url} payload for two very different events:
- a human
@donpetry-botcomment, and - a machine-added
review_requested: donpetry-bot, which our own automation adds together withneeds-human-review.
Because pr-review.yml set FORCE_REVIEW from event_name alone, every automated review request became a human-grade gate bypass. Each one approved past the maintainer-comment gate, and the next event run dismissed the approval (#1813). On #2022 this repeated until the per-PR automation budget was exhausted. The 10-07 health report shows 25 of 26 pr-review failures came from the repository_dispatch path.
Risk
Medium. This changes which runs may bypass the review gates. The change is stricter: no run gains a bypass, and dispatched runs lose the gate bypass until the listener sends the flag. Trade-off: a genuine human @donpetry-bot mention no longer skips the gates or re-runs a review that already has a verdict at the same SHA. It re-reviews with gates armed, which matches what the next event run enforced anyway. Restoring the human break-glass needs a listener change in petry-projects/.github (send client_payload[force_review]=true only for trusted human comment mentions, never for review_requested). I can't push to that repo from this session, so that is a follow-up.
Test plan
- New
tests/test_pr_review_force_review_wiring.bats(registered inlint.yml). It asserts thatFORCE_REVIEWrequires the explicit payload flag and is never derived from the event name alone, and that a dispatch getsFORCE_RE_REVIEW. The second test fails against the previous line. - Ran
bats tests/test_pr_review_force_review_wiring.bats tests/test_review_one_pr_force_ci_failing.bats: 5/5 pass. - Ran
bash scripts/dev-lead-lint.sh: passes. Parsed both changed workflows with PyYAML. actionlint is not installed here.
Rollback
Revert this PR. No non-revertible side effects.
Monitoring
In PR Review Agent — Trigger runs with event repository_dispatch, the log should show FORCE_REVIEW: false and FORCE_RE_REVIEW: true. The approve→dismiss pairs on a single head SHA (Dismissing approval due to a PR issue comment lacking a verified disposition (#1813)) should stop. The daily PR Review Agent health report's repository_dispatch failure share should drop.
Interaction contract
- N/A — this PR does not add or change an agentic role.
Checklist
-
shellcheck scripts/*.shpasses for any changed shell scripts (no shell scripts changed; repo lint passes). - Docs updated where behavior or standards changed (the workflow's own FORCE flag comment block).
🤖 Generated with Claude Code
https://claude.ai/code/session_019K6UsD7r25pAcRjJL3G6Kx
Generated by Claude Code