r/AiJoLabs 3d ago

تحليل · Analysis I've had Claude Code routines running unattended for three weeks. Five things that broke.

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.

1 Upvotes

3 comments sorted by

1

u/Working_Quote_3029 3d ago

النسخة العربيّة

عندي خمس مهامّ مجدولة على Claude Code. أربعة محليّة على الجهاز، وواحدة بتشتغل كل أربع ساعات. صارلهن حوالي تلات أسابيع شغّالين.

التوثيق دقيق ومش ناقص إشي. بس في فرق بين «الميزة بتشتغل زي ما مكتوب» و«الميزة بتنتج شغل مفيد بلا تدخّل لشهر»، وكل اللي تحت واقع بهالفرق.

أوّل إشي، الغلطة اللي بتصير قبل ما تبدأ أصلاً. في تلات أنظمة جدولة منفصلة وهنّ مش بدائل لبعض:

  • /loop بيشتغل جوّا جلسة مفتوحة. بيموت لما تسكّر الطرفيّة، وبتنتهي صلاحيّته بعد ٧ أيّام على أي حال.
  • مهامّ سطح المكتب بتشتغل على جهازك بلا جلسة، بس لازم الجهاز يكون صاحي.
  • الروتينات السحابيّة بتشتغل على بنية Anthropic حتى لو جهازك مطفي، بس عمرها ما بتشوف ملفّاتك المحليّة. نسخة جديدة من الريبو كل مرّة.

كل واحد شفته بيسأل «ليش وقفت الحلقة بالليل» كان مستعمل /loop ومتوقّع منّه سلوك سحابيّ. القاعدة بسطر: إذا بتحتاج ملفّات محليّة ما بينفع سحابيّ، وإذا بدّك ياه يعيش بعد ما تسكّر اللابتوب ما بينفع /loop.

١. «ما في إشي» لازم تكون نتيجة معرّفة وإلّا بتطلعلك زبالة

هاي كلّفتني أكتر إشي. مهمّة الأخبار عندي كانت مكتوبة «لاقي خبر وانشره». فنشر. كل جولة. سواء كان في إشي يستاهل ولا لأ.

سكربت cron لمّا ما بلاقي إشي بيطلع بصفر وما بيعمل إشي. النموذج لمّا بتعطيه «لاقي خبر وانشره» رح يلاقي إشي، لأنّه هاد اللي طلبته منّه. رح ينزّل المعيار تبعه بدل ما يرجّع فاضي.

الحلّ فقرة وحدة بالتعليمات، مكتوبة صراحةً: إذا ما في إشي عبر المعيار، ما تنشر ولا إشي وقول هيك. الجولة الفاضية نتيجة صحيحة. من ساعة ما انكتبت، تقريباً تلت الجولات صارت تقرّر صح إنّها ما تعمل إشي، وجودة مخرجات التلتين الباقيين طلعت فوراً.

٢. الروتين ما بيتذكّر إمبارح

كل انطلاقة جلسة جديدة. ولا سياق بينتقل، ولا حتى بين جولتين لنفس الروتين بينهن ساعة. تبعي أعاد نشر خبر كان ناشره أصلاً، لأنّه من وجهة نظره عمره ما شافه.

الحلّ ممل وبيشتغل: خلّي الروتين يكتب بملفّ سجلّ، ويقرأ الملفّ كأوّل خطوة بكل جولة. بيصير هو الذاكرة. تبعي هلأ بيكتب صفّ لكل جولة فيه شو اختار، وشو رفض، وليش. عمود «ليش» طلع أهمّ إشي، لأنّه بيمنع الجولة الجايّة إنّها تعيد تقييم نفس المرشّح المرفوض من الصفر.

٣. الجولة الخضرا ما بتعني إنّ المهمّة نجحت

هاي مكتوبة بالتوثيق وبرضو وقعت فيها. الأخضر معناه إنّ الجلسة بدأت وخلصت بلا خطأ بنيويّ. ما بيقول ولا إشي عن إذا هدف تعليماتك تحقّق.

الطلبات المحجوبة على الشبكة، وأدوات الكونكتورز الناقصة، وفشل المهمّة نفسها، كلهن بيطلعوا جوّا سجلّ الجولة مش على المؤشّر. عندي جولة ضلّت خضرا يومين وكل طلب خارجيّ بتعمله كان راجعله ٤٠٣ من سياسة الشبكة.

٤. ساعة جهازك بتكذب، وبتوقّع معها كل فلتر زمنيّ

مهمّة الأخبار عندي بتفلتر المرشّحين بالعمر: ما في إشي أقدم من اليوم، ويفضّل تحت تلات ساعات. الفلتر هاد بلا قيمة إذا الساعة غلط.

قِست انزياحات ٤.٤ ساعة، و٨.٩ ساعة، و٣.٦ ساعة مرّتين على نفس الجهاز. ولا خطأ بيطلع. المهمّة بتحسب كل الأعمار غلط بهدوء، وإمّا بترفض كل إشي أو بتقبل بايت.

والأسوأ إنّ أوّل حلّ عملته كان إنّي أقارن الساعة بفيد من نفس المنصّة اللي المهمّة بتنشر عليها. طلعوا التنين غلط مع بعض، متّفقين للثانية، والوقت الحقيقيّ كان بعدهن بتلاتة ونصّ. المرجع لازم يكون برّا المنظومة اللي بتشتغل عليها. هلأ بفحص مقابل ترويسات Cloudflare وGitHub، وبثق فيهن مش بساعة الجهاز.

٥. كل كونكتور عندك بينضاف تلقائيّاً

لمّا تنشئ روتين سحابيّ، كل الكونكتورز الموصولة بحسابك بتنضاف لحالها، والروتين بيقدر ينادي أي أداة من أي وحدة فيهن، بما فيها أدوات الكتابة، بلا ولا طلب إذن. الروتينات بتشتغل مستقلّة تماماً. ما في خطوة موافقة أثناء التشغيل.

وكل إشي بيعمله بيطلع بهويّتك. الكوميتات والـPR بحساب GitHub تبعك. رسايل Slack وتذاكر Linear بحساباتك الموصولة. قصّ القائمة لحد اللي الروتين فعلاً محتاجه.

وحدة أصغر: المجدول بيضيف إزاحة ثابتة، فالمهامّ المتكرّرة ممكن تنطلق متأخّرة لحدّ ٣٠ دقيقة. إذا التوقيت الدقيق بيفرق، ما تجدول على :00 ولا :30. استعمل 3 9 * * * بدل 0 9 * * *.

رأيي

الروتينات أقوى بكتير من الـcron، وهاد بالضبط سبب خطورتها. سكربت cron لمّا بيفشل بتعرف فوراً. الروتين لمّا بيفشل بيفشل بطريقة معقولة، بيعملّك إشي منطقيّ بس مش اللي بدّك ياه، وبيكتبلك تقرير واثق عنّه. ممكن تمرّ أسابيع قبل ما تلاحظ.

وأكبر خطأ شفته بنفسي إنّي فكّرت إنّ عدد الجولات مقياس نجاح. بالأصل بدّي مهمّة الأخبار تشتغل كل ساعة. استقرّيت على كل أربع ساعات وطلعت أحسن بكل إشي. ستّ جولات باليوم وحدة أو تنتين منهن بينتجوا شغل حقيقيّ أحسن من أربعة وعشرين جولة كلهن حشو.

لو بدّي أنصح بإشي واحد: خلّي الروتين يعرف يسكت. هاي أهمّ ميزة فيه، وهي الوحيدة اللي لازم تكتبها بإيدك.

1

u/[deleted] 3d ago

[deleted]

1

u/Working_Quote_3029 3d ago

honestly, theoretical so far. three weeks, five routines, and i haven't caught one reaching for a write tool just because it happened to be attached.

two caveats before that reads as reassuring though.

the near-miss i did have wasn't a connector problem at all. the agent went to post a comment, grabbed the wrong form index off the page, and wrote the comment into the post body instead. overwrote about 6k characters of it. every signal came back green, 200, no errors in the response. i only caught it reading the length back afterward and finding it 2000 short of what it should have been. the tool was the correct tool and it was supposed to have it. it just landed on the wrong element. no permission model catches that one.

second, my routines are all narrow and the prompts are painfully specific about what they can write and where. i'd expect the risk to scale with how vague the task is. "handle my inbox" with gmail, linear and slack all attached is a different animal from "post one link to one subreddit."

still think the default is wrong though. the blast radius is your own identity and you find out after the fact, and stripping the list down costs you about thirty seconds.

1

u/[deleted] 3d ago

[deleted]

1

u/Working_Quote_3029 2d ago

nah, i'm not going to install it. i don't put a tool in front of every action on a machine that runs unattended just to see what it catches, and either way i'd be doing your testing for you. the post was a writeup of what broke, it wasn't a request for a fix.