WorkflowStem.SpecBehaviour behaviour (workflow_stem v0.2.0)

Copy Markdown View Source

Behaviour for stem-native workflow specs.

A workflow is considered stem-enabled if a module implementing this behaviour exists for the workflow handle.

Spec shape

The map returned by spec/0 supports the following top-level keys:

  • :id — workflow handle (same as workflow_handle/0)
  • :profile:stepwise | :fsm | :flow

  • :persona — optional persona slug loaded by hosts (e.g. Atrapos)
  • :initial_state — atom naming the starting state
  • :states%{state_name => state_map}
  • :transitions%{event => %{to: state_name}}
  • :routingoptional, %{route_name => {module, function}}
                    maps each route name referenced in a state's `:route`
                    to the MFA-like resolver that returns the branch key
                    at runtime. The resolver must be a pure function of
                    `(event, opts)` returning the branch key.

Per-state :route (ALF primitives)

A state may declare :route instead of (or alongside) :action. All ten ALF DSL macros are exposed 1:1 — see deps/alf/lib/dsl.ex:

{:stage,      name_or_mod, opts}          # stage/2 — basic step
{:switch,     name,        %{key => body}} # switch/2 — branch on resolver
{:composer,   module,      opts}          # composer/2 — fan-out/in, :memo
{:goto,       name,        opts}          # goto/2 — jump to labelled point
{:goto_point, name}                       # goto_point/2 — label marker
{:done,       name,        opts}          # done/2 — drop packet (terminal)
{:dead_end,   name}                       # dead_end/2 — packet terminator
{:from,       module,      opts}          # from/2 — inline other pipeline
{:plug_with,  module,      body}          # plug_with/2 — plug + body + unplug
{:tbd,        name}                       # tbd/2 — placeholder, compiles noop

opts is a keyword list (all optional): :count, :opts (user options), plus primitive-specific keys — :to/:if for :goto, :memo for :composer.

:route may be a single primitive tuple or a list of them. Inside branches (:switch bodies, :plug_with bodies), the contents are lists of the same tuple shapes — compiled recursively. Examples:

%{route: {:switch, :triage, %{simple: [{:stage, :s1}],
                              deep:   [{:stage, :s2}, {:done, :end}]}}}

%{route: [
  {:stage, :prep},
  {:switch, :route, %{a: [...], b: [...]}},
  {:done, :terminal}
]}

Every :switch or :goto name MUST appear as a key in the top-level :routing map, mapping to a pure {module, function} resolver with signature (event, opts) -> branch_key | boolean. WorkflowStem.Compiler materialises the whole tree into ALF components.

Backwards compatibility: specs without :route or :routing continue to run on the static WorkflowStem.Pipelines.Stepwise pipeline exactly as before (state-machine transitions via StepwiseAdvance).

Summary

Callbacks

artifact()

@callback artifact() :: %{artifact_hash: String.t(), spec: map()}

enabled?()

(optional)
@callback enabled?() :: boolean()

spec()

@callback spec() :: map()

workflow_handle()

@callback workflow_handle() :: String.t()