---
name: schedule
description: Create, list, or delete scheduled tasks that fire on a cron schedule, at an absolute time, or after a delay, via the sema scheduler. - When the user wants to schedule recurring work ("every morning run X"), a one-time reminder ("remind me at 3pm", "in 30 minutes check Y"), or to inspect/cancel existing scheduled tasks. Tasks run locally through the sema scheduler daemon, not in a cloud service.
---

# /schedule — sema scheduled tasks

sema schedules work through three engine tools backed by the local scheduler daemon: `CronCreate`, `CronList`, `CronDelete`. There are no cloud routines: a scheduled task fires on THIS machine while sema's scheduler daemon is running, in a fresh unattended run — it will not see this conversation.

## Creating a task: CronCreate

Parameters:

- `prompt` (string, required): fully self-contained instructions for the future task. It runs unattended — inline every path, id, and acceptance criterion it needs; never reference "the file we discussed".
- `schedule` (union, required) — pick the kind by intent:
  - Recurring: `{ "kind": "cron", "expr": "M H DoM Mon DoW" }` — standard 5-field cron in the user's LOCAL timezone (a 6-field form with leading seconds is also accepted). `"0 9 * * *"` means 9am local; no timezone conversion needed.
  - One-shot at a wall-clock time: `{ "kind": "at", "atMs": <epoch ms> }`.
  - One-shot after a delay: `{ "kind": "delay", "delaySec": <seconds> }`.
- `label` (string, optional): short name. Also the dedup key — creating again with the same schedule+label upserts (idempotent), so use a stable label when updating a task instead of piling up duplicates.

Examples:

- "check the deploy in 30 minutes" → `{ "kind": "delay", "delaySec": 1800 }`
- "tomorrow at 8:57am run the smoke test" → `{ "kind": "at", "atMs": <epoch ms of that local time> }`
- "every 5 minutes" → `{ "kind": "cron", "expr": "*/5 * * * *" }`
- "weekdays around 9am" → `{ "kind": "cron", "expr": "57 8 * * 1-5" }`

**Avoid the :00 and :30 minute marks when the request is approximate.** Every user who asks for "hourly" gets `0 *`, so the fleet stampedes on the hour. Nudge a few minutes early or late (`7 * * * *`, `57 8 * * *`) unless the user names an exact time and clearly means it.

Invalid cron expressions are rejected with a specific field error, including calendar-impossible dates ("Feb 31") that would never fire — fix and retry rather than switching kinds.

## Inspecting and cancelling

- `CronList` (no parameters): lists your scheduled tasks — `id`, human-readable schedule, `label`. It deliberately does NOT return each task's prompt (security boundary); track what a task does via its label.
- `CronDelete` with `{ "id": "<id from CronCreate/CronList>" }`: cancels a task. Idempotent — deleting an already-gone id reports that instead of erroring.

## Execution model and limits

- **Backend**: the sema shell scheduler daemon persists tasks to `scheduled_tasks.json` under the sema config home (`SEMA_CONFIG_DIR`, default `~/.sema`), so durable tasks survive shell restarts; the daemon polls the store and launches each firing as a new unattended run.
- **Scope isolation**: list/cancel only see tasks in your own scope; the task runs as the principal that scheduled it (pinned at schedule time — a task cannot escalate privileges).
- **Default caps** (deployment-tunable): at most 32 scheduled tasks per scope, minimum delay 10s, minimum cron interval 60s, scheduling horizon 30 days.
- **Locality caveat**: if the machine (or the scheduler daemon) is off at fire time, the task does not run in a cloud fallback. Tell the user this when they schedule something that clearly assumes an always-on service.

## When NOT to use this

- One-off work the user wants NOW — just do it.
- Recurring-interval loops phrased as "/loop" or "keep checking X" — use the loop skill, which layers parsing and self-pacing (ScheduleWakeup) on top of these same tools.
- Fleet/server-side scheduling for TOB deployments — that is service-plane configuration, not a session tool call.
