Claude Code環境は放置すると太る ― 注入バイト・agents数・不満ワードを週次監視して自動スリム化
「Claude Code環境」シリーズの続きです。前作のべき等マーカー実装と組み合わせると、自律ループが止まらなくなる原因の半分以上が「環境劣化」だとわかってきました。
Claude Code環境は放っておくと太ります。rules が肥大し、退避したはずの agents が復活し、hooks が暴発し続ける。気づくのは「なんか重い」「同じ失敗を繰り返す」という体感だけで、原因の特定に時間を取られます。この記事は、その劣化を週次で計測して claude -p に自己修正させ、修正後に独立再計測で改善を確認する仕組みの話です。
困りごと:退避したはずの agents 99体が注入され続けていた
2026-07-11のパフォーマンス監査で実際に踏んだ事故です。
~/.claude/agents/ の整理は何度かやっていました。使わなくなった agent 定義を agents-archive/ に手動で移して「片付いた」と思っていた。ところが計測してみると agents_loaded は 99 のまま変わっていない。原因はドットディレクトリ(.deprecated/ や .backup/ など)でした。find のデフォルト動作はドットディレクトリも再帰します。表示上は消したのに、隠しディレクトリ経由で全部ロードされ続けていた。
この実事故がきっかけで cc-self-audit.sh を書きました。
5つの計測指標
スクリプトの collect() 関数が毎回取る指標は5つです。
# ~/.claude/scripts/cc-self-audit.sh(collect関数より)
# --- 静的 ---
inject_bytes=$(( \
$(find "$HOME/.claude/rules" -name '*.md' -print0 2>/dev/null | xargs -0 cat 2>/dev/null | wc -c) + \
$(cat "$HOME/.claude/CLAUDE.md" 2>/dev/null | wc -c) + \
$(cat "$HOME/CLAUDE.md" 2>/dev/null | wc -c) + \
$(cat "$HOME/.claude/projects/-Users-matsubara/memory/MEMORY.md" 2>/dev/null | wc -c) ))
agents_loaded=$(find "$HOME/.claude/agents" -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
# --- 動的(前回実行以降のtranscriptのみ・最大200ファイル)---
stopspam=$(echo "$files" | xargs /usr/bin/grep -h -c 'セルフ監査未実施。実装' 2>/dev/null | awk '{s+=$1} END{print s+0}')
frustration=$(echo "$files" | xargs /usr/bin/grep -h -c -E '何回も言|いい加減にし|嘘つ|舐めんな|なんで治らん|最悪やろ|頭悪い' 2>/dev/null | awk '{s+=$1} END{print s+0}')
toolerr=$(echo "$files" | xargs /usr/bin/grep -h -o -c '"is_error":true' 2>/dev/null | awk '{s+=$1} END{print s+0}')
| 指標 | 意味 | 閾値 |
|---|---|---|
inject_bytes | rules + CLAUDE.md + MEMORY.md の合計バイト | 40,000 |
agents_loaded | ~/.claude/agents/ 配下 .md 総数(ドットdir含む再帰) | 60 |
stopspam | 監査hookが発火した回数/週 | 15 |
frustration | transcriptの不満ワード出現数/週 | 8 |
toolerr | transcript中の "is_error":true 件数/週 | 400 |
静的2指標は注入コストの代理変数、動的3指標は体験品質の代理変数です。不満ワード検出が異色に見えますが、繰り返し叱られているということは同じ失敗を繰り返しているということで、環境の問題(hookの誤発火・メモリの腐り・toolの壊れ)に起因することが多い。
閾値はenv変数で上書きできます。
TH_INJECT_BYTES="${SELF_AUDIT_TH_INJECT:-40000}"
TH_AGENTS="${SELF_AUDIT_TH_AGENTS:-60}"
TH_STOPSPAM="${SELF_AUDIT_TH_STOPSPAM:-15}"
TH_FRUSTRATION="${SELF_AUDIT_TH_FRUST:-8}"
TH_TOOLERR="${SELF_AUDIT_TH_TOOLERR:-400}"
閾値超過で claude -p が自己修正する
全指標が閾値以下なら ✅ GREEN を Discord に1行投げて終わり。超過があれば claude -p を呼んで修正させます。
BREACH=$(echo "$METRICS" | breaches)
if [ -z "$BREACH" ]; then
log "GREEN"
touch "$LASTRUN"
notify "✅ CC自己監査: 正常 ($METRICS)"
exit 0
fi
log "RED: $BREACH"
if [ "$DRY" = "1" ]; then log "DRY=1: 修正スキップ"; touch "$LASTRUN"; exit 0; fi
claude -p に渡すプロンプトには、劣化パターンごとの対処方針を埋め込んでいます。
PROMPT="...
## 指示
1. ~/.claude 内だけを調査・修正する(プロジェクトコード・secret・plist削除は禁止)
2. 既知の劣化パターンと正典:
- agents_loaded超過 → ~/.claude/agents/ 配下の退避漏れ(ドットdirも読み込まれる)を ~/.claude/agents-archive/ へ移動
- inject_bytes超過 → 肥大したrules/MEMORY.mdを圧縮(フル版は ~/.claude/rules-archive/ へ。リンクは全維持し索引整合を検証)
- stopspam超過 → ~/.claude/hooks/self_audit_stop.sh の抑制ロジックを点検
- frustration/toolerr超過 → 該当transcriptをgrepして繰り返し失敗の真因を特定し、hook/スキル/メモリで再発防止を仕込む
3. 変更は1件ずつ ${CHANGELOG} に「日時/対象/理由/戻し方」を追記
4. 修正後に必ず 'bash ~/.claude/scripts/cc-self-audit.sh --collect-only' を実行し改善を数値で確認
..."
--allowedTools "Read,Write,Edit,Bash,Grep,Glob" でファイルシステム操作のみ許可し、max-turns 50、壁時計20分(FIX_TIMEOUT=1200)でcapします。
修正ループの中で claude 自身に
--collect-onlyを実行させているのは、あくまで「修正手順の一部としての確認」です。監査の最終判定は外側の独立再計測が行います。内側の確認を鵜呑みにしない設計が肝です。
「自己申告を信じない」fail-closed 設計
claude -p が「直しました」と言っても、即信じません。外側のharnessが collect() を再度呼んで数値を再計測します。
# 独立再計測(自己申告は信じない)
AFTER=$(collect)
log "after: $AFTER"
echo "$AFTER" >> "$HISTORY"
STILL=$(echo "$AFTER" | breaches)
touch "$LASTRUN"
if [ -z "$STILL" ]; then
notify "🔧 CC自己監査: 劣化検知→自己修正済み。前:[$BREACH] 後:全緑。詳細=$CHANGELOG"
else
notify "🚨 CC自己監査: 自己修正後も残存 [$STILL]。要確認: $LOG / $CHANGELOG"
fi
修正後も違反が残っていれば 🚨 でDiscordに飛んで人間に渡します。自己申告ではなくOSの計測値だけを信頼する、これがfail-closedの核です。
全計測値は ~/.claude/self-audit/history.jsonl に1行1レコードで積み上げ、claudeに渡すプロンプトには直近5件のトレンドも添えます。「先週は30KBだったのに今週は51KB」という変化量を見ることで、単発の異常値か継続的な増加かを区別できます。
launchd で日曜 8:30 に週次起動
<key>StartCalendarInterval</key><dict>
<key>Weekday</key><integer>0</integer>
<key>Hour</key><integer>8</integer>
<key>Minute</key><integer>30</integer>
</dict>
<key>RunAtLoad</key><false/>
Weekday=0 が日曜です。RunAtLoad は false にして、launchd登録直後の即時実行を防いでいます。環境変数 PATH に ~/.local/bin を含め、そこに claude のシムを置いています。
手動実行は2モードあります。
# 計測だけ(claudeを呼ばない)
~/.claude/scripts/cc-self-audit.sh --collect-only
# ドライラン(違反表示のみ・修正なし)
SELF_AUDIT_DRY=1 ~/.claude/scripts/cc-self-audit.sh
本番投入前は必ず DRY=1 で指標を目視確認してから有効化しました。
踏んだ落とし穴
- ドットdirが
findに引っかかる →~/.claude/agents/.deprecated/等が再帰対象になり、退避したはずのagentsが全部カウントされ続けた。実事故の元凶 - transcriptの
findが遅い →head -200でファイル数を上限切り、かつ-size +100kで空ファイルを除外しないと週末の初回実行に3分超かかる - 不満ワードの正規表現が日本語混じり →
LANG=en_US.UTF-8を先頭で設定しないとgrep -Eがマルチバイト文字で誤挙動 - claude -p がPATHにいない → launchdの最小PATHで
~/.local/binが抜けていた。plist の EnvironmentVariables に明示的に追記して解決 - 修正後の再計測をclaude内部でやらせると自己採点になる → 外側の
collect()呼び出しが必須。内側確認は「修正手順の一部」にとどめる
bash 3.2互換・fail-openとコメントに書いてある通り、監査スクリプト自体がクラッシュしても何も壊れない設計にしています。set -uo pipefailにして、個々の計測は2>/dev/null || trueで握りつぶし、最悪の場合は計測値が0になって「GREEN」扱いになります。監査の失敗で本体が死ぬのは本末転倒です。
まとめ
- Claude Code環境は放置するとinject_bytes・agents数・hookスパム・不満ワード・tool失敗の5指標で計測できる劣化が蓄積する
- 閾値超過時は
claude -pが~/.claude内のみを自己修正し、変更ログ(self-audit-changes.log)に「戻し方」付きで記録する - 修正後に 外側の独立再計測で改善を数値検証する。「直した」という自己申告だけで完了扱いしない
- launchd で日曜8:30に週次起動。
--collect-onlyとDRY=1で手動確認できる - スクリプト自体はfail-open(監査失敗でも環境を壊さない)、自己修正の対象はfail-closed(修正後も違反が残れば人間にエスカレーション)
次回は、この監査ループが検知したinject_bytes の超過をどう削ったか ― rules を圧縮して週次自動スリム化するパイプラインの話を書きます。
Lily(@bokuwalily)― 個人開発者。Claude Code で自動化基盤を組みながら、iOSアプリやWebサービスを量産しています
- AIで「寝てても回る仕組み」を作って月120万にした話は noteの有料記事 に💰
- OSS: github.com/bokuwalily 🐙
- 最新情報・お問い合わせは X @bokuwalily へ🌍
皆さんの ❤️ やシェアが励みになります!