Swaps the return value of an Oban worker callback (perform/1, and Pro's process/1)
for a different but still valid Oban outcome — the higher-signal analogue of
Mutare.Mutators.ReturnValue's sentinel.
It is behaviour-gated: it fires only inside a module that implements Oban.Worker (or
Oban.Pro.Worker), read from the enclosing module's behaviour set
(context.behaviours, gathered by Mutare from a use Oban.Worker's injected
@behaviour). Everywhere else it is inert.
Because every swap is itself a valid Oban return, the mutant runs as a legitimate job that behaves differently — so a survivor pinpoints a precise gap: no test checks this job's success / failure / retry / cancel / snooze semantics.
The swaps
:ok -> {:error, :mutare} a completed job is now retryable
{:ok, value} -> {:error, :mutare} a completed job is now retryable
{:error, reason} -> :ok ★ a failure is silently swallowed
{:error, reason} -> {:cancel, reason} a transient failure becomes a permanent cancel
{:cancel, reason} -> {:error, reason} a permanent cancel becomes retryable
{:cancel, reason} -> :ok a cancel becomes a quiet success
{:snooze, seconds} -> :ok a reschedule is dropped
{:discard, reason} -> :ok (legacy discard) silently succeeds
{:discard, reason} -> {:cancel, reason}
:discard -> :ok (legacy bare discard) silently succeedsThe headline is {:error, reason} → :ok: the canonical Oban survivor-finder. If a
test inserts a job that should fail and never asserts the job ends up retryable /
discarded, that mutant lives.
Ignore variants
Each mutant is labelled by the Oban return it becomes — ok, error, or cancel — so a
# mutare:ignore[oban_worker_return:<label>] directive can suppress one kind of swap
without silencing the family:
{:error, reason} # mutare:ignore[oban_worker_return:ok] best-effort, failure unassertedkeeps the {:error, reason} → {:cancel, reason} mutant while dropping the
failure-swallowing :ok swap on that line. See Mutare.Ignore.
Each mutant reuses the original reason operand and injects only literal control atoms, so
every one is a valid return that compiles — the single metamutant build is never at
risk. Return tails are delivered by Mutare's structural return_replacements/2 hook, so the
swaps also reach a worker that returns from a branch tail of a case/cond/if/with in
tail position, not just the clause body.
Deliberately left alone
- The
{:ok, value}'svalueand a{:snooze, _}'ssecondsare dropped (they don't survive the tag change), never rewritten — a value/seconds swap isMutare.Mutatorsterritory and rarely an Oban-semantics question. - A bare
:okcarries no payload to preserve, so its only swap is to a sentinel error. - A bare
:canceltail is not matched: it is not a member of Oban'st:Oban.Worker.result/0union (only:discardhas a bare legacy form). Oban logs an unknown return and completes the job anyway, so an:okswap there would differ only by a log line — an unkillable no-op.
Summary
Functions
The deployment requirement: Oban.Worker must be loadable in the Mutare process, or the
use Oban.Worker expansion that surfaces @behaviour Oban.Worker cannot run and this mutator
silently never fires. Declaring it turns that into a loud Mutare.EnvironmentError at startup.
Oban.Pro.Worker is deliberately not listed — Pro is optional, and its absence only narrows
the gate.
Offer the alternative Oban return(s) for tail, but only inside a worker module (read from
context.behaviours). The context-aware form of
Mutare.Mutator.Structural.return_replacements/1.
Classifies an emitted swap by its replacement's return tag (Mutare.Mutator.variant/2).
The swap-target vocabulary for # mutare:ignore[oban_worker_return:<label>] — each mutant
is labelled by the Oban return it becomes: "ok", "error", or "cancel".
Functions
The deployment requirement: Oban.Worker must be loadable in the Mutare process, or the
use Oban.Worker expansion that surfaces @behaviour Oban.Worker cannot run and this mutator
silently never fires. Declaring it turns that into a loud Mutare.EnvironmentError at startup.
Oban.Pro.Worker is deliberately not listed — Pro is optional, and its absence only narrows
the gate.
Offer the alternative Oban return(s) for tail, but only inside a worker module (read from
context.behaviours). The context-aware form of
Mutare.Mutator.Structural.return_replacements/1.
Classifies an emitted swap by its replacement's return tag (Mutare.Mutator.variant/2).
Every mutant this family produces is exactly one of :ok, {:error, _}, or {:cancel, _},
so the label reads directly off the mutated node.
The swap-target vocabulary for # mutare:ignore[oban_worker_return:<label>] — each mutant
is labelled by the Oban return it becomes: "ok", "error", or "cancel".