# `Snodo.Extensions.Tasks.RetryPolicy`
[🔗](https://github.com/joshrotenberg/snodo/blob/v0.1.0/extensions/tasks/lib/snodo/extensions/tasks/retry_policy.ex#L1)

Immutable, JSON-safe retry timing for durable task work.

A policy is an exact, finite list of delays. Entry zero is used for the
first retry requested by an executor, entry one for the second, and so on.
Persisting the expanded list keeps scheduling deterministic across releases
and restarts; no runtime callback is re-evaluated after recovery.

The empty policy returned by `none/0` is the default and never retries.
Retry delays bound application-requested retries only. Re-delivery after an
ambiguous runner or node failure remains part of the runner's at-least-once
recovery contract and does not consume this list.

# `delay_ms`

```elixir
@type delay_ms() :: non_neg_integer()
```

# `t`

```elixir
@type t() :: %Snodo.Extensions.Tasks.RetryPolicy{delays_ms: [delay_ms()]}
```

# `exponential`

```elixir
@spec exponential(delay_ms(), non_neg_integer(), keyword()) ::
  {:ok, t()} | {:error, term()}
```

Expands a capped exponential policy into exact persisted delays.

`:max_delay_ms` defaults to the largest portable timer delay supported by
this package. The first retry uses `initial_delay_ms`, then each subsequent
entry doubles until the cap is reached.

# `exponential!`

```elixir
@spec exponential!(delay_ms(), non_neg_integer(), keyword()) :: t()
```

Builds an exponential policy or raises `ArgumentError`.

# `fixed`

```elixir
@spec fixed(delay_ms(), non_neg_integer()) :: {:ok, t()} | {:error, term()}
```

Builds `retries` entries with one fixed delay.

# `fixed!`

```elixir
@spec fixed!(delay_ms(), non_neg_integer()) :: t()
```

Builds a fixed-delay policy or raises `ArgumentError`.

# `from_map`

```elixir
@spec from_map(term()) :: {:ok, t()} | {:error, term()}
```

Decodes and validates a persistence map produced by `to_map/1`.

# `new`

```elixir
@spec new([delay_ms()]) :: {:ok, t()} | {:error, term()}
```

Builds a policy from its exact retry delays.

# `new!`

```elixir
@spec new!([delay_ms()]) :: t()
```

Builds an exact-delay policy or raises `ArgumentError`.

# `next_delay`

```elixir
@spec next_delay(t(), non_neg_integer()) :: {:ok, delay_ms()} | :exhausted
```

Returns the next persisted delay without modifying the policy.

# `none`

```elixir
@spec none() :: t()
```

Returns the default policy, which never retries executor outcomes.

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Encodes a policy as a stable JSON-safe persistence map.

# `validate`

```elixir
@spec validate(t()) :: :ok | {:error, term()}
```

Validates a policy constructed locally or decoded by an adapter.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
