---
title: "Claude Codeを任意の使用率で止めるガードを作っている"
description: "Claude Codeのrate_limitsはstatusLineにしか渡らず、実行を止められるhookには渡ってきません。この非対称をstate fileで橋渡しし、5時間枠・週次枠を任意のパーセンテージで止めるガードを試作した記録です。"
category: "AI"
tags: ["Claude Code"]
publishedAt: "2026-07-29"
lastmod: "2026-08-17"
---

Claude CodeはPro/Maxプランのセッションで、5時間枠と週次枠の消費率を `rate_limits` としてstatusLineに渡しています。ところが、この数値を読めるのはstatusLineコマンドだけで、実行を止められるhookにはこのデータが渡ってきません。読める場所と止められる場所が分かれているこの非対称を、state fileで橋渡しして任意の使用率でClaude Codeを止めるガードを試作しました。

```json
// statusLineの入力に含まれるrate_limits。公式ドキュメントの例示値
"rate_limits": {
  "five_hour": { "used_percentage": 23.5, "resets_at": 1738425600 },
  "seven_day": { "used_percentage": 41.2, "resets_at": 1738857600 }
}
```

[statusLineのドキュメント](https://code.claude.com/docs/en/statusline)によれば、この値は追加のAPI呼び出しなしにstatusLineへ渡ってきます。ここから先の作りを、詰まった順に紹介します。

この記事の末尾で残した宿題のうち、Teamプレミアムシートでの確認は済みました。2つのhookを実際に発火させたところまでを続きの記事に書いています。

::card[/posts/claude-code-usage-guard-live-fire]

## 使用量を自分で止める手段がない

Claude Codeの組み込みの停止は、ウィンドウを使い切る100%の壁だけです。60%まで来たら今日はここで止めておく、週の後半のために予算を残しておく、といった任意の地点で止める手段は用意されていません。

Team・Enterpriseプランではこれがさらに狭くなります。[公式サポート記事](https://support.claude.com/en/articles/12005970-manage-usage-credits-for-team-and-seat-based-enterprise-plans)によれば、usage creditsの購入や組織全体の支出上限の設定ができるのは**Owner・Primary Ownerだけ**です。座席ベースのEnterpriseプランのメンバーは上限に達すると「使用クレジットをリクエストする」リンクが表示されますが、**Teamプランのメンバーにはこのリンク自体がありません**。Teamプランで超過に気づいた時点でできるのは、管理者に直接頼むことだけです。

自分のペースで作業を配分したい、オンコール当番のために予備を残しておきたい、といった要求は珍しいものではありません。それを自分で設定する一次的な手段がないので、外側から作ることにしました。

## rate_limitsはどこから来るのか

`rate_limits` の出どころは公式には文書化されていません。Anthropicの[Rate limitsのAPIリファレンス](https://platform.claude.com/docs/en/api/rate-limits)が挙げているレスポンスヘッダーは `anthropic-ratelimit-{requests,tokens,input-tokens,output-tokens}-*` と `anthropic-priority-*` の系列だけで、`unified` や `5h` を含む名前は出てきません。ユーザー報告では `anthropic-ratelimit-unified-5h-*` というヘッダー名が挙がっていますが、これは公式文書にない名称で、`five_hour`(5時間枠)の由来としては辻褄が合う一方、`rate_limits` にもう1つ含まれる `seven_day`(週次枠)の由来までは説明できません。確かなのは、Claude Codeがこの数値をセッション中メモリに保持し、statusLineコマンドへの入力に含めて渡しているという観測できる挙動だけです。セッションのトランスクリプトには一切書き出されないので、ローカルのJSONLファイルを集計する既存ツールは実測ではなく推定しかできません。

## 読めるのはstatusLine、止められるのはhookという非対称

hookはClaude Codeの中で実行を止められる唯一の仕組みですが、hookに渡ってくる入力JSONには `rate_limits` が含まれていません。共通して入ってくるのは `session_id` や `transcript_path`、`hook_event_name` などで、使用率はそこにありません。

[hooksのリファレンス](https://code.claude.com/docs/en/hooks)を確認しても、共通の入力フィールドに `rate_limits` はなく、使用率データが乗るのはstatusLine向けの入力だけです。hookへの公開を求める声は [anthropics/claude-code#38380](https://github.com/anthropics/claude-code/issues/38380) のようなIssueとして立っていますが、Anthropicからの正式な回答が付いた形跡はなく、動きがないまま自動でクローズされています。読める場所と止められる場所がドキュメント上ではっきり分かれている以上、両者を橋渡しするのが現実的なルートになります。

既存のツールはこの分かれ目のどちらか片側に立っています。statusLine向けのトラッカーは `rate_limits` を読んで表示するだけで、[claude-code-limiter](https://github.com/howincodes/claude-code-limiter) や [clauditor](https://github.com/IyadhKhalfallah/clauditor) のような実行を止めるツールは、本物のウィンドウではなく自前のターン数やトークン数の集計で判定しています。自前集計だと、同じアカウントを複数マシンから使っている分の消費が抜け落ちます。Team・Enterpriseプランで一番困る場面がまさにそこなので、両者のあいだが空いていました。

橋渡しという発想自体には前例があります。[Zennの記事](https://zenn.dev/trust_delta/articles/claude-code-context-warning-001)は、コンテキスト使用率をstatusLineからファイル経由でUserPromptSubmit hookへ渡し、自動コンパクトの前に警告を出す仕組みを紹介しています。ただしこちらは警告止まりで実行はブロックせず、対象も `rate_limits` ではなく `context_window` です。

![Claude CodeのメモリからstatusLineとhookの両方へ入力が渡るが、rate_limitsを含むのはstatusLine側だけ。statusLine側のsensorがstate fileへ書き出し、hook側のgateがそれを読んでしきい値と比較する](/images/posts/claude-code-usage-rate-guard/read-and-block.svg)

`rate_limits` はstatusLineの入力にしか乗らず、hookの入力にはそもそも欄がありません。sensor役がstatusLine側でその値を一度state fileへ落とし、gate役がhook側でそれを読み直すことで、hookの入力にない情報をhookの判定に使えるようにしています。

## state fileで橋渡しする

sensor役のstatusLineスクリプトが `rate_limits` を毎回state fileへ書き出し、gate役のhookスクリプトがそれを読んで判定します。

```bash
# sensor: statusLineの入力からrate_limitsを取り出して書き出す
printf '%s' "$input" | jq -c '{
  five_hour: .rate_limits.five_hour,
  seven_day: .rate_limits.seven_day,
  observed_at: (now | floor)
}' > "$STATE"
```

gate側は書き出された `used_percentage` をしきい値と比較し、超えていればブロックします。しきい値は5時間枠・週次枠でそれぞれ独立に設定でき、環境変数が設定ファイルより優先されます。

state fileが古すぎる場合や `rate_limits` 自体が入っていない場合は、判定せずに通します。`claude -p` のようにstatusLineが動かない実行ではstate fileが更新されないので、古い数値で止めるより通す方を選びました。ガードの信頼性はstate fileの中身をどこまで信用できるかで決まるので、あいまいな場合は必ず通す側に倒しています。

欠落は例外的な事態ではありません。statusLineのドキュメントは `rate_limits` について「セッション中の最初のAPIレスポンスより後にのみ現れる」と書いており、さらに `five_hour` と `seven_day` はそれぞれ独立して欠落しうるとも明記しています。片方だけ入っている状態を想定していない実装だと、セッション開始直後に判定が壊れます。

## PreToolUseが要る理由

止める場所として `UserPromptSubmit` だけでは足りませんでした。このhookはターンの境界でしか発火しないので、一度始まったターンの中は止められません。サブエージェントへのファンアウトや長時間の自律的な作業は、新しいプロンプトを送らないままウィンドウを消費し続けます。

![1つのターンの中で、UserPromptSubmitはターン頭に1回だけ発火する。PreToolUseはBashやReadやTaskといったツール呼び出しのたびに発火するので、サブエージェントへのファンアウトを含めてターンの内側で何度も判定できる。走り出したターンの内側では新しいプロンプトが送られないため、UserPromptSubmitの出番がない](/images/posts/claude-code-usage-rate-guard/hook-timing.svg)

`PreToolUse` はツール呼び出しのたびにターンの内側で発火するので、暴走を実際に食い止められるのはこちらです。[パーミッションのドキュメント](https://code.claude.com/docs/en/permissions)には「Claude Codeがツール呼び出しを行うとき、`PreToolUse` hookは権限プロンプトより前に実行される」「exit code 2で終了するhookは、権限ルールが評価される前にツール呼び出しを止める」とあります。`bypassPermissions` はこのプロンプト自体を省略するモードなので、プロンプトより前に走る `PreToolUse` はモードに関わらず動きます。エージェントのファンアウトを監視する用途では、こちらのほうが実質的な効き目があります。

## 使用量を消費しない設計

ガードがブロックするのはAPI呼び出しの前なので、ブロックされたターンはAPIを叩きません。設計上、消費されるトークンはありません。

一つだけ落とし穴があります。`UserPromptSubmit`、`UserPromptExpansion`、`SessionStart` はhookの標準出力がそのままコンテキストに注入される特別なイベントです。通過するたびに何か出力していると、その分がプロンプトのたびに課金されます。ガードは通過経路で標準出力・標準エラーとも**0バイト**になるよう作り、テストスクリプトでそれを固定しています。

## Teamプランでの検証が残っている

`rate_limits` が届くことを確認できたのは、いまのところMaxプランのセッションだけです。公式ドキュメントが数値の提供を約束しているのはPro/Maxプランに対してで、動機になったTeamプランのプレミアム座席で同じデータが届くかどうかはまだ確かめていません。

ウィンドウを実際に使い切った状態でガードがどう振る舞うかも、本番のワークロードではまだ試していません。しきい値を下げれば疑似的には再現できますが、本物の消費で枠が詰まった瞬間の挙動は別マシンで検証する必要があります。常用に組み込む前に、この二つは確かめておくつもりです。