Event-driven backstop for the terminal {:workflow_finished, _} notification.
Baton.Worker announces a workflow the instant its last step returns cleanly.
But a step that crashes — raises, exits, times out, or otherwise exhausts
its retries via an exception — never returns through the worker's result
handler, so Baton.Completion.check/2 is not called for it. Historically the
only thing that caught those workflows was Baton.Plugin's periodic sweep,
which meant up to a full interval of latency before the finish event fired.
This module closes that gap by listening to Oban's own job telemetry. It
attaches to [:oban, :job, :exception] and, when a workflow job reaches a
terminal :discard, calls Baton.Completion.announce/2 immediately. Oban
writes the job's terminal state to the database before emitting the event
(ack_event precedes emit_event in the executor), so announce/2 reads the
already-settled state with no overlay needed. The exactly-once claim in
Baton.Completion means this never double-fires with the worker path.
With this attached, the plugin's sweep is a true backstop (for workflows
killed entirely outside job execution, e.g. an external Oban.cancel_all_jobs
that never runs perform/1) rather than the primary detector — so the sweep
interval can be long without delaying the common crash case.
Wiring
Baton.Plugin attaches this in its init/1 and detaches on terminate/1, so
hosts that run the plugin get it for free. Hosts that do not run the plugin can
attach it from their own application start:
Baton.CompletionReporter.attach()