Skip to content

Cron Jobs

/cron arms a scheduled instruction on a chat. Kanna owns the timer, so the job keeps firing across server restarts, and every run is recorded in the chat you armed it from.

/cron check CI and report failures inline every 5m
/cron write the daily standup summary spawn 0 9 * * 1-5

Every job picks one, and the difference is where the work happens.

Mode Where each run executes What the arming chat becomes
inline The arming chat itself A monitoring view — context is cleared before every run
spawn A brand-new chat per run, in the same project A dashboard — one run card per fire, with a link and live status

inline is for a recurring check you want to watch in one place. Kanna clears the chat’s context before every cycle (the /clear machinery), so the agent starts fresh each time and the chat never accumulates history. That means anything you discussed in the chat by hand is wiped on the next fire — the arming card says so.

spawn is for work that deserves its own thread. Each fire creates a chat in the arming chat’s project and runs the instruction there; the arming chat collects a run card showing the trigger time, the instruction, a link to the spawned chat, and a status pill that goes Running → Completed / Failed live.

/cron <instruction> <inline|spawn> <schedule> arm a job
/cron list show this chat's jobs
/cron remove <id> disarm one job
/cron pause <id> stop firing, keep the job
/cron resume <id> start firing again
/cron usage help

The instruction needs no quotes. Kanna anchors on the last inline / spawn token in the line, so everything before it is the instruction verbatim and everything after is the schedule. If your instruction genuinely ends with the word “inline” or “spawn”, wrap it in double quotes:

/cron "audit everything inline" spawn @weekly

A chat can hold any number of jobs, each with its own id, schedule, and mode.

Four syntaxes, all accepted anywhere a schedule is expected:

Syntax Example Meaning
5-field cron */15 9-17 * * 1-5 Every 15 min, 9am–5pm, weekdays
6-field cron */30 * * * * * Every 30 seconds — the extra field is a leading second
Shortcut @hourly @daily @weekly @monthly Top of the hour / midnight / Sunday / the 1st
Interval every 30s every 5m every 2h Every N seconds, minutes, or hours from the moment you armed it

Cron fields support *, N, N-M, */S, N-M/S, comma lists, and month / weekday names (jan, mon). Day-of-month and day-of-week follow the standard vixie rule: when both are restricted the day matches if either matches.

There is no minimum cadence: every 1s and * * * * * * both arm. What bounds a fast job is not a rule but reality — a run is a whole agent turn, so a job scheduled every 5 seconds against work that takes 20 will spend most of its ticks skipping.

Those skips are recorded, not hidden, but consecutive skips collapse into one card carrying how many ticks it stands for (Cron runs skipped 9× — chat was busy). Without that, a fast job would bury its own output under skip notices. A slow job is unaffected: an isolated skip is still reported the moment it happens.

Pick a cadence with the work in mind. If each run takes 20 seconds, every 30s gets you every run; every 2s gets you the same runs plus a lot of skips.

Schedules run on server-local time. Per-job timezones are not supported.

Validation catches mistakes before anything arms

Section titled “Validation catches mistakes before anything arms”

A mistyped schedule is never sent to the model as a prompt. Any line starting with /cron is intercepted, and an invalid one produces an error card showing the line you typed and naming the exact problem — plus a complete, ready-to-send corrected command you can copy whenever the fix is unambiguous:

You typed Kanna says It suggests
/cron check ci spwan @daily unknown mode “spwan” /cron check ci spawn @daily
/cron check ci inline every 5min interval unit “min” is not valid — use s, m, or h /cron check ci inline every 5m
/cron nightly build spawn 0 3 * * cron schedule has 4 fields, expected 5 or 6 /cron nightly build spawn 0 3 * * *
/cron nightly build spawn 0 3 cron schedule has 2 fields, expected 5 or 6 /cron nightly build spawn 0 3 * * *
/cron check ci inline 0 9 * * 8 day-of-week field “8” is out of range 0-7 — (ambiguous, no guess)

Nothing arms until the command is valid.

Some mistakes have no mechanical answer. 9am every day is English where a schedule belongs, and when the mode is missing entirely nothing can tell whether the job should run in this chat or spawn its own. Rather than leave you re-typing, Kanna hands those lines to the agent, which repairs and schedules the job — or asks what you meant:

You /cron check CI inline 9am every day
⚠ Invalid /cron command
/cron check CI inline 9am every day
cron schedule has 3 fields, expected 5
Claude "9am every day" is `0 9 * * *` in cron. Scheduling that.
✅ Cron armed — cron-a1b2, every day at 09:00, next run tomorrow 09:00

Where your intent is genuinely ambiguous the agent asks first and schedules your answer instead of guessing. It steps in only where Kanna had no suggestion of its own, so every copy-a-fix case above stays instant and costs nothing. Set KANNA_CRON_REPAIR=disabled to turn it off.

A schedule that parses but can never occur — /cron report inline 0 0 30 2 *, February 30th — is refused at arm time and goes to the agent the same way.

If a scheduled fire arrives while the previous run is still going, Kanna skips that tick and says so in the chat — it never queues runs up behind a slow one. In inline mode a busy chat (you’re mid-turn, or a question is waiting) also skips.

Skips are visible one-liners, so a job that keeps missing its window is obvious rather than silent. Consecutive skips share one line and a count, so a fast schedule reports about once a minute instead of once per tick — the miss is never hidden, only stated once.

Jobs are durable: they’re recorded on the chat’s event log and re-armed when the server starts. Fires that were missed while the server was down are skipped, not replayed — Kanna appends a single notice per job saying how many were missed and arms the next future occurrence. A restart never triggers a burst of catch-up runs.

Deleting a chat disarms its jobs. Chats spawned by a spawn job are ordinary chats and survive independently.

Three surfaces, all doing the same thing:

  • The chat footer panel — every armed job on the current chat with its humanized schedule, mode, a live next-fire countdown, last run status, and pause / resume / remove buttons.
  • /cron list|pause|remove|resume — the same operations typed.
  • The global Cron Jobs page (/cron in the sidebar) — every job across all projects and chats, grouped by project, with links into each job’s chat and the same controls. This is the place to answer “what do I have running anywhere?”

Chats with an armed, unpaused job are flagged in the sidebar.

Both run work repeatedly, for different reasons:

  • Cron fires on the clock, forever, whether or not there’s progress to make. Use it for polling, reports, and periodic checks.
  • A loop (setup_loop) fires on completion and stops when its goal is met, driven by a verify command and a tracking file. Use it for finishing a body of work.

A cron job has no goal and no oracle; it stops when you stop it.