Repository navigation
Exit 45 When a Held Wait Releases a Head Whose Required Checks Never Ran - #3483
Conversation
A held wait exited 0 on a DIRTY merge, a BEHIND merge with a required context unregistered, and a payload with no commit matching the head, though the help said the required checks had settled. Each now exits 45 with status=CHECKS_NOT_RUN naming which, and the wait help and the README's exit table and held-wait paragraph say so together. Closes #3471 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The local review found the exit 45 text claiming more than the read proves, a DRAFT merge reaching it with a remedy that does not apply, and the rules read even where no path used them. The unregistered check now applies to DIRTY and BEHIND only, reads the rules there alone, shares one whole-rollup helper with the BLOCKED hold, and names a check from another app. The help and README state each exception. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second local review found the unread-payload remedy pointing at status, which cannot reach exit 45, and no open-state guard on the DIRTY and BEHIND path. The rules now come through required_for, the unread-rules note no longer promises a hold this path never makes, and the status names checks missing from the read rather than checks not run, since an unread payload's checks may have run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
A recorded local strict-review pass covers head |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
The three findings the local pass left open on #3483: the 45 entry's hold sentence now names the conditions the hold needs, the README no longer counts its cases, and each cause carries its own remedy, a check from another app included. checks_never_ran now takes the rules and decides the whole state, so wait holds no second guard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved review comments remain in the help text, status guidance, and README documentation.
3 open issues
What changed in this PR
This pull request adds exit code 45 (CHECKS_MISSING) for held waits whose required checks never ran or cannot be trusted, while preserving documented exception behavior.
Changes:
- Adds missing-check and required-context detection for relevant merge states.
- Updates
waithelp text and README documentation. - Adds regression coverage for missing, unreadable, truncated, draft, and app-mismatched checks.
| File | Summary |
|---|---|
tests/test_pr_review.py |
Adds coverage for the new exit-45 behavior and related exception paths. |
scripts/README.md |
Documents exit 45 and held-wait behavior; an open comment requests removing the incidental case count. |
scripts/pr_review.py |
Implements exit 45 and updates help and status guidance; open comments request clearer conditions, remedies, and wording. |
scripts/pr_review_checks.py |
Adds missing-check classification, merge-state handling, and reusable required-context checks. |
Review effort: Lite
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #3483 +/- ##
===========================================
+ Coverage 81.00% 81.03% +0.03%
===========================================
Files 25 25
Lines 8903 8918 +15
===========================================
+ Hits 7212 7227 +15
Misses 1691 1691
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The local pass on the fix push found the DIRTY and BEHIND rules read ahead of the attestation guard, so a Copilot-covered head made an API call and could print an unread-rules note on a run that judged nothing. The summary lines now name a check from another app, the unreadable remedy points at gh pr checks rather than another wait that can read the same mismatch, and _unmet_note judges only a rollup read whole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second local pass on the fix push found the help and README never saying the pull request must be open, though a closed one exits 0. required_unmet folds into required_unregistered, its only caller, and a test pins that the exit 45 rules read skips every merge but DIRTY and BEHIND. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
A recorded local strict-review pass covers head |
The local pass on the last push found the unreadable remedy naming a command that reads the same last commit, the status line claiming the poll cannot help a payload that a later read may fix, and the README row and the closed sentence wider than the code. required_for now skips a pull request no longer open, so a closed one reads no rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A push-triggered workflow still runs on a conflicted head, so the empty-rollup cause names the pull_request workflow, as the help and README already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
A recorded local strict-review pass covers head |
) ## Summary Promotes the pull requests merged into develop since release 2.0.1184. - [#3483](#3483): a held `wait` exits `45` with `status=CHECKS_MISSING` where it used to exit `0` over checks that never ran. The cases are a payload with no commit matching the head, a `DIRTY` merge with no check posted, and a `DIRTY` or `BEHIND` merge whose required context is unregistered or came from another app. The `wait` help and `scripts/README.md` name each exception that stays at `0`. - [#3485](#3485): the exit-0 list in the help and README names a pull request no longer open, which this promotion's own review found missing. - [#3486](#3486): where the head's rollup holds a type the reader cannot parse, the `CHECKS_MISSING` clause and the `BLOCKED` hold's note say a required check may sit in that unread part, which this promotion's second review found missing. - [#3490](#3490): the no-check-posted cause of exit `45` is scoped to `DIRTY` in the help and README and names its remedy, which this promotion's third review found missing. - [#3491](#3491): the `wait` help and README restore the caveat that a review-present exit `0` does not poll the checks, which this promotion's fourth review found dropped. Closes #3471 ## Constituent coverage Each change this promotion carries had coverage on its own pull request into develop: | Pull request | Merged head | Coverage | | --- | --- | --- | | [#3483](#3483) | `8f29cadd` | attested local pass at the merged head, base `develop`. A Copilot round on the earlier head `ee530a4b` opened three findings, each fixed in `995ce78a` and resolved, so no Copilot round covers the merged head | | [#3485](#3485) | `275e4c8b` | attested local pass at the merged head, base `develop`, and a Copilot table on the merged head naming exactly its 2 changed files, no open findings | | [#3486](#3486) | `1f9fde03` | attested local pass at the merged head, base `develop`. A Copilot round on the earlier head `b8a74e8a` opened three findings, each fixed later on the branch and resolved, so no Copilot round covers the merged head | | [#3490](#3490) | `ba37c856` | attested local pass at the merged head, base `develop`, and a Copilot table on the merged head naming exactly its 4 changed files. Its headline's test-wording preference was declined in a comment | | [#3491](#3491) | `8c65e4d8` | attested local pass at the merged head, base `develop`, and a Copilot table on the merged head naming exactly its 2 changed files, verdict clean, no open findings | 🤖 Generated with [Claude Code](https://claude.com/claude-code)

Summary
A held
waitexited0once nothing held the poll, even where the head's required checks never ran: aDIRTYmerge, aBEHINDmerge with a required context unregistered, and a payload whose rollup commit is not the head. The maintainer decided on #3471 that these paths get a new exit code naming that state.waitnow exits45withstatus=CHECKS_MISSINGon an open, attested held head the poll released, under a merge other thanCLEAN,UNSTABLE, orHAS_HOOKS, where the payload carries no commit matching the head or no check has posted.45where the merge readsDIRTYorBEHINDand a context the base branch's rules require is missing from a rollup read whole, or came from another app.DRAFTmerge, unread rules, and aCHECKS TRUNCATEDrollup stay at exit0, and a pull request no longer open never exits45. The help and README name each exception.pr_review_checks.pygainschecks_never_ran,required_unregistered, which replacesrequired_unmetand now guards theBLOCKEDhold and_unmet_notealike, andMERGE_UNGATED.required_fortakes the merge states it reads rules for, and reads none for a pull request no longer open.waithelp,scripts/README.md's exit table, and its held-wait paragraph are rewritten together.Part of #3471, which the promotion pull request closes.
Local pass findings
Every
introducedfinding the local passes raised is fixed on this branch, the earlier list in this body included. These were declined asstyleon text this change wrote, a preference between defensible forms per fleet-code-review's rule to omit preferences:45decision living partly inrequired_for'sgatedargument rather than wholly inchecks_never_ran. Keeping it in the caller is what limits the rules read toDIRTYandBEHIND.gatednaming the merges whose rules are read, where the prose calls any merge outsideMERGE_UNGATEDgated.Test plan
python -m unittest test_pr_review test_pr_review_size test_pr_review_copilotfromtests/: 534 tests pass.prose_lint.py --check sentence-lengthis clean over the diff.🤖 Generated with Claude Code