cd /news/developer-tools/the-bigger-your-claude-md-gets-the-s… · home topics developer-tools article
[ARTICLE · art-71858] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

The Bigger Your CLAUDE.md Gets, the Slower Claude Code Runs — Measuring Context Injection Bytes Weekly to Catch Degradation Early

A developer found that bloated CLAUDE.md files and orphaned agent configurations were causing Claude Code to run slowly, with context injection exceeding 40KB and 99 archived agents still being loaded. To catch degradation early, they built a weekly self-audit script that measures injection bytes, agent count, stop-hook spam, frustration keywords, and tool errors, automatically attempting fixes when thresholds are breached.

read5 min views1 publishedJul 24, 2026

This is the next installment in my "Claude Code environment" series. In the previous post on the self-audit frustration loop, I wrote about a "never-ending improvement loop." This time I want to dig into what sits at the root of it: detecting and self-correcting context injection bloat.

At the start of every session, Claude Code unconditionally injects the full contents of rules/*.md

, ~/.claude/CLAUDE.md

, ~/CLAUDE.md

, and MEMORY.md

. Every time you add a rule, this byte count silently grows. In an audit on 2026-07-11, I found that total injection had exceeded 40KB, and 99 agents I thought I had archived were still being loaded — the main cause of my performance degradation. Since then, I've kept a permanent loop running that measures this automatically every week and attempts self-correction when thresholds are exceeded.

Adding rules to CLAUDE.md is a good thing. But "adding" only moves in one direction. After half a year, forgotten sections get duplicated, an archive you thought was compressed turns out to still be live, and files you moved to .agents/

keep getting loaded anyway.

The core issue is that degradation is invisible unless you measure it. Claude's gut feeling of "this seems sluggish" may be correct, but nobody routinely checks how many KB are being injected. I needed a mechanism that produces the numbers weekly and automatically attempts a fix when a human-defined threshold is exceeded.

The collect()

function in ~/.claude/scripts/cc-self-audit.sh

assembles the metrics. There are two axes: static and dynamic measurement.

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'

recurses into dot directories too. Even if you think "I moved them to agents-archive/

, so it's fine," if that directory lives under ~/.claude/agents/

as a dot directory, the files keep getting loaded — that's the trap (this is exactly how 99 agents survived on 2026-07-11).

Three metrics are aggregated from the transcripts (.jsonl

) since the last run.

STOP_MARKER = 'Stop hook feedback:\\n[~/.claude/hooks/self_audit_stop.sh]: '
FRUST_RE = re.compile(r'何回も言|いい加減にし|嘘つ|舐めんな|なんで治らん|最悪やろ|頭悪い')

stopspam counts only "lines that the Stop hook actually injected into the prompt." A naive grep also picks up the same string when it happens to appear inside a tool_result, inflating the number 6–10x over reality (I fixed exactly this on 2026-07-12, correcting 40 → 6). For frustration, only text blocks of type=user

that are not isMeta

— i.e., the human's own raw utterances — are counted.

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失敗/週

They're overridable via environment variables, so tuning them later never requires touching the script.

breaches()

lists the items that exceeded their thresholds and hands them to claude -p

.

BREACH=$(echo "$METRICS" | breaches)

if [ -z "$BREACH" ]; then
  log "GREEN"
  notify "✅ CC自己監査: 正常 ($METRICS)"
  exit 0
fi

When there are breaches, the prompt restricts fixes to inside ~/.claude

only (touching project code, secrets, or deleting plists is explicitly forbidden).

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' を実行し改善を数値で確認
..."

After claude -p

finishes, the script itself calls collect()

once more for an independent re-measurement. Claude's self-reporting is not trusted.

AFTER=$(collect)
STILL=$(echo "$AFTER" | breaches)

if [ -z "$STILL" ]; then
  notify "🔧 CC自己監査: 劣化検知→自己修正済み。前:[$BREACH] 後:全緑。"
else
  notify "🚨 CC自己監査: 自己修正後も残存 [$STILL]。要確認: $LOG"
fi

When everything is green, it sends ✅ a single line

; breach followed by a successful fix sends 🔧 with before/after numbers

; breaches remaining after the fix sends 🚨

— all to the #01_alerts

channel on Discord. Since it's weekly, it never gets noisy — a low-noise setup that doubles as a liveness check.

Here's the core of ~/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

and LowPriorityIO=true

are there because claude -p

can run for up to 1200 seconds (the default value of FIX_TIMEOUT

). To avoid squeezing the Mac's foreground work, the job is explicitly treated as background.

Note:If you don't setRunAtLoad: false

, the script runs every time youbootstrap

the plist. Don't forget to setRunAtLoad

tofalse

for weekly jobs.

The "weekly/monthly batch liveness" section of ~/.claude/scripts/automation-health.sh

watches the modification time of 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

If cc-self-audit.sh

didn't run in a given week, automation-health.sh

emits a WARN saying "over 192h." This closes the loop of "monitoring the monitor."

.agents-backup/

, find -name '*.md'

still picks it up. You either need to place agents-archive/

~/.claude/

, or specify an exclusion path in the agents_loaded

measurement expressionclaude -p

says "I compressed inject_bytes," the numbers can't be confirmed without an independent re-measurement via collect()

. Always fetch the post-fix score with --collect-only

and record it in HISTORY

STILL

remains non-empty. In that case, the 🚨

notification hands the decision to a human/opt/homebrew/bin

and ~/.local/bin

in the plist's EnvironmentVariables. launchd's PATH is a different beast from your interactive shell'sfind ... | wc -c

. TH_INJECT_BYTES=40000

/ TH_AGENTS=60

, then tune via env overrides to fit your own environmentclaude -p

scoped to ~/.claude

only, then automation-health.sh

gives you double-layered monitoring that can detect "the monitoring itself is dead"Next time, I plan to write about how to compress the "bloated MEMORY.md" this loop detects while keeping the index consistent throughout.

*Written by Lily — I ship iOS apps and automate my content stack with Claude Code.

Follow along: Portfolio · X · GitHub*

── more in #developer-tools 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/the-bigger-your-clau…] indexed:0 read:5min 2026-07-24 ·