◆ NFRGate / Rule Reference

R2 — Retries back off

critical 🛡️ reliability · static analyzable

Any retry logic on failure uses exponential backoff with jitter, not a fixed interval or unbounded loop.

Python Implementation

R2: retries on failure use exponential backoff with jitter, not a fixed
interval or unbounded loop. rubric_store/definitions/reliability.yaml tags
this static_analyzable, but jitter specifically can't be verified reliably
from syntax alone — this rule is honest about that split:

- A sleep call with a bare numeric literal delay (`sleep(2)`) is an
  unambiguous fixed-interval retry -> fail, high confidence.
- A sleep call whose delay is an arithmetic expression involving another
  value (`sleep(2 ** attempt)`, `sleep(base * attempt)`) looks
  backoff-shaped -> pass, but at reduced confidence, because jitter isn't
  verified and the confidence-gating policy (routing/policy.py) should
  route anything this rule isn't sure about to a human rather than
  auto-approve it.
- Anything else (a variable, a function call, ...) is genuinely not
  classifiable from syntax -> unclear, not a guess.

Only sleep calls inside a loop are evaluated at all — a bare `sleep()` with
no enclosing while/for isn't a retry pattern this rule has an opinion on.

Java Implementation

R2 for Java: Thread.sleep(...) delay inside a retry loop — fixed literal
vs. arithmetic (backoff-shaped) vs. unrecognized, same three-way split as
the Python version and for the same reason (jitter isn't statically
verifiable either way).

Go Implementation

R2 for Go: time.Sleep(...) delay inside a retry loop. Unlike Python/Java,
a bare binary_expression isn't itself the backoff signal here — Go's
`N * time.Second` idiom means a binary_expression is also how you write a
FIXED delay. See ast_helpers.classify_go_duration for the actual logic.
← All rules