Schedulers

The scheduler lets you persist a prompt plus a trigger and have Slo run it later, without a human sitting in the TUI. Daily summaries, repo-watch monitoring, release-prep reviews, one-shot audits — anything you'd otherwise remember to run yourself.

Slo does not ship a long-running daemon that sleeps and polls. Instead it integrates with the scheduler your operating system already runs:

  • macOSlaunchd
  • Linuxcron

Those wake up on schedule and invoke slo schedule execute --name <schedule-id>, which loads the persisted schedule and handles exactly one fire.

slo schedule list and reports — the scheduler's operational view.

Core concepts

Session modes decide whether fires accumulate state:

ModeEach fire…Use it for
freshruns as a new, isolated headless rundaily summaries, batch checks, one-off analyses
continuoustargets one evolving session over timerepo-watch, release-prep, rolling operational reviews

Delivery paths are how a fire actually lands:

  • fresh_headless — a brand-new slo run process.
  • resume_headless — an existing session resumed headlessly with slo run --resume.
  • session_inbox — work queued into an already-running TUI session instead of starting a second writer.

Run records — every fire appends an immutable record under $SLO_HOME/schedules/<id>/runs/<run_id>.json, giving you a full reporting trail of what executed, queued, coalesced, or was skipped.

Quick start

Create a fresh daily summary:

slo schedule add \
  --name "Daily Summary" \
  --prompt "Summarize the repo changes and failing tests from today." \
  --cron "0 18 * * *" \
  --cwd /Users/you/Code/my-project \
  --session-mode fresh

Register it with the OS scheduler so it actually fires:

slo schedule install --name "Daily Summary"

That's it. At 18:00 every day, launchd (or cron) wakes and runs the prompt as a headless slo run, writing one run record per fire.

Command reference

Everything lives under slo schedule:

CommandWhat it does
addCreate a new persisted schedule definition.
editRewrite an existing schedule in place (keeps its id).
listList schedules with mode, trigger, target, install state, last delivery.
showShow one schedule summary.
execute (alias run)Fire one schedule now — the canonical OS entrypoint.
install / uninstallRegister / remove the schedule with the OS scheduler.
reportsList immutable run records for a schedule.
show-reportShow one run record in detail.
cleanupPrune old run records, keeping the newest --keep (default 100).

Triggers

add and edit take exactly one trigger:

FlagMeaning
--cron "0 18 * * *"Five-field cron expression
--interval-seconds 300Fire every N seconds
--run-at "2026-07-20T09:00:00+07:00"One-shot fire at an ISO-8601 timestamp

Add --tz to set the timezone for cron-style triggers (default UTC), and --model / --cwd to override the model and working directory passed to the headless run.

Examples

A fresh daily report with no state

slo schedule add \
  --name "Daily Failures" \
  --prompt "Summarize all failing tests in the repo." \
  --cron "0 9 * * *" \
  --cwd /Users/you/Code/my-project \
  --session-mode fresh

slo schedule install --name "Daily Failures"

Each fire is independent: start process → run prompt → write one run record → exit.

A one-shot future audit

slo schedule add \
  --name "Release Audit" \
  --prompt "Audit the release branch and summarize deployment blockers." \
  --run-at "2026-07-20T09:00:00+07:00" \
  --session-mode fresh

A continuous repo-watch with coalescing

For high-frequency monitoring, target one rolling session and coalesce pending work so the session isn't flooded when you return after being away:

slo schedule add \
  --name "Repo Watch" \
  --prompt "Check the repo and summarize new failures, flaky tests, and regressions." \
  --interval-seconds 300 \
  --cwd /Users/you/Code/my-project \
  --session-mode continuous \
  --session-reference repo-watch \
  --coalescing-mode latest_by_key \
  --coalescing-key repo-watch

latest_by_key means a newer fire replaces an older pending item with the same key rather than piling up — the run record shows delivery_state=coalesced.

Inspect and prune

slo schedule list
slo schedule reports --name "Repo Watch"
slo schedule show-report --name "Repo Watch" --run-id 6d9a7e5b-...
slo schedule cleanup --name "Repo Watch" --keep 25

Firing into a live TUI session safely

This is the most important safety property in the design. An open TUI session is already the writer for that conversation — it owns the in-memory state and may be mid-turn. Starting a second headless --resume against it would create a split-brain writer.

The scheduler avoids that with a lease + inbox model. When a continuous schedule targets a session that is currently open in the TUI:

  1. The runtime does not spawn a second process.
  2. It enqueues the work as an inbox message.
  3. The TUI polls its inbox only when idle, claims the message, and starts a normal prompt turn inside the existing session.

So recurring scheduled work feeds a live session without ever creating concurrent writers. If the target session is not currently open, the fire simply resumes it headlessly (resume_headless) instead.

How the wake-up works

slo schedule add        →  persist prompt + trigger under $SLO_HOME/schedules/<id>/
slo schedule install    →  register with the OS scheduler
OS scheduler fires      →  slo schedule execute --name <id>
Slo runtime             →  handle one fire, write one run record

macOS (launchd) writes one plist per schedule under ~/Library/LaunchAgents/com.slo.schedule.<name>.plist. Supports cron and interval triggers.

Linux (cron) rewrites the user crontab with a stable marker block per schedule. Supports cron triggers.

Scheduler invocation logs are provisioned under $SLO_HOME/schedules/logs/; the authoritative result metadata is always the JSON run record under runs/.

Scheduling dynamic workflows and orchestrator check-ins

A scheduled fire runs the standard headless runner, so pair it with a continuous session that is driving a dynamic workflow and the scheduler becomes the heartbeat for supervised, recurring work.

The pattern: an orchestrator owns a long-lived session; the scheduler fires on a timer; each fire lands in that session's inbox and asks the orchestrator to check in on its sub-agents — poll which phases are still running, retry anything that stalled, and synthesize whatever has completed since the last tick.

A scheduled fire waking an orchestrator to check in on its running sub-agents.

# 1. Open the orchestrator's rolling session in the TUI
slo --resume nightly-review

# 2. Schedule a recurring check-in into that same session
slo schedule add \
  --name "Nightly Review Check-in" \
  --prompt "Check in on the running sub-agents: report which phases are
            still running, retry anything stalled, and synthesize completed work." \
  --cron "0 * * * *" \
  --session-mode continuous \
  --session-reference nightly-review \
  --coalescing-mode latest_by_key \
  --coalescing-key nightly-review

slo schedule install --name "Nightly Review Check-in"

Every hour the orchestrator wakes, inspects its children via the inbox turn, retries what failed, and folds finished results into the running session — a supervised loop that needs no human at the keyboard. Watch it any time with slo visualize --session nightly-review --follow.

Next steps