CI fails when the external reviewer did not actually review - #378
Merged
Conversation
Sourcery posts a quota refusal as a *review*, so it reads as one to everything downstream. Twenty-six consecutive pull requests merged behind that sentence with nothing anywhere saying so. The classification lives in scripts/external_review_verdict.sh, called by the workflow and executed by the test, for the reason the public-api script is a script: a `case` inside a `run:` block is testable only by copying it, and the copy goes stale. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewer's GuideAdds a CI guard that ensures the external Sourcery reviewer actually reviewed the pull request, centralizing the classification logic in a testable script and wiring it into the gate job’s dependencies. Sequence diagram for external review CI gatingsequenceDiagram
participant PR as PullRequest
participant Job as external-review
participant GitHub as GitHubAPI
participant Sourcery as Sourcery
participant Verdict as external_review_verdict.sh
participant Gate as gate
Job->>GitHub: gh pr view(PR, labels)
alt no-external-review label
Job-->>Gate: exit 0
else review required
loop up to 12 polls
Job->>GitHub: gh api pulls/PR/reviews
GitHub-->>Job: Sourcery review bodies
opt no review yet
Job->>Job: sleep 10
end
end
Job->>Verdict: review bodies on stdin
alt quota refusal, diff-size refusal, or no review
Verdict-->>Job: exit 1
Job-->>Gate: external-review failed
else genuine review
Verdict-->>Job: exit 0
Job-->>Gate: external-review passed
end
end
Gate->>Gate: evaluate external-review in needs
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
The new entry stays in [Unreleased]: this has not shipped, and 0.13.0 went out while it was open.
This was referenced Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
gate's own comment already argues this case:That is what happened to the external reviewer, and nobody noticed for a day and a half. Sourcery answers a quota refusal as a review:
A review is what a reader looking for one finds — so this arrives in the same shape as a review that found nothing, satisfies anything checking that a review exists, and says nothing to anyone.
Twenty-six consecutive pull requests merged behind that sentence, from #343 (Aug 22, 15:53) to #376. Among them the largest changes in this repo: #369 (1,631 lines, breaking CLI grammar, shipped as 0.9.0), #367 (1,940 lines, shipped as 0.8.0 and withdrawn again in #376 twenty-two hours later), #371, #370, #362. Across the three days, 29 of 38 pull requests got no external review; three got a review with findings.
The second refusal is worse targeted than the first:
That one fires on size, so it exempts precisely the changes that least tolerate going unread. #369 is the pull request that got it.
What this adds
An
external-reviewjob insidegate'sneeds. It reads the pull request's reviews and refuses three things: a refusal on quota, a refusal on diff size, and no review at all after two minutes of polling. Sourcery normally answers inside a minute — well inside the shortest job here — but "well inside" is not "before", and a race lost would fail a pull request for the reviewer's latency rather than for anything about the change.It fails rather than warns, because a warning is precisely what already did not work. The way past it is the
no-external-reviewlabel (created on the repo), so merging unreviewed is a decision recorded on a named pull request rather than a default that quietly stopped applying.Where the classification lives
scripts/external_review_verdict.sh, called by the workflow and executed by the test — the same reasonscripts/public-api-snapshots.shis a script and not arun:block. Acasestatement that exists only inside a workflow is testable only by copying it, and the copy is the half that goes stale.How it was verified
test/test_external_review_guard.pyruns the real script against the real bodies those pull requests received — both refusals verbatim, plus the verbatim "reviewed your changes and they look great" that #330/#331/#333/#334/#337/#342 got, which is the case the guard must not fail and the one a naive "did the review say anything useful" check gets wrong.Red before green, both directions:
cat >/dev/null; echo okturns the four refusal tests red and leaves the four pass-through tests green.external-reviewfromgate'sneedsturnstest_the_gate_requires_the_jobred — the same shape of nothing-gating-anything the job exists to catch.Also replayed the classifier over the archived review bodies of all 38 pull requests from the window: it passes the 6 that were genuine reviews and fails exactly the 26 refusals, distinguishing #369's diff-cap message from the weekly-quota one.
pixi run styleclean (pylint 10.00/10),yaml.safe_loadon the workflow clean.Note on this pull request's own CI
The
external-reviewjob will fail on this pull request, because Sourcery's weekly quota is still exhausted. That is the guard working, on the first change it covers. Applyno-external-reviewto merge it — which is the intended flow, and makes this PR its own demonstration.What this does not do
It does not judge review quality, and it does not gate on a human having read anything — only on the external reviewer having answered about the code rather than about its own billing.
🤖 Generated with Claude Code
Summary by Sourcery
Require a substantive external reviewer response before CI can pass, while providing an explicit override for unreviewed merges.
New Features:
external-reviewCI job that verifies the external reviewer produced a genuine review before allowing the gate to pass.no-external-reviewlabel override for explicitly merging without external review.Bug Fixes:
Enhancements:
CI:
Documentation:
Tests: