Files
workflows-py/docs
Sohel Bhongade 47c7e3f249 fix(retry): correct attempt-index off-by-one between wait strategies (#734)
## Summary

Fixes an off-by-one error in retry wait-strategy indexing.

## Problem

`_ComposableRetryPolicy.next()` forwarded the runtime's 1-indexed
`attempts` value (which always starts at 1 on the first failure, see
`runtime/control_loop/reduce.py: failures = this_execution.attempts +
1`) straight to the configured wait strategy.

Every attempt-indexed wait strategy (`wait_chain`, `wait_exponential`,
`wait_incrementing`, `wait_exponential_jitter`,
`wait_random_exponential`) is documented and unit-tested as 0-indexed,
where index 0 is the delay before the first retry.

The mismatch meant every wait strategy silently skipped its first
configured delay and used the second delay for the first retry, third
for the second, and so on. For `wait_chain` specifically, this meant the
first strategy in the chain was never used at all.

## Solution

Subtract 1 from `attempts` before passing it to the wait strategy, so it
receives the 0-indexed value its contract expects.

Stop conditions are unaffected and continue to receive the original
1-indexed `attempts`, which is the correct convention for
`stop_after_attempt`.

The existing unit tests for `.next()` were also updated because they
called it directly with attempts starting at 0, an assumption that did
not match how the runtime actually invokes it. A regression test was
added that reproduces the real retry-loop calling convention end-to-end
and fails with the previous behavior.

## Tests

- Full test suite: 857 passed
- Ruff formatting/lint checks passed
- Type checking passed

Fixes #733

---------

Co-authored-by: Adrian Lyjak <adrianlyjak@gmail.com>
2026-08-12 21:06:24 +00:00
..