# `Jido.Plugin.Schedules`
[🔗](https://github.com/agentjido/jido/blob/v2.3.0/lib/jido/plugin/schedules.ex#L1)

Utilities for expanding and managing plugin schedules.

This module handles:
- Expanding schedule declarations from plugin manifests
- Generating unique job IDs namespaced to plugin instances
- Generating signal types for schedule triggers
- Managing timezone configuration

## Schedule Formats

Schedules can be specified in several formats:

- `{"*/5 * * * *", ActionModule}` - Simple schedule with default timezone
- `{"*/5 * * * *", ActionModule, tz: "America/New_York"}` - With timezone
- `{"*/5 * * * *", ActionModule, signal: "custom.signal"}` - Custom signal type

## Signal Type Generation

By default, schedule signal types are auto-generated as:
`"{route_prefix}.__schedule__.{action_name}"`

For example, a plugin with route_prefix "slack" and action RefreshToken
would generate signal type "slack.__schedule__.refresh_token".

Custom signal types can be specified with the `:signal` option.

## Job ID Namespacing

Job IDs are namespaced as tuples to ensure uniqueness across plugin instances:
`{:plugin_schedule, state_key, ActionModule}`

# `schedule_spec`

```elixir
@type schedule_spec() :: %{
  cron_expression: String.t(),
  action: module(),
  job_id: {:plugin_schedule, atom(), module()},
  signal_type: String.t(),
  timezone: String.t()
}
```

Represents an expanded schedule specification.

# `expand_schedules`

```elixir
@spec expand_schedules(Jido.Plugin.Instance.t()) :: [schedule_spec()]
```

Expands schedules from a plugin instance.

Takes a plugin instance and returns expanded schedule specifications
with unique job IDs and auto-generated signal types.

## Input Formats

- `{"*/5 * * * *", ActionModule}` - Simple with default timezone
- `{"*/5 * * * *", ActionModule, tz: "America/New_York"}` - With timezone
- `{"*/5 * * * *", ActionModule, signal: "custom.signal"}` - Custom signal

## Examples

    iex> instance = Instance.new(SlackPlugin)  # route_prefix: "slack"
    iex> expand_schedules(instance)
    [
      %{
        cron_expression: "*/5 * * * *",
        action: SlackActions.RefreshToken,
        job_id: {:plugin_schedule, :slack, SlackActions.RefreshToken},
        signal_type: "slack.__schedule__.refresh_token",
        timezone: "Etc/UTC"
      }
    ]

    iex> instance = Instance.new({SlackPlugin, as: :support})
    iex> expand_schedules(instance)
    [
      %{
        cron_expression: "*/5 * * * *",
        action: SlackActions.RefreshToken,
        job_id: {:plugin_schedule, :slack_support, SlackActions.RefreshToken},
        signal_type: "support.slack.__schedule__.refresh_token",
        timezone: "Etc/UTC"
      }
    ]

# `schedule_route_priority`

```elixir
@spec schedule_route_priority() :: integer()
```

Returns the default priority for schedule-generated routes.

# `schedule_routes`

```elixir
@spec schedule_routes(Jido.Plugin.Instance.t()) :: [{String.t(), module(), keyword()}]
```

Generates routes for schedule signal types.

Schedule signal types need routes so they can be dispatched through
the normal signal routing pipeline. These routes have low priority
to avoid conflicting with explicit routes.

Returns a list of route tuples suitable for `Routes.detect_conflicts/1`.

