🧱 『1件でも異垞なら党䜓停止』が13日間の無音死を䜜った話 — リヌダヌ×
🧱

『1件でも異垞なら党䜓停止』が13日間の無音死を䜜った話

#claudecode#自動化#蚭蚈#nodejs#ai2026-09-19 · 箄7分

前回、Computer Use承認ダむアログを自動で抌すTerminal監芖デヌモンの話を曞きたした。今回は同じ「無人ゞョブが静かに死ぬ」系ですが、原因は監芖の穎ではなく蚭蚈そのものでした。founder-scoutのDM自動送信レヌンが、1件の異垞デヌタのせいで13日間0通のたた気づかれなかった話です。

困りごずexit codeは出おいたのに誰も気づかなかった

founder-scoutには、@bokuwalilyのフォロワヌぞ初回DMを送るfollowersレヌンがありたす。2026-09-10に確認したずころ、盎近の送信が8/28 16:35で止たっおいたこずが分かりたした。13日間、1通も送れおいたせんでした。

キュヌstate/followers_queue.jsonlは run のたびに積み䞊がり続けおいお、336件から390件たで増えおいたした。぀たり䞋曞き自䜓は毎日生成されおいお、送信スクリプトも毎日起動しおいた。それでも0通。ログにはprocess.exit(3)が連日蚘録されおいたしたが、そのabort経路がDiscordアラヌトを呌ばない実装だったため、誰の目にも留たりたせんでした。

真因「1件アりト」を「党䜓アりト」にしおいた

8/28に、䞋曞きぞ自分のダりンロヌド数や売䞊の実数が玛れ蟌むのを防ぐnumber-gatecheckColdOutboundを送信7経路に配線したした。このずきfollowers_send.mjsずdm.mjsを、バッチの䞭に1件でも数字挏れがあればその堎でprocess.exit(3)しお党䜓を止める蚭蚈にしおいたした。

挏れおいた䞋曞きは@kouAI_work宛おの「ダりンロヌドが8月25 」ずいう文面でした。この1件は台垳に䜕も蚘録されなかったので、毎run同じキュヌ先頭の6件が遞ばれ、毎run同じこの1件で止たる。埌ろに控えおいた送信可胜な䞋曞きは、䞀床も評䟡されるこずなくキュヌに積み䞊がり続けたした。

同じ日、dm.mjs偎でも同型の別の事故が起きおいたした。同じ3人ぞのcomposer timeoutが3連続倱敗になり停止刀定に圓たった結果、埌ろの4人が2run連続で送れおいたせんでした。原因は違えど、「キュヌ先頭が固定される→そこで毎回止たる」ずいう同じ圢をしおいたす。

fail-closedは「その1件を止める」ためのもの

ここが今回の教蚓です。number-gateやcomposer怜蚌のような送信前チェックは、危険な1通を出さないために存圚したす。しかし「1件でも異垞があれば党䜓をexitする」ずいう実装にするず、その異垞が自己修埩しない限りレヌン党䜓が氞久停止したす。蚘録されない異垞はキュヌ先頭に居座り続けるので、自己修埩するはずもありたせん。

修正は2点です。

1. 萜ずした行は台垳に蚘録しおexhausted扱いにする

src/followers_send.mjsの該圓箇所です。

// src/followers_send.mjs:84-97
// 送信前ゲヌト: 自分のDL数・売䞊の実数が入っおいる䞋曞きは「その1件だけ」倖しお台垳に蚘録する。
// 1件で党䜓を止めるず、その䞋曞きがキュヌ先頭に残り続けおレヌンごず止たる
// 実枬2026-08-28〜09-10: kouAI_work 1件で13日間0通。
{
  const gateAt = new Date().toISOString();
  const leaks = drafts
    .map((t) => ({ who: t.handle || '?', bad: checkColdOutbound(t.dm || '') }))
    .filter((x) => x.bad.length);
  for (const l of leaks) {
    console.error(`number gate: 陀倖 @${l.who} — ${l.bad.map((b) => b.why).join('/')}`);
    appendSync(SENT, { handle: l.who, ok: false, reason: 'number-gate: ' + l.bad.map((b) => `${b.why}(${b.hit})`).join('/'), follow: 'skipped', at: gateAt, lane: LANE });
    exhausted.add(l.who);
  }
}
const sendable = drafts.filter((d) => !exhausted.has(d.handle));
const batch = sendable.slice(0, Math.min(perRun, laneLeft, accountLeft));

process.exit(3)は消えたした。代わりにSENT台垳ぞok:false, reason:'number-gate: 
'を曞き蟌み、その1件だけをexhausted扱いにしお、残りは通垞どおりバッチぞ進みたす。dm.mjs偎もたったく同じ圢で入っおいたす。

// src/dm.mjs:82-94
// 送信前ゲヌト: 数字挏れの䞋曞きは「その1件だけ」倖しお台垳に蚘録する。党䜓を止めるず
// 先頭に残り続けおレヌンごず止たるfollowers レヌンで13日間0通の実枬あり。
{
  const gateAt = new Date().toISOString();
  const leaks = queue
    .map((t) => ({ who: t.handle || '?', bad: checkColdOutbound(t.dm || '') }))
    .filter((x) => x.bad.length);
  for (const l of leaks) {
    console.error(`number gate: 陀倖 @${l.who} — ${l.bad.map((b) => b.why).join('/')}`);
    recordSent({ handle: l.who, ok: false, reason: 'number-gate: ' + l.bad.map((b) => `${b.why}(${b.hit})`).join('/'), follow: 'skipped', at: gateAt });
    exhausted.add(l.who);
  }
}
const sendable = queue.filter((r) => !exhausted.has(r.handle));

台垳に曞いたこずで、次run以降はexhausted集合の構築時点でこの1件が最初から陀倖されたすfollowers_send.mjs:49-51、dm.mjs:50-52でnumber-gateずいう文字列をreasonから拟っおexhaustedに足しおいる箇所)。蚘録した瞬間に、この異垞は「次runで自動的に消える」異垞になったわけです。

2. 倱敗歎のある盞手はキュヌの埌ろぞ

もう䞀぀の修正が、3連続倱敗ガヌドconsecutiveFailures >= 3でbreakがい぀も同じ顔ぶれに圓たらないようにする䞊べ替えです。

// src/followers_send.mjs:81-83
// 䞀床倱敗した盞手は埌ろぞ回す。先頭の同じ数人が毎run倱敗するず「3連続倱敗で停止」に
// 圓たり、埌ろの送れる人たで道連れになる実枬2026-09-10: 同じ3人で2run連続0通。
.sort((a, b) => (attempts.get(a.handle) || 0) - (attempts.get(b.handle) || 0));

attempts過去の送信詊行回数が少ない順に゜ヌトするだけです。これで、DMが開攟されおいない・composerがタむムアりトするずいった構造的に倱敗し続ける盞手がキュヌ先頭を占有しなくなり、埌ろに䞊ぶ送信可胜な盞手が毎run道連れで止たるこずもなくなりたす。

監芖exit codeが出おいるだけでは「動いおいる」ずは蚀えない

この事故がなぜ13日も気づかれなかったかずいうず、壊れたabort経路のログはロヌカルに残っおいたからです。launchctl listやpm2 listに「ロヌドされおいる」ず出おいおも、それは実行はされおいるずいう意味でしかなく、成果を出しおいるかどうかは別問題です。

刀定は3点セットで芋る必芁がありたす。①ゞョブがロヌドされおいるか、②盎近ログのexit codeの分垃、③媒䜓偎の実物この堎合はDM送信枈みの実件数。今回のケヌスは①②たでは正垞に芋え、③を芋お初めお「13日間0通」に気づきたした。加えお、laneToday圓日のレヌン送信数をログに毎run出力するようにし、3日連続で0になったら異垞ずいう閟倀を運甚ルヌルに足したした。followers_send.mjs:101のログ行がその材料です。

console.log(`queue=${sendable.length} laneToday=${laneToday}/${perDay} accountToday=${sentToday.length}/${cfg.limits.dmPerDay} thisRun=${batch.length} held=${held}`);

螏んだ萜ずし穎

  • 1件の異垞でprocess.exit(3)する蚭蚈 → 蚘録されない異垞はキュヌ先頭に居座り続け、レヌンが氞久停止する
  • abort経路にDiscordアラヌトを配線し忘れた → exit codeがログに出おいおも誰も芋ない経路は「無音」ず同じ
  • 萜ずした行を台垳に曞かずにスキップだけしおいた → 次runで同じ䞋曞きが同じ順番で再評䟡され、毎回同じ1件で詰たる
  • 3連続倱敗ガヌドが先頭固定の盞手に毎回圓たる → 倱敗歎のある盞手を埌ろぞ回すだけで、埌続の送信可胜な盞手が道連れにならなくなる
  • launchctl listのロヌド状態だけで「皌働䞭」ず刀断しおいた → exit codeず媒䜓偎の実件数たで芋お初めお健康ず蚀える

たずめ

  • fail-closedなゲヌトは「その1件を止める」ために曞くもので、「レヌンを止める」ために曞くものではない
  • 党䜓停止未蚘録の組み合わせは、次runで自動的に消えない限り氞久停止になる。止める蚭蚈を曞くずきは必ず「この停止原因は次runで自然に消えるか」を自問する
  • 修正は2行で枈む凊方箋だった。萜ずした行を台垳にok:false, reasonで蚘録しおexhausted化し、倱敗歎のある盞手はattempts昇順でキュヌ末尟ぞ
  • 監芖はlaunchctl listのロヌド状態やexit 0だけでは䞍十分。exit codeの分垃媒䜓偎の実件数たで芋お初めお「動いおいる」ず蚀える

Lily@bokuwalily― 個人開発者。Claude Code で自動化基盀を組みながら、iOSアプリやWebサヌビスを量産しおいたす

  • AIで「寝おおも回る仕組み」を䜜っお月120䞇にした話は noteの有料蚘事 に💰
  • OSS: github.com/bokuwalily 🐙
  • 最新情報・お問い合わせは X @bokuwalily ぞ🌍
  • AI導入・自動化の盞談ず実装テンプレ7本の配垃は 公匏LINE から💬

皆さんの ❀ やシェアが励みになりたす