3 Token Burn From an Infinite Retry Loop: How I Made Claude Refill My Article Queue When It Runs Dry A developer built an automated system that uses Claude to refill its own article topic queue when it runs dry, ensuring daily publication without human intervention. The system, managed by launchd on macOS, decouples article generation from deployment and uses a JSON queue as a buffer, with Claude generating new topics from real work artifacts when the queue empties. The developer reports that this setup has maintained a streak of daily articles underpinning ¥1.2M in monthly revenue. Back when I was a college student earning ¥100,000 a month, one finished article meant my week was over. Today launchd stocks one every morning at 8:00, and the series that underpins ¥1.2M in monthly revenue hasn't gone dark once. The difference isn't talent or discipline — it's that I built, exactly one time, an environment that refills its own topic queue. When you try to mass-produce articles on your own, you almost always hit the same wall: when the topics run out, everything stops . You can block time on the calendar for topic brainstorming, but if you're not in the mood that day, you skip it. Any design where a human is the bottleneck will jam somewhere, guaranteed. The strategy I took was to hand the work over to the environment . Not the work of writing articles — instead I assembled, one time only, a "detect that topics ran out and refill them" mechanism plus a "convert topics into articles" mechanism, then left them alone. The only action a human performs is the last one: checking the finished article and publishing it to note or Zenn. The article-daily-stock.sh script introduced in this article is the core of that. There are three key points in the design. Key point 1: Decouple stocking from deployment As the comment at the top of the script says, the design is such that "even if the Zenn deploy deploy-next jams, this part does not stop" lines 1–11 of the actual code . Success or failure of generation is judged solely by "was a stock file written to ~/content/article ?" The job that publishes articles zenn-daily runs in a separate process, and the two are independent of each other. A structure where one job failing doesn't cause a chain stop is what supports stability over long-term operation. Key point 2: Use the queue as a buffer Topics are stacked as an array of objects in a JSON file at ~/zenn-articles/.topic-queue.json . The current queue has 16 topics waiting. Every morning at 8:00 it pulls the first one off, converts it into an article, and when done moves that topic to done-queue . As long as the queue has entries left, the 8 AM job can jump straight into the real work. The problem is the moment the queue bottoms out . Traditionally that would end with "zero articles today." But I wanted to avoid that. Because once you break the streak for even one day, the "I don't have to write today either" collapse of the habit starts. Key point 3: When it's empty, Claude invents the topic itself When the queue goes empty, the script calls neither an external service nor an API — it calls claude -p running locally, has it auto-plan exactly one next article topic, and inserts it at the head of the queue. The basis it uses is "that day's actual work." Daily briefs, memory files, the automation scripts themselves — Claude finds "things that could become a technical article" from the concrete artifacts piling up every day. Presenting real, existing files and paths as evidence rather than fabricating them is hardcoded into the instructions. If the generated JSON doesn't pass required-field validation, the script exits without writing the done-marker. launchd's catch-up slot 10:35 automatically re-runs the same script, so "refill failure → retry at the next slot" is part of one and the same mechanism. launchd com.shun.article-daily ├─ 8:00 StartCalendarInterval └─ 10:35 StartCalendarInterval(キャッチアップ) │ ▼ claude-quota-guard.py ← Claude Maxトークン枯渇時に即abort │ ▼ run-and-notify.sh ← 完了/失敗をDiscordへ通知 │ ▼ article-daily-stock.sh apply │ ├─ 0 done-marker 確認(当日生成済み?) │ YES → audit のみ実行して exit 0 │ ├─ 1 全ストックを audit + 自己修復 │ サムネ欠落 → gen note thumbs.py で再生成 │ 本文不正 → done-queue からネタを取り戻してキュー再投入 │ ├─ 2 jq 'length' .topic-queue.json │ │ │ ├─ = 1 → 4 キュー先頭取得 へ │ │ │ └─ == 0 → 3 自動立案モード │ │ │ ▼ │ BRIEF LATEST(当日のdaily brief md) │ + ~/.claude/memory/ │ + ~/.claude/scripts/ 実ファイル │ │ │ ▼ │ claude -p REPLENISH PROMPT │ --model sonnet --effort high │ --max-turns 20 │ │ │ JSON検証(slug / title / sources / thumb title) │ ├─ NG → done-marker 書かず exit 0 │ │ ↑ 10:35スロットが拾う │ └─ OK → queue 先頭へ insert │ ├─ 4 キュー先頭 → SLUG / TITLE / EMOJI / NO を取得 │ ├─ 5 claude -p で記事執筆(--max-turns 40, 最大1500秒) │ ├─ 6 検証(title有無・70字以内・1200bytes以上・スタブ語なし) │ NG → ファイル破棄 / done-marker 書かず exit 0 │ ├─ 7 content/article/articles/ へストック ├─ 8 gen note thumbs.py でサムネ生成 → thumbnails/ ├─ 9 coverage.json を upsert ├─ 10 manifest を ready 化 ├─ 11 queue pop → done-queue へ移動 └─ 12 git push(best-effort・失敗しても done-marker は立つ) If you look at StartCalendarInterval in ~/Library/LaunchAgents/com.shun.article-daily.plist , the fire times are two slots: 8:00 and 10:35.