MimirWorkflows.Dag (mimir_workflows v0.7.0)

Copy Markdown View Source

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

dep_spec()

@type dep_spec() :: %{id: step_id(), depends_on: [step_id()]}

Shape expected by waves/1 — see the moduledoc's host-seam-contract note.

input_spec()

@type input_spec() :: %{id: step_id(), input_from: [step_id()]}

Shape expected by infer_edges/1 — see the moduledoc's host-seam-contract note.

step_id()

@type step_id() :: term()

Opaque step identifier; compared by value only, never atomized.

Functions

infer_edges(items)

@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.

waves(steps)

@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.