# `WorkflowStem.Registry`
[🔗](https://github.com/fosferon/workflow_stem/blob/main/lib/workflow_stem/registry.ex#L1)

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.

# `enabled?`

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

# `ensure_instance`

```elixir
@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:
  * If the spec declares no `:route` primitives, returns the shared
    `WorkflowStem.Pipelines.Stepwise` module. No per-agent compilation.
  * Otherwise, compiles (if not already cached) an ALF module via
    `WorkflowStem.Pipeline.Builder`, starts it, caches it, and returns
    the module.

# `ensure_instance`

```elixir
@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`

```elixir
@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`

```elixir
@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`

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

# `spec_modules`

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

---

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