Repository navigation
feat: wait for the checks another app reports after CI, before reading it - #25
Merged
Merged
Conversation
…g it A review is triggered when the Tests or Lint workflow finishes, and it skips while any check on the head commit is still running. Codecov, with its default wait_for_ci, reports only after every other check has finished, so its checks land 35 to 50 seconds after Tests, and nothing triggers a review when they do. Measured on sdk-py: the review read CI from 24 seconds before Codecov finished to 8 seconds after, so it usually reviewed without Codecov, and on 21efa02 it read while Codecov was in progress, skipped, and never came back. `wait_for_checks` lists such checks by name, and `wait_for_checks_seconds` caps the wait (default 300, at most 300). The new `reviewbot wait-for-checks` step, which runs before the Codex lease is borrowed, returns at once while a GitHub Actions job is still running, since its workflow triggers the review again; otherwise it polls until every listed check exists and has finished, or the cap passes. It never fails the job. The default lists nothing, so a repository that does not set it behaves as before.
`lease_report` reads its window, the last seven days, from the clock. The test fixed its only hold at 2026-09-21T10:00Z, so from 2026-09-28T10:00Z that hold fell outside the window and the test failed on every branch, main included. The hold now starts a day before the test runs.
🟡 Needs changes — Code reviewThis change adds policy-driven polling for delayed check runs, pins the subsequent review to the polled head, and accounts for that wait in the credential-lease budget. The polling deadline does not actually bound elapsed runtime because another API read starts after the final sleep reaches the deadline. Rating patch 4/6 (works with reservations) · proof 5/6 (reproducible) · overall 4/6 FindingsShould fix
Since last review
Reviewed |
The shortcut that ended the wait while any GitHub Actions check run was unfinished assumed that job's workflow would trigger the review again. Only the caller's `workflow_run.workflows` do, and only for a pull_request run: a CodeQL or Copilot job, a push-event run of Tests or a reporter workflow ended the wait, `run` skipped on the pending check, and nothing brought the review back (review finding 55a3117e). The wait now ends once every listed check exists and no check run is still running, from any app. With two trigger workflows both runs wait; the repositories that list Codecov borrow the Codex lease, so the second run then finds the head reviewed. Also: - a red conclusion ends the wait at once, since `run` skips it anyway; - at the deadline the log says whether the review reads CI without a check that never appeared, or skips because one is still running; - the last sleep ends at the deadline instead of overrunning it (7941a128); - `check_runs` reads every page and returns the conclusion (48de260e); - a transport error that is not a GitHubError no longer escapes the wait, and the step ends in `|| true`, so the wait can never skip the review; - the step runs before the pull request checkout, so a push during the wait is fetched instead of the new head being reviewed against the old files; - `_lease_minutes` leaves room for the wait when checks are listed and require_ci_green is on: from timeout_minutes 17 the worst case was 93 to 95 minutes against the job's 90; - a policy may not list the review's own check, which the wait never sees; - `lease_report` takes `now`, so its test uses fixed dates again (5e548d48); - setup.md says the wait needs require_ci_green, that a failing listed check skips the review, and that the names are check-run names.
MarketDataDev02
force-pushed
the
feat/wait-for-listed-checks
branch
from
September 29, 2026 23:48
1e28a55 to
0412566
Compare
A push during the wait could move the pull request to a head whose listed checks were never polled: the checkout fetched it, and `run` reviewed it as soon as its own CI was green, before Codecov reported. That head's own trigger then found it already reviewed and skipped (review finding 04a30a84). The wait now writes the commit it polled as the step output `head`, and `run` skips when the pull request has moved past it; the new head's own CI triggers the review that waits for it. Nothing is written when the policy asks for no wait, so those reviews run as before. The red conclusions the wait stops on are now the constant `ci_state` uses, so the two cannot drift (0c04032b). `cancelled` and `stale` stay out of it: `ci_state` does not count them as red, so the review still runs on them, and the wait keeps waiting for the checks that review will read.
The wait paginated the check runs but `ci_state` read only the first 100, so on a commit with more runs a failure on a later page stopped the wait as red while `run` saw green and reviewed anyway (review finding 5da929df). Both now read the listing through one helper that follows every page.
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.
Closes #24
The problem
A review is triggered when the
TestsorLintworkflow finishes, andshould_skipskips it while any check run on the head commit is not completed ("CI has not finished on the head commit"). Codecov, with its defaultwait_for_ci, postscodecov/patchandcodecov/projectonly after every other check has finished: it creates and completes them within one or two seconds, 35 to 50 seconds afterTests. Nothing triggers a review when they finish.Measured on MarketDataApp/sdk-py #159, comparing when the Tests-triggered run started
reviewbot runwith when Codecov's checks completed:reviewbot: skipped, CI has not finished on the head commitSo a review usually reads CI before Codecov exists, and
require_ci_greennever sees coverage; and when it reads during the second Codecov is in progress (21efa02: checks created 18:30:28Z, completed 18:30:30Z, read about 18:30:26Z), it skips and nothing brings it back. That head was reviewed only after a manual@marketdata-code-review re-review. Codecov reports on sdk-py, sdk-csharp and sdk-php.The change
wait_for_checks(list of check-run names, default[]) andwait_for_checks_seconds(default 300, from 0 to 300) in the policy, validated inconfig.py. A policy may not list its owncheck_name, which the wait never sees.GitHub.check_runs(sha, exclude_check_name): the name, status and conclusion of every check run but the bot's own, across every page of the listing.ci_statereads the same pages through the same helper, so the wait and the review judge the same runs.reviewbot wait-for-checks --pr N: reads the policy from the base branch; does nothing when forced, whenrequire_ci_greenis off or when nothing is listed; otherwise polls every 10 seconds, the last sleep ending at the deadline, until every listed check exists and no check run on the head is still running, from any app. It stops at once when CI is already red, by the sameFAILED_CONCLUSIONSthatci_stateuses (failure, timed_out, action_required), sincerunskips that anyway.cancelledandstaleare not red forci_state, so the review still runs on them and the wait keeps waiting for the checks it will read. At the deadline it logs whether the review reads CI without a check that never appeared, or skips because one is still running. Any error, a raw transport error included, is logged and the command exits 0.head, andreviewbot rungets it asREVIEWBOT_WAITED_HEAD: when the pull request has moved past that commit, the run skips, and the new head's own CI triggers the review that waits for its checks. Nothing is written when the policy asks for no wait, so those reviews run as before.review.yml: a "Wait for the checks the policy lists" step (id: wait) after the gate and before the pull request checkout and "Borrow the Codex credential", so the wait holds the runner but never the shared lease. It ends in|| true, so it cannot skip the review.docs/setup.md: the fourth choice under "Add a policy", including that the wait needsrequire_ci_green, that a failing listed check skips the review, and that the names are check-run names.A repository that does not set the key behaves as before: the step reads the pull request and the policy, and returns.
With two trigger workflows (sdk-py's
TestsandLint) both runs wait. The repositories that list Codecov borrow the Codex lease, so the second run then finds the head reviewed and skips; a repository without the lease may review twice.Budget
The wait is at most 300 seconds and happens before the lease, so it adds to the job's wall time, not to the lease.
_lease_minutessubtracts it from the lease wait's room when checks are listed andrequire_ci_greenis on, so overhead, the wait, the lease wait and both review attempts fit the 90-minute job for everytimeout_minutesfrom 1 to 30.Rollout order
Merge this first. Then each repository adds, on its default branch:
The policy loader rejects unknown keys, so a repository that sets it before this is merged fails every review with "policy.yml is malformed". Once a repository sets it, a red Codecov check makes the review skip with "CI is red on the head commit", as
require_ci_greenintends; list onlycodecov/projectto gate on the project status alone.Tests
tests/test_wait_for_checks.py(24): waits until both Codecov checks exist and finish, reading the base branch's policy and the head's check runs; a listed check still running, and an unrelated Actions workflow still running, keep the wait going; each of the three red conclusions ends it at once; it gives up after exactly the cap, the last sleep ending at the deadline, and says whether a check never appeared or is still running; it names the polled head, and names none when forced, unlisted, not gating on CI, malformed or failing; no listed check, a forced review, orrequire_ci_green: falseread nothing; a malformed policy, an API error and a dropped connection leave the decision to the review; the real client over a fake transport requests the base policy and the head's check runs; the CLI passes the pull request, repository, token andforce.tests/test_cli.py(3): a head that moved after the wait is not reviewed; an unmoved head goes on toshould_skip; the command forwardsREVIEWBOT_WAITED_HEAD.tests/test_config.py(7),tests/test_github.py(3),tests/test_workflow.py(3),tests/test_checkout_command.py(32): the defaults, a repository's list, the cap, the names and the own-check refusal; the status and conclusion per check run, every page read, and a failure on a later page makingci_statered; the step order, id, token,force,|| trueand the pinned head reaching the review; the lease wait leaving room for the wait at everytimeout_minutes, and keeping its whole room when nothing waits.uv run pytest: 755 passed on Windows. The other 54 fail on an unmodified checkout ofmainon Windows too, because their fakeclaudeandcodexexecutables are POSIX scripts; ubuntu CI runs them.ruff check .andruff format --check .are clean.Also: a test that expired
tests/test_lease_report.py::test_the_command_prints_a_report_for_the_storefailed this pull request's first CI run and fails onmainas well: it dated its only hold 2026-09-21T10:00Z, whilelease_reportread its seven-day window from the clock, so from 2026-09-28T10:00Z the hold fell outside the window.lease_reportnow takesnow, and the test passes a fixed one with its fixed dates.