# `Orchid.Step`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L1)

Defines the behavior and specification for a workflow step.

A `Step` is the atomic unit of work in Orchid. It is responsible for receiving
data (wrapped in `Orchid.Param`), performing a specific task, and returning
new data.

It focuses solely on processing logic, unaware of the larger workflow context.

### Usage

To define a step, `use Orchid.Step` and implement the `c:run/2` callback:

    defmodule MySteps.Upcase do
      use Orchid.Step
      alias Orchid.Param

      @impl true
      def run(input_param, _opts) do
        payload = Param.get_payload(input_param)

        result =
          Param.new(:result, :string)
          |> Param.set_payload(String.upcase(payload))

        {:ok, result}
      end
    end

### Input Pattern Matching

The structure of the `input` argument in `c:run/2` depends on how you define
the input keys in your Recipe:

* **Single Key:** If input is `:my_data`, `run/2` receives a single `%Param{}`.
* **List of Keys:** If input is `[:a, :b]`, `run/2` receives a list `[%Param{}, %Param{}]`.
* **Tuple of Keys:** If input is `{:a, :b}`, `run/2` receives a tuple `{%Param{}, %Param{}}`.

### Step Options

Options can be passed when defining the step in a recipe or injected dynamically.
Reserved options include:

* `:extra_hooks_stack` - A list of additional hooks to run for this specific step.
* `:__reporter_ctx__` - A map contains telemetry meta used by `report/3` to send progress updates.

# `implementation`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L56)

```elixir
@type implementation() :: module() | function() | nil
```

The implementation of a step.

* `module()`: A module that implements the `Orchid.Step` behavior.
* `function()`: A function `fn input, opts -> {:ok, params} | {:error, term} end`.

# `input`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L71)

```elixir
@type input() :: tuple() | Orchid.Param.t() | [Orchid.Param.t()]
```

# `input_keys`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L69)

```elixir
@type input_keys() :: io_key()
```

# `io_key`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L61)

```elixir
@type io_key() ::
  atom()
  | binary()
  | [atom()]
  | [binary()]
  | tuple()
  | MapSet.t(atom())
  | MapSet.t(binary())
```

# `output`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L72)

```elixir
@type output() :: tuple() | Orchid.Param.t() | [Orchid.Param.t()]
```

# `output_keys`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L70)

```elixir
@type output_keys() :: io_key()
```

# `step_options`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L75)

```elixir
@type step_options() :: keyword()
```

Options passed to the step execution context.

# `step_schema`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L81)

```elixir
@type step_schema() :: {implementation(), input_keys(), output_keys()}
```

The schema definition of a step within a recipe.
Format: `{Implementation, InputKeys, OutputKeys}`.

# `step_with_options`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L87)

```elixir
@type step_with_options() ::
  {implementation(), input_keys(), output_keys(), step_options()}
```

A fully qualified step definition including options.
Format: `{Implementation, InputKeys, OutputKeys, Options}`.

# `t`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L93)

```elixir
@type t() :: step_schema() | step_with_options()
```

# `nested?`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L98)

```elixir
@callback nested?() :: boolean()
```

Determines if this step contains an inner recipe (Nested Step).

# `run`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L119)

```elixir
@callback run(input(), step_options()) :: {:ok, output()} | {:error, term()}
```

Executes the step logic.

## Arguments
* `input` - The input parameters. Structure depends on `input_keys` definition.
* `opts` - The keyword list of options available to this step.

## Return Values
* `{:ok, output}` - Execution succeeded. `output` must match the structure of `output_keys`.
* `{:error, reason}` - Execution failed. The step (and potentially the workflow) halts.

# `validate_options`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L106)

```elixir
@callback validate_options(step_options()) :: :ok | {:error, term()}
```

Validates the options passed to the step before execution.

This is useful for checking required configuration keys (e.g., API tokens, thresholds).
Returns `:ok` if valid, or `{:error, reason}` otherwise.

# `ensure_full_step`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L147)

```elixir
@spec ensure_full_step(t()) :: step_with_options()
```

Normalizes a step structure into the full 4-element tuple format.

# `extract_schema`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L142)

```elixir
@spec extract_schema(t()) :: step_schema()
```

Extracts the basic schema `{Impl, Input, Output}` from a step definition.

# `inject_options`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L129)

```elixir
@spec inject_options(
  t(),
  keyword()
) :: step_with_options()
```

Injects or merges options into a step definition.

Supports both 3-element tuple (Schema) and 4-element tuple (Full Step).

# `report`
[🔗](https://github.com/SynapticStrings/Orchid/blob/main/lib/orchid/step.ex#L163)

```elixir
@spec report(keyword(), term(), term()) :: :ok
```

Reports progress or status to the executor.

## Example

    def run(input, opts) do
      report(opts, :processing, "Start heavy calculation...")
      # ...
      report(opts, :uploading, 50)
      {:ok, result}
    end

---

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