# `Dsxir.Predictor.Parallel`

Fan-out predictor helper. Runs N predictor calls concurrently under
`Dsxir.TaskSupervisor`, replaying the caller's `Dsxir.Settings.snapshot/0`
in each worker so settings-scoped state (metadata, lm, adapter, cache)
is preserved.

Returns `{prog', results}` where `results` mirrors the input order:
successful entries are `{:ok, prediction}` and failures are
`{:error, exception}`. The caller decides whether to raise — Parallel does
not.

## Iron Law of OTP

Workers are short-lived `Task`s, no mutable state across calls. Concurrency
is required (fan-out is the purpose). Fault isolation is required, hence
`Task.Supervisor.async_stream_nolink/4`: a worker crash does not link back
to the caller, and `on_timeout: :kill_task` reaps slow workers so they do
not block the stream.

# `request`

```elixir
@type request() :: {atom(), map()}
```

# `result`

```elixir
@type result() :: {:ok, Dsxir.Prediction.t()} | {:error, Exception.t()}
```

# `run`

```elixir
@spec run(Dsxir.Program.t(), [request()], keyword()) ::
  {Dsxir.Program.t(), [result()]}
```

Run `requests` concurrently under `Dsxir.TaskSupervisor` and merge each
worker's predictor state back into the returned program. Results preserve
input order, with failures surfaced as `{:error, exception}` tuples rather
than raised.

---

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