「Claude Code環境」シリーズの続きです。前回の自己監査フラストレーションループで「止まらない改善ループ」を書きましたが、今回はその根本にある注入バイト肥大の検知と自己修正の話です。
Claude Codeはセッション開始時に rules/*.md・~/.claude/CLAUDE.md・~/CLAUDE.md・MEMORY.md を無条件に全文注入します。ルールを追加するたびにこのバイト数は無意識に増え、2026-07-11の監査で注入合計が40KB超&退避したはずのagents 99体が読み込まれ続けているのがパフォーマンス低下の主因と判明しました。以来、これを毎週自動計測して閾値超過で自己修正するループを常設しています。
困りごと:誰も気づかないうちに太る
CLAUDE.md にルールを書き足すのは良いことです。でも「書き足す」は一方向にしか動かない。半年も経てば忘れた節が重複し、圧縮したはずのアーカイブが実は生きていて、.agents/ に移したファイルが引き続き読み込まれている ―― という状態になります。
問題は計測していないと劣化が見えないことです。Claude が「なんか重い気がする」という直感は正しくても、何KBが注入されているかを普段確認する人はいません。週次で数値を出して、人間が設定した閾値を超えたら自動で修正を試みる仕組みが必要でした。
何を計測するか
~/.claude/scripts/cc-self-audit.sh の collect() 関数がメトリクスを組み立てます。静的計測と動的計測の2軸です。
静的計測:注入バイト数と agents 数
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 ' ')
find ... -name '*.md' でドットdirも再帰します。「agents-archive/ に移動したから大丈夫」と思っていても、~/.claude/agents/ の下にドットdirとして置いてある場合は 読み込まれ続けるのが罠です(2026-07-11にこれで99体が生き残っていました)。
動的計測:stopspam・不満ワード・tool失敗
前回実行以降の transcript(.jsonl)から3つを集計します。
STOP_MARKER = 'Stop hook feedback:\\n[~/.claude/hooks/self_audit_stop.sh]: '
FRUST_RE = re.compile(r'何回も言|いい加減にし|嘘つ|舐めんな|なんで治らん|最悪やろ|頭悪い')
stopspam は「Stop hook が実際にプロンプトに注入した行」だけを数えます。単純な grep だと、tool_result にたまたま含まれる同じ文字列も拾ってしまい、実態の6〜10倍の数字になります(2026-07-12にこれで 40→6 に修正しました)。frustration は isMeta でない type=user のテキストブロック、つまり本人の生発言だけを対象にします。
閾値
TH_INJECT_BYTES="${SELF_AUDIT_TH_INJECT:-40000}" # rules+CLAUDE.md+MEMORY.md 合計
TH_AGENTS="${SELF_AUDIT_TH_AGENTS:-60}" # ~/.claude/agents 配下 .md 総数
TH_STOPSPAM="${SELF_AUDIT_TH_STOPSPAM:-15}" # 監査hook発火/週
TH_FRUSTRATION="${SELF_AUDIT_TH_FRUST:-8}" # 不満ワード/週
TH_TOOLERR="${SELF_AUDIT_TH_TOOLERR:-400}" # tool失敗/週
環境変数で上書き可能にしてあるので、チューニングが必要になってもスクリプトを触らずに済みます。
違反検知 → 自己修正 → 独立再計測
閾値を超えた項目を breaches() が一覧にして、claude -p に渡します。
BREACH=$(echo "$METRICS" | breaches)
if [ -z "$BREACH" ]; then
log "GREEN"
notify "✅ CC自己監査: 正常 ($METRICS)"
exit 0
fi
違反があった場合のプロンプトは ~/.claude 内だけを修正させます(プロジェクトコード・secret・plist削除は明示禁止)。
PROMPT="あなたはClaude Code環境の自己監査・自己修復エージェント。週次監査で劣化を検知した。
...
1. ~/.claude 内だけを調査・修正する(プロジェクトコード・secret・plist削除は禁止)
2. 既知の劣化パターンと正典:
- agents_loaded超過 → ~/.claude/agents/ 配下の退避漏れを ~/.claude/agents-archive/ へ移動
- inject_bytes超過 → 肥大したrules/MEMORY.mdを圧縮(フル版は ~/.claude/rules-archive/ へ)
...
4. 修正後に必ず 'bash ~/.claude/scripts/cc-self-audit.sh --collect-only' を実行し改善を数値で確認
..."
claude -p が終わったら、スクリプト自身が collect() をもう一度呼んで独立再計測します。Claude の自己申告は信じません。
# 独立再計測(自己申告は信じない)
AFTER=$(collect)
STILL=$(echo "$AFTER" | breaches)
if [ -z "$STILL" ]; then
notify "🔧 CC自己監査: 劣化検知→自己修正済み。前:[$BREACH] 後:全緑。"
else
notify "🚨 CC自己監査: 自己修正後も残存 [$STILL]。要確認: $LOG"
fi
緑のときは ✅ 1行のみ、違反→修正成功は 🔧 前後の数値、修正後も残存は 🚨 で Discord の #01_alerts に流します。週次なので高頻度にはならず、生存確認を兼ねた低ノイズ運用です。
launchd で日曜朝 8:30 に回す
~/Library/LaunchAgents/com.lily.cc-self-audit.plist の核心部分です。
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>8</integer>
<key>Minute</key><integer>30</integer>
<key>Weekday</key><integer>0</integer> <!-- 0=日曜 -->
</dict>
<key>Nice</key><integer>10</integer>
<key>LowPriorityIO</key><true/>
<key>ProcessType</key><string>Background</string>
Nice=10 と LowPriorityIO=true を入れているのは、claude -p が最大1200秒(FIX_TIMEOUT の既定値)走ることがあるためです。Mac のフォアグラウンド処理を圧迫しないよう、明示的にバックグラウンド扱いにしています。
RunAtLoad: false にしておかないと、plist を bootstrap するたびにスクリプトが走ります。週次ジョブは RunAtLoad を false にするのを忘れずに。
automation-health.sh との連携
~/.claude/scripts/automation-health.sh の「週次/月次バッチの死活」セクションが cc-self-audit.log の更新時刻を監視しています。
declare -a CRON_JOBS=(
"weekly cleanup-misc:$CLAUDE/logs/cleanup-misc.log:192"
"weekly env-audit:$CLAUDE/logs/env-audit-latest.md:192"
...
)
for entry in "${CRON_JOBS[@]}"; do
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
cc-self-audit.sh が走らなかった週は automation-health.sh が「192h超過」のWARNを出します。これで「監視の監視」が閉じています。
踏んだ落とし穴
-
ドットdirへの退避はゼロ効果 ―
.agents-backup/のようにドット始まりにしてもfind -name '*.md'は拾う。agents-archive/を~/.claude/の外に置くか、agents_loadedの計測式で除外パスを指定するかのどちらかが必要 - grep の誤検知で動的メトリクスが10倍に ― transcript には tool_result として「そのスクリプトのソース」が埋まっている。STOP_MARKER の完全マッチ+python3 の json parse で isMeta でない user ブロックのみに絞ること
-
自己申告だけで完了扱い ―
claude -pが「inject_bytes を圧縮しました」と言ってもcollect()で独立再計測しないと数値が確認できない。修正後のスコアを必ず--collect-onlyで取得してHISTORYに残す -
FIX_TIMEOUT を短くしすぎると修正が中断 ― 既定の1200秒(20分)で足りないケースはほぼなかったが、修正対象が多い時は途中でタイムアウトして
STILLが残ったままになる。その場合は🚨通知で人間が判断する -
launchd に PATH を明示しないと claude が見つからない ― plist の EnvironmentVariables に
/opt/homebrew/binと~/.local/binの両方を入れる。素のシェルとは PATH が別物
まとめ
- Claude Code の注入バイト数は
find ... | wc -cで計れる。計測しないと見えない劣化 - 閾値は
TH_INJECT_BYTES=40000/TH_AGENTS=60を起点に、自分の環境に合わせて env 上書きでチューニング - 修正は
claude -pに~/.claude内限定で任せ、直後に独立再計測して自己申告を検証する - 日曜朝 8:30 の launchd+
automation-health.shの監視二重化で「監視が死んでいる」を検知できる - 週1・結果1行なので通知疲れがない。緑が続くことが健康の証明になる
次回は、このループが検知した「肥えた MEMORY.md」を圧縮しながら索引の整合を壊さずに保つ方法を書く予定です。
Lily(@bokuwalily)― 個人開発者。Claude Code で自動化基盤を組みながら、iOSアプリやWebサービスを量産しています
- 制作物・記事は bokuwalily.com にまとめています🖥️
- AIで「寝てても回る仕組み」を作って月120万にした話は noteの有料記事 に💰
- OSS: github.com/bokuwalily 🐙
- 最新情報・お問い合わせは X @bokuwalily へ🌍
皆さんの ❤️ やシェアが励みになります!