> ## Documentation Index
> Fetch the complete documentation index at: https://docs-code.palyra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Routines and Cron Scheduler

<details>
  <summary>Relevant source files</summary>

  The following files were used as context for generating this wiki page:

  * crates/palyra-cli/src/args/cron.rs
  * crates/palyra-cli/src/args/jobs.rs
  * crates/palyra-cli/src/args/routines.rs
  * crates/palyra-cli/src/commands/cron.rs
  * crates/palyra-cli/src/commands/jobs.rs
  * crates/palyra-cli/src/commands/routines.rs
  * crates/palyra-cli/tests/help\_snapshots/cron-add-help.txt
  * crates/palyra-cli/tests/help\_snapshots/cron-update-help.txt
  * crates/palyra-cli/tests/help\_snapshots/routines-upsert-help.txt
  * crates/palyra-daemon/src/application/tool\_runtime/routines.rs
  * crates/palyra-daemon/src/cron.rs
  * crates/palyra-daemon/src/routines.rs
  * crates/palyra-daemon/src/transport/http/handlers/console/cron.rs
  * crates/palyra-daemon/src/transport/http/handlers/console/flows.rs
  * crates/palyra-daemon/src/transport/http/handlers/console/hooks.rs
  * crates/palyra-daemon/src/transport/http/handlers/console/jobs.rs
  * crates/palyra-daemon/src/transport/http/handlers/console/routines.rs
  * crates/palyra-daemon/tests/admin\_surface.rs
</details>

The Palyra automation layer is built around **Routines**, which unify scheduled tasks, event-driven triggers, and manual operations into a single execution framework [crates/palyra-daemon/src/routines.rs#1-7](http://crates/palyra-daemon/src/routines.rs#1-7). The system uses a deterministic cron scheduler loop to manage time-based execution while enforcing safety constraints like approval gates, quiet hours, and concurrency policies [crates/palyra-daemon/src/cron.rs#1-13](http://crates/palyra-daemon/src/cron.rs#1-13).

## System Architecture

The automation subsystem consists of three primary layers: the **Routine Registry** (storing metadata and policy), the **Cron Scheduler** (managing the clock and misfires), and the **Gateway Runtime** (executing the actual agent runs).

### Automation Component Map

The following diagram maps high-level automation concepts to their implementation entities in the codebase.

Title: Routine and Scheduler Entity Map

```mermaid theme={null}
graph TD
    subgraph "Natural Language Space"
        A["'Every 5 minutes'"] --> B["Cron Expression"]
        C["'Don't run at night'"] --> D["Quiet Hours"]
        E["'Ask me first'"] --> F["Approval Mode"]
    end

    subgraph "Code Entity Space"
        B --> G["CronJobRecord"]
        D --> H["RoutineQuietHours"]
        F --> I["RoutineApprovalMode"]

        G --> J["spawn_scheduler_loop"]
        H --> K["RoutineRegistry"]
        I --> K

        J -- "dispatches" --> L["GatewayRuntimeState"]
        K -- "validates" --> L
    end

    style G fill:none,stroke-width:2px
    style K fill:none,stroke-width:2px
    style J fill:none,stroke-width:2px
```

Sources: [crates/palyra-daemon/src/cron.rs#1-13](http://crates/palyra-daemon/src/cron.rs#1-13), [crates/palyra-daemon/src/routines.rs#1-10](http://crates/palyra-daemon/src/routines.rs#1-10), [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#1-10](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#1-10)

## The Scheduler Loop

The `spawn_scheduler_loop` is the heart of Palyra's time-based automation. It operates on a 15-second idle sleep interval [crates/palyra-daemon/src/cron.rs#75-75](http://crates/palyra-daemon/src/cron.rs#75-75) and performs several critical tasks:

1. **Due Tick Scanning**: Identifies `CronJobRecord` entries where `next_run_at_unix_ms` is in the past.
2. **Misfire Recovery**: Applies policies (Skip/CatchUp) for ticks missed during daemon downtime.
3. **Maintenance Tasks**: Hosts periodic re-audits of skills, memory maintenance, and embedding backfills [crates/palyra-daemon/src/cron.rs#7-13](http://crates/palyra-daemon/src/cron.rs#7-13).

### Scheduler Data Flow

Title: Scheduler Loop Logic (spawn\_scheduler\_loop)

```mermaid theme={null}
sequenceDiagram
    participant Loop as spawn_scheduler_loop
    participant DB as JournalStore (SQLite)
    participant Matcher as CronMatcher
    participant Gateway as GatewayRuntimeState

    Loop->>DB: Query Due Jobs (next_run_at <= now)
    DB-->>Loop: List of CronJobRecord
    loop for each Job
        Loop->>Matcher: Calculate Next Tick
        Matcher-->>Loop: next_unix_ms
        Loop->>Loop: Apply Stable Jitter
        alt Misfire Detected
            Loop->>Loop: Apply MisfirePolicy (Skip/CatchUp)
        end
        Loop->>DB: Update next_run_at (Atomic Advance)
        Loop->>Gateway: Dispatch Routine Run
    end
```

Sources: [crates/palyra-daemon/src/cron.rs#3-13](http://crates/palyra-daemon/src/cron.rs#3-13), [crates/palyra-daemon/src/cron.rs#75-80](http://crates/palyra-daemon/src/cron.rs#75-80)

## Misfire and Concurrency Policies

Palyra provides granular control over how the scheduler handles missed windows and overlapping runs.

### Misfire Policies (`CronMisfirePolicy`)

* **Skip**: The default policy. If the daemon was offline during a scheduled tick, it simply calculates the next future tick and moves on [crates/palyra-daemon/src/cron.rs#62-62](http://crates/palyra-daemon/src/cron.rs#62-62), [crates/palyra-cli/src/args/cron.rs#89-90](http://crates/palyra-cli/src/args/cron.rs#89-90).
* **CatchUp**: Attempts to run missed ticks. To prevent "thundering herds," catch-up is capped at 3 runs [crates/palyra-daemon/src/cron.rs#79-79](http://crates/palyra-daemon/src/cron.rs#79-79), and outages older than 24 hours are routed to operator review [crates/palyra-daemon/src/cron.rs#80-80](http://crates/palyra-daemon/src/cron.rs#80-80).

### Concurrency Policies (`CronConcurrencyPolicy`)

When a new run is triggered while a previous instance of the same routine is still active:

| Policy       | Behavior                                                                                                                                                |
| :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Forbid**   | The new run is skipped entirely. Default for most routines [crates/palyra-cli/src/args/cron.rs#83-84](http://crates/palyra-cli/src/args/cron.rs#83-84). |
| **QueueOne** | One new run is queued to start immediately after the current one finishes.                                                                              |
| **Replace**  | The currently running instance is cancelled, and the new one starts immediately.                                                                        |

Sources: [crates/palyra-daemon/src/cron.rs#61-62](http://crates/palyra-daemon/src/cron.rs#61-62), [crates/palyra-cli/src/args/cron.rs#83-90](http://crates/palyra-cli/src/args/cron.rs#83-90), [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#33-39](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#33-39)

## Routine Execution Constraints

Routines are more than just cron jobs; they wrap execution with safety and delivery logic.

### Approval Modes

Routines support three `RoutineApprovalMode` levels [crates/palyra-daemon/src/routines.rs#46-46](http://crates/palyra-daemon/src/routines.rs#46-46):

1. **None**: Run immediately when triggered.
2. **BeforeEnable**: The routine is created in a `Disabled` state and requires a one-time operator approval to be enabled [crates/palyra-cli/src/args/cron.rs#124-124](http://crates/palyra-cli/src/args/cron.rs#124-124).
3. **BeforeFirstRun**: Requires approval specifically for the first execution after creation or modification.

### Quiet Hours

The `RoutineQuietHours` configuration allows operators to define windows (e.g., 22:00 to 07:00) during which a routine will not fire, regardless of the trigger [crates/palyra-daemon/src/routines.rs#49-49](http://crates/palyra-daemon/src/routines.rs#49-49). Ticks occurring during quiet hours are treated as misfires and follow the configured `MisfirePolicy`.

### Stable Jitter

To prevent multiple routines from firing at the exact same second (e.g., "every hour" tasks all hitting at 00:00:00), Palyra uses **Stable Jitter**. The jitter is derived from a hash of the routine ID and its schedule, ensuring that while the offset is random, it remains consistent across daemon restarts [crates/palyra-daemon/src/cron.rs#10-13](http://crates/palyra-daemon/src/cron.rs#10-13).

## The palyra.routines Tool Interface

The daemon exposes routine management to agents through two specialized tools:

1. **`palyra.routines.query`**: Allows listing routines, checking run logs, and previewing upcoming schedules [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#3-4](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#3-4).
2. **`palyra.routines.control`**: Allows agents to create, pause, resume, or delete routines [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#4-5](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#4-5).

### Tool Implementation Details

The tool executor (`execute_routines_tool`) ensures that agents cannot bypass security policies. For example, if an agent tries to create a routine with an "Every 1 second" interval, the system enforces a minimum interval of 30 seconds [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#58-58](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#58-58).

Sources: [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#1-10](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#1-10), [crates/palyra-daemon/src/application/tool\_runtime/routines.rs#80-113](http://crates/palyra-daemon/src/application/tool_runtime/routines.rs#80-113)

## CLI Interface

The `palyra cron` and `palyra routines` commands provide the primary operator interface for automation.

| Command                  | Purpose                                                                                                                                                                    |
| :----------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `palyra cron add`        | Creates a schedule-triggered routine using cron, at, or every syntax [crates/palyra-cli/src/args/cron.rs#54-70](http://crates/palyra-cli/src/args/cron.rs#54-70).          |
| `palyra routines upsert` | The unified command for all trigger types (webhook, file-watch, etc.) [crates/palyra-cli/src/args/routines.rs#54-55](http://crates/palyra-cli/src/args/routines.rs#54-55). |
| `palyra cron status`     | Displays a dashboard of upcoming runs and recent outcomes [crates/palyra-cli/src/args/cron.rs#15-28](http://crates/palyra-cli/src/args/cron.rs#15-28).                     |
| `palyra routines logs`   | Shows the execution history for a specific routine [crates/palyra-cli/src/args/routines.rs#124-133](http://crates/palyra-cli/src/args/routines.rs#124-133).                |

### Schedule Preview

The `palyra routines schedule-preview` command allows testing natural language or cron strings against the `CronMatcher` to see exactly when they will fire in a given timezone [crates/palyra-cli/src/args/routines.rs#161-167](http://crates/palyra-cli/src/args/routines.rs#161-167).

Sources: [crates/palyra-cli/src/commands/cron.rs#1-5](http://crates/palyra-cli/src/commands/cron.rs#1-5), [crates/palyra-cli/src/args/cron.rs#1-12](http://crates/palyra-cli/src/args/cron.rs#1-12), [crates/palyra-cli/src/args/routines.rs#1-15](http://crates/palyra-cli/src/args/routines.rs#1-15)
