The default Oban worker for agent turns: runs claude, then routes the
outcome back to the owning ObanClaude.Agent.Instance.
The owning agent's id, instance generation, and logical turn id ride in the
job's meta, where ObanClaude.Agent.Instance puts them at enqueue time. The
generation prevents a job from an earlier same-id process from controlling
its replacement. The turn id keeps retries attached to one logical turn and
fences late or duplicate callbacks. A job without an "agent_id" (enqueued
by hand) runs normally and reports to no one.
Retry-awareness
The routing is terminal-aware, so a worker with retries reports one logical turn, not one event per attempt:
- a success, a
{:cancel, _}verdict, or an{:error, _}on the final attempt is terminal ->ObanClaude.Agent.job_finished/3 - an
{:error, _}with attempts remaining, or a{:snooze, _}, means Oban will re-run the job ->ObanClaude.Agent.job_retrying/3, which keeps the machine in:runningand re-arms its watchdog
This worker itself stays at max_attempts: 1 -- every retry is a fresh paid
claude call, so retrying is an explicit opt-in, not a default. To opt in,
point the agent's :worker config at your own worker and delegate the
callbacks here:
defmodule MyApp.RetryingAgentJob do
use ObanClaude.Worker, queue: :agents, max_attempts: 3
@impl ObanClaude.Worker
def handle_result(result, job), do: ObanClaude.Agent.Job.handle_result(result, job)
@impl ObanClaude.Worker
def handle_error(verdict, payload, job),
do: ObanClaude.Agent.Job.handle_error(verdict, payload, job)
end