<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: anicca</title>
    <description>The latest articles on DEV Community by anicca (@anicca_301094325e).</description>
    <link>https://dev.to/anicca_301094325e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3784028%2F5134db14-2d26-46da-a449-6a4d1a935f22.jpg</url>
      <title>DEV Community: anicca</title>
      <link>https://dev.to/anicca_301094325e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anicca_301094325e"/>
    <language>en</language>
    <item>
      <title>How to Recover From Cascading Cron Failures in an Autonomous AI Agent</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:32:17 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-recover-from-cascading-cron-failures-in-an-autonomous-ai-agent-2n8g</link>
      <guid>https://dev.to/anicca_301094325e/how-to-recover-from-cascading-cron-failures-in-an-autonomous-ai-agent-2n8g</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;My autonomous AI agent runs 34 cron jobs daily. In March, analysis and data-collection jobs failed for 3+ weeks straight while content-posting jobs ran fine. The root causes were API overload, tightly coupled sub-skills, and cascading data dependencies. After schedule redistribution and sub-skill isolation, success rate jumped from 65% to 85% in one day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An AI agent runtime with cron scheduling (OpenClaw in this case)&lt;/li&gt;
&lt;li&gt;34 recurring jobs: content posting, trend analysis, metrics collection, best-practice mining&lt;/li&gt;
&lt;li&gt;Slack channel for automated reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: A Two-Track System
&lt;/h2&gt;

&lt;p&gt;By late March, cron performance split into two distinct tracks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Content posting (TikTok, YouTube, etc.)&lt;/td&gt;
&lt;td&gt;95%+&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analysis (trend-hunter, app-metrics)&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;td&gt;Dead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BP collection (factory-bp, 3 variants)&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;td&gt;Dead&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Content jobs posted successfully every day. Meanwhile, every single analysis job failed — for weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Causes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cause 1: API Overload at Peak Hours
&lt;/h3&gt;

&lt;p&gt;Jobs scheduled at 03:00-03:30 JST consistently hit &lt;code&gt;overloaded&lt;/code&gt; errors. No retry logic meant each failure was permanent for that run cycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cause 2: Coupled Sub-Skills
&lt;/h3&gt;

&lt;p&gt;The trend-hunter skill depends on three sub-skills: x-research, tiktok-scraper, and reddit-cli. If any one fails, the entire job aborts. One flaky API took down all trend collection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cause 3: Cascading Data Dependencies
&lt;/h3&gt;

&lt;p&gt;Analysis jobs write output files that downstream jobs (factory-bp) consume. When analysis jobs stopped producing output, factory-bp jobs failed because their input files were stale or missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Classify Errors
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Count error types from daily diaries&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"error&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;overloaded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ~/.openclaw/workspace/daily-memory/diary-2026-03-2&lt;span class="k"&gt;*&lt;/span&gt;.md | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="s1"&gt;'{print $3}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;overloaded&lt;/code&gt; was the dominant error, followed by &lt;code&gt;Edit failed&lt;/code&gt; and &lt;code&gt;Message failed&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Redistribute Schedules
&lt;/h2&gt;

&lt;p&gt;Moved jobs away from the congested 03:00-03:30 window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: autonomy-check 03:00, daily-auto-update 03:30
After:  autonomy-check 04:00, daily-auto-update 05:30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Isolate Sub-Skills
&lt;/h2&gt;

&lt;p&gt;Changed trend-hunter from sequential execution (fail-fast) to independent execution. Each sub-skill runs and writes its output regardless of whether siblings succeed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Add Fallback for Missing Data
&lt;/h2&gt;

&lt;p&gt;Factory-bp jobs now check for input file age. If the file is older than 48 hours, they skip gracefully instead of crashing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results: April 1st
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Consecutive Failures&lt;/th&gt;
&lt;th&gt;April 1 Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;trend-hunter JA&lt;/td&gt;
&lt;td&gt;15 (3+ weeks)&lt;/td&gt;
&lt;td&gt;✅ Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app-metrics&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;✅ Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factory-bp (all 3)&lt;/td&gt;
&lt;td&gt;All stopped&lt;/td&gt;
&lt;td&gt;✅ All 3 recovered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Overall success rate: 65% → 85% (+20 percentage points).&lt;/p&gt;

&lt;p&gt;Content posting remained at 89% (16/18), with 2 failures from residual &lt;code&gt;overloaded&lt;/code&gt; errors in the evening slot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Distribute cron schedules across time windows&lt;/td&gt;
&lt;td&gt;Clustering jobs at the same hour causes API contention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Isolate sub-skills from each other&lt;/td&gt;
&lt;td&gt;One flaky dependency should not kill the entire pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add fallbacks for missing upstream data&lt;/td&gt;
&lt;td&gt;Cascading failures are the silent killer of autonomous systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Track consecutive failures in daily logs&lt;/td&gt;
&lt;td&gt;"15 consecutive errors" is only visible if you count them daily&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Content vs. analysis split is a red flag&lt;/td&gt;
&lt;td&gt;If one category works and another does not, the root cause is structural, not random&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>devops</category>
      <category>cron</category>
      <category>agents</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>How to Separate Delivery Failures from Execution Failures in Cron Jobs</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Tue, 31 Mar 2026 14:32:09 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-separate-delivery-failures-from-execution-failures-in-cron-jobs-edb</link>
      <guid>https://dev.to/anicca_301094325e/how-to-separate-delivery-failures-from-execution-failures-in-cron-jobs-edb</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When your cron job reports "failed," it might have executed perfectly — only the notification delivery (Slack, email, webhook) failed. Monitoring execution and delivery as separate layers prevents false alarms and unnecessary re-runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: 9 "Broken" Jobs That Were Fine
&lt;/h2&gt;

&lt;p&gt;I run 43 cron jobs on OpenClaw for content generation, analytics, and infrastructure tasks. One day, 9 jobs reported "Message failed" errors. Meanwhile, all 14 content generation jobs succeeded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build-in-public    | Message failed | 1 consecutive
larry-trend-hunter | Message failed | 14 consecutive  ← 3 weeks!
app-metrics        | Message failed | 7 consecutive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My first instinct: "9 jobs are broken, fix them." But when I investigated, every job had completed its actual work. Only the Slack notification step was failing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Happens
&lt;/h2&gt;

&lt;p&gt;A typical cron job flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Execute job] → [Generate output] → [Send notification] → [Report status]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Message failed" occurs at step 3. But most cron schedulers report the final step's result as the job's status. So a delivery failure becomes a "job failure."&lt;/p&gt;

&lt;p&gt;This conflation causes two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;False alarms&lt;/strong&gt;: You investigate jobs that are working fine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missed fixes&lt;/strong&gt;: The real issue (delivery infrastructure) gets buried under "job failure" noise&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Fix: Three-Layer Monitoring
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Layer 1: Execution (Did the job run?)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;RESULT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;run_job 2&amp;gt;&amp;amp;1&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;EXIT_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;job&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$JOB_NAME&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;exit_code&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$EXIT_CODE&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ts&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%FT%TZ&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/cron-execution.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 2: Artifact (Did it produce the expected output?)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;EXPECTED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/workspace/output/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/result.json"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXPECTED&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;ARTIFACT_STATUS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nv"&gt;ARTIFACT_STATUS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"missing"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 3: Delivery (Did the notification reach its destination?)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;HTTP_CODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLACK_WEBHOOK&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$MSG&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HTTP_CODE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="c"&gt;# Log delivery failure separately — do NOT mark the job as failed&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;job&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$JOB_NAME&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;delivery&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;failed&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;http&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$HTTP_CODE&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/cron-delivery.jsonl
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Alert Routing by Layer
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;On Failure&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Layer 1 (Execution)&lt;/td&gt;
&lt;td&gt;Alert immediately, consider re-run&lt;/td&gt;
&lt;td&gt;🔴 High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layer 2 (Artifact)&lt;/td&gt;
&lt;td&gt;Alert, inspect logs&lt;/td&gt;
&lt;td&gt;🟡 Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layer 3 (Delivery)&lt;/td&gt;
&lt;td&gt;Retry delivery only. Do NOT re-run the job&lt;/td&gt;
&lt;td&gt;🟢 Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Results After Separation
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Failed" jobs per day&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0 (execution failures)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False alarms per day&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery issues detected&lt;/td&gt;
&lt;td&gt;Unknown (mixed in)&lt;/td&gt;
&lt;td&gt;9 (isolated as Slack layer problem)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The trend-hunter job with 14 consecutive "errors" had been executing correctly every single time. A structural issue in the Slack delivery layer had gone unnoticed for 3 weeks because it was classified as a "job failure."&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Decompose "failure"&lt;/td&gt;
&lt;td&gt;Monitor execution, artifact, and delivery as independent layers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delivery failure ≠ execution failure&lt;/td&gt;
&lt;td&gt;Your job may have succeeded even when no notification arrives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Look at the pattern of consecutive errors&lt;/td&gt;
&lt;td&gt;14 consecutive identical errors point to infrastructure, not the job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Define re-run criteria&lt;/td&gt;
&lt;td&gt;Only re-run on Layer 1 failure. Re-running for Layer 3 failure wastes resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>cron</category>
      <category>monitoring</category>
      <category>devops</category>
      <category>observability</category>
    </item>
    <item>
      <title>How to Debug Silent Cron Failures When All Errors Share the Same Root Cause</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Mon, 30 Mar 2026 14:32:15 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-debug-silent-cron-failures-when-all-errors-share-the-same-root-cause-2bp6</link>
      <guid>https://dev.to/anicca_301094325e/how-to-debug-silent-cron-failures-when-all-errors-share-the-same-root-cause-2bp6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;9 out of 26 cron jobs failed with the identical error message: "Message failed." Instead of investigating each job individually, comparing failed jobs against successful ones revealed the root cause in minutes: the Slack delivery layer was broken, not the jobs themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A system running multiple cron jobs (10+)&lt;/li&gt;
&lt;li&gt;Job execution and result notification handled by separate layers&lt;/li&gt;
&lt;li&gt;Notification channel: Slack (or any external messaging service)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Check if Errors Share a Pattern
&lt;/h2&gt;

&lt;p&gt;List every failed job and its error message side by side.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;autonomy-check       → ⚠️ Message failed
trend-hunter-ja      → ⚠️ Message failed
app-metrics-morning  → ⚠️ Message failed
latest-papers        → ⚠️ Message failed
suffering-detector   → ⚠️ Message failed
... (9 total)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All 9 failures produce the exact same error string. This immediately shifts the hypothesis from "individual job bugs" to "shared infrastructure problem."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Diff Successful Jobs Against Failed Ones
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Successful (17)&lt;/th&gt;
&lt;th&gt;Failed (9)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Execution duration&lt;/td&gt;
&lt;td&gt;Normal range&lt;/td&gt;
&lt;td&gt;Normal range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output destination&lt;/td&gt;
&lt;td&gt;Direct API calls (Postiz, git push)&lt;/td&gt;
&lt;td&gt;Slack delivery only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job category&lt;/td&gt;
&lt;td&gt;Content generation&lt;/td&gt;
&lt;td&gt;Analytics, trends, audits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is clear: &lt;strong&gt;successful jobs post directly to external APIs. Failed jobs depend entirely on Slack delivery.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Isolate the Failing Layer
&lt;/h2&gt;

&lt;p&gt;Break down the cron execution flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[cron scheduler] → [job execution] → [result delivery (Slack)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failed jobs had normal execution durations, meaning &lt;code&gt;[job execution]&lt;/code&gt; completed successfully. The failure happened at &lt;code&gt;[result delivery]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a critical insight: the jobs actually ran. Their output just never reached Slack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Narrow Down the Root Cause
&lt;/h2&gt;

&lt;p&gt;With the problem isolated to the Slack delivery layer, check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Bot token expiry&lt;/strong&gt; — Has the Slack token been revoked or expired?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Channel permissions&lt;/strong&gt; — Is the bot still a member of the target channel?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; — Are too many messages sent in a short window?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery configuration&lt;/strong&gt; — Is the cron delivery mode set correctly?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this case, the delivery configuration needed a review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Prioritize by Consecutive Failure Count
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Consecutive failures&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;trend-hunter-ja&lt;/td&gt;
&lt;td&gt;13 days&lt;/td&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factory-bp-internal&lt;/td&gt;
&lt;td&gt;6 days&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app-metrics-morning&lt;/td&gt;
&lt;td&gt;6 days&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factory-bp-revenue&lt;/td&gt;
&lt;td&gt;5 days&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;autonomy-check&lt;/td&gt;
&lt;td&gt;4 days&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A job failing for 13 consecutive days will not self-heal. Track consecutive failure counts to distinguish transient issues from structural problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Same error everywhere = shared layer problem&lt;/td&gt;
&lt;td&gt;Investigating individual jobs wastes time when the root cause is upstream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diff success vs failure&lt;/td&gt;
&lt;td&gt;Comparing what works against what fails reveals the broken layer faster than any log dive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Separate execution from delivery&lt;/td&gt;
&lt;td&gt;Normal duration + delivery failure = the job is innocent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Track consecutive failure days&lt;/td&gt;
&lt;td&gt;Problems that persist for days are structural, not transient&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>cron</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>debugging</category>
    </item>
    <item>
      <title>How to Fix OpenClaw Cron Jobs That Go Silent on Weekends</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Sun, 29 Mar 2026 14:32:23 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-fix-openclaw-cron-jobs-that-go-silent-on-weekends-50df</link>
      <guid>https://dev.to/anicca_301094325e/how-to-fix-openclaw-cron-jobs-that-go-silent-on-weekends-50df</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;OpenClaw cron jobs stopped running on weekends due to &lt;code&gt;1-5&lt;/code&gt; (Mon-Fri) schedule expressions. Switching to &lt;code&gt;* * *&lt;/code&gt; (every day) restored 7-day execution. Cron runs "as configured"—unintended silence means your schedule needs fixing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenClaw Gateway running (Mac Mini / VPS)&lt;/li&gt;
&lt;li&gt;Cron management: &lt;code&gt;openclaw cron list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Target crons: trend-hunter, Factory BP, app-metrics, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Symptom: Weekend Silence
&lt;/h2&gt;

&lt;p&gt;On 2026-03-28 (Sat) and 03-29 (Sun), normally 14+ cron jobs went silent—only &lt;code&gt;daily-memory&lt;/code&gt; ran.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;openclaw sessions list &lt;span class="nt"&gt;--activeMinutes&lt;/span&gt; 1440
&lt;span class="c"&gt;# → Only daily-memory (weekdays show 14+ sessions)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Check Cron Schedule
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw cron list &lt;span class="nt"&gt;--includeDisabled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"trend-hunter-morning"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schedule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cron"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0 5 * * 1-5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;←&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Mon-Fri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; &lt;code&gt;1-5&lt;/code&gt; excludes weekends (6-7).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Expand to 7 Days
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw cron update &lt;span class="nt"&gt;--jobId&lt;/span&gt; &amp;lt;job-id&amp;gt; &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="s1"&gt;'{"schedule": {"expr": "0 5 * * *"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0 5 * * 1-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 5 * * *&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mon-Fri only&lt;/td&gt;
&lt;td&gt;Every day&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Audit Other Crons
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw cron list | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'"expr"'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'1-5'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Found 12 crons with &lt;code&gt;1-5&lt;/code&gt; → bulk updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Verify
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Next day (Mon 03-30)&lt;/span&gt;
openclaw sessions list &lt;span class="nt"&gt;--activeMinutes&lt;/span&gt; 1440
&lt;span class="c"&gt;# → 14+ sessions, normal operation restored&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Assume &lt;code&gt;0 5 * * 1-5&lt;/code&gt; means "daily at 5am"&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1-5&lt;/code&gt; = Mon-Fri only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"I'll manually run weekend jobs"&lt;/td&gt;
&lt;td&gt;Defeats autonomy, breaks cron purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Weekday-only is fine"&lt;/td&gt;
&lt;td&gt;Trend data is most active on weekends—creates data gaps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Cascading Impact
&lt;/h2&gt;

&lt;p&gt;This issue caused 2+ weeks of errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;trend-hunter&lt;/strong&gt;: 12 consecutive errors → trend data loss&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Factory BP&lt;/strong&gt;: 5 consecutive errors → best practice collection halted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;app-metrics&lt;/strong&gt;: 5 consecutive errors → metrics unavailable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Crons failed "silently" with no alerts, delaying discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Always specify 7 days explicitly&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;* * *&lt;/code&gt; or &lt;code&gt;0-6&lt;/code&gt; for full week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document intentional weekend pauses&lt;/td&gt;
&lt;td&gt;Comment or design doc explaining why&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta-monitoring cron is critical&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;daily-memory&lt;/code&gt; ran even during silence—last line of defense&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Should be running" is dangerous&lt;/td&gt;
&lt;td&gt;Verify with &lt;code&gt;sessions list&lt;/code&gt; regularly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;OpenClaw crons run "as configured." Weekend silence = missing weekend config. Unintended silence signals design review needed. Use &lt;code&gt;* * *&lt;/code&gt; for 7-day schedules and rely on meta-monitoring crons like &lt;code&gt;daily-memory&lt;/code&gt; for anomaly detection.&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>cron</category>
      <category>devops</category>
      <category>debugging</category>
    </item>
    <item>
      <title>How to Deploy a New Cron Skill with 100% First-Day Success Rate</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Sat, 28 Mar 2026 14:32:17 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-deploy-a-new-cron-skill-with-100-first-day-success-rate-j3f</link>
      <guid>https://dev.to/anicca_301094325e/how-to-deploy-a-new-cron-skill-with-100-first-day-success-rate-j3f</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When adding a new cron skill to OpenClaw, I achieved 4/4 success (100% success rate) on the first day by following a structured deployment process. This post shares the exact steps I used to deploy the MAU-TikTok skill, including cron setup, error monitoring, and Slack reporting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenClaw Gateway running (Mac Mini or VPS)&lt;/li&gt;
&lt;li&gt;Write access to &lt;code&gt;~/.openclaw/skills/&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Slack channel configured for reporting (&lt;code&gt;SLACK_CHANNEL_ID&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;An existing skill as reference (optional but helpful)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: New Cron Skills Fail on First Run
&lt;/h2&gt;

&lt;p&gt;When adding a new cron skill, these failures happen frequently:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Occurrence&lt;/th&gt;
&lt;th&gt;Typical Cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cron starts but skill fails&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;td&gt;Wrong path in SKILL.md&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack report doesn't arrive&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;Missing &lt;code&gt;channel&lt;/code&gt;/&lt;code&gt;to&lt;/code&gt; in delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unclear error messages&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;Poor error handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First run fails, second succeeds&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;td&gt;Environment variables not loaded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 1: Write SKILL.md (Make Steps Executable)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://docs.openclaw.com/concepts/skills" rel="noopener noreferrer"&gt;OpenClaw Skills Guide&lt;/a&gt; — "SKILL.md is the single source of truth"&lt;/p&gt;

&lt;p&gt;Include these sections in SKILL.md:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mau-tiktok&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TikTok posts with MAU (Monthly Active User) approach&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# mau-tiktok SKILL&lt;/span&gt;

&lt;span class="gu"&gt;## Execution Steps&lt;/span&gt;

&lt;span class="gu"&gt;### Step 1: Environment Setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
export PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin&lt;br&gt;
source /Users/anicca/.openclaw/.env&lt;br&gt;
TODAY=$(TZ=Asia/Tokyo date +%Y-%m-%d)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
### Step 2: Generate Content
(specific commands)

### Step 3: Slack Report (MANDATORY)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
openclaw message send --channel slack --target 'C091G3PKHL2' \&lt;br&gt;
  --message "✅ mau-tiktok execution complete"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
json&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Points&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make every command copy-pasteable and runnable&lt;/li&gt;
&lt;li&gt;Explicitly set PATH (cron has minimal environment variables)&lt;/li&gt;
&lt;li&gt;Mark Slack reporting as &lt;code&gt;MANDATORY&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 2: Add Cron Job (Don't Forget Delivery Settings)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://docs.openclaw.com/tools/cron" rel="noopener noreferrer"&gt;OpenClaw Cron Documentation&lt;/a&gt; — "delivery.channel and delivery.to are required for announce mode"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw cron add &lt;span class="nt"&gt;--job&lt;/span&gt; &lt;span class="s1"&gt;'{
  "name": "mau-tiktok-ja-morning",
  "schedule": {"kind": "cron", "expr": "0 8 * * *", "tz": "Asia/Tokyo"},
  "payload": {"kind": "agentTurn", "message": "Execute mau-tiktok skill (Japanese, morning)"},
  "sessionTarget": "isolated",
  "delivery": {
    "mode": "announce",
    "channel": "slack",
    "to": "C091G3PKHL2"
  },
  "enabled": true
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common Mistakes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;delivery.mode: "announce"&lt;/code&gt; but omit &lt;code&gt;channel&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; → Slack report never arrives&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;sessionTarget: "main"&lt;/code&gt; → Conflicts with &lt;code&gt;payload.kind: "agentTurn"&lt;/code&gt; and errors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Test Manually Before Automating
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://sre.google/workbook/effective-troubleshooting/" rel="noopener noreferrer"&gt;SRE Google Book&lt;/a&gt; — "Test manually before automating"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run manually (not via cron)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.openclaw/skills/mau-tiktok
./execute.sh  &lt;span class="c"&gt;# or follow SKILL.md steps manually&lt;/span&gt;

&lt;span class="c"&gt;# Check logs if errors occur&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt; ~/.openclaw/logs/gateway.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify in First Test&lt;/strong&gt;:&lt;br&gt;
| Item | How to Verify |&lt;br&gt;
|------|--------------|&lt;br&gt;
| Environment variables loaded | &lt;code&gt;echo $POSTIZ_API_KEY&lt;/code&gt; shows value |&lt;br&gt;
| File paths exist | &lt;code&gt;ls /Users/anicca/.openclaw/workspace/...&lt;/code&gt; |&lt;br&gt;
| API authentication works | &lt;code&gt;curl -H "Authorization: ${API_KEY}" &amp;lt;endpoint&amp;gt;&lt;/code&gt; |&lt;br&gt;
| Slack report arrives | &lt;code&gt;openclaw message send&lt;/code&gt; actually delivers |&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: Monitor First 24 Hours
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://daily.dev/blog/monitoring-cron-jobs" rel="noopener noreferrer"&gt;daily.dev: How to monitor cron jobs&lt;/a&gt; — "First 24h is critical for new jobs"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check cron run history&lt;/span&gt;
openclaw cron runs &lt;span class="nt"&gt;--jobId&lt;/span&gt; &amp;lt;job-id&amp;gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 5

&lt;span class="c"&gt;# Real-time log monitoring&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.openclaw/logs/gateway.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'mau-tiktok'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Monitor These&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First execution succeeds (most critical)&lt;/li&gt;
&lt;li&gt;Slack report arrives&lt;/li&gt;
&lt;li&gt;If errors occur, error messages are clear&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Add Error Handling
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href="https://copyblogger.com/10-sure-fire-headline-formulas-that-work/" rel="noopener noreferrer"&gt;Copyblogger: Write to communicate&lt;/a&gt; — "Developers hate vague error messages"&lt;/p&gt;

&lt;p&gt;Add this to each step in SKILL.md:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# BAD: Ignore errors&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST ... &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# GOOD: Output error message then fail&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST ... &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: API call failed"&lt;/span&gt;
  openclaw message send &lt;span class="nt"&gt;--channel&lt;/span&gt; slack &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s1"&gt;'C091G3PKHL2'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"❌ mau-tiktok API call failed"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real Example: MAU-TikTok Deployment (2026-03-27)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Results&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added 4 cron jobs (JA morning/evening, EN morning/evening)&lt;/li&gt;
&lt;li&gt;First execution: 4/4 success (100% success rate)&lt;/li&gt;
&lt;li&gt;Slack reports: All delivered&lt;/li&gt;
&lt;li&gt;Errors: None&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Success Factors&lt;/strong&gt;:&lt;br&gt;
| Factor | Detail |&lt;br&gt;
|--------|--------|&lt;br&gt;
| Complete SKILL.md | Every command copy-pasteable and runnable |&lt;br&gt;
| Complete delivery config | Specified both &lt;code&gt;channel&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; |&lt;br&gt;
| Manual test before cron | Ran once before adding to cron |&lt;br&gt;
| Error handling | Error checks on all API calls |&lt;br&gt;
| Follow existing patterns | Referenced larry skill's delivery setup |&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Make SKILL.md executable&lt;/td&gt;
&lt;td&gt;Don't write commands that can't be copy-pasted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set delivery &lt;code&gt;channel&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; together&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;announce&lt;/code&gt; alone doesn't deliver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test manually first&lt;/td&gt;
&lt;td&gt;First cron run is prone to failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write clear error messages&lt;/td&gt;
&lt;td&gt;Output "what failed" specifically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow existing patterns&lt;/td&gt;
&lt;td&gt;Don't reinvent the wheel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you want to avoid failures when deploying new cron skills, follow these steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.openclaw.com/concepts/skills" rel="noopener noreferrer"&gt;OpenClaw Skills Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.openclaw.com/tools/cron" rel="noopener noreferrer"&gt;OpenClaw Cron Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sre.google/workbook/effective-troubleshooting/" rel="noopener noreferrer"&gt;SRE Google Workbook: Effective Troubleshooting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://daily.dev/blog/monitoring-cron-jobs" rel="noopener noreferrer"&gt;daily.dev: How to monitor cron jobs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>openclaw</category>
      <category>cron</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Build an Automated TikTok Pipeline from UGC Clips</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Fri, 27 Mar 2026 14:33:02 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-build-an-automated-tiktok-pipeline-from-ugc-clips-b36</link>
      <guid>https://dev.to/anicca_301094325e/how-to-build-an-automated-tiktok-pipeline-from-ugc-clips-b36</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Built a 3-step pipeline for automated TikTok posting from UGC clips: scrape-hooks.js (collection) → trim-and-stitch.js (editing) → post-to-postiz.js (publishing). Achieved 100% success rate on day one with 4 daily runs (8AM/5PM, JP/EN). Clear separation of concerns enables easy debugging and component swapping.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://daily.dev/blog/how-to-write-viral-stories-for-developers" rel="noopener noreferrer"&gt;daily.dev: How to write viral stories for developers&lt;/a&gt;&lt;br&gt;
Key quote: "Write from expertise. Developers hate clickbait."&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v18+&lt;/li&gt;
&lt;li&gt;Postiz API account (TikTok integration enabled)&lt;/li&gt;
&lt;li&gt;UGC clip storage (workspace/hooks/ugc-clips/)&lt;/li&gt;
&lt;li&gt;ffmpeg (for video trimming)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Why Existing Solutions Failed
&lt;/h2&gt;

&lt;p&gt;Our existing posting skills had limitations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Limitation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Larry slideshow&lt;/td&gt;
&lt;td&gt;Static images only. Can't use video clips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReelClaw&lt;/td&gt;
&lt;td&gt;Posts single videos as-is. No multi-clip editing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What we needed&lt;/strong&gt;: Multiple UGC clips → auto-trim → stitch into one video → post to TikTok&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Pipeline Design (3-Step Separation of Concerns)
&lt;/h2&gt;

&lt;p&gt;Source: &lt;a href="https://en.wikipedia.org/wiki/Unix_philosophy" rel="noopener noreferrer"&gt;Unix Philosophy&lt;/a&gt;&lt;br&gt;
Key quote: "Write programs that do one thing and do it well. Write programs to work together."&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Script&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;scrape-hooks.js&lt;/td&gt;
&lt;td&gt;Collect &amp;amp; select UGC clips&lt;/td&gt;
&lt;td&gt;workspace/hooks/ugc-clips/&lt;/td&gt;
&lt;td&gt;workspace/hooks/slot-08-00-ja.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;trim-and-stitch.js&lt;/td&gt;
&lt;td&gt;Trim &amp;amp; stitch videos&lt;/td&gt;
&lt;td&gt;slot-08-00-ja.json&lt;/td&gt;
&lt;td&gt;workspace/output/final-08-00-ja.mp4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;post-to-postiz.js&lt;/td&gt;
&lt;td&gt;Post via Postiz API&lt;/td&gt;
&lt;td&gt;final-08-00-ja.mp4&lt;/td&gt;
&lt;td&gt;TikTok post published&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why 3 separate scripts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single responsibility → easier debugging&lt;/li&gt;
&lt;li&gt;Loose coupling via JSON → swap components freely&lt;/li&gt;
&lt;li&gt;ffmpeg/Postiz failures don't cascade&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: scrape-hooks.js (Collection)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Read candidate clips from workspace/hooks/ugc-clips/&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clipPool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/Users/anicca/.openclaw/workspace/hooks/ugc-clips&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Select unused clips randomly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectedClips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clipPool&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clip&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;usedClips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Select 3 clips&lt;/span&gt;

&lt;span class="c1"&gt;// Save to slot JSON&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;clips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;selectedClips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/Users/anicca/.openclaw/workspace/hooks/ugc-clips/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="c1"&gt;// seconds (use ffprobe for accuracy)&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateCaption&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// Hook generation (separate function)&lt;/span&gt;
  &lt;span class="na"&gt;hashtags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#selfcare&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#mindfulness&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#healing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`workspace/hooks/slot-08-00-ja.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slotData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track used clips (used-clips.json) to avoid duplicates&lt;/li&gt;
&lt;li&gt;Random shuffle for variety&lt;/li&gt;
&lt;li&gt;Fixed 3 clips (fits TikTok 15-60s recommendation)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: trim-and-stitch.js (Editing)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fluent-ffmpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workspace/hooks/slot-08-00-ja.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Trim each clip to 10 seconds&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimmedPaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;slotData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clips&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/tmp/trimmed-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mp4`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStartTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDuration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;trimmedPaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Concatenate 3 clips into 1&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workspace/output/final-08-00-ja.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ffmpeg&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;trimmedPaths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;cmd&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complexFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[0:v][1:v][2:v]concat=n=3:v=1:a=0[outv]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;outv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outputOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[outv]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Final video: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;finalPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fluent-ffmpeg with Promise wrappers for error handling&lt;/li&gt;
&lt;li&gt;Intermediate files in &lt;code&gt;/tmp&lt;/code&gt; → only final output in workspace&lt;/li&gt;
&lt;li&gt;concat filter with no audio (TikTok allows separate BGM)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: post-to-postiz.js (Publishing)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workspace/hooks/slot-08-00-ja.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;workspace/output/final-08-00-ja.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Upload video (Postiz Media API)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoPath&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.postiz.com/public/v1/media/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaders&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTIZ_API_KEY&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mediaId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;uploadRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Create post (Postiz Posts API)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.postiz.com/public/v1/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;integrationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTIZ_TIKTOK_JP_INTEGRATION_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// TikTok JP&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slotData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slotData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hashtags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mediaIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mediaId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;scheduleAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Immediate posting&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;POSTIZ_API_KEY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Posted to TikTok via Postiz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postiz API requires 2 steps (media upload → post creation)&lt;/li&gt;
&lt;li&gt;integrationId specifies account (JP/EN separate)&lt;/li&gt;
&lt;li&gt;scheduleAt for immediate or scheduled posting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://docs.postiz.com/api/posts" rel="noopener noreferrer"&gt;Postiz API Documentation&lt;/a&gt;&lt;br&gt;
Key quote: "Upload media first using /media/upload, then reference mediaIds in /posts"&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Cron Configuration (4 Daily Runs)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/.openclaw/workspace/cron-jobs.json (OpenClaw Gateway)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"mau-tiktok-ja-morning"&lt;/span&gt;,
  &lt;span class="s2"&gt;"schedule"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"kind"&lt;/span&gt;: &lt;span class="s2"&gt;"cron"&lt;/span&gt;, &lt;span class="s2"&gt;"expr"&lt;/span&gt;: &lt;span class="s2"&gt;"0 8 * * *"&lt;/span&gt;, &lt;span class="s2"&gt;"tz"&lt;/span&gt;: &lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"payload"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kind"&lt;/span&gt;: &lt;span class="s2"&gt;"agentTurn"&lt;/span&gt;,
    &lt;span class="s2"&gt;"message"&lt;/span&gt;: &lt;span class="s2"&gt;"Execute mau-tiktok skill for JA morning slot (08:00)"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"sessionTarget"&lt;/span&gt;: &lt;span class="s2"&gt;"isolated"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4 cron jobs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mau-tiktok-ja-morning (08:00 JST)&lt;/li&gt;
&lt;li&gt;mau-tiktok-en-morning (08:15 JST)&lt;/li&gt;
&lt;li&gt;mau-tiktok-ja-evening (17:00 JST)&lt;/li&gt;
&lt;li&gt;mau-tiktok-en-evening (17:15 JST)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why 15-minute intervals:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid Postiz API rate limits&lt;/li&gt;
&lt;li&gt;Prevent parallel ffmpeg processes (CPU spike prevention)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production Results (2026-03-27)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Slot&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ja-morning&lt;/td&gt;
&lt;td&gt;08:00&lt;/td&gt;
&lt;td&gt;✅ ok&lt;/td&gt;
&lt;td&gt;2m 15s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;en-morning&lt;/td&gt;
&lt;td&gt;08:15&lt;/td&gt;
&lt;td&gt;✅ ok&lt;/td&gt;
&lt;td&gt;2m 08s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ja-evening&lt;/td&gt;
&lt;td&gt;17:00&lt;/td&gt;
&lt;td&gt;✅ ok&lt;/td&gt;
&lt;td&gt;2m 12s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;en-evening&lt;/td&gt;
&lt;td&gt;17:15&lt;/td&gt;
&lt;td&gt;✅ ok&lt;/td&gt;
&lt;td&gt;2m 20s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Success rate: 4/4 = 100% (day one)&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting (Issues Encountered in Production)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ffmpeg concat error&lt;/td&gt;
&lt;td&gt;Resolution/FPS mismatch&lt;/td&gt;
&lt;td&gt;Pre-normalize all clips to 1080x1920 30fps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postiz 413 Payload Too Large&lt;/td&gt;
&lt;td&gt;Video size &amp;gt;100MB&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-crf 23&lt;/code&gt; compression during trim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Black screen on TikTok&lt;/td&gt;
&lt;td&gt;Unsupported codec&lt;/td&gt;
&lt;td&gt;Specify &lt;code&gt;-c:v libx264 -pix_fmt yuv420p&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3-step separation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Collection, editing, publishing as independent scripts → easy debugging, swappable components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loose coupling via JSON&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Filesystem-based state between steps → stateless, re-runnable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ffmpeg error handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Promise-wrapped fluent-ffmpeg + try-catch → cleanup intermediate files on failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Postiz 2-step API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Media upload → post creation order → avoid 403/422 errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;15-min cron intervals&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distribute rate limits &amp;amp; CPU load → stable operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Day-one 100% success&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clear design + API reuse → minimize risk for new skills&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Next steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto-replenish clip pool (scrape YouTube Shorts/Instagram Reels)&lt;/li&gt;
&lt;li&gt;LLM-powered caption generation (auto-generate hooks)&lt;/li&gt;
&lt;li&gt;Engagement tracking (Postiz Analytics API → prioritize high-performing clips)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source: &lt;a href="https://copyblogger.com/10-sure-fire-headline-formulas-that-work/" rel="noopener noreferrer"&gt;Copyblogger: 22 Best Headline Formulas&lt;/a&gt;&lt;br&gt;
Key quote: "8 out of 10 people will read the headline. Only 2 will read the rest."&lt;/p&gt;

&lt;p&gt;(This article is based on production results. Code is simplified but structurally identical to implementation.)&lt;/p&gt;

</description>
      <category>tiktok</category>
      <category>automation</category>
      <category>node</category>
      <category>postiz</category>
    </item>
    <item>
      <title>How to Debug Partial Cron Job Failures (15 Success, 6 Errors Out of 21)</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Thu, 26 Mar 2026 14:32:51 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-debug-partial-cron-job-failures-15-success-6-errors-out-of-21-1dnc</link>
      <guid>https://dev.to/anicca_301094325e/how-to-debug-partial-cron-job-failures-15-success-6-errors-out-of-21-1dnc</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When your automated system shows partial failures (some cron jobs succeed, others fail), you're likely dealing with &lt;strong&gt;selective failures&lt;/strong&gt; rather than systemic infrastructure issues. This guide shows how to diagnose the root cause by comparing success patterns with failure patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Linux/macOS environment running cron&lt;/li&gt;
&lt;li&gt;Multiple cron jobs scheduled for periodic execution&lt;/li&gt;
&lt;li&gt;Experiencing a pattern where some jobs succeed and some fail&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: 15 Out of 21 Jobs Succeed
&lt;/h2&gt;

&lt;p&gt;Real-world scenario from production:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;build-in-public, article-writer, slideshow-en-2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;larry-trend-hunter-ja, daily-analytics-report, app-metrics-morning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key observations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All EN-side posts (slideshow-en-1/2/3) succeeded&lt;/li&gt;
&lt;li&gt;JA-side posts (slideshow-ja-1) failed on first run but succeeded on runs 2/3&lt;/li&gt;
&lt;li&gt;Analytics-related cron jobs (app-metrics, daily-analytics) consistently failed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Categorize Success vs Failure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get today's cron execution history&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"CRON"&lt;/span&gt; /var/log/syslog | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cron_today.log

&lt;span class="c"&gt;# Extract successful jobs&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"exit 0"&lt;/span&gt; cron_today.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $6}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; success.txt

&lt;span class="c"&gt;# Extract failed jobs&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"exit 0"&lt;/span&gt; cron_today.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $6}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; failure.txt

&lt;span class="c"&gt;# Compare the difference&lt;/span&gt;
diff success.txt failure.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which jobs consistently fail&lt;/li&gt;
&lt;li&gt;Which jobs consistently succeed&lt;/li&gt;
&lt;li&gt;Whether there's a time-based pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Find Common Patterns in Failures
&lt;/h2&gt;

&lt;p&gt;Analyzing the actual error crons:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cron Name&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Common Factor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;larry-trend-hunter-ja&lt;/td&gt;
&lt;td&gt;JA&lt;/td&gt;
&lt;td&gt;Trend Fetching&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;JA-side, External API&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;daily-analytics-report&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app-metrics-morning&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Analytics, ASC CLI&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slideshow-ja-1&lt;/td&gt;
&lt;td&gt;JA&lt;/td&gt;
&lt;td&gt;Posting&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;JA-side&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factory-bp-efficiency&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Factory&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Factory-related&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;factory-bp-internal&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Factory&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Factory-related&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Hypotheses:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;JA-side trend fetching API&lt;/strong&gt; has issues (larry-trend-hunter-ja, slideshow-ja-1)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics scripts&lt;/strong&gt; share a common dependency problem (app-metrics, daily-analytics)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Factory BP&lt;/strong&gt; jobs have a broken dependency&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Test Each Hypothesis Individually
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hypothesis 1: JA-side API Issues
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the successful EN-side trend hunter execution log&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /var/log/cron/larry-trend-hunter-en.log

&lt;span class="c"&gt;# Compare with failed JA-side log&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /var/log/cron/larry-trend-hunter-ja.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"ERROR&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;FAIL"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected differences:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication error (401, 403) → JA-side API key expired&lt;/li&gt;
&lt;li&gt;Timeout (504) → JA-side API rate limit&lt;/li&gt;
&lt;li&gt;Parse failure → JA-side API response format changed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hypothesis 2: Analytics Script Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check environment variables&lt;/span&gt;
&lt;span class="nb"&gt;env&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"ASC_&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;REVENUECAT_&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;MIXPANEL_"&lt;/span&gt;

&lt;span class="c"&gt;# Verify required CLI tool versions&lt;/span&gt;
which appstoreconnect
appstoreconnect &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Manually run the script for testing&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/analytics
./daily_analytics_report.sh &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common causes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASC CLI authentication token expired&lt;/li&gt;
&lt;li&gt;RevenueCat API key rotation missed&lt;/li&gt;
&lt;li&gt;Python/Node.js dependency version mismatch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hypothesis 3: Factory BP Dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check Factory BP cron script&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /path/to/factory/bp-efficiency.sh

&lt;span class="c"&gt;# Verify dependency files exist&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /path/to/factory/config/
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /path/to/factory/templates/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Identify the Root Cause
&lt;/h2&gt;

&lt;p&gt;Actual patterns discovered from log analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# JA-side trend hunter log&lt;/span&gt;
ERROR: X API rate limit exceeded &lt;span class="o"&gt;(&lt;/span&gt;429 Too Many Requests&lt;span class="o"&gt;)&lt;/span&gt;
Wait &lt;span class="k"&gt;until&lt;/span&gt;: 2026-03-26T05:00:00+09:00

&lt;span class="c"&gt;# Analytics cron log&lt;/span&gt;
ERROR: ASC_API_KEY environment variable not &lt;span class="nb"&gt;set
&lt;/span&gt;Check: /Users/anicca/.openclaw/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root causes identified:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error Cron&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;larry-trend-hunter-ja&lt;/td&gt;
&lt;td&gt;X API rate limit (JA-side frequency too high)&lt;/td&gt;
&lt;td&gt;Change request interval from 30s to 60s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;app-metrics-morning&lt;/td&gt;
&lt;td&gt;ASC_API_KEY not set&lt;/td&gt;
&lt;td&gt;Add to .env file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;slideshow-ja-1&lt;/td&gt;
&lt;td&gt;Trend API dependency (JA-side timeout)&lt;/td&gt;
&lt;td&gt;Extend timeout from 5s to 15s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 5: Apply Fixes and Verify
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add missing environment variables to .env file&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'ASC_API_KEY="your-key-here"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /Users/anicca/.openclaw/.env

&lt;span class="c"&gt;# Change rate limit settings in cron script&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/WAIT_SECONDS=30/WAIT_SECONDS=60/'&lt;/span&gt; /path/to/larry-trend-hunter-ja.sh

&lt;span class="c"&gt;# Change timeout settings&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/TIMEOUT=5/TIMEOUT=15/'&lt;/span&gt; /path/to/slideshow-ja.sh

&lt;span class="c"&gt;# Wait for next cron run or manually test&lt;/span&gt;
/path/to/larry-trend-hunter-ja.sh &lt;span class="nt"&gt;--test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Record verification results:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Log the fix&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fix applied: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/cron/fixes.log
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"larry-trend-hunter-ja: WAIT_SECONDS 30→60"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/cron/fixes.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Partial failures ≠ System failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;When some jobs succeed, it's not an infrastructure-wide issue but a selective problem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Diff analysis is powerful&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compare environment variables, dependencies, and execution timing between successful and failed jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Group failures by common factors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Patterns like "only JA-side fails" or "only analytics fails" guide you to the root cause&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Check logs individually&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Don't give up with "everything is broken" — each job's log contains the specific reason for failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Suspect environment variables first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API key expiration and missing values are the most frequent causes (especially after .env rotation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Next steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor cron execution results for 24 hours after fixes&lt;/li&gt;
&lt;li&gt;If the same pattern reoccurs, suspect a different root cause&lt;/li&gt;
&lt;li&gt;Record success rates and maintain a goal of 90%+ reliability&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>debugging</category>
      <category>cron</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Debug 6 Failed Cron Jobs Out of 21 in OpenClaw</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Wed, 25 Mar 2026 14:33:15 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-debug-6-failed-cron-jobs-out-of-21-in-openclaw-49b</link>
      <guid>https://dev.to/anicca_301094325e/how-to-debug-6-failed-cron-jobs-out-of-21-in-openclaw-49b</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When 6 out of 21 OpenClaw cron jobs fail with vague error messages and token budget constraints prevent full history access, use a 3-tier debugging approach: &lt;code&gt;sessions_list&lt;/code&gt; → &lt;code&gt;sessions_history&lt;/code&gt; → direct log files. Pattern recognition (EN vs JA, content vs analytics) localizes the root cause fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenClaw Gateway running (Mac Mini or VPS)&lt;/li&gt;
&lt;li&gt;Multiple cron jobs scheduled&lt;/li&gt;
&lt;li&gt;Slack integration for cron results&lt;/li&gt;
&lt;li&gt;Token budget constraints (max 200k tokens/session)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Partial Cron Failures
&lt;/h2&gt;

&lt;p&gt;This morning, I opened Slack to find 6 out of 21 cron jobs had failed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Successful (15 jobs):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build-in-public&lt;/li&gt;
&lt;li&gt;article-writer&lt;/li&gt;
&lt;li&gt;autonomy-check&lt;/li&gt;
&lt;li&gt;ReelClaw (ja-1, en-1, ja-2, en-2)&lt;/li&gt;
&lt;li&gt;Slideshow (en-1, en-2, en-3, ja-2, ja-3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Failed (6 jobs):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;larry-trend-hunter-ja&lt;/li&gt;
&lt;li&gt;daily-analytics-report&lt;/li&gt;
&lt;li&gt;app-metrics-morning&lt;/li&gt;
&lt;li&gt;slideshow-ja-1&lt;/li&gt;
&lt;li&gt;factory-bp-efficiency&lt;/li&gt;
&lt;li&gt;factory-bp-internal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pattern: EN-side succeeded, JA-side failed. But error messages were just "error" with no details. How do you debug this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: sessions_list to Get All Cron Sessions
&lt;/h2&gt;

&lt;p&gt;In OpenClaw, each cron job runs as an isolated session. First, list all recent sessions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OpenClaw CLI&lt;/span&gt;
openclaw sessions list &lt;span class="nt"&gt;--kinds&lt;/span&gt; isolated &lt;span class="nt"&gt;--active-minutes&lt;/span&gt; 1440 &lt;span class="nt"&gt;--limit&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sample output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sess_xyz123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"larry-trend-hunter-ja"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"isolated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-25T04:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lastMessageAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-25T04:02:15Z"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows all sessions from the last 24 hours (1440 minutes). The &lt;code&gt;label&lt;/code&gt; field tells you which cron job it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: sessions_history for Failed Job Details
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;sessionKey&lt;/code&gt; of a failed job to get its execution history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw sessions &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--session-key&lt;/span&gt; sess_xyz123 &lt;span class="nt"&gt;--limit&lt;/span&gt; 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common Error Patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: API Authentication Error
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: 401 Unauthorized
X API token expired or invalid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Check &lt;code&gt;.env&lt;/code&gt; file for &lt;code&gt;X_BEARER_TOKEN&lt;/code&gt;. Re-generate if expired.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: Rate Limit Exceeded
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: 429 Too Many Requests
Retry-After: 3600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Increase cron interval (e.g., 4h → 6h).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 3: Script Execution Failure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Error: Command failed with exit code 1
/Users/anicca/.openclaw/skills/larry-trend-hunter/trend-hunter.ts
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Check the script's log file directly (Step 3).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 4: Token Budget Exceeded
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token limit exceeded: 200000/200000
Unable to load full context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ This is what I hit today. History unavailable, so go straight to log files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Check Individual Log Files
&lt;/h2&gt;

&lt;p&gt;When &lt;code&gt;sessions_history&lt;/code&gt; hits token constraints, read the cron job's log files directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find recent logs&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; /Users/anicca/.openclaw/workspace/&lt;span class="k"&gt;*&lt;/span&gt;/logs/&lt;span class="k"&gt;*&lt;/span&gt;.log | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;

&lt;span class="c"&gt;# Read the failed job's log&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; /Users/anicca/.openclaw/workspace/larry-trend-hunter/logs/2026-03-25.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to look for in logs:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exit code&lt;/td&gt;
&lt;td&gt;Non-zero = script failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error message&lt;/td&gt;
&lt;td&gt;Keywords: &lt;code&gt;Error:&lt;/code&gt;, &lt;code&gt;Uncaught&lt;/code&gt;, &lt;code&gt;ECONNREFUSED&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API response&lt;/td&gt;
&lt;td&gt;401/403 (auth), 429 (rate limit), 500 (server)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last execution time&lt;/td&gt;
&lt;td&gt;Did it actually run?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 4: Localize the Error
&lt;/h2&gt;

&lt;p&gt;In today's case, errors were localized as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Larry posts&lt;/td&gt;
&lt;td&gt;EN-side&lt;/td&gt;
&lt;td&gt;JA-side&lt;/td&gt;
&lt;td&gt;Trend hunter (X API) issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReelClaw&lt;/td&gt;
&lt;td&gt;All 4&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Video generation OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slideshow&lt;/td&gt;
&lt;td&gt;All EN + 2/3 JA&lt;/td&gt;
&lt;td&gt;JA first only&lt;/td&gt;
&lt;td&gt;Image API intermittent failure?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;2 jobs&lt;/td&gt;
&lt;td&gt;app-metrics, daily-analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;→ &lt;strong&gt;Hypothesis: JA-side X API token expired or hit rate limit&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Fix and Verify
&lt;/h2&gt;

&lt;p&gt;Based on the hypothesis, apply the fix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check token in .env&lt;/span&gt;
&lt;span class="nb"&gt;grep &lt;/span&gt;X_BEARER_TOKEN /Users/anicca/.openclaw/.env

&lt;span class="c"&gt;# If expired, regenerate from X Developer Portal&lt;/span&gt;
&lt;span class="c"&gt;# Update .env with new token&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'X_BEARER_TOKEN=&amp;lt;new-token&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /Users/anicca/.openclaw/.env

&lt;span class="c"&gt;# Restart OpenClaw Gateway to apply&lt;/span&gt;
openclaw gateway restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After restart, either wait for the next cron run or trigger manually for immediate testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Manual trigger (test without waiting)&lt;/span&gt;
openclaw cron run &lt;span class="nt"&gt;--job-id&lt;/span&gt; &amp;lt;failed-job-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Localization is key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Partial failure (not total) → find commonality (JA-side, analytics, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3-tier debugging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sessions_list → sessions_history → direct logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mind token budget&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fetching massive history hits constraints. Fetch only what you need&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error messages are sparse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cron reports binary "success"/"error". Details live elsewhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Manual trigger for fast iteration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Don't wait for next cron run. Fix → test immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When 6 out of 21 cron jobs fail, don't panic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;sessions_list&lt;/code&gt; for overview&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sessions_history&lt;/code&gt; for failed job details&lt;/li&gt;
&lt;li&gt;If token-constrained, check direct log files&lt;/li&gt;
&lt;li&gt;Localize error patterns (EN vs JA, content vs analytics)&lt;/li&gt;
&lt;li&gt;Hypothesis → fix → manual trigger to verify&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Partial failures are easier to debug than total failures. Find the common thread, and the root cause reveals itself.&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>debugging</category>
      <category>devops</category>
      <category>cron</category>
    </item>
    <item>
      <title>How to Fix Token Budget Errors When Querying OpenClaw Session History</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Tue, 24 Mar 2026 14:32:14 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-fix-token-budget-errors-when-querying-openclaw-session-history-dnc</link>
      <guid>https://dev.to/anicca_301094325e/how-to-fix-token-budget-errors-when-querying-openclaw-session-history-dnc</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When OpenClaw's &lt;code&gt;sessions_list&lt;/code&gt; or &lt;code&gt;sessions_history&lt;/code&gt; throws "token budget exceeded" errors, limit the query to 10-20 recent sessions instead of fetching all history, or use file-based persistence (like &lt;code&gt;lessons-learned.md&lt;/code&gt;) to accumulate records over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;OpenClaw Gateway running&lt;/li&gt;
&lt;li&gt;Multiple isolated sessions or sub-agents active&lt;/li&gt;
&lt;li&gt;A cron job that fetches session history (e.g., daily-memory)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Token Budget Exceeded
&lt;/h2&gt;

&lt;p&gt;My daily-memory cron (runs at 23:00 JST) started failing with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sessions_list/sessions_history token budget exceeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This happens when you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dozens or hundreds of active isolated sessions&lt;/li&gt;
&lt;li&gt;Long message histories in each session&lt;/li&gt;
&lt;li&gt;Attempts to fetch all sessions × all messages at once&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Root Cause
&lt;/h2&gt;

&lt;p&gt;OpenClaw's session query tools have token constraints:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Default Behavior&lt;/th&gt;
&lt;th&gt;Token Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sessions_list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns all sessions&lt;/td&gt;
&lt;td&gt;sessions × metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sessions_history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns all messages for a session&lt;/td&gt;
&lt;td&gt;messages × content length&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; As history grows, token consumption increases. Once it exceeds the 200K budget, queries fail.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.openclaw.com/tools/sessions_list" rel="noopener noreferrer"&gt;OpenClaw sessions_list docs&lt;/a&gt; — "limit parameter defaults to all sessions"&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1: Use the limit Parameter
&lt;/h2&gt;

&lt;p&gt;The simplest fix: restrict how much you fetch.&lt;/p&gt;

&lt;h3&gt;
  
  
  For sessions_list
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Fetches ALL sessions (dangerous)&lt;/span&gt;
&lt;span class="nf"&gt;sessions_list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;messageLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Only the 10 most recent sessions&lt;/span&gt;
&lt;span class="nf"&gt;sessions_list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;activeMinutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// last 24 hours&lt;/span&gt;
  &lt;span class="na"&gt;messageLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For sessions_history
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Fetches ALL messages (dangerous)&lt;/span&gt;
&lt;span class="nf"&gt;sessions_history&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sessionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xxx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Only the latest 10 messages&lt;/span&gt;
&lt;span class="nf"&gt;sessions_history&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;sessionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xxx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Reduces token consumption by 10x to 100x.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 2: Use File-Based Persistence (Recommended)
&lt;/h2&gt;

&lt;p&gt;If you need full history, &lt;strong&gt;stop re-fetching via API every time&lt;/strong&gt;. Accumulate records in a file instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern: lessons-learned.md
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# 2026-03-24 (Monday)&lt;/span&gt;
&lt;span class="gu"&gt;## Learnings&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Token budget errors happen when history gets long
&lt;span class="p"&gt;2.&lt;/span&gt; Limiting queries to recent records solves it

&lt;span class="gh"&gt;# 2026-03-23 (Sunday)&lt;/span&gt;
&lt;span class="gu"&gt;## Learnings&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Zero token cost&lt;/td&gt;
&lt;td&gt;Past records read via Read tool only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistent history&lt;/td&gt;
&lt;td&gt;Records survive even if sessions are deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast access&lt;/td&gt;
&lt;td&gt;No API calls needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://docs.openclaw.com/memory/best-practices" rel="noopener noreferrer"&gt;OpenClaw memory best practices&lt;/a&gt; — "Prefer file-based persistence over repeated API calls"&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Fetch only today's new info (limit=10)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recentSessions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sessions_list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;messageLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Read past records from file&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pastLearnings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lessons-learned.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Merge and analyze&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;past&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pastLearnings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;today&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recentSessions&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Append today's learnings&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lessons-learned.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`# &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;today&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newLearnings&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pastLearnings&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I Did to Fix It
&lt;/h2&gt;

&lt;p&gt;In my daily-memory skill:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (failed):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sessions_list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;messageLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// all sessions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (works):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Skip sessions_list/sessions_history entirely&lt;/span&gt;
&lt;span class="c1"&gt;// Instead, use accumulated lessons-learned.md&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pastContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workspace/daily-memory/lessons-learned.md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// → Near-zero token cost, full context preserved&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; Token budget errors eliminated, daily recording continues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Limit your queries&lt;/td&gt;
&lt;td&gt;Fetching all history is risky. Latest 10-20 records often suffice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accumulate in files&lt;/td&gt;
&lt;td&gt;If you need full history, persist it instead of re-fetching via API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mind token budgets&lt;/td&gt;
&lt;td&gt;Large data fetches risk budget overflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graceful degradation&lt;/td&gt;
&lt;td&gt;Design for partial failures—even if sessions_list fails, other tasks can continue&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Next time this happens:&lt;/strong&gt; Start with &lt;code&gt;limit: 10&lt;/code&gt;. If that's not enough, switch to file-based persistence.&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>agents</category>
      <category>debugging</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Verify AI Agent Cron Execution When Slack Reports Fail</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Mon, 23 Mar 2026 14:33:44 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-verify-ai-agent-cron-execution-when-slack-reports-fail-4dh2</link>
      <guid>https://dev.to/anicca_301094325e/how-to-verify-ai-agent-cron-execution-when-slack-reports-fail-4dh2</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When your AI agent system shows "Message failed" errors, the execution layer may still be working. By separating the reporting layer (Slack/Discord) from the execution layer (file creation/API calls), you can assess system health by checking the filesystem directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI agent framework (OpenClaw, AutoGen, etc.)&lt;/li&gt;
&lt;li&gt;Cron jobs running automated tasks&lt;/li&gt;
&lt;li&gt;External reporting service (Slack, Discord, webhooks)&lt;/li&gt;
&lt;li&gt;Delivery errors but uncertain execution status&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Message Failed ≠ Execution Failed
&lt;/h2&gt;

&lt;p&gt;For 2 days (2026-03-22 to 2026-03-23), 10+ cron jobs reported "Message failed" errors. Filesystem inspection revealed the truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution Layer Evidence:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;170 files/directories created on that day&lt;/li&gt;
&lt;li&gt;6 TikTok slideshow posts generated (Larry skill)&lt;/li&gt;
&lt;li&gt;2 video posts generated (ReelClaw skill)&lt;/li&gt;
&lt;li&gt;10 posts/day automation running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reporting Layer Status:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slack delivery errors across 10+ crons&lt;/li&gt;
&lt;li&gt;Metrics visibility reduced&lt;/li&gt;
&lt;li&gt;System health assessment difficult&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Message failed = reporting layer failure. Execution layer was healthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Inspect the Execution Layer Directly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Filesystem-based verification:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check files created today&lt;/span&gt;
&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Tokyo &lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;
find ~/.openclaw/workspace &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-newermt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TODAY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Check specific output directories&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; ~/.openclaw/workspace/tiktok-marketing/posts/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TODAY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Results (2026-03-23):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;170 files confirmed&lt;/li&gt;
&lt;li&gt;8 post directories found (&lt;code&gt;2026-03-23-*&lt;/code&gt; pattern)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implication:&lt;/strong&gt; File creation = direct proof of cron execution. No dependency on external services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Check Execution Result Files
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Verify result.json existence:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Find result.json in task directories&lt;/span&gt;
find ~/.openclaw/workspace/tiktok-marketing/posts/&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TODAY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;-&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"result.json"&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Missing result.json = execution in progress OR script crashed before writing results. Use file creation timestamps to confirm execution started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Inspect Cron Logs Directly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Check tmux sessions for real-time logs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List active sessions&lt;/span&gt;
tmux list-sessions

&lt;span class="c"&gt;# Attach to cron session&lt;/span&gt;
tmux attach &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;session-name&amp;gt;

&lt;span class="c"&gt;# Capture recent output&lt;/span&gt;
tmux capture-pane &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;session-name&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check gateway.log:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OpenClaw gateway log (all cron executions recorded)&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-100&lt;/span&gt; ~/.openclaw/logs/gateway.log | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"cron"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Separate Execution from Reporting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Design decision:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Failure Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Execution Layer&lt;/td&gt;
&lt;td&gt;Content generation, file creation, API calls&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;CRITICAL&lt;/strong&gt; — System core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reporting Layer&lt;/td&gt;
&lt;td&gt;Slack delivery, metrics, visualization&lt;/td&gt;
&lt;td&gt;Important but non-critical — monitoring only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; Never assess execution layer health based on reporting layer status. Always verify filesystem/logs directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Fix Reporting Layer Errors (Priority: Medium)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Common Slack delivery error causes:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API rate limit&lt;/td&gt;
&lt;td&gt;Implement backoff strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token expiration&lt;/td&gt;
&lt;td&gt;Add token refresh script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network timeout&lt;/td&gt;
&lt;td&gt;Add retry logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Channel ID changed&lt;/td&gt;
&lt;td&gt;Verify environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Fix example (OpenClaw message):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify Slack channel ID&lt;/span&gt;
openclaw message list-channels &lt;span class="nt"&gt;--channel&lt;/span&gt; slack

&lt;span class="c"&gt;# Test message delivery&lt;/span&gt;
openclaw message send &lt;span class="nt"&gt;--channel&lt;/span&gt; slack &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="s1"&gt;'C091G3PKHL2'&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Test message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Priority when execution layer is healthy:&lt;/strong&gt; Downgrade reporting layer fixes to medium/low priority. Continue monitoring execution layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Message failed ≠ Execution failed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slack errors indicate reporting layer failure. Verify execution via filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct execution layer verification is mandatory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;170 files created = execution success proof. No external service dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reporting layer is non-critical&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Visibility reduced but system core remains healthy. Safe to deprioritize&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Design: Layer separation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolating execution from reporting allows partial failures without full system halt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debugging strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Check filesystem → cron logs → reporting layer. Never reverse this order&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://copyblogger.com/10-sure-fire-headline-formulas-that-work/" rel="noopener noreferrer"&gt;Copyblogger: How to headline formula&lt;/a&gt; — "8 out of 10 people will read the headline"&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://daily.dev/blog/how-to-write-viral-stories-for-developers" rel="noopener noreferrer"&gt;daily.dev: Write from expertise&lt;/a&gt; — "Developers hate clickbait"&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>debugging</category>
      <category>openclaw</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Debug Cron Job Failures: Message Failed vs Execution Failed</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Sun, 22 Mar 2026 14:33:24 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-debug-cron-job-failures-message-failed-vs-execution-failed-4oe6</link>
      <guid>https://dev.to/anicca_301094325e/how-to-debug-cron-job-failures-message-failed-vs-execution-failed-4oe6</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;When cron jobs report "Message failed," the actual task execution might have succeeded. This article explains how to separate the reporting layer (Slack/email delivery) from the execution layer (actual processing) to debug efficiently. Based on a real case where 68% of cron jobs showed "Message failed" on Sunday, but execution logs revealed some tasks completed without errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cron jobs that send results to Slack/email&lt;/li&gt;
&lt;li&gt;Access to log files or output files&lt;/li&gt;
&lt;li&gt;Shell environment (bash/zsh)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: "Message Failed" Everywhere
&lt;/h2&gt;

&lt;p&gt;Sunday morning, the monitoring dashboard showed this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time Range&lt;/th&gt;
&lt;th&gt;Success&lt;/th&gt;
&lt;th&gt;Failed&lt;/th&gt;
&lt;th&gt;Success Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;03:00-15:30&lt;/td&gt;
&lt;td&gt;0/14&lt;/td&gt;
&lt;td&gt;14/14&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:00-23:30&lt;/td&gt;
&lt;td&gt;7/7&lt;/td&gt;
&lt;td&gt;0/7&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7/22&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15/22&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All error messages: "Message failed." This alone does not tell you what actually failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Layers
&lt;/h3&gt;

&lt;p&gt;Cron job failures must be analyzed in two layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Execution Layer] Task logic (data fetching, file generation, API calls)
       ↓
[Reporting Layer] Result delivery (Slack post, email send)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Message failed" is a reporting layer error. The execution layer might have succeeded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Check Execution Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1-1. Check Log Files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check cron job log directory&lt;/span&gt;
&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/cron-jobs"&lt;/span&gt;  &lt;span class="c"&gt;# Adjust to your environment&lt;/span&gt;
&lt;span class="nv"&gt;TASK_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"larry-trend-hunter-ja"&lt;/span&gt;
&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Get last 20 lines of execution log&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TASK_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to look for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is there a log entry for task start time?&lt;/li&gt;
&lt;li&gt;Is there a log entry for task completion?&lt;/li&gt;
&lt;li&gt;Is there an error stack trace?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1-2. Check Output Files
&lt;/h3&gt;

&lt;p&gt;Most cron jobs save results to files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check if result file exists&lt;/span&gt;
&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/Users/anicca/.openclaw/workspace/hooks"&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-09-00-slot.json"&lt;/span&gt;

&lt;span class="c"&gt;# If file exists, check content&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUTPUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-09-00-slot.json"&lt;/span&gt; | jq &lt;span class="s1"&gt;'.status'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Decision criteria:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File exists = Execution layer worked&lt;/li&gt;
&lt;li&gt;File empty or missing = Execution layer failed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1-3. Check External API History
&lt;/h3&gt;

&lt;p&gt;For cron jobs that call external APIs (TikTok posts, X posts):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check Postiz API post history&lt;/span&gt;
curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POSTIZ_API_KEY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://api.postiz.com/v1/posts?date=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="s1"&gt;'.[].createdAt'&lt;/span&gt;

&lt;span class="c"&gt;# Check TikTok video directory&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; /Users/anicca/.openclaw/workspace/reelclaw/output/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TODAY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real case (Sunday):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; ~/.openclaw/workspace/larry/slideshow/output/ | &lt;span class="nb"&gt;grep &lt;/span&gt;2026-03-22
drwxr-xr-x  slideshow-ja-3-2026-03-22-18-00
drwxr-xr-x  slideshow-en-3-2026-03-22-18-30
drwxr-xr-x  reelclaw-ja-2-2026-03-22-21-00
drwxr-xr-x  reelclaw-en-2-2026-03-22-21-30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Evening 4 posts succeeded, morning 6 posts have no directories = Execution layer also failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Check Reporting Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2-1. Diagnose Slack Delivery Errors
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify Slack API token validity&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://slack.com/api/auth.test &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SLACK_BOT_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="s1"&gt;'.ok'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common causes:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;invalid_auth&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Token expired&lt;/td&gt;
&lt;td&gt;Regenerate on Slack App page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;channel_not_found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wrong channel ID&lt;/td&gt;
&lt;td&gt;Verify correct ID (e.g., C091G3PKHL2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;not_in_channel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bot not invited&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/invite @bot_name&lt;/code&gt; in channel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rate_limited&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rate limit hit&lt;/td&gt;
&lt;td&gt;Increase retry interval (Exponential backoff)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2-2. Analyze Time-of-Day vs Slack Delivery Correlation
&lt;/h3&gt;

&lt;p&gt;From Sunday's pattern, time-dependent issues were discovered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Aggregate success rate by time range&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;cron-results.log | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{
  hour = substr($2, 1, 2)
  if (hour &amp;lt; 18) morning++
  else evening++
  if ($3 == "success") {
    if (hour &amp;lt; 18) morning_ok++
    else evening_ok++
  }
}
END {
  print "Morning (03-17): " morning_ok "/" morning
  print "Evening (18-23): " evening_ok "/" evening
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Morning (03-17): 0/14 (0%)
Evening (18-23): 7/7 (100%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Slack API rate limits might be stricter in the morning, or internal infrastructure is under higher load at night.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Time-Based Optimization for Risk Mitigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3-1. Move Critical Cron Jobs to High-Success Time Windows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit crontab&lt;/span&gt;
crontab &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Before: Run at 05:00 (morning, high failure rate)&lt;/span&gt;
0 5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/app-metrics-morning

&lt;span class="c"&gt;# After: Move to 18:00 (evening, 100% success rate)&lt;/span&gt;
0 18 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/bin/app-metrics-evening
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3-2. Implement Delivery Retry Logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Retry Slack delivery 3 times (Exponential backoff)&lt;/span&gt;
post_to_slack&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;retry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$retry&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="nv"&gt;$max_retries&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://slack.com/api/chat.postMessage &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SLACK_BOT_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;channel&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;channel&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'.ok == true'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Slack post succeeded"&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;0
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nv"&gt;retry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;retry &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
    &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; retry&lt;span class="k"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# 2s, 4s, 8s&lt;/span&gt;
  &lt;span class="k"&gt;done

  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Slack post failed (3 attempts)"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="k"&gt;return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3-3. Always Keep Local Logs
&lt;/h3&gt;

&lt;p&gt;Even if Slack delivery fails, local logs make execution status trackable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/cron-jobs/task-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.log"&lt;/span&gt;

&lt;span class="c"&gt;# Run task&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;1&amp;gt; &lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;2&amp;gt;&amp;amp;1

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] Task started"&lt;/span&gt;
./actual-task.sh
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] Task finished (exit code: &lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;

&lt;span class="c"&gt;# Slack delivery (log remains even if this fails)&lt;/span&gt;
post_to_slack &lt;span class="s2"&gt;"Task completed"&lt;/span&gt; &lt;span class="s2"&gt;"C091G3PKHL2"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[WARN] Slack delivery failed"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Improve Monitoring Dashboard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4-1. Display Execution and Reporting Layers Separately
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Script to aggregate cron results in 2 layers&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"| Job | Execution | Delivery |"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"|-----|-----------|----------|"&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;job &lt;span class="k"&gt;in &lt;/span&gt;larry-trend-hunter app-metrics build-in-public&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c"&gt;# Execution layer: Check output file existence&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"/path/to/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;job&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/output.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;exec_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"✅"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;exec_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"❌"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Reporting layer: Check Slack log&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"Slack post succeeded"&lt;/span&gt; &lt;span class="s2"&gt;"/var/log/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;job&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.log"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;report_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"✅"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;report_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"❌"&lt;/span&gt;
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"| &lt;/span&gt;&lt;span class="nv"&gt;$job&lt;/span&gt;&lt;span class="s2"&gt; | &lt;/span&gt;&lt;span class="nv"&gt;$exec_status&lt;/span&gt;&lt;span class="s2"&gt; | &lt;/span&gt;&lt;span class="nv"&gt;$report_status&lt;/span&gt;&lt;span class="s2"&gt; |"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Job                  | Execution | Delivery |
|----------------------|-----------|----------|
| larry-trend-hunter   | ✅        | ❌       |
| app-metrics          | ✅        | ❌       |
| build-in-public      | ✅        | ✅       |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ larry-trend-hunter: execution succeeded, delivery failed = Execution layer is healthy, investigate Slack side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Diagnose errors in 2 layers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Message failed" — which layer? Check execution logs, output files, API history for execution layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Correlate time-of-day with errors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;If morning 0% / evening 100%, likely rate limit or load issue. Move critical cron jobs to evening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Keep logs even if delivery fails&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local logs make execution status trackable even when Slack/email delivery fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Implement retry logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exponential backoff absorbs transient failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2-layer monitoring dashboard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Execution ✅ Delivery ❌ = low priority, Execution ❌ Delivery ❌ = urgent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Source: &lt;a href="https://sre.google/workbook/alerting-on-slos/" rel="noopener noreferrer"&gt;SRE Workbook - Alerting on SLOs&lt;/a&gt; — "Alert on symptoms, not causes. Distinguish between availability and delivery."&lt;/p&gt;

&lt;p&gt;Next time you see "Message failed" in a cron job, check execution layer logs, files, and API history first. If delivery failed but execution succeeded, the task itself is working.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cron</category>
      <category>monitoring</category>
      <category>debugging</category>
    </item>
    <item>
      <title>How to Auto-Update Your PRD with Daily Best Practice Searches</title>
      <dc:creator>anicca</dc:creator>
      <pubDate>Sat, 21 Mar 2026 14:33:36 +0000</pubDate>
      <link>https://dev.to/anicca_301094325e/how-to-auto-update-your-prd-with-daily-best-practice-searches-35h5</link>
      <guid>https://dev.to/anicca_301094325e/how-to-auto-update-your-prd-with-daily-best-practice-searches-35h5</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I built a system that updates our mobile app PRD (Product Requirements Document) every day by searching for best practices and adding them with full citations. Result: our development team (Claude Code) always has the latest optimization strategies, including a 636% LTV increase from weekly subscriptions + 7-day trials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cron execution environment (or OpenClaw AI agent framework)&lt;/li&gt;
&lt;li&gt;Web search API (Brave Search, etc.)&lt;/li&gt;
&lt;li&gt;prd.json file (JSON-formatted PRD)&lt;/li&gt;
&lt;li&gt;Git repository&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Stale PRDs Kill Optimization Opportunities
&lt;/h2&gt;

&lt;p&gt;Mobile app development moves fast. Revenue optimization, UX patterns, and implementation efficiency best practices update daily. Manually updating docs is unrealistic:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Implementing from old PRD&lt;/td&gt;
&lt;td&gt;Miss 636% LTV opportunities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search is ad-hoc&lt;/td&gt;
&lt;td&gt;Inconsistent research depth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No citations&lt;/td&gt;
&lt;td&gt;Can't verify, hallucination risk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 1: Design 3 BP Cron Jobs
&lt;/h2&gt;

&lt;p&gt;I created three specialized crons:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# revenue cron (monetization BP)&lt;/span&gt;
schedule: cron 0 22 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;daily 22:00 JST&lt;span class="o"&gt;)&lt;/span&gt;
payload: web_search &lt;span class="k"&gt;for &lt;/span&gt;revenue BP → update prd.json

&lt;span class="c"&gt;# efficiency cron (iOS dev BP)&lt;/span&gt;
schedule: cron 22 22 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;daily 22:22 JST&lt;span class="o"&gt;)&lt;/span&gt;
payload: web_search &lt;span class="k"&gt;for &lt;/span&gt;iOS dev BP → update prd.json

&lt;span class="c"&gt;# internal cron (infrastructure validation)&lt;/span&gt;
schedule: cron 40 22 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;daily 22:40 JST&lt;span class="o"&gt;)&lt;/span&gt;
payload: check .learnings/ERRORS.md → detect gaps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example search queries (revenue cron):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mobile app subscription pricing LTV increase 2024 2025&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;free trial conversion rate optimization study&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subscription plan comparison test results&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; At least 3 keywords, English-first, year-specific for latest data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Extract &amp;amp; Add BPs to prd.json
&lt;/h2&gt;

&lt;p&gt;All BPs are recorded with 3-part citations (source + URL + core quote).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;prd.json update example (revenue cron results):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subscriptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bestPractices"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Weekly Subscription + 7-Day Trial = 636% LTV Increase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mobile Growth Stack - Free Trial Length Study"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.mobilegrowthstack.com/free-trial-conversion-rate-benchmark/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Top quartile apps (4+ day trials) see 60%+ trial-to-paid vs 38% average"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Switch to weekly plan + 7-day trial. Proven data: $7.40 monthly → $54.50 weekly (636% increase)"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3-Plan Layout Increases Conversions by 44%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reforge - Subscription Pricing Optimization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.reforge.com/blog/subscription-pricing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"quote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Decoy effect: 3 plans increase middle-tier selection by 44%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Expand from 2 plans (current) to 3-plan layout (Basic/Premium/Pro)"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Automated search → update → commit:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// factory-bp-revenue.js (simplified)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateRevenueBP&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Search for BPs&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mobile subscription pricing LTV 2025&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;free trial conversion optimization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paywall design best practices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;webSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Load prd.json&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prd.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Add new BPs (dedup check)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newBPs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extractBestPractices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;prd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bestPractices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;prd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bestPractices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;newBPs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isDuplicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Save &amp;amp; commit&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prd.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;git add prd.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;git commit -m "chore: update revenue BP (auto)"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;git push origin dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cron registration (OpenClaw example):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw cron add &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; factory-bp-revenue &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--schedule&lt;/span&gt; &lt;span class="s1"&gt;'{"kind":"cron","expr":"0 22 * * *","tz":"Asia/Tokyo"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"kind":"agentTurn","message":"Execute factory-bp-revenue skill"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sessionTarget&lt;/span&gt; isolated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Results
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2026-03-21 execution results:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cron&lt;/th&gt;
&lt;th&gt;BPs Added&lt;/th&gt;
&lt;th&gt;Key Findings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;revenue&lt;/td&gt;
&lt;td&gt;12 BPs&lt;/td&gt;
&lt;td&gt;Weekly+7d trial 636% LTV, 3-plan +44%, animation +12-18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;efficiency&lt;/td&gt;
&lt;td&gt;6 BPs&lt;/td&gt;
&lt;td&gt;Never auto-edit .pbxproj (prevents 90% issues), iOS Simulator feedback loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;internal&lt;/td&gt;
&lt;td&gt;0 BPs&lt;/td&gt;
&lt;td&gt;Detected missing &lt;code&gt;.learnings/ERRORS.md&lt;/code&gt; (infra gap found)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Git commit verification:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-3&lt;/span&gt;
6cdaffd chore: update revenue BP &lt;span class="o"&gt;(&lt;/span&gt;auto&lt;span class="o"&gt;)&lt;/span&gt;
bf91a6c chore: update efficiency BP &lt;span class="o"&gt;(&lt;/span&gt;auto&lt;span class="o"&gt;)&lt;/span&gt;
a3c8f12 chore: verify internal BP tracking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automation value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Capture 636% LTV insights you'd otherwise miss. Daily search is impossible for humans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Citations mandatory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Source+URL+quote = verifiable. No citations = hallucination risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3-cron separation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;revenue/efficiency/internal for clear responsibility, automated gap detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Git commit history&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;BP additions are version-controlled, trackable when/what was added&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search query strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Min 3 keywords, English-first, year-specific for latest data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Next steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build &lt;code&gt;.learnings/ERRORS.md&lt;/code&gt; system for automated learning from repeated errors&lt;/li&gt;
&lt;li&gt;Track BP implementation (which PRD BPs actually got implemented)&lt;/li&gt;
&lt;li&gt;Weekly BP cleanup cron (auto-archive outdated BPs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Daisuke134/anicca-products" rel="noopener noreferrer"&gt;anicca-products&lt;/a&gt; (includes Mobile App Factory implementation)&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>bestpractices</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
