defmodule Mobus.Stepwise.Error do
@moduledoc """
Structured error metadata for capability and engine errors.
Provides richer error classification than raw reason terms,
enabling orchestrators to make policy decisions (retry, skip,
escalate) without string-parsing error reasons.
## Usage
Capabilities can return structured errors:
{:error, %Mobus.Stepwise.Error{
type: :validation,
source: :capability,
reason: "email already exists",
recoverable?: true,
retryable?: false
}}
The engine passes these through transparently. Orchestrators
can pattern-match on the struct:
case Result.error_reason(result) do
%Error{retryable?: true} -> schedule_retry(...)
%Error{recoverable?: false} -> escalate(...)
raw_reason -> handle_legacy(raw_reason)
end
"""
@enforce_keys [:type, :reason]
defstruct [
:type,
:reason,
:source,
:detail,
recoverable?: true,
retryable?: false
]
@type t :: %__MODULE__{
type: atom(),
reason: term(),
source: atom() | nil,
detail: term() | nil,
recoverable?: boolean(),
retryable?: boolean()
}
@doc "Creates a new structured error."
@spec new(atom(), term(), keyword()) :: t()
def new(type, reason, opts \\ []) do
%__MODULE__{
type: type,
reason: reason,
source: Keyword.get(opts, :source),
detail: Keyword.get(opts, :detail),
recoverable?: Keyword.get(opts, :recoverable?, true),
retryable?: Keyword.get(opts, :retryable?, false)
}
end
@doc "Returns `true` if the value is a `%Mobus.Stepwise.Error{}` struct."
@spec structured?(term()) :: boolean()
def structured?(%__MODULE__{}), do: true
def structured?(_), do: false
end