WorkflowStem.Registry (workflow_stem v0.2.0)

Copy Markdown View Source

Registry for stem-native workflow specs + per-agent pipeline instances.

Specs are discovered by scanning a configurable OTP application's module list for modules matching a prefix that implement WorkflowStem.SpecBehaviour.

Configuration (in config/*.exs):

config :workflow_stem,
  spec_discovery: {:my_app, "Elixir.MyApp.Workflows.Specs."}

If spec_discovery is not set, defaults to scanning :workflow_stem itself.

This keeps stem enablement ergonomic (no per-workflow config allowlists) while remaining predictable (we only scan the application module list, not the filesystem).

Per-agent pipeline instances

When a spec declares :route primitives (see WorkflowStem.SpecBehaviour), each agent gets its own compiled ALF pipeline instance keyed on {agent_id, workflow_handle}. ensure_instance/2 lazily compiles and caches the module via :persistent_term; release_instance/2 stops and evicts it (e.g. on persona change / hot-swap).

Specs without routes fall through to the shared static WorkflowStem.Pipelines.Stepwise pipeline — no per-agent compilation happens and all existing workflows keep working unchanged.

Summary

Functions

Ensure a pipeline instance exists for {agent_id, workflow_handle} and return the module to call.

Variant that accepts the spec map directly, bypassing module discovery.

Return a cached pipeline instance module for {agent_id, workflow_handle}, or nil if none has been compiled yet.

Stop and evict a cached pipeline instance. Safe to call even if no instance was ever compiled. Next ensure_instance/2 call will re-compile from the current spec.

Functions

enabled?(workflow_handle)

@spec enabled?(String.t() | nil) :: boolean()

ensure_instance(agent_id, workflow_handle)

@spec ensure_instance(term(), String.t()) :: {:ok, module()} | {:error, term()}

Ensure a pipeline instance exists for {agent_id, workflow_handle} and return the module to call.

Behaviour:

ensure_instance(agent_id, workflow_handle, spec)

@spec ensure_instance(term(), String.t(), map()) :: {:ok, module()} | {:error, term()}

Variant that accepts the spec map directly, bypassing module discovery.

Useful for callers that already have the spec in hand (e.g. Atrapos loading a persona YAML) and for tests where the spec module isn't registered in :application.get_key/2.

instance_module(agent_id, workflow_handle)

@spec instance_module(term(), String.t()) :: module() | nil

Return a cached pipeline instance module for {agent_id, workflow_handle}, or nil if none has been compiled yet.

release_instance(agent_id, workflow_handle)

@spec release_instance(term(), String.t()) :: :ok

Stop and evict a cached pipeline instance. Safe to call even if no instance was ever compiled. Next ensure_instance/2 call will re-compile from the current spec.

spec_module_for(workflow_handle)

@spec spec_module_for(String.t()) :: module() | nil

spec_modules()

@spec spec_modules() :: [module()]