Repository navigation
spec(provider): adapters.t27 backoff reads as (base_delay * 2) ** attempt, not base_delay * 2 ** attempt #7443
Copy link
Copy link
Closed as not planned
Closed as not planned
Copy link
Labels
Description
specs/provider/adapters.t27:357, in calculate_retry_delay:
const delay = base_delay * @as(usize, 2) ** attempt;
The comment above the fn says "retry delay with backoff", so the intent reads as base_delay * 2^attempt. That is not what the spec says.
What the parser reads
t27c parses ** at the multiplicative level, left-associative, together with *, / and % (parse_expr_multiplicative in bootstrap/src/compiler.rs). So the line is
(base_delay * 2) ** attempt
For the spec's own test values (attempt = 2, base_delay = 100) that is 200 ** 2 = 40000, not 100 * 4 = 400. The result is capped at MAX_BACKOFF_MS, so the cap hides the error.
Why nothing caught it
- The only test,
provider_calculate_retry_delay, assertsdelay >= 100. Both readings pass. - Until gen-c: a numeric a ** b is lowered as an array repeat, /* repeat: a ** b */ {0} #7427, gen-c lowered every
**as an array repeat, so the C was/* repeat: ... */ {0}. The value never reached a running check. - In the Zig reference,
**is array repetition only, so the line is not a numeric power there either.
Proposed change
- Write the power with explicit parentheses,
base_delay * (@as(usize, 2) ** attempt), or use a shift,base_delay << attempt. The shift has the same meaning in every backend. - Make the test pin the value:
calculate_retry_delay(2, .exponential, 100) == 400. Add a case that hits the cap. - Reseal the spec.
specs/portable/relay_observer.t27:198 already uses the parenthesised form, base_delay * (base ** @as(u32, attempt)), so it is not affected.
Found by the gen-c defects lane while fixing #7427. Part of #5980.
Boundary
specs/portable/relay_observer.t27specs/provider/adapters.t27bootstrap/src/compiler.rs
User Scenarios
- Given the spec
specs/provider/adapters.t27definescalculate_retry_delaywithbase_delay * @as(usize, 2) ** attempt, when the parser reads the expression, then it interprets it as(base_delay * 2) ** attemptdue to left-associative**at multiplicative precedence. - Given a test call
calculate_retry_delay(2, .exponential, 100), when the current spec runs, then the computed delay is40000(capped) instead of the intended400. - Given the test
provider_calculate_retry_delayonly assertsdelay >= 100, when the buggy expression executes, then the test passes despite the wrong backoff formula.
Requirements
- FR-001: The expression in
calculate_retry_delayMUST computebase_delay * (2 ** attempt)by using explicit parentheses or a left-shift equivalent. - FR-002: The test
provider_calculate_retry_delayMUST assert the exact expected delay value400forcalculate_retry_delay(2, .exponential, 100). - FR-003: A new test case MUST verify the cap behavior by asserting
calculate_retry_delay(10, .exponential, 100) == MAX_BACKOFF_MS. - FR-004: The Provider spec seal MUST be regenerated after the spec changes.
Success Criteria
t27c check specs/provider/adapters.t27exits 0t27c test specs/provider/adapters.t27exits 0 and runs 2 tests (the existing pinned test and the new cap test)specs/provider/adapters.t27containsbase_delay * (@as(usize, 2) ** attempt)orbase_delay << attemptat the retry delay calculation site.trinity/seals/Provider.jsonis updated with the new seal hash
Reactions are currently unavailable
Activity
gHashTag commentedon Oct 7, 2026
Reopened 2026-10-07: the bulk close of issues without a
## Boundarysection was reverted by the owner. Nothing to do here; this comment replaces the earlier close notice.github-actions commentedon Oct 17, 2026
Re-filed as #9889.
An attempt claimed this issue on 2026-10-08 and left an empty branch. A dispatch row that has spent its retry ceiling is released once and then keeps its issue for good, which is the right rule for a third identical failure and the wrong outcome for an issue nobody ever attempted twice. The new issue carries the same work, the same boundary, and today's brief.
Closing this one so the swarm can take the work again.