# `Chimeway.Dispatch.ObanWorker`
[🔗](https://github.com/jonlunsford/chimeway/blob/v1.0.0/lib/chimeway/dispatch/oban_worker.ex#L51)

Oban worker that performs a single Chimeway delivery by delivery_id.

Job args contain only `delivery_id` (UUID string). Full payload is never
stored in Oban job args — the delivery row is the source of truth.

## Transactional enqueue

Insert this worker's job inside the same `Ecto.Multi` as delivery row creation:

    alias Chimeway.Dispatch.ObanWorker

    Ecto.Multi.new()
    |> Ecto.Multi.insert(:delivery, delivery_changeset)
    |> Oban.insert(:job, ObanWorker.new(%{delivery_id: delivery.id}))
    |> Chimeway.Repo.transaction()

Rolling back the `Ecto.Multi` also rolls back the job — no orphaned jobs.

## Idempotency and terminal-state short-circuit

The worker checks delivery terminal states (via `Chimeway.Deliveries.terminal_states/0`)
on every execution. A delivery already in `:succeeded`, `:suppressed`, or `:cancelled`
returns `:ok` immediately with no adapter call and no new attempt row.

## Phase 14 retry contract (REL-02 / REL-03)

OSS Oban 2.21.1 has no exhausted callback. This worker uses an in-band
`attempt == max_attempts` guard inside `perform/1` to know when it has reached
the final retry — that is the moment the durable `:cancelled retries_exhausted`
state is written via `Deliveries.exhaust_delivery/1`.

Return-value contract:

- Successful delivery -> `:ok`.
- Permanent / bounced failure -> `:ok` (record_attempt already converged the
  delivery to `:cancelled` with the appropriate suppression_reason; Oban does
  not retry).
- Transient failure with retries remaining (`attempt < max_attempts`) ->
  `{:error, reason}`; Oban schedules a retry under its default exponential-
  with-jitter `c:Oban.Worker.backoff/1`.
- Transient failure on the final attempt (`attempt == max_attempts`) ->
  `Deliveries.exhaust_delivery/1` writes the `:cancelled retries_exhausted`
  terminal state, then this function returns `:ok` so the Oban job is marked
  `:completed` instead of `:discarded` (RESEARCH Pitfall 1: keeps operator
  telemetry dashboards clean — the durable explanation lives on the delivery
  row, not on the Oban job).

---

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