Skip to content

Exit 45 When a Held Wait Releases a Head Whose Required Checks Never Ran - #3483

Merged
ptr727 merged 8 commits into
developfrom
fix/3471-held-wait-exit-code
Oct 25, 2026
Merged

ptr727 merged 8 commits into
developfrom
fix/3471-held-wait-exit-code

Conversation

@ptr727

@ptr727 ptr727 commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

A held wait exited 0 once nothing held the poll, even where the head's required checks never ran: a DIRTY merge, a BEHIND merge 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.

  • wait now exits 45 with status=CHECKS_MISSING on an open, attested held head the poll released, under a merge other than CLEAN, UNSTABLE, or HAS_HOOKS, where the payload carries no commit matching the head or no check has posted.
  • It also exits 45 where the merge reads DIRTY or BEHIND and a context the base branch's rules require is missing from a rollup read whole, or came from another app.
  • A DRAFT merge, unread rules, and a CHECKS TRUNCATED rollup stay at exit 0, and a pull request no longer open never exits 45. The help and README name each exception.
  • Each cause in the status line carries its own remedy.
  • pr_review_checks.py gains checks_never_ran, required_unregistered, which replaces required_unmet and now guards the BLOCKED hold and _unmet_note alike, and MERGE_UNGATED. required_for takes the merge states it reads rules for, and reads none for a pull request no longer open.
  • The wait help, 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 introduced finding the local passes raised is fixed on this branch, the earlier list in this body included. These were declined as style on text this change wrote, a preference between defensible forms per fleet-code-review's rule to omit preferences:

  • The exit 45 decision living partly in required_for's gated argument rather than wholly in checks_never_ran. Keeping it in the caller is what limits the rules read to DIRTY and BEHIND.
  • gated naming the merges whose rules are read, where the prose calls any merge outside MERGE_UNGATED gated.
  • The rules read running before the unreadable or empty-rollup cause decides the outcome. The read is memoized, and its note is accurate there.
  • The help's exit-0 sentence on where a required check can stay unregistered reading as wider than the attested held head. Its "then" ties it to the attested clause before it.
  • The unreadable-payload string running past 100 characters. No gate enforces the width on a string literal.

Test plan

  • python -m unittest test_pr_review test_pr_review_size test_pr_review_copilot from tests/: 534 tests pass.
  • Mutations removing the open-state guard, or reading the rules ahead of the attestation guard, each fail a test.
  • The pre-commit gates (ruff, mypy, prose, eol) pass, and prose_lint.py --check sentence-length is clean over the diff.

🤖 Generated with Claude Code

ptr727 and others added 3 commits October 25, 2026 14:05
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>
Copilot AI lite review requested due to automatic review settings October 25, 2026 21:23
@ptr727

ptr727 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head ee530a4bd6fac64c6f9a137b597d2a7b3e334bdb, the content this pull request carries at that commit against develop, and it recorded 4 findings.

@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 wait help 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

Comment thread scripts/README.md Outdated
Comment thread scripts/pr_review.py Outdated
Comment thread scripts/pr_review.py Outdated
@codecov

codecov Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.03%. Comparing base (0da95bc) to head (8f29cad).

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              
Flag Coverage Δ
python-3.13 81.03% <100.00%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

ptr727 and others added 2 commits October 25, 2026 14:31
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>
@ptr727

ptr727 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head 5528f34b6ce9c88a4447556dd586bf843f1ba238, the content this pull request carries at that commit against develop, and it recorded 5 findings.

ptr727 and others added 2 commits October 25, 2026 14:42
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>
@ptr727

ptr727 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

A recorded local strict-review pass covers head 8f29cadd36b5e90bd99fe44afb5c6699612dc403, the content this pull request carries at that commit against develop, and it recorded 2 findings.

@ptr727
ptr727 merged commit 2377e52 into develop Oct 25, 2026
13 checks passed
@ptr727
ptr727 deleted the fix/3471-held-wait-exit-code branch October 25, 2026 22:05
ptr727 added a commit that referenced this pull request Oct 26, 2026
)

## 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants