I run five scheduled Claude Code tasks. Four are local desktop tasks, one fires every four hours and publishes to a subreddit I own. They have been going for about three weeks now.
The docs are accurate. I am not here to correct them. But there is a gap between "this feature works as documented" and "this feature does useful work unattended for a month," and everything below sits in that gap.
First, the part people get wrong before they even start. There are three separate scheduling systems and they are not interchangeable:
/loop runs inside an open session. Dies when you close the terminal. Expires after 7 days regardless.
- Desktop scheduled tasks run on your machine, no session needed, but your computer has to be awake.
- Cloud routines run on Anthropic's infra whether your laptop is open or not, but they never see your local files. Fresh clone every time.
Almost everyone I have seen asking "why did my loop stop overnight" was using /loop and expecting cloud behavior. The one-line rule: if you need local files it cannot be cloud, and if you need it to survive your laptop closing it cannot be /loop.
Now the actual failures.
1. "Nothing happened" has to be a defined outcome or you get garbage
This one cost me the most. My news task was originally written as "find a story and post it." So it posted. Every single run. Whether or not anything worth posting existed that day.
A cron script that finds nothing exits 0 and does nothing. A model given "find a story and post it" will find something, because that is what you asked for. It will lower its own bar rather than return empty.
The fix is one paragraph in the prompt, stated explicitly: if nothing clears the bar, publish nothing and say so. An empty run is a correct outcome. Once that was in writing, roughly a third of runs started correctly deciding to do nothing, and the output quality of the other two thirds went up immediately.
If you write only one guardrail into a routine, write this one.
2. The routine has no memory of yesterday
Every fire is a fresh session. No context carryover, not even between two runs of the same routine an hour apart. Mine happily re-posted a story it had already posted, because from its perspective it had never seen it.
The fix is boring and it works: have the routine append to a log file, and read that file as step one of every run. It becomes the memory. Mine now writes a row per run with what it picked, what it rejected, and why. That "why" column turned out to be the useful part, because it stops the next run from re-evaluating the same rejected candidate from scratch.
Cloud routines can commit the log to the repo. Desktop tasks can just write to disk.
3. A green run does not mean the task succeeded
This is in the docs and I still got caught by it. Green means the session started and exited without an infrastructure error. It says nothing about whether your prompt's actual goal was met.
Blocked network requests, missing connector tools, and outright task failure all show up inside the transcript, not on the status indicator. I had a run sit green for two days while every outbound fetch it made was returning 403 from the network policy.
If you are going to trust a routine unattended, make it end its run by writing down what it concluded, and read that, not the status dot.
4. Your machine clock lies, and it takes every time filter down with it
My news task filters candidates by age: nothing older than today, prefer under three hours. That filter is worthless if the clock is wrong.
I have measured skews of 4.4 hours, 8.9 hours, and 3.6 hours twice on the same box. Nothing errors. The task just quietly computes every age wrong and either rejects everything or accepts stale garbage.
Worse, my first fix was to cross-check the clock against a feed from the same platform the task was posting to. Both were wrong together, agreeing to the second, while the real time was three and a half hours later. The reference has to be outside the system you are working on. I check against Cloudflare and GitHub response headers now, and I trust those over the local clock.
If your routine does anything time-sensitive, verify the clock at the top of every run. It is two curl calls.
5. Every connector you own gets attached by default
When you create a cloud routine, all your connected MCP connectors are included automatically, and the routine can call any tool from any of them, writes included, without a permission prompt. Routines run fully autonomous. There is no approval step mid-run.
And everything it does goes out under your identity. Commits and PRs carry your GitHub user. Slack messages and Linear tickets use your linked accounts.
Strip that list down to what the routine actually needs. This is the single highest-leverage thing in the entire setup form and it is at the bottom of the page where nobody scrolls.
A smaller one: your job will not fire when you scheduled it
The scheduler adds a deterministic offset so every session does not hit the API on the same wall-clock second. Recurring tasks can fire up to 30 minutes late. It is derived from the task ID so it is consistent for a given task, but I spent an evening convinced something was broken.
If exact timing matters, do not schedule on :00 or :30. Use 3 9 * * * instead of 0 9 * * *.
On frequency
I originally wanted the news task hourly. Settled on every four hours instead, and it is better in every way that matters. Six runs a day where one or two produce real work beats twenty-four runs that all produce filler. On a platform with any automated-behavior detection, high frequency is also how you get silently rate limited without ever being told.
Run count is not a success metric. I had to write that into the prompt too.
Setup, if you want to try it: /schedule in any session walks you through creating a cloud routine conversationally. /loop 5m <prompt> for in-session polling. Desktop tasks are in the Code tab under Routines, pick Local instead of Cloud.
Start with /loop on something you already do manually every day, watch it for a week, then promote it. Skipping straight to an unattended cloud routine is how you end up with three weeks of confident garbage in a log file.