Walkthrough: Signals, Telemetry, and Sensors

Copy Markdown View Source

This walkthrough covers the operational integration points:

  1. Emit Jido signals from Ash notifications.
  2. Override signal dispatch at runtime.
  3. Subscribe to Jido-namespaced telemetry.
  4. Forward dispatched signals into Jido.Sensor.Runtime.

1. Enable Signals and Telemetry in DSL

jido do
  action :create,
    emit_signals?: true,
    signal_dispatch: {:noop, []},
    signal_include: [:id],
    telemetry?: true

  action :update,
    emit_signals?: true,
    signal_dispatch: {:noop, []},
    signal_type: "my_app.post.updated",
    signal_source: "/my_app/posts",
    telemetry?: true
end

Behavior is opt-in:

  • No signals are emitted unless emit_signals? is true.
  • No telemetry is emitted unless telemetry? is true.

AshJido.Notifier remains the recommended Ash-native path for resource lifecycle publications to a Jido signal bus. Generated actions use the same AshJido.SignalFactory payload builder when emit_signals? is enabled, then dispatch the signal through signal_dispatch.

Both paths produce the same envelope conventions:

  • signal.type is {prefix}.{resource_short_name}.{action_name} unless explicitly overridden.
  • signal.source follows /ash/{resource_short_name}/{action_type}/{action_name} unless explicitly overridden.
  • signal.subject identifies the primary key as /{resource_short_name}/{id} when available.
  • signal.extensions["jido_metadata"] includes Ash resource, action, action type, and timestamp metadata.

Generated-action signals put primary key data in signal.data by default. Use signal_include to widen payloads intentionally. Notifier publications use the publication include mode (:pkey_only, :all, :changes_only, or selected fields).

2. Dispatch to a Runtime Target

Runtime context can override DSL dispatch configuration:

context = %{
  domain: MyApp.Blog,
  signal_dispatch: {:pid, target: self()}
}

{:ok, _post} = MyApp.Blog.Post.Jido.Create.run(%{title: "Hello", author_id: id}, context)

assert_receive {:signal, %Jido.Signal{} = signal}
signal.type
signal.source
signal.data

If signaling is enabled and no dispatch config can be resolved (DSL or context), execution fails early with a validation-style error.

3. Subscribe to Telemetry

Generated actions emit these telemetry events when enabled:

  • [:jido, :action, :ash_jido, :start]
  • [:jido, :action, :ash_jido, :stop]
  • [:jido, :action, :ash_jido, :exception]
:telemetry.attach_many(
  "ash-jido-observer",
  [
    [:jido, :action, :ash_jido, :start],
    [:jido, :action, :ash_jido, :stop],
    [:jido, :action, :ash_jido, :exception]
  ],
  fn event, measurements, metadata, _config ->
    IO.inspect({event, measurements, metadata}, label: "ash_jido_telemetry")
  end,
  nil
)

stop and exception events include :duration and result_status metadata (:ok or :error).

4. Bridge Dispatch Messages to Sensors

AshJido.SensorDispatchBridge accepts common signal envelopes and forwards them to Jido.Sensor.Runtime.event/2:

# forward one
:ok = AshJido.SensorDispatchBridge.forward({:signal, signal}, sensor_runtime)

# forward many
%{forwarded: count, errors: errors} =
  AshJido.SensorDispatchBridge.forward_many([
    signal,
    {:signal, signal},
    :not_a_signal
  ], sensor_runtime)

# mailbox-safe variant
:ok | :ignored | {:error, :runtime_unavailable} =
  AshJido.SensorDispatchBridge.forward_or_ignore(message, sensor_runtime)

Supported envelopes for forward/2:

  • %Jido.Signal{}
  • {:signal, %Jido.Signal{}}
  • {:signal, {:ok, %Jido.Signal{}}}