GenServer that drives a single council run from start to finish.
Public surface
Most callers should use CouncilEx.start/3 (unsupervised, mirrors
GenServer.start/3) or CouncilEx.start_link/3 (linked, mirrors
GenServer.start_link/3). Both return {:ok, pid}. Fetch the
auto-generated run_id from the pid via
CouncilEx.RunServer.run_id/1 if needed.
For tenant isolation or bulk-terminate semantics, use
CouncilEx.Supervisor to group runs under a caller-owned
DynamicSupervisor.
Direct start_link/1 is supported for callers who want to embed run
servers under their own custom supervision tree. Required opts:
:run_id— string identifier (typically generated byCouncilEx.start/3; pass any unique binary if starting manually).:council— council id (a module or"dynamic:" <> id).:spec—%CouncilEx.Spec{}produced bymod.__council__()orCouncilEx.DynamicCouncil.to_spec/1.:input— the run input (any term).
Optional opts:
:user_opts— keyword list forwarded to the runner (e.g.:verbose, retry tuning, etc.). Defaults to[].:callers—$callerschain to inherit (set automatically when called from a process that already has one).
The process registers under CouncilEx.Runner.Registry keyed by
run_id. PubSub events are broadcast on "council_ex:run:" <> run_id.
Summary
Types
Stable curated subset of run state.
Functions
Returns a specification to start this module under a supervisor.
Default registry used when :registry opt is not passed.
Look up the pid serving run_id. Returns {:error, :unknown_run}
when nothing is registered, {:error, :runner_dead} when the
registry holds a stale entry whose process has exited (rare; the
Registry normally cleans these up automatically).
Fetch the run_id from a runner pid.
Start a RunServer unsupervised and unlinked.
Start a RunServer linked to the calling process.
Curated read of run state for diagnostics.
Build a via tuple keyed by run_id under the bundled registry.
Types
@type run_summary() :: %{ run_id: binary(), council: module(), status: :pending | :running | :completed | :failed | :cancelled, current_round: %{name: atom(), idx: non_neg_integer()} | nil, rounds_completed: non_neg_integer(), started_at: DateTime.t() }
Stable curated subset of run state.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec default_registry() :: atom()
Default registry used when :registry opt is not passed.
Override via app config:
config :council_ex, :runner_registry, MyApp.RunReg
Look up the pid serving run_id. Returns {:error, :unknown_run}
when nothing is registered, {:error, :runner_dead} when the
registry holds a stale entry whose process has exited (rare; the
Registry normally cleans these up automatically).
Pass :registry in opts for caller-owned registries.
Fetch the run_id from a runner pid.
@spec start(keyword()) :: GenServer.on_start()
Start a RunServer unsupervised and unlinked.
Mirrors GenServer.start/3. The runner has no parent — caller is
responsible for the pid. Lose the reference and you have a leak.
Most consumers should call CouncilEx.start/3 rather than this
directly.
@spec start_link(keyword()) :: GenServer.on_start()
Start a RunServer linked to the calling process.
Mirrors GenServer.start_link/3. The runner is registered under a
Registry (default CouncilEx.Runner.Registry, override via
:registry) keyed by run_id.
Use this when the caller owns the run's lifecycle — caller dies, run
dies. Most consumers should call CouncilEx.start_link/3 rather than
this directly; this is the supervisor-friendly entry point used by
CouncilEx.Supervisor.start_link/4 and any caller-owned
DynamicSupervisor child spec.
@spec state( binary(), keyword() ) :: {:ok, run_summary()} | {:error, :not_found}
Curated read of run state for diagnostics.
Returns {:ok, run_summary()} for an active or recently-completed run, or
{:error, :not_found} if the run id isn't registered.
Build a via tuple keyed by run_id under the bundled registry.
Useful for GenServer.call/2 against a known run id.
Pass :registry in opts for caller-owned registries.