# `StatifierOban.Invoke.ChildStartWorker`
[🔗](https://github.com/riddler/statifier_oban/blob/v0.17.3/lib/statifier_oban/invoke/child_start_worker.ex#L1)

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 execution 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 executions.

## 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. An execution store that is down or
  contended is an environment fact, and `c:StatifierOban.Invoke.ChildStarter.start_child/5`
  is 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 execution. 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 an
execution 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.

`timeout/1` returns the job's run-time bound: the config's
`:child_start_timeout`, written into the job's meta by
`StatifierOban.Invoke.FanOut`, or `:infinity` for a job whose row
carries none. Oban enforces it from outside the job process; a start
that runs past it fails with `Oban.TimeoutError` and retries while
attempts remain, which the seam's idempotence on the index makes
safe, and a timed-out last attempt is the exhausted start described
above.

---

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