The Oban worker that creates one child of a fan-out.
StatifierOban.Invoke.FanOut enqueues N of these up front, one per
item, and each one calls the host-wired
StatifierOban.Invoke.ChildStarter seam for its own index, with the
fan-out's aggregation policy. Creating the run is the seam's;
scheduling the call is this module's, which is the whole of the
division ADR-0007 draws between this package and the one that owns
durable runs.
The key
Jobs are unique on the four-component
{scope, invoke_id, macrostep, index} (ADR-0007 decision 4), read off
the args at the top level, over every state and an infinite period.
The first three are StatifierOban.Invoke.Worker's triple (ADR-0003)
and are shared by every child of one fan-out - they are one
invocation, planned in one macrostep - so index is what keeps a
replayed start from conflicting with its own sibling instead of with
itself. Re-enqueueing a fan-out therefore starts exactly the children
that are missing, which is the property decision 3's non-atomic slice
is safe because of.
The outcomes
- the seam returns
:ok-> the job completes; - the seam returns
{:error, reason}-> the job retries with{:start_failed, reason}recorded. A run store that is down or contended is an environment fact, andStatifierOban.Invoke.ChildStarter.start_child/5is idempotent on the index by contract, so retrying is what at-least-once means here; - the config names no
:child_starter, or one this node cannot resolve ->{:error, {:invalid_child_starter, _}}and a retry, the same deploy-shaped environment error an unresolvable delivery module is; - the row will not decode, or does not carry a usable
{index, count}or a readable"policy"-> the job cancels with{:undecodable, reason}, because no number of retries makes a corrupt row decodable.
Each successful start emits
[:statifier_oban, :invoke, :child_started] (ADR-0006's 2026-09-06
amendment) - the per-child seam event the bridge opens a linked root
on, carrying the child's index and the fan-out's count. A start
that retries emits nothing until it succeeds; the retry itself is
Oban's exception event.
Nothing here delivers into the run. A child start is not an answer:
the invocation is answered once, by the settlement side, when every
child has settled. A start that can never succeed exhausts its retries
and is visible on the job row, leaving its index without a run record -
the partially-started fan-out ADR-0007 decision 2 and sb-ADR-0009
decision 8 both specify behaviour for, and which the settlement side
is the one positioned to notice.