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:
- macOS →
launchd - Linux →
cron
Those wake up on schedule and invoke slo schedule execute --name <schedule-id>, which loads the persisted schedule and handles exactly one fire.
Core concepts
Session modes decide whether fires accumulate state:
| Mode | Each fire… | Use it for |
|---|---|---|
fresh | runs as a new, isolated headless run | daily summaries, batch checks, one-off analyses |
continuous | targets one evolving session over time | repo-watch, release-prep, rolling operational reviews |
Delivery paths are how a fire actually lands:
fresh_headless— a brand-newslo runprocess.resume_headless— an existing session resumed headlessly withslo 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:
| Command | What it does |
|---|---|
add | Create a new persisted schedule definition. |
edit | Rewrite an existing schedule in place (keeps its id). |
list | List schedules with mode, trigger, target, install state, last delivery. |
show | Show one schedule summary. |
execute (alias run) | Fire one schedule now — the canonical OS entrypoint. |
install / uninstall | Register / remove the schedule with the OS scheduler. |
reports | List immutable run records for a schedule. |
show-report | Show one run record in detail. |
cleanup | Prune old run records, keeping the newest --keep (default 100). |
Triggers
add and edit take exactly one trigger:
| Flag | Meaning |
|---|---|
--cron "0 18 * * *" | Five-field cron expression |
--interval-seconds 300 | Fire 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:
- The runtime does not spawn a second process.
- It enqueues the work as an inbox message.
- 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.
# 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
- Headless & server agent — the
slo runthe scheduler fires under the hood. - Dynamic workflows — the orchestrator and sub-agent model referenced above.