DEV Community

Zhengxin
Zhengxin

Posted on

Claude Code Tools Deep Dive (12): The Cron Family

This is the twelfth article in my series on Claude Code tools. The first eleven explored Claude Code’s spatial toolkit: from the local filesystem to the public web, from one Claude to multiple Claudes, and from immediate actions to persistent task lists. Their temporal model is fundamentally synchronous: Claude calls a tool, it executes, and a result returns immediately.

Real engineering has another class of requests:

  • “Remind me to check CI in 30 minutes.”
  • “Check every five minutes whether the deployment is ready.”
  • “Run a morning self-check at 9 tomorrow.”
  • “In an hour, review this proposal again.”

The common feature is that the action is not “do it now.” It is “automatically trigger it at a future time.”

That requires a time primitive. Claude Code’s answer is the Cron family: three tools—CronCreate, CronDelete, and CronList—that form a scheduled-execution system.

This series begins with a prerequisite article explaining what tools are and how Claude uses them. Like the other articles, this one follows the four-layer framework introduced there.

The Cron family: CronCreate / CronDelete / CronList

Like the Task family, these three tools are semantically coupled and share one data model: the session’s list of cron jobs. They are clearer as a family than as isolated operations.

Family overview

Tool Responsibility
CronCreate Create a future prompt trigger using standard five-field cron syntax
CronDelete Cancel a scheduled job
CronList List all jobs scheduled in the current session

A related tool: ScheduleWakeup is a specialized cousin used by the /loop skill to schedule its next self-wakeup. It is optimized for dynamic loops and is worth mentioning alongside Cron.

The core division is:

  • CronCreate is the engine. Most calls create jobs.
  • CronList and CronDelete are management tools. They inspect and clean up.

The biggest difference from Tasks is this: Task records work that remains to be done; Cron schedules an action for the future. Task waits for Claude to choose the next item. Cron triggers automatically when the time arrives.

What it does

The Cron family solves how Claude can execute actions across time:

  1. Break synchronous limits: Claude can schedule a future self-wakeup rather than only respond to the current request.
  2. Schedule precisely: standard cron syntax (M H DoM Mon DoW) supports arbitrary times and periods.
  3. Support one-shot and recurring modes: a recurring boolean selects the lifecycle.
  4. Provide lightweight reminders: “remind me in 30 minutes” does not require a background task.
  5. Proactively observe external state: Claude can check CI or a deployment at a scheduled time.

This is the first tool family that crosses time. The previous eleven tools are actions at a point: a tool call happens and finishes. Cron is a schedule on a timeline: mark a point, then let the runtime fire automatically.

A concrete example

Scenario: The user says, “I just pushed a deployment. It should finish in about eight minutes. Check the CI status then and tell me if anything is wrong.”

This is a classic “wait for external state to change” task.

Bad alternative 1: sleep

Bash(command: "sleep 480 && gh run list", timeout: 500000)
Enter fullscreen mode Exit fullscreen mode

The main Claude is blocked for eight minutes and cannot answer another question. Synchronous waiting wastes conversational time.

Bad alternative 2: poll every minute

while true:
    Bash(command: "gh run list")
    sleep 60
Enter fullscreen mode Exit fullscreen mode

This consumes context once per minute. Eight minutes means eight calls, and logs fill the main context. The context budget is spent on waiting.

How CronCreate solves it

Claude schedules a one-shot wakeup eight minutes in the future:

CronCreate(
  cron: "13 22 29 7 *",           # one trigger at 22:13 on July 29
  recurring: false,
  prompt: "Check CI with gh run list. Tell the user if it failed; otherwise confirm briefly."
)
Enter fullscreen mode Exit fullscreen mode

At runtime:

  • the job is stored in session memory
  • the main Claude immediately returns to the user instead of blocking
  • the user can ask other questions or start other work
  • at 22:13, the runtime invokes the prompt as a new Claude call
  • Claude runs gh run list and reports the status

The experience looks like:

[22:05] User: I pushed a deployment; check CI in eight minutes.
[22:05] Claude: Done—I scheduled an automatic check for 22:13.
              You can keep working in the meantime.
[22:05–22:12] User: continues with other work
[22:13] Claude: CI check complete; all three workflows are green.
Enter fullscreen mode Exit fullscreen mode

Key insight: CronCreate moves the responsibility for waiting from the main Claude to the runtime. Claude schedules the work and gets out of the way, consuming neither conversation time nor context while waiting.

Combining the tools: inspect or cancel

If the user changes their mind—“Never mind, I’ll check CI myself”—Claude can call:

CronList()                         # find the job ID
CronDelete(id: "cron_xxx")        # cancel it
Enter fullscreen mode Exit fullscreen mode

If the user asks “What did you schedule?”, CronList provides the answer.

Proactive vs. passive wakeups

Cron supports two modes.

One-shot (recurring: false) is for known moments:

  • remind me to review this PR tomorrow at 9
  • check CI again in 30 minutes
  • remind me to eat lunch at noon

The minute, hour, day-of-month, and month are pinned. The job fires once and disappears.

Recurring (recurring: true) is for monitoring with no known end:

  • check CI every five minutes until I say stop
  • inspect queue length every hour
  • run a morning self-check every day

Typical expressions include */5 * * * *, 0 * * * *, and 0 9 * * *. Recurring jobs live for at most seven days, fire one final time, then are deleted. This prevents forgotten jobs from consuming resources indefinitely.

When it is triggered

Use Cron for:

  • waiting for an external asynchronous event such as CI or deployment
  • reminders and actions at a known time
  • periodic monitoring every N minutes
  • handing a future check back to Claude after the current conversation ends

Do not use Cron for:

  • second- or subsecond-level actions; cron has minute resolution
  • exact event-driven responses; use Monitor
  • waits already covered by harness notifications, such as completion of a background Bash task or subagent
  • persistent jobs across sessions; Cron is session-only, stored in memory and gone when Claude exits

Choose among the waiting primitives by meaning:

Need Primitive
One-shot event notification, such as CI completion Bash run_in_background with harness notification
Listening for a change with no fixed time Monitor
A reminder or one-time delay CronCreate with recurring: false
Periodic monitoring CronCreate with recurring: true
/loop self-wakeup ScheduleWakeup

Cron is not the only way to wait. The right primitive depends on whether the trigger is a completion event, a change event, a clock time, or a recurring interval.

Technical design

1. Naming

CronCreate / CronDelete / CronList

This is another complete dual loop: Create attaches a job, Delete removes it, and List observes it. A scheduled job has a lifecycle—created, active, expired or cancelled—so observation and cancellation deserve first-class operations.

“Cron” reuses forty years of Unix crontab convention rather than inventing a DSL. Anyone who has used crontab -e on Linux or macOS already understands the idea and much of the syntax. Reusing an industry convention reduces cognitive load. List is plural rather than Get, signaling that multiple jobs may be returned.

2. Tool-level descriptions

Cron’s descriptions cover eight concerns: session-only lifetime, the seven-day cap, load spreading away from :00 and :30, exceptions where exact half-hours are correct, when to use Monitor instead, language signals for one-shot versus recurring jobs, local-time semantics, and transparent jitter.

State the session-only lifetime first

Jobs live only in this Claude session—nothing is written to disk, and the job is gone when Claude exits.

This prevents Claude from promising a weekly job that cannot survive the session. The simplified design avoids the complexity of user authorization, multi-session synchronization, and durable-job failure handling. The tradeoff is that long-lived jobs belong in system cron or a cloud scheduler, not this family.

Explain the seven-day cap proactively

Recurring tasks auto-expire after seven days—they fire one final time, then are deleted. This bounds session lifetime. Tell the user about the seven-day limit when scheduling recurring jobs.

Claude must tell the user about this limit when creating a recurring job. The cap is also an anti-forgetting mechanism: a forgotten hourly monitor cannot consume resources forever.

Spread load by avoiding :00 and :30

Every user who asks for “9am” gets 0 9, and every user who asks for “hourly” gets 0 *, which means requests from across the planet land on the API at the same instant. When the user’s request is approximate, pick a minute that is NOT 0 or 30.

This is a rare case where a tool prompt includes a server-operations concern. If every “9am” becomes 9:00, requests from every timezone create a synchronized load spike. Choosing :03 or :57 spreads the fleet.

The prompt explains the reason, not just the rule, so Claude can reason about exceptions.

Explain when :00 and :30 are correct

Only use minute 0 or 30 when the user names that exact time and clearly means it, such as “at 9:00 sharp,” “at half past,” or coordinating with a meeting. When in doubt, nudge a few minutes early or late—the user will not notice, and the fleet will.

The exception prevents the load-spreading rule from becoming dogma. Exact meeting coordination remains exact.

Point live watching to Monitor

Not for live watching. CronCreate reruns a prompt at fixed wall-clock intervals. To watch a log file, process, or command output and be notified the moment something changes, use Monitor instead—Monitor streams events as they happen; cron polls on a schedule.

The tool explicitly points to its sibling rather than expecting Claude to compare tools unaided. Cron is scheduled polling; Monitor is event streaming.

Infer one-shot jobs from user language

For “remind me at X” or “at

The wording itself signals recurring: false, so Claude need not ask the user whether a reminder should repeat.

Use local time, not UTC conversion

Uses standard five-field cron in the user’s local timezone: minute, hour, day-of-month, month, day-of-week. 0 9 * * * means 9am local—no timezone conversion needed.

This avoids a classic sysadmin mistake: manually converting a user’s local time to UTC and getting it wrong.

Make jitter transparent

The scheduler adds a small deterministic jitter on top of whatever you pick.

Claude is told that a job chosen for :57 might fire at :58. Recurring tasks may be delayed by up to 10%, capped at 15 minutes; one-shot tasks scheduled exactly at :00 or :30 may fire up to 90 seconds early. The runtime spreads load even when Claude chooses a round time.

Fire only while the REPL is idle

Jobs only fire while the REPL is idle, not mid-query.

If a cron job becomes due while Claude is processing a user request, it waits until the current response finishes. A scheduled trigger cannot interrupt the user’s active thought.

3. Field-level descriptions

CronCreate exposes:

  • cron: five fields in the user’s local timezone—minute hour day-of-month month day-of-week
  • prompt: the prompt to invoke at the scheduled time
  • recurring: boolean, default true; false creates a one-shot job
  • durable: a legacy field with no effect

CronDelete takes the id returned by CronCreate. CronList takes no arguments. The interesting design work is concentrated in CronCreate.

Use a standard cron string instead of a new DSL

0 9 * * * means 9am every day, exactly as it does in Unix crontab. A JSON object such as { minute: "*/5", hour: "*", ... } might look more structured, but it would make both users and models learn a new language. Industry convention wins.

The recurring: true default encodes a preference

Without the field, a job repeats. That aligns with Cron’s typical use cases: CI monitoring, deployment observation, and periodic health checks. One-shot reminders are the less common case and require an explicit recurring: false.

durable is a transparent historical artifact

The description says that durable has no effect. The field likely remains for compatibility after an earlier persistence design was removed. It is neither hidden nor silently honored; Claude is told not to spend effort configuring it.

4. Schema validation

Field Type Default Schema constraint
cron string none; required five-field shape, shallow validation
prompt string none; required no length limit
recurring boolean true boolean
durable boolean none legacy field

The meaningful constraints are runtime behaviors:

  • seven-day expiry for recurring jobs
  • firing only while the REPL is idle
  • automatic jitter and load spreading
  • session-only lifetime

Cron’s syntax is too flexible for a schema to classify every schedule as good or bad. Both 7 * * * * and 0 * * * * are legal. Natural-language guidance teaches Claude which valid schedule best matches the user’s intent.

ScheduleWakeup: the specialized version

CronCreate is the general scheduler. The /loop skill uses ScheduleWakeup for dynamic-interval loops:

  • Claude itself is the caller rather than an external trigger.
  • The previous loop prompt is passed forward automatically.
  • The tool description accounts for a five-minute prompt-cache TTL.
  • The usual interval is 60–1,200 seconds.

The division is simple: general scheduling uses CronCreate; self-scheduling inside /loop uses ScheduleWakeup. It is Cron’s loop-specialized relative.


Division of responsibility among neighboring tools

Dimension Interaction trio Locate + perceive + execute Bash Agent Task family Web pair Cron family
Role Collaborative alignment Modify code Execute commands Derive Claude Externalize memory Reach the web Trigger the future
Time model Present Present Present Present, fork/join Across time Present Future, scheduled
State None Disk None Subagent Runtime storage None Session-only, max seven days
Naming pattern Enter / Exit Read / Edit / Write Single Single CRUD family Fetch / Search Create / Delete / List
Main benefit User alignment Precise code changes Engineering workflow Context space Against forgetting Controlled information Wait for the world to change

Cron vs. Task

Both families create state across time, but in opposite directions:

  • Task: leaves unfinished work in the present, recording what should be done.
  • Cron: schedules a future prompt, recording when something should happen.

Task is a box of notes that Claude checks manually. Cron is an alarm that rings automatically. Task means “Claude chooses to call List”; Cron means “the clock wakes Claude.”

Cron vs. Bash background execution

Both are asynchronous, but their triggers differ:

  • Bash background: the machine waits for a command to finish once.
  • Cron: the runtime waits for a time and fires once or repeatedly.

Bash background handles “wait for CI to finish.” Cron handles “check every five minutes.” One is asynchronous I/O; the other is asynchronous time.

Cron vs. Agent

Both create parallel work in different dimensions:

  • Agent creates spatial parallelism by forking a new context.
  • Cron creates temporal parallelism by queueing work for the future.

The first eleven tools perform actions now. Cron is the only primitive that treats future time as a first-class input. It does not add a new capability so much as provide a trigger moment for every other capability.


Summary

The Cron family’s most interesting signal is a single design line visible at every layer: reuse industry conventions to reduce cognitive load.

  • Naming: reuse forty years of Unix crontab vocabulary rather than inventing concepts.
  • Fields: represent schedules as standard five-field strings, not a new JSON DSL.
  • Defaults: recurring: true matches monitoring, while one-shot reminders are explicit exceptions.
  • Timezone: use local time and avoid UTC conversion.
  • Schema: remain intentionally thin because valid cron syntax cannot by itself distinguish good intent from bad intent.

Another unusual signal is the server’s perspective encoded in the tool prompt. Avoiding :00 and :30 distributes load across the fleet. Most tools care only about Claude using them correctly; Cron also accounts for the operational consequences of thousands of scheduled requests.

Honest transparency runs throughout the family: session-only lifetime is stated at the start, the seven-day cap must be disclosed, durable is labeled ineffective, and jitter is exposed. Claude should not promise what the runtime cannot deliver.

The Create / Delete / List trio forms a complete lifecycle, just like the Task family. Create does the substantive work; List observes and Delete manages the active state.

The next article will examine Monitor: Cron wakes Claude when the clock reaches a point; Monitor wakes Claude when an event occurs. One is proactive scheduled polling; the other is passive event-driven streaming.

Top comments (0)