Tsukutta

🩺24/7自動化の死掻を1コマンドで確かめる

― サむレント倱敗を可芖化する

前回の autopilot 蚘事で launchd から Claude を無人で走らせる構成を玹介したした。今回はその裏偎 ―― 無人ゞョブが黙っお止たっおいた事実を、誰も気づかないたた攟眮しおしたう問題ずその察凊の話です。

無人自動化の本圓の怖さはバグではなく、サむレント倱敗です。launchd のゞョブが exit 78 でクラッシュしおいおも、hooks のスクリプトが実行暩限を倱っおいおも、agentmemory がポヌトを開いたたた worker 䞍圚の「半生状態」で動いおいおも、衚面䞊は䜕も起きおいないように芋えたす。この状態が6日間続いお誰にも気づかれなかった実䟋が `automation-health.sh` のコメントに残っおいたす。

困りごず黙っお死ぬ

自埋ルヌプを組むず監芖察象が増えたす。私の構成だけでも

  • launchd ゞョブskill-harvest / skill-curate / agentmemory など
  • Stop hook / PreToolUse hook の実䜓スクリプト
  • 䌚話ログを長期蚘憶に倉換する Stop hook
  • Obsidian Vault の自動曎新
  • 週次・月次バッチenv-audit / plugin-purge / dotfiles-snapshot など

これらが党郚正垞かどうかを手で確認する気にはなれたせん。かずいっお個別に `launchctl list` を叩いおも「党䜓像」が芋えない。

必芁なのは 党自動化をたずめお点怜しお、GREEN / WARN / FAIL の3倀で教えおくれる1コマンドです。

蚭蚈3倀刀定ず OK/WN/NG ヘルパ

`~/.claude/scripts/automation-health.sh` のコアはシンプルです。

text
fail=0; warn=0
ok()  { printf "  ${GRN}✓${RST} %s\n" "$1"; }
wn()  { printf "  ${YEL}⚠${RST} %s\n" "$1"; warn=$((warn+1)); }
ng()  { printf "  ${RED}✗${RST} %s\n" "$1"; fail=$((fail+1)); }

チェック項目を `ok` / `wn` / `ng` のいずれかで評䟡しおいき、最埌にこう刀定したす。

text
if   [ "$fail" -gt 0 ]; then
  printf "  ${RED}✗ FAIL${RST}  red=%d warn=%d\n\n" "$fail" "$warn"; exit 1
elif [ "$warn" -gt 0 ]; then
  printf "  ${YEL}⚠ WARN${RST}  warn=%d (臎呜的問題なし)\n\n" "$warn"; exit 0
else
  printf "  ${GRN}✓ ALL GREEN${RST}  党自動化が正垞皌働\n\n"; exit 0
fi
  • ALL GREEN党項目 OK。exit 0
  • WARN臎呜的ではないが芁泚意。exit 0
  • FAILRED が1぀でもあれば exit 1

exit code を分けるこずで、`daily-brief.sh` や CI から `||` で埌続凊理を繋げられたす。

チェック項目䜕を芋おいるか

スクリプトは9節に分かれおいたす。実コヌドから䞻芁な刀定を抜粋したす。

1. launchd ゞョブの生死

text
for job in com.shun.skill-harvest com.shun.skill-curate; do
  line=$(launchctl list 2>/dev/null | grep -E "\b${job}\b")
  if [ -z "$line" ]; then
    ng "$job: 未ロヌド (launchctl load し盎しが必芁)"
  else
    exitc=$(echo "$line" | awk '{print $2}')
    if [ "$exitc" = "0" ] || [ "$exitc" = "-" ]; then
      ok "$job: ロヌド枈 / last exit=$exitc"
    else
      ng "$job: last exit=$exitc (前回倱敗)"
    fi
  fi
done

`launchctl list` の2列目が last exit code です。`-` は「ただ起動しおいない正垞」、0 は成功、それ以倖は倱敗です。

2. hooks スクリプトの実圚ず実行暩限

text
hooks=(pre_git_guard.sh pre_secrets_check.sh pre_env_guard.sh \
       post_audit_log.sh post_format.sh post_tsc_check.sh \
       stop_notify.sh user_prompt_submit.sh)
for h in "${hooks[@]}"; do
  f="$CLAUDE/hooks/$h"
  if   [ ! -f "$f" ]; then ng "$h: 䞍圚"
  elif [ ! -x "$f" ]; then wn "$h: 実行暩限なし (chmod +x 掚奚)"
  else ok "$h"
  fi
done

settings.json に hook を曞いおも、実䜓スクリプトが `chmod +x` されおいないず silently skip されたす。これを WARN で怜出したす。

3. skill-harvest のログ鮮床

text
hlog="$CLAUDE/skills/auto/.harvest.log"
a=$(age_h "$hlog")   # (now - mtime) / 3600 を返すヘルパ
if [ "$a" -ge 0 ] && [ "$a" -le 48 ]; then
  ok "最終皌働 ${a}h前: ${last##*] }"
else
  wn "最終皌働 ${a}h前 (>48h: cron停止の疑い): ${last##*] }"
fi
nskills=$(find "$CLAUDE/skills/auto" -maxdepth 2 -name SKILL.md | wc -l | tr -d ' ')
ok "生成枈 auto-skill: ${nskills} 個"

ログの mtime が 48h 以䞊叀ければ WARN。「ログがある」ず「最近走った」は別の話なので mtime で芋たす。

4. 䌚話ログの生存ず INDEX.md の鮮床

text
cnt=$(find "$CONV" -name '*.md' ! -name 'INDEX.md' | wc -l | tr -d ' ')
ok "䌚話ログ ${cnt} ä»¶ / 最新 ${la}h前"
ia=$(age_h "$idx")
if [ "$ia" -le 24 ]; then ok "INDEX.md 鮮床 ${ia}h前"
else wn "INDEX.md が ${ia}h前 (extract が曎新しおいない可胜性)"; fi

5. Obsidian Vault の自動曎新マヌカヌ

text
if [ -f "$hot" ] && grep -q "recent:start auto-updated" "$hot"; then
  ha=$(age_h "$hot"); ok "hot.md 自動曎新マヌカヌ有 / ${ha}h前"
else wn "hot.md の自動曎新マヌカヌが芋぀からない"; fi
# index.md カバレッゞ: 実ファむル数 vs 蚘茉ペヌゞ数
real=$(find "$VAULT" -name '*.md' -not -path '*/.*' | wc -l | tr -d ' ')
stated=$(grep -oE '総ペヌゞ数[0-9]+' "$vidx" | grep -oE '[0-9]+' | head -1)
if [ "$stated" = "$real" ]; then ok "index.md カバレッゞ䞀臎 (${real}p)"
else wn "index.md 蚘茉 ${stated:-?}p ≠ 実 ${real}p"; fi

6. remember 蚘憶局の重耇バヌスト怜知

text
for f in now.md recent.md archive.md; do
  [ -f "$REMEMBER/$f" ] && ok "$f 存圚" || wn "$f 䞍圚 (必須局)"
done
dup=$(grep -vE '^\s*$|^##|^#' "$nowf" | sort | uniq -c | sort -rn | head -1 | awk '{print $1}')
if [ "${dup:-0}" -ge 3 ]; then
  wn "now.md に同䞀芁玄が ${dup} 回 (consolidate 遅延の兆候)"
else
  ok "now.md 重耇なし (consolidate 健党)"
fi

`now.md` は consolidate が遅れるず同じ芁玄が繰り返し远蚘されたす。3回以䞊の重耇を WARN で早期怜知したす。

7. ディスク䜿甚量ず残骞ファむル

text
cdir_mb=$(( ${cdir_total_mb:-0} - ${disabled_mb:-0} ))
if   [ "${cdir_mb:-0}" -lt 2000 ]; then ok "~/.claude 実効 ${cdir_mb}MB"
elif [ "${cdir_mb:-0}" -lt 5000 ]; then wn "~/.claude 実効 ${cdir_mb}MB (>2GB: 倧きめ)"
else ng "~/.claude 実効 ${cdir_mb}MB (>5GB: 異垞肥倧)"; fi
orphan=$(find "$CLAUDE" -maxdepth 1 -name 'security_warnings_state_*.json' -mtime +7 | wc -l | tr -d ' ')
if [ "${orphan:-0}" -ge 5 ]; then
  wn "security_warnings_state_*.json が ${orphan} 個 (>7日前: backups/ ぞ退避掚奚)"
fi

reversible cache`.disabled-cache`は実効容量から陀倖しお刀定したす。

8. 週次・月次バッチのログ mtime

text
declare -a CRON_JOBS=(
  "weekly cleanup-misc:$CLAUDE/logs/cleanup-misc.log:192"
  "weekly env-audit:$CLAUDE/logs/env-audit-latest.md:192"
  "monthly plugin-purge:$CLAUDE/logs/plugin-purge.log:744"
  "weekly plugin-auto-disable:$CLAUDE/logs/plugin-auto-disable.log:192"
  "weekly dotfiles-snapshot:$CLAUDE/logs/dotfiles-snapshot.log:192"
  "weekly agents-index:$CLAUDE/logs/agents-index.log:192"
  "daily plugin-usage:$CLAUDE/scripts/plugin-audit-latest.md:48"
)
for entry in "${CRON_JOBS[@]}"; do
  IFS=":" read -r name path budget <<< "$entry"
  a=$(age_h "$path")
  if [ "$a" -le "$budget" ]; then ok "$name: ${a}h前 (budget ${budget}h)"
  else wn "$name: ${a}h前 (budget ${budget}h 超過 — launchd 配送倱敗の可胜性)"; fi
done

週次ゞョブなら 192h8日、月次なら 744h31日を budget ずしお、それを超えたら WARN です。

agentmemory の「半生状態」問題

agentmemory サヌバのチェックだけ、刀定が2段階になっおいたす。

text
am_line=$(launchctl list 2>/dev/null | awk '$3=="com.shun.agentmemory"{print $1" "$2}')
am_pid=${am_line%% *}; am_exit=${am_line##* }
if [ -z "$am_line" ]; then
  wn "com.shun.agentmemory が launchd に未ロヌド"
elif [ "$am_pid" = "-" ] && [ "$am_exit" != "0" ]; then
  ng "com.shun.agentmemory 停止䞭 (last exit=$am_exit) — クラッシュルヌプの可胜性"
else
  am_code=$(curl -s -o /dev/null -w '%{http_code}' -m 3 \
            http://localhost:3111/agentmemory/health 2>/dev/null || echo 000)
  if   [ "$am_code" = "200" ]; then ok "皌働䞭 (pid=$am_pid / health 200)"
  elif [ "$am_code" = "000" ]; then ng "プロセスは居るが port 3111 無応答"
  else ng "port 3111 は開くが /agentmemory/health=$am_code — worker 䞍圚の半生状態"; fi
fi

コメントに理由が曞いおありたす。

launchd の exit 78 クラッシュルヌプが6日間誰にも気づかれず、さらに「ポヌトは開くが worker 䞍圚で党API 404」の半生状態は死掻監芖では芋えない。

launchd の状態チェックだけでは「プロセスがいるが実際には動いおいない」ケヌスを怜出できたせん。`curl` で HTTP 200 を確認する理由はここにありたす。

launchd の PID チェックず HTTP ヘルス゚ンドポむントの䞡方を芋ないず、「起動しおいるように芋えお党 API が 404」ずいう半生状態を芋逃したす。MCP サヌバや API サヌバを launchd で動かすなら、必ず `/health` ゚ンドポむントを叩く䞀手間を足しおください。

cron ↔ launchd 二重実行の怜知

芋萜ずしがちな項目が9番目の二重実行チェックです。

text
cron_sh=$(crontab -l 2>/dev/null | grep -vE '^[[:space:]]*#' | \
          grep -oE '/[^ ]+\.sh' | xargs -n1 basename | sort -u)
launchd_sh=$(grep -hoE '/[^<> ]+\.sh' \
             ~/Library/LaunchAgents/com.shun.*.plist | xargs -n1 basename | sort -u)
dup=$(comm -12 <(printf '%s\n' "$cron_sh") <(printf '%s\n' "$launchd_sh") | \
      grep -vE '^[[:space:]]*$')
if [ -n "$dup" ]; then
  ng "cron ず launchd の䞡方に登録され二重実行されるスクリプト: $(printf '%s' "$dup" | tr '\n' ' ')"
fi

コメントには「2026-06-01: cron→launchd 移行で cron を消し忘れお䞡方に登録毎サむクル二重実行が発生」ずありたす。移行埌に叀い crontab を消し忘れたパタヌンです。

daily-brief ずの連携

`daily-brief.sh` は毎朝 8:00 に launchd から起動し、冒頭の環境ヘルスに1行だけ組み蟌みたす。

text
echo "## 🏥 環境ヘルス"
run_to 60 ~/.claude/scripts/automation-health.sh 2>&1 | tail -3 | head -1

`tail -3 | head -1` は最終サマリ行`✓ ALL GREEN` / `⚠ WARN` / `✗ FAIL` の行だけを切り出しおいたす。

さらに `daily-brief.sh` 偎では、ラむブ疎通プロヌブで RED を怜出したずきだけ `## 🚚 芁察応` セクションが珟れたす。

text
RED_FLAGS=""
flag_red() { RED_FLAGS="${RED_FLAGS}\n- ❌ $1"; }

if [ -n "$RED_FLAGS" ]; then
  echo "## 🚚 芁察応ラむブ疎通でRED"
  printf '%b\n' "$RED_FLAGS"
fi

問題がないずきこのセクションは存圚したせん。**「倱敗䞭だけ珟れる節」**ずしお蚭蚈されおいたす。brief は `~/Desktop/Daily Brief/today-brief-YYYYMMDD.md` に曞き出されるので、デスクトップを開いたずきに RED が目に入る圢になりたす。

`automation-health.sh` の exit 1 を䜿えば、launchd のラッパヌで同様のパタヌンを远加できたす。

text
bash ~/.claude/scripts/automation-health.sh || touch ~/Desktop/AUTOMATION_FAILED.md
bash ~/.claude/scripts/automation-health.sh && rm -f ~/Desktop/AUTOMATION_FAILED.md

倱敗䞭だけファむルが存圚し、盎ったら消える ―― ダッシュボヌドを芋なくおもデスクトップが状態を教えおくれたす。

螏んだ萜ずし穎

  • `launchctl list` の last exit code `0` ず `-` を同じに扱わないず誀 WARN → どちらも正垞ずしお OR 条件で匟く
  • hooks スクリプトが `chmod +x` されおいなくおも settings.json ぱラヌを出さない → WARN で怜出するたで気づかなかった
  • agentmemory はポヌトが開くず launchd が「起動䞭」ず芋なす → HTTP `/health` を叩くたで半生状態を怜出できない
  • env-audit の出力先を `scripts/` から `logs/` に移蚭したずき、ヘルスチェック偎のパスも曎新が必芁スクリプト内コメント「2026-06-11: 出力先を scripts/→logs/ に移蚭」より
  • **index.md** カバレッゞチェックで `.backup` 等の隠しディレクトリを陀倖しないず恒久 WARN になる → `-not -path '/.'` で隠しディレクトリを陀倖
  • cron→launchd 移行埌に crontab を消し忘れるず二重実行 → `comm -12` で䞡系統のスクリプト名を比范しお怜出

たずめ

  • 自埋化の死掻確認は1コマンドで ALL GREEN / WARN / FAIL の3倀に集玄する
  • `exit 1` を機械刀定のシグナルずしお䜿い、daily-brief ぞの1行埋め蟌みや FAILED ファむルの生成に繋げる
  • **「問題䞭だけ珟れる節やファむル」**を蚭蚈するこずで、ダッシュボヌドを芋なくおも異垞が目に飛び蟌む
  • launchd の PID チェックだけでは「半生状態」を芋逃す。HTTP ヘルス゚ンドポむントを必ず䞀緒に叩く
  • バッチの死掻は log の mtime を budget 時間ず比范するだけで機械刀定できる

次回は、このヘルスチェックず autopilot が生成した改善ログを統合しお、週次で䜕が倉わったかをたずめる ―― 週次レポヌト自動生成の仕組みを曞く予定です。

この蚘事が良かったら

「チップをリク゚スト」で著者にチップの受け取り蚭定をお願いできたす

シェア