Step-spec-level DAG operations: minimal-phase grouping for execution
(waves/1) and opt-in dependency inference (infer_edges/1).
waves/1 operates on any maps carrying :id and :depends_on; ids are
opaque terms compared by value — never converted. Guards run before
Kahn's algorithm because Kahn alone would silently drop cycle members
and treat an unknown dependency as forever-unsatisfied.
dep_spec/input_spec are a host-seam contract, not a runtime-validated
one: waves/1 and infer_edges/1 check values they can reason about
(unknown depends_on ids, cycles) but assume every element already has
the required keys — a map missing :id or :depends_on/:input_from
raises (KeyError) rather than returning a diagnostic. Callers that
build these maps from a schema-validated %MimirWorkflows.Spec{} (as
MimirWorkflows.Compiler does) get the shape for free; hand-built specs
are the caller's responsibility.
Summary
Types
Shape expected by waves/1 — see the moduledoc's host-seam-contract note.
Shape expected by infer_edges/1 — see the moduledoc's host-seam-contract note.
Opaque step identifier; compared by value only, never atomized.
Functions
Opt-in edge inference: input_from ∪ prev-in-order. Declaring a linear
workflow needs zero edges (each item implicitly depends on its
predecessor); only divergences are declared via input_from. Returns
deduplicated {upstream, downstream} pairs suitable for Graph.
Groups steps into minimal execution phases (fewest phases, most in-phase
parallelism). Each step lands in phase max(dep phases) + 1 (0 when it
has no dependencies).
Types
Shape expected by waves/1 — see the moduledoc's host-seam-contract note.
Shape expected by infer_edges/1 — see the moduledoc's host-seam-contract note.
@type step_id() :: term()
Opaque step identifier; compared by value only, never atomized.
Functions
@spec infer_edges([input_spec()]) :: [MimirWorkflows.Graph.edge()]
Opt-in edge inference: input_from ∪ prev-in-order. Declaring a linear
workflow needs zero edges (each item implicitly depends on its
predecessor); only divergences are declared via input_from. Returns
deduplicated {upstream, downstream} pairs suitable for Graph.
@spec waves([dep_spec()]) :: {:ok, [[step_id()]]} | {:error, :cyclic} | {:error, {:unknown_dependency, step_id()}}
Groups steps into minimal execution phases (fewest phases, most in-phase
parallelism). Each step lands in phase max(dep phases) + 1 (0 when it
has no dependencies).
Returns {:error, {:unknown_dependency, id}} if any depends_on id is
not a step in the list, and {:error, :cyclic} for dependency cycles.