# Retry strategies

Strategies can use one of two signatures:

- Context-aware: `strategy(ctx: BackoffContext) -> float`
- Legacy: `strategy(attempt: int, klass: ErrorClass, prev_sleep_s: float | None) -> float`

`BackoffContext` provides:

- `attempt` – 1-based attempt number
- `classification` – structured `Classification` (retry hints, details)
- `remaining_s` – deadline time remaining (seconds)
- `prev_sleep_s` – previous sleep value
- `cause` – `"exception"` or `"result"`, indicating whether the failure came from a raised exception or a classified return value

Built-ins (all return a `StrategyFn`):

- `decorrelated_jitter(base_s=0.25, max_s=30.0)` – sleeps uniformly in `[base_s, prev_sleep*3]`, clamped to `max_s`.
- `equal_jitter(base_s=0.25, max_s=30.0)` – exponential with jitter in `[cap/2, cap]`, `cap = min(max_s, base_s*2^attempt)`.
- `token_backoff(base_s=0.25, max_s=20.0)` – gentler exponential (`1.5^attempt`) with jitter.
- `retry_after_or(fallback, jitter_s=0.25)` – honors `Classification.retry_after_s` when present, otherwise calls `fallback`.
- `adaptive(fallback, window_s=60.0, target_success=0.9, min_multiplier=1.0, max_multiplier=5.0)` – wraps a fallback strategy with adaptive scaling based on recent success/failure rate (thread-safe).

Example:

```python
from redress import ErrorClass, RetryPolicy, default_classifier
from redress.strategies import decorrelated_jitter, equal_jitter

policy = RetryPolicy(
    classifier=default_classifier,
    strategy=decorrelated_jitter(max_s=10.0),  # default for most classes
    strategies={
        ErrorClass.RATE_LIMIT: decorrelated_jitter(max_s=60.0),
        ErrorClass.SERVER_ERROR: equal_jitter(max_s=30.0),
    },
)
```

Retry-After-aware example:

```python
from redress import RetryPolicy
from redress.extras import http_retry_after_classifier
from redress.strategies import decorrelated_jitter, retry_after_or

policy = RetryPolicy(
    classifier=http_retry_after_classifier,
    strategy=retry_after_or(decorrelated_jitter(max_s=10.0)),
)
```
