0 of 3 Articles Published for 3 Days Straight: The 41-Second Timeout Margin That Killed My Automation A developer built an autonomous Claude Code environment that publishes three affiliate articles daily to Hatena Blog, recovering from a 41-second timeout margin that caused three days of zero publications. The system uses four shell scripts and macOS launchd to run three times a day, with idempotency ensuring the daily publish count converges to three. The developer reports monthly revenue above 1.2M yen from this automation. For three mornings in a row, my audit log printed the same line: published today: 0 / target: 3 . Nothing crashed. The scripts ran, exited, and produced nothing. The entire cause turned out to be a 41-second margin — a 300-second timeout against a process that actually takes 259 seconds. Changing one number to 600 turned 0/3 into 3/3 the next morning. Some background: I went from earning 100k yen a month as a university student to 600k a month juggling multiple gigs, then lost all of it overnight to a company-initiated layoff. Over the following six months I built an autonomous Claude Code environment, and I'm now above 1.2M yen in monthly revenue. At the core of it is a system that publishes three affiliate articles every morning without a human touching anything. The difference between people who keep earning from affiliate marketing and people who drop out is not writing skill, and not a nose for picking products. It's whether you can keep going . Articles that tend to earn on Rakuten Affiliate share a common pattern: spec-comparison articles about home appliances and gadgets priced above 50,000 yen, with lots of reviews and in stock. Robot vacuums, portable power stations, heat-pump washer-dryers, fully automatic coffee makers. The search intent is "I want to compare before I buy," so product link click-through is high and it fits the structure of affiliate marketing well. The problem is cost. Researching the specs of a high-ticket appliance on the web, building a comparison table, and finishing an article good enough to include the "honestly weak points" section takes 30 to 40 minutes. Three articles is close to two hours. Almost nobody has the willpower to repeat that 365 days a year. I don't either. What you need here isn't "trying harder" — it's an environment that keeps running even when you don't try hard . Once the system is built, the running cost is just API calls. The affiliate-factory I built is a simple structure made of four shell scripts. macOS launchd the successor to cron fires three times a day — morning, midday, and night — and three affiliate articles get published to Hatena Blog every day without any human involvement. There's one more design-level core idea: idempotency . A naive script doesn't care how many articles have already been published today. If the morning batch fails, the day ends at zero. This system first counts "how many were successfully published today" and "how many drafts are left on the Desktop," and generates only the number still missing against the target of three. daily.sh PUB TODAY=$ find "$ARCHIVE" -maxdepth 1 -name "${TODAY} .md" 2 /dev/null | wc -l | tr -d ' ' DRAFTS=$ find "$OUT" -maxdepth 1 -name ' .md' 2 /dev/null | wc -l | tr -d ' ' NEED=$ TARGET - PUB TODAY - DRAFTS "$NEED" -lt 0 && NEED=0 Even if the morning batch is wiped out by API limits, the midday batch calculates "we still need 3 today" and refills. The evening batch fills the last one. No matter how many times it runs, the day's publish count converges to three. Once you understand this design, the roles of the four scripts look completely different. I assume many readers are in the situation of "having to write an article every day is exhausting." I was too. But to be precise, what's exhausting is "making the decision to write an article every day." When the system takes over the decision, the human just looks at the published articles. Here's the structure of the whole system as an ASCII diagram. launchd 毎朝・昼・夜の3回 │ ▼ daily.sh ← 司令塔。冪等に「今日あと何本必要か」を計算 │ ├─ NEED本分ループ ──────────────────────────────────────────┐ │ │ │ generate.sh │ │ │ claude -p + WebSearch で製品を選び記事を生成 │ │ │ timeout 600s / 最大3リトライ │ │ └──→ ~/Desktop/アフィリ記事/YYYY-MM-DD HHMMSS.md ──┘ │ ├─ post-to-hatena.sh --publish --all │ │ Desktop/ .md を blogsync ではてなブログへ全件公開 │ └──→ published/ へアーカイブ(Desktopキューから除去) │ └─ audit-heal.sh │ 壊れ記事の掃除、カバレッジ表の出力、異常時はmacOS通知 └──→ logs/audit-YYYY-MM-DD.log The role of daily.sh is simple. Calculate what's left for today, call generate.sh only as many times as needed, then run publishing and auditing in order. That's it. daily.sh(抜粋) TARGET=3 TODAY="$ date +%Y-%m-%d " PUB TODAY=$ find "$ARCHIVE" -maxdepth 1 -name "${TODAY} .md" 2 /dev/null | wc -l | tr -d ' ' DRAFTS=$ find "$OUT" -maxdepth 1 -name ' .md' 2 /dev/null | wc -l | tr -d ' ' NEED=$ TARGET - PUB TODAY - DRAFTS "$NEED" -lt 0 && NEED=0 if "$NEED" -gt 0 ; then for i in $ seq 1 "$NEED" ; do bash "$DIR/generate.sh" "$i" || echo " daily ⚠ 生成1本失敗(後続の再実行で補充されます)。" done fi bash "$DIR/post-to-hatena.sh" --publish --all bash "$DIR/audit-heal.sh" PUB TODAY counts the files under published/ carrying today's prefix. DRAFTS is the number of drafts still sitting directly on the Desktop. NEED is the difference, clamped to 0 if it goes negative. The important part is that processing doesn't stop when one generate.sh run fails. || echo swallows the error and a later batch refills the remainder. set -uo pipefail is declared at the top, while individual generation failures are absorbed inside the loop. The design keeps the whole flow alive without losing track of what happened. generate.sh is the heart of this system. Using WebSearch, it picks a high-ticket appliance that sells well on Rakuten, researches the specs, and writes the article. Claude does all of that on its own. Prompt structure The prompt is defined in a heredoc inside generate.sh . It instructs Claude through the following steps. The EXCL variable at the top of the prompt holds the list of already-covered products. It's read from posted-products.log and handed to Claude as "do not pick these products this time," so the same product doesn't come up repeatedly. Invoking the claude command generate.sh(抜粋) GEN TIMEOUT="${AFFILIATE FACTORY GEN TIMEOUT:-600}" for attempt in 1 2 3; do RESP=$ timeout "$GEN TIMEOUT" "$CLAUDE" -p "$PROMPT" \ --allowedTools WebSearch \ --model sonnet \ --permission-mode auto \