skip to content

Allure 3's `quality-gate` command validates rules as results are read, and `--fast-fail` makes it exit at the first breach. Why can a suite that retries its failed tests turn that gate red on a run whose finished report is green?

level: seniorimportance: should knowfreq 46%

answer

  1. the gate does not wait for everything
  2. batches validated as results arrive
  3. the retry flag is assigned retroactively
  4. it exits before the passing attempt lands

basics

~20 s

Because the gate validates in batches while results are still being read. A failed first attempt is only recognised as superseded once its later, passing attempt has been read, and with fast-fail the command has already exited by then.

solid answer

~40 s

The command does not wait for the whole results directory. It subscribes to results as they are read and validates each batch against accumulating state, so a rule can breach part-way through. Attempts of the same test are grouped by a hash the reader computes from the test's identity, its parameters and its environment, and every attempt but the surviving one is flagged as a retry and excluded from validation. That flag is recomputed each time another attempt joins the group, so while only the failing first attempt has been read it *is* the survivor and it counts toward `maxFailures`. With `--fast-fail` the process exits there, before the passing retry is ever read. Without the flag the same directory reaches the final pass, which validates every result with retries excluded, and can be green.

go deeper

for a junior

Know that a retried test writes more than one result for the same test, and that the report shows the surviving attempt rather than counting each attempt separately.

for a middle

Explain that the gate validates incrementally as results are read, and that the retry flag is computed by the reader when a sibling attempt arrives rather than carried in the file.

for a senior

Demonstrate the failure: an early exit fires on a result a later attempt would have withdrawn, so the verdict depends on read order and cannot be reasoned about.

for a principal

Take a position on whether an early-exit gate belongs in a pipeline that retries at all, and say what you would rather spend the saved time on.

## The command validates while it reads It is tempting to picture a gate as something that happens once, at the end, over a finished set of results. Allure 3's `quality-gate` command is not built that way. It starts the report, subscribes to results as the reader emits them, and validates each incoming batch against state that accumulates across batches. A failure-counting rule does not recount from scratch on every batch; it adds this batch's failures to the running total it stored last time and compares the total against the threshold. That design is what makes an early exit possible at all. If the total has already passed the ceiling, no later batch can bring it back down, so there is nothing to gain by reading the rest. `--fast-fail` turns that observation into behaviour: at the first breach the command prints what it found and exits non-zero immediately. There is also a second, ordinary pass. When the reader has finished, the command validates once more over **all** results, this time with retries explicitly excluded, and that pass produces the verdict when nothing fast-failed. ## How a retried test is recognised Allure 3 does not read "this is attempt two" out of a result file. The reader computes a grouping key -- `retryHash` -- from the test case's identity, a hash of its parameters, and the environment it ran in. Results that produce the same key are attempts at the same thing. The store keeps those attempts in a list and, **every time another attempt is added, re-sorts the list and recomputes the flags**: the attempt that sorts first is the surviving one and is not a retry; every other member of the group is flagged as a retry. Validation then skips anything flagged as a retry, along with anything a known-issues rule has resolved as muted or accepted, and anything already processed in an earlier batch. The consequence is the whole of this question. **Being a retry is not a property a result has when it arrives. It is a property the store confers on it later, when a sibling attempt shows up.** ## The sequence that produces the false red 1. The suite runs a test, it fails, and the framework retries it. Two results are written for that test. 2. The reader reaches the failing attempt first. Its group has one member, so it sorts first, so it is not a retry. 3. The gate validates that batch. The failure counts. If the ceiling was zero, the rule breaches. 4. `--fast-fail` is set, so the command prints the breach and exits non-zero. 5. The passing attempt is still sitting in the results directory, unread. Had it been read, the earlier attempt would have been re-flagged as a retry and dropped from validation, and the final pass would have found no failure at all. Run the same directory without `--fast-fail` and the outcome inverts: the early breach is recorded but stops nothing, the reader finishes, the final pass runs over all results with retries excluded, and the gate goes green. **So the verdict depends on the order the results were read**, which depends on the file system, which is not something you control or can usefully reason about. That is not a bug you can configure away; it is what a decision taken before all the evidence has arrived means. ## What to do about it - **Do not combine an early-exit gate with framework-level retries.** If your suite retries, run the gate over the finished results directory and accept that you pay for the whole read. The point of retrying is that the first outcome is not the answer, and a gate that acts on the first outcome has contradicted that. - **Note that Allure's own runner takes the same position.** Allure 3's `run` command declines to validate the gate at all when it is asked to rerun failures: it runs the test command and warns that gate validation was skipped for that run. The tool has already concluded that these two features do not compose. - **If you genuinely need the fast exit, attach it to a rule retries cannot affect.** The stop-at-first-breach behaviour belongs to a rule group rather than to the gate as a whole, so a group holding, say, an environment-coverage rule can end the command early while the failure-count and success-rate rules sit in a group that is only reported at the end. - **Be precise about what you are buying.** The fast exit shortens the tail of the *reading* step, not the test run -- the tests have already executed and written their results. On a very large results directory that is worth something; on a small one it is noise beside the risk. ## The general shape of the trap Any gate that evaluates a stream, over a corpus where a later record can retract an earlier one, will sometimes act on a record that was about to be retracted. The retry is the everyday instance, but the same reasoning covers any late-arriving result: a shard that finishes last, an attachment that lands after its result, an environment that only appears once its first test has been read. If a rule's input can still change, an early verdict on it is a guess.

  • Two runs over the same results directory give different gate verdicts with `--fast-fail` on. What single input changed?
    The order the results were read. The early exit depends on whether the failing attempt reached the gate before its passing sibling, and that ordering comes from the file system rather than from anything you configured. Dropping the early exit removes the dependence entirely.
  • You still want an early exit somewhere. Which rule would you put behind it?
    One whose input cannot be retracted by a later result: an environment-coverage rule, or a threshold on a value that only grows. Failure counts and success rates are exactly the wrong choice, because a retry can withdraw a failure the gate has already counted and acted on.

It is the difference between a referee who whistles the instant the ball crosses the line and one who waits for the video review. The early whistle is faster, and every so often it stops play on a call the replay would have overturned.

saying these in an interview costs you the question

  • Thinking the gate always sees the whole results directory first
  • Believing a retry is marked in the result file itself
  • Combining fast-fail with framework retries and trusting the verdict
  • Claiming the fast exit saves the test run's own time