Spectre.Extension behaviour (Spectre v0.3.0)

Copy Markdown View Source

Compile-time contract for on-demand Spectre libraries.

Extensions can be mounted explicitly after use Spectre.Agent:

use Spectre.Agent
use Spectre.Kinetic
use Spectre.Lens

An installed package can instead list its adapters in the agent_extensions manifest field. use Spectre.Agent, stack: MyStack registers those adapters automatically with the immutable installation configuration.

Extensions contribute data and runtime ports to the single Spectre definition. They must not install their own @before_compile hook or manipulate private Agent attributes. New ecosystem packages should publish a Spectre.Stack.Installable manifest; Extension mounts remain source-compatible and can be materialized through Spectre.Stack.Installation.from_extension_mount/1.

Version-zero extensions keep receiving their registration options directly. Version-one extensions may register namespaced attributes in setup/2, compile those attributes once through compile/2, consume namespaced flow options, and contribute runtime ports from the immutable compiled value. Spectre.Agent remains the only owner of an @before_compile hook.

Summary

Functions

Returns the optional planner contributed by an extension.

Returns provider mounts contributed by an extension.

Returns non-action effect executors contributed by an extension.

Lets mounted extensions translate one package-local handler form into the ordinary Agent handler AST.

Fetches one compiled extension mount from an Agent definition.

Merges infrastructure defaults contributed by compiled extensions.

Registers an extension on a module already initialized with use Spectre.Agent.

Callbacks

action_planner(term)

(optional)
@callback action_planner(term()) :: {module(), keyword()} | module() | nil

action_providers(term)

(optional)
@callback action_providers(term()) :: [
  Spectre.Action.Provider.Mount.t()
  | {term(), module()}
  | {term(), module(), keyword()}
]

agent_config(term)

(optional)
@callback agent_config(term()) :: keyword()

api_version()

(optional)
@callback api_version() :: pos_integer()

compile(module, keyword)

(optional)
@callback compile(
  module(),
  keyword()
) :: term() | {:ok, term()} | {:error, term()}

effect_executors(term)

(optional)
@callback effect_executors(term()) :: [
  Spectre.Effect.Executor.Mount.t()
  | {atom(), module()}
  | {atom(), module(), keyword()}
]

expand_handler(t, t, keyword)

(optional)
@callback expand_handler(Macro.t(), Macro.Env.t(), keyword()) ::
  {:ok, Macro.t()} | :ignore | {:error, term()}

flow_constraints(keyword, term)

(optional)
@callback flow_constraints(
  keyword(),
  term()
) :: {[Spectre.Flow.Constraint.t()], keyword()} | {:error, term()}

id()

(optional)
@callback id() :: term()

inference_selector(term)

(optional)
@callback inference_selector(term()) :: {module(), keyword()} | module() | nil

setup module, keyword

(optional)
@callback setup(
  module(),
  keyword()
) :: :ok

Functions

action_planner(mount)

@spec action_planner(Spectre.Extension.Mount.t()) :: {module(), keyword()} | nil

Returns the optional planner contributed by an extension.

action_providers(mount)

@spec action_providers(Spectre.Extension.Mount.t()) :: [
  Spectre.Action.Provider.Mount.t()
]

Returns provider mounts contributed by an extension.

effect_executors(mount)

@spec effect_executors(Spectre.Extension.Mount.t()) :: [
  Spectre.Effect.Executor.Mount.t()
]

Returns non-action effect executors contributed by an extension.

Each executor owns exactly one effect kind. Core retains effect ownership, policy, persistence, idempotency, replay, and terminal lifecycle mutation; the contributed module performs only the external capability call.

expand_handler(owner, handler, caller)

@spec expand_handler(module(), Macro.t(), Macro.Env.t()) :: {:ok, Macro.t()} | :ignore

Lets mounted extensions translate one package-local handler form into the ordinary Agent handler AST.

This is the namespace-safe alternative to importing every package verb into the Agent module. Exactly zero or one extension may claim a form.

fetch(agent, id)

@spec fetch(module(), term()) :: {:ok, Spectre.Extension.Mount.t()} | {:error, term()}

Fetches one compiled extension mount from an Agent definition.

merge_agent_config(config, mounts)

@spec merge_agent_config(
  keyword(),
  [Spectre.Extension.Mount.t()]
) :: keyword()

Merges infrastructure defaults contributed by compiled extensions.

An explicit Agent option wins over an extension default. Two extensions may not silently contribute the same key, even when the Agent overrides it, because that would leave the selected implementation ambiguous.

register!(owner, extension, opts \\ [])

@spec register!(module(), module(), keyword()) :: :ok

Registers an extension on a module already initialized with use Spectre.Agent.