Fix when the ready command requests a review and when it answers
What does this MR do and why?
#1889 (closed) lists four fixes for @gitlab-bot ready and the reviewer writes around it. The first, how bot writes add a reviewer, is shared by several processors and gets a merge request of its own. This one makes the other three, in the ready command, its delayed job and its rate limit: the order of its writes, the review it never requests after waiting for a diff, and the commands it drops without a reply. The two merge requests change no line in common, merge cleanly in either order, and the suite passes on the result, so each can be reviewed and merged on its own.
One commit per fix, plus one the last fix needed:
- Request reviewers before labelling ready for review. The command added workflowready for review before it wrote the reviewers.
AutomatedReviewRequestDocreacts to that label and reads the reviewers from the label event, so it saw the list from before the command: it could not tell that the command had named a technical writer, which is what the reviewer check from !4443 (merged) looks for, and it wrote that older list back with the writer added. The command now writes the reviewers first, so the label event carries them. The label still goes on when the reviewers cannot be written, as it did when it came first. - Request the review after the ready command's wait. When the diff was not ready yet, the command replied that it would wait 5 minutes before assigning a reviewer, and
MarkReadyForReviewJobthen only marked the merge request ready and added the label; the review request left that path in !4198 (merged). The job now hands the event back to the command. With a diff, the command requests the review it would have requested at once, and that counts as a use of the command. With the diff still empty, it says so and leaves the merge request as it is. It does nothing when the merge request is no longer open, or when a command sent during the wait, by anyone, already requested the review; every review request is remembered for 5 minutes for that. The reply to a command that finds no diff now says what happens after the wait. - Keep the rate limit's expiry where the first command set it. Each counted command stored the counter again with the time left rounded to whole seconds, which moved the expiry by up to half a second every time. The
rate limited API requestsexample that sends 100 commands for a team member failed now and then because of it. Once the command also posts a reply, in the next commit, it failed in 2 of 6 runs here without this commit and in none of 6 with it. - Answer every ready command. In projects with their own review process,
PROJECTS_WITH_EXTERNAL_REVIEW_PROCESSESand the Distribution projects, the command now replies that it did not request a reviewer, also when the command names one. Over the hourly limit it replies once in the period, and a later command in the same period is dropped without a reply, as before. The limit counts the commands sent while the merge request has no changes separately from the others, so the reply says when the command can be used again: at a time, or once the changes show.RateLimitanswers a dropped command only for a processor that definesreply_to_rate_limited_command, so the other commands that include it behave as they did; #1327 asks for this kind of answer in general, and they can opt in the same way.
Two more commits came out of the review:
- Check whether the review was requested before reading the MR. After the wait, the job now checks whether a command sent during it already requested the review before it reads the merge request and its changes. A review request is remembered for exactly as long as the wait, so one made right at its start could be forgotten during those two calls and the review requested twice. The same commit says more plainly in the command's documentation how the uses before the merge request has changes are counted.
- Keep a title the author changed during the ready command's wait. Marking the merge request ready writes its title without the draft prefix. After the wait the job took that title from the command's event, so a rename made during the wait was undone; it now takes the title from the merge request it reads.
Each reply mentions the person who sent the command, then says one of these:
this project reviews community contributions through its own process, so we added ~"workflow::ready for review" and did not request a reviewer.this command did not run: it can be used once an hour on a merge request.
You can use it again from 2026-10-17 15:43 UTC.this command did not run, because you already used it while this merge request had no changes.
Once your changes show on the **Changes** tab, you can use it again.this merge request still contains no changes, so no review was requested.
Once your changes show on the **Changes** tab, use `@gitlab-bot ready` again.The time in the second one is the first whole minute after the limit lifts, and a team member's says 100 times instead of once. The reply to a command that finds no diff now reads "We will check again in 5 minutes, and then request a review or tell you that there are still no changes." instead of "We will wait 5 minutes before assigning a reviewer."
Related to #1889 (closed)
Expected impact & dry-runs
This changes one reactive processor, its job and the rate limit module, and no policy, so no policy dry-run applies. I have not run it against the playground project.
@gitlab-bot ready |
Before | After |
|---|---|---|
| Requests reviewers | Adds the label, then writes the reviewers | Writes the reviewers, then adds the label, also when the reviewer write fails |
| Sent before the diff exists | After 5 minutes, marks the merge request ready and adds the label, no reviewer | After 5 minutes, requests the review as the command does, or says the diff is still empty; nothing if the merge request was closed or a later command already requested the review |
| In a project with its own review process | Marks ready and adds the label, no reply | The same, and one sentence saying no reviewer was requested |
| Over the hourly limit | Dropped, no reply | Dropped, with one reply in the hour saying when it can be used again |
The other commands that include RateLimit reply to nothing new. The documentation text of the command describes the wait and the replies.
I checked it in ruby:4.0.7, the image .gitlab-ci.yml uses, with the bundle from Gemfile.lock, on top of master at cc4e529a:
- Each commit: the full suite and RuboCop on the files it touches. 0 failures and no offenses on every commit.
- Each fix fails without its code: every commit's specs against its parent's code fail (1, 9, 1, 10, 1 and 1 failing examples) and pass against its own. The examples that only keep a behaviour the parent already had, such as the label going on when the reviewer write fails, pass on both. Together, the three spec files of the head fail 23 of their 86 examples against the code on
master. - The
specsandundercoveragejobs as CI runs them at the head: 7416 examples, 0 failures, line coverage 98.36%, andundercover -cagainstmasterreports no missing coverage in the changes. - RuboCop as the
lint-rubyjob runs it (rubocop -P -E .) at the head: 679 files, no offenses. - Together with the other merge request for #1889 (closed): the two branches merge with no conflict, and the suite on the merged tree gives 7437 examples, 0 failures.
- Danger's commit and title linters from
gitlab-dangerfiles4.12.0, the version inGemfile.lock: no problems in the six commit messages or this title.
Full suite, RuboCop and the red/green proof per commit
For each commit: bundle exec rspec over the whole suite, rubocop --force-exclusion on the Ruby files the commit touches, then the commit's specs against its parent's code (red) and against its own (green).
=== a3a173da Request reviewers before labelling ready for review
suite: 7392 examples, 0 failures, 1 pending
rubocop on touched files: 2 files inspected, no offenses detected
red, its specs on the parent code: 52 examples, 1 failure
green, its specs on its own code: 52 examples, 0 failures
=== 4159b1ff Request the review after the ready command's wait
suite: 7401 examples, 0 failures, 1 pending
rubocop on touched files: 4 files inspected, no offenses detected
red, its specs on the parent code: 62 examples, 9 failures
green, its specs on its own code: 62 examples, 0 failures
=== 8b001341 Keep the rate limit's expiry where the first command set it
suite: 7402 examples, 0 failures, 1 pending
rubocop on touched files: 2 files inspected, no offenses detected
red, its specs on the parent code: 10 examples, 1 failure
green, its specs on its own code: 10 examples, 0 failures
=== f558b8f2 Answer every ready command
suite: 7414 examples, 0 failures, 1 pending
rubocop on touched files: 5 files inspected, no offenses detected
red, its specs on the parent code: 84 examples, 10 failures
green, its specs on its own code: 84 examples, 0 failures
=== 7bff1c96 Check whether the review was requested before reading the MR
suite: 7415 examples, 0 failures, 1 pending
rubocop on touched files: 2 files inspected, no offenses detected
red, its specs on the parent code: 68 examples, 1 failure
green, its specs on its own code: 68 examples, 0 failures
=== 30419693 Keep a title the author changed during the ready command's wait
suite: 7416 examples, 0 failures, 1 pending
rubocop on touched files: 3 files inspected, no offenses detected
red, its specs on the parent code: 72 examples, 1 failure
green, its specs on its own code: 72 examples, 0 failuresAction items
- No environment variable is added.
- The handbook's Triage Operations page describes the ready command without these replies. I can open a handbook merge request once this one is merged.