# `Cucumber.RunCoordinator`
[🔗](https://github.com/huddlz-hq/cucumber/blob/main/lib/cucumber/run_coordinator.ex#L1)

Run-wide coordination process for cucumber test runs.

Started (or reset) by `Cucumber.compile_features!/1` — which runs from
`test_helper.exs` before any test executes, so startup is race-free. State
is keyed by a run id that changes on every `compile_features!` call, so
repeated runs in one VM (`mix test.watch`, test harnesses) never see stale
state.

Current run-scoped concerns:

  * **BeforeAll once-guard** — `before_all_context/1` runs the run's
    `before_all` hooks exactly once and caches the merged context (or the
    first error) for every subsequent scenario. The hooks execute in the
    first caller's process — never inside the coordinator, so a hook may
    call back into it (e.g. `Cucumber.attach/4`). The coordinator only
    grants that caller the run; concurrent callers (`@async` feature
    modules) wait for its result.
  * **AfterAll hand-off** — `register_after_all/1` stores the run's
    `after_all` hooks; the `ExUnit.after_suite/1` callback registered by
    the compiler claims them exactly once via `run_after_all/1`.
  * **Attachments** — `record_attachment/2` collects
    `Cucumber.Attachment` structs (see `Cucumber.attach/4`) in run order;
    `attachments/0` returns them.
  * **Cucumber Messages sink** — when `config :cucumber, messages: path`
    is set, `Cucumber.Messages.Emitter` configures the sink with the
    run's static envelopes and id maps (`configure_messages/1`); the
    runner then appends run-time envelopes in order (`emit_runtime/1`,
    `take_message_ids/1`, `finish_test_case/3`), and the `after_suite`
    callback flushes the NDJSON file (`flush_messages/1`), reconciling
    test cases the runner never finished (e.g. a killed test process).
    Serializing emission through this process is what keeps the stream
    ordered under `@async` features. Note that `ExUnit.after_suite/1`
    callbacks fire on *every* `ExUnit.run/1` — the sink flushes (and
    disables itself) when the first run in the VM completes, so a project
    that invokes a nested `ExUnit.run/1` mid-suite with the sink enabled
    gets the stream truncated at that point (`Cucumber.BehaviorCase`
    relies on exactly this per-run firing to capture streams in tests).

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `ensure_started`

```elixir
@spec ensure_started() :: integer()
```

Starts the coordinator for a new run, or resets it if already running.

Returns the new run id.

# `run_id`

```elixir
@spec run_id() :: integer()
```

Returns the current run id.

---

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