# `Threadline.Retention`
[🔗](https://github.com/szTheory/threadline/blob/v0.9.0/lib/threadline/retention.ex#L1)

Batched retention purge for `audit_changes` and empty `audit_transactions`.

Requires **`config :threadline, :retention`** with **`enabled: true`** before any
destructive run (see `Threadline.Retention.Policy`). Callers must pass **`repo:`**
explicitly, matching `Threadline.Query` conventions.

Cutoff is derived from `Threadline.Retention.Policy.cutoff_utc_datetime_usec!/0`
unless you pass **`cutoff:`** (UTC `DateTime`, microsecond) for a stricter window
than policy alone (operators only — must be **older** than the policy cutoff).

# `purge_result`

```elixir
@type purge_result() :: %{
  deleted_changes: non_neg_integer(),
  deleted_transactions: non_neg_integer(),
  batches_run: non_neg_integer()
}
```

Accumulator returned by `purge/1` on success (counts are cumulative).

# `purge`

```elixir
@spec purge(keyword()) :: purge_result() | {:error, :disabled}
```

Deletes captured changes older than the retention cutoff in batches, then removes
orphan `audit_transactions` when `delete_empty_transactions` is true in config.

## Options

- **`:repo`** — required `Ecto.Repo`.
- **`:batch_size`** — max rows per delete pass (default `500`).
- **`:max_batches`** — max outer iterations, each consisting of one change batch
  plus orphan draining (default `10_000`).
- **`:dry_run`** — when `true`, no deletes; returns counts of rows that **would**
  match delete predicates (`:deleted_changes` / `:deleted_transactions` are
  those counts, `:batches_run` is `0`).

Returns `{:error, :disabled}` when `:retention` → `enabled` is not `true`.
Successful calls return a result map (see `purge_result/0`).

---

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