Field note

It fired on the wrong day

Three separate scheduling behaviours that surprise people, all documented, all of which we hit before reading about them. The first one is a fifty-year-old rule that reads backwards from how everyone writes it.

1. The two day fields are OR-ed, not AND-ed

Write 0 10 1-7 * 1 intending "the first Monday of the month" and you get something else entirely: every Monday, and also every day from the 1st to the 7th.

When both the day-of-month and day-of-week fields are restricted, a date matches if either one matches. This follows standard vixie-cron semantics and Claude Code documents it: "When both day-of-month and day-of-week are constrained, a date matches if either field matches."

There is no expression for "first Monday". Schedule it weekly and have the job itself check the date, or schedule it monthly and have the job find the Monday.

We caught this one by reading back the human-readable schedule the tool printed before the first fire. That habit is worth more than knowing the rule.

2. Recurring jobs are deliberately offset

A scheduler that fires every job at exactly :00 creates a thundering herd. So a deterministic per-task offset is added: the same task always gets the same delay, derived from its id.

The size is worth checking rather than assuming. Session-scoped loops can fire up to 30 minutes after the scheduled time, or up to half the interval for jobs running more often than hourly. Desktop tasks are described as a delay of a few minutes -- on our machine the actual values, which the task list exposes, run from 13 seconds to 582.

Two consequences. If exact timing matters, avoid :00 and :30. And if you monitor for missed runs, your tolerance window must exceed your largest offset, or the monitor invents failures.

3. A missed run can come back at the wrong hour

Desktop scheduled tasks catch up. When the app starts or the machine wakes, it checks whether each task missed a run in the last seven days, and starts exactly one catch-up run for the most recently missed time, discarding anything older.

The documentation gives the consequence better than we could: "A task scheduled for 9am might run at 11pm if your computer was asleep all day."

We watched this happen. A machine woke on a Saturday and eleven weekday-only tasks fired within sixty seconds of each other, each one a catch-up for its last missed slot.

The guard belongs inside the prompt. Have the job read the clock -- or better, have its data command report the current state -- and stop if it is firing outside the window it was written for. Ours did: the market watch woke on Saturday, saw the market was closed, said so in one line and wrote nothing. That is the difference between a catch-up run that is harmless and one that writes a midnight reading into a log as if it were the scheduled check.

The general shape

All three are the same kind of surprise: the scheduler is doing something deliberate and documented that does not match the mental model of "it runs when I said". None is a bug. All three are cheap to design around and expensive to discover in production.

Where this came from

This page is one section of agent-bash-security, one of the Agent Skills in this set. The skills are plain SKILL.md markdown -- no install, no runtime, no dependency. Drop a folder into ~/.claude/skills/ and invoke it by name.

Four of them are free and need no signup: github.com/resilientmindset01-commits/claude-code-skills-free

Written by ResilientMindset. Not affiliated with, endorsed by, or sponsored by Anthropic. Every claim about Claude Code on this page was checked against Anthropic's own documentation on the date shown.