Claude Codeの日付ミスをHookで機械的に防いだ話
ある日、Claude Codeに「今日は何日?」と聞いたら、間違った答えが返ってきました。
原因を辿ると、直前に公開した記事のpubDate(公開日)をそのまま「今日」だと勘違いしていたことが分かりました。記事の日付が間違っていたから、今日の日付も間違えて答えた、という循環的なミスです。
「CLAUDE.mdに『日付はシステム提供のcurrentDateを見ろ』って書いてあるのに、なんで守られてないの?」と聞いたら、Claudeから返ってきたのは「保証はできない。文書に書くだけでは、次も同じミスが起きる可能性がある」という答えでした。
だったら文書じゃなくて、機械的に強制する仕組みを作ろう。そう思って、記事の日付ミスをブロックするHookを作った話をまとめます。
「今日は何日?」で変な答えが返ってきた
このブログでは、記事を公開するたびにpubDateというフィールドに公開日を書いています。
pubDate: '2026-07-04T20:00:00'
前日にこの記事を公開したばかりの状態で「今日は何日?」と聞いたところ、Claude Codeは記事に書いたpubDateを見て「今日は7/3」と答えました。実際は7/4でした。
よく考えると当たり前の話で、記事のpubDateはあくまで「その記事を公開した日」であって、「今日の日付」の保証にはなりません。それなのに、直近の作業内容から逆算して答えてしまいました。しかも記事のpubDate自体、Claudeが1日ズレて書いてしまっていたので、間違いの上に間違いを重ねる形になっていました。
CLAUDE.mdに書いても守られる保証はあるのか
このミスを指摘したとき、ふと気になって聞いてみました。
CLAUDE.mdに書いたことが全然守られてないけど、次も守られないんじゃないの。ちゃんと守られる保証はあるの?
全プロジェクト共通で読み込まれるグローバルのCLAUDE.md(~/.claude/CLAUDE.md)には、以前から「日付はシステムが提供するcurrentDateを最優先で参照する」と書いてあります。それなのに守られなかった。
Claudeから返ってきた答えは「保証はない。CLAUDE.mdはあくまで指示書であって、読み落としや思い込みが起きる可能性は残る」という説明でした。
さらに自分でも調べてみると、CLAUDE.mdは「毎回読んでもらう前提のお願い」でしかなく、コンテキスト(会話履歴や指示の分量)が多くなると後半の内容ほど忘れられやすいこともあるとのこと。同じミスを繰り返さないよう確実な仕組みを作るなら、文書ではなくHookが必要だということが分かりました。
ということで、Hookで日付ミスを防ぐことにしました。
💡 Hookの基本的な仕組み(
settings.jsonへの設定方法、デスクトップ版特有の注意点)はこちらの記事、exit codeとstderrがなぜClaudeに伝わるのかはこちらの記事で詳しく解説しています。
最初のアイデアには穴があった
最初にClaudeに提案されたHookのルールはこんな感じでした。
記事ファイルの pubDate が「今日の日付」と一致してるかチェックする
これで新規記事の日付ミスは防げそうです。でも、ふと引っかかって、Claude Codeにこう聞いてみました。
でもそれだと
pubDateだけでしょ?修正した場合もpubDateが今日の日付になっちゃうんじゃないの?修正した場合は更新日付を直したいんだっけ?
たしかにその通りでした。このブログのルールでは、pubDateは「初出公開日」で修正時に変更してはいけません。直すべきはupdatedDate(最終更新日)の方です。
「pubDateが今日じゃなければブロック」という単純なルールのままだと、何日も前に公開した記事をちょっと修正しただけで、毎回引っかかってしまう穴がありました。新規記事と既存記事の修正では、そもそも守るべきルールが違います。
新規記事と既存記事の修正を区別する設計に
そこで、Gitの管理状態を使って「新規記事」か「既存記事の修正」かを判定する設計に変更しました。
function isTracked(relPath) {
try {
execSync(`git ls-files --error-unmatch "${relPath}"`, { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
git ls-filesは「そのファイルがGitで追跡されているか」を教えてくれるコマンドです。これを使うと、以下のように振り分けられます。
| 状態 | 判定 | 適用するルール |
|---|---|---|
| まだ一度もcommitしていない新規ファイル | 未追跡(untracked) | pubDateが今日の日付でなければブロック |
| 過去にcommit済みの既存記事 | 追跡済み(tracked) | pubDateの変更をブロック/updatedDateが古ければリマインド |
既存記事のチェックでは、Gitの直近のcommit内容(HEAD)と比較して、pubDateが変わっていないかも見ています。
function getHeadPubDate(relPath) {
try {
const headContent = execSync(`git show HEAD:"${relPath}"`, { encoding: 'utf8' });
return extractField(headContent, 'pubDate');
} catch (e) {
return null;
}
}
pubDateが変わっていたらブロックします。Hookが「exit code 2」(処理を終了コード2で止める)を返すと、その内容がClaude Codeに伝わって作業がストップする仕組みです。一方updatedDateが今日になっていない場合は、ブロックまではせず、リマインドだけ出すようにしました(exit code 0 + 標準出力、こちらは処理を止めずにメッセージだけ届けます)。修正ついでにupdatedDateを直し忘れるのはよくあるミスですが、ブロックするほどではないと判断したからです。
3パターンをテストして実際に動かした
作ったあとは、ターミナルで単体テストをしてから本番に組み込みました。
- 新規記事・pubDateが今日と違う →
exit code 2でブロック - 既存記事・pubDateを変更しようとした →
exit code 2でブロック - 既存記事・pubDateは正しい・updatedDateだけ古い →
exit code 0+ リマインドのみ
3パターンとも狙い通りに動きました。実際に既存記事のpubDateを書き換えるテストをしたときは、こんなメッセージが返ってきました。
⚠️ 既存記事のpubDateを変更しようとしています。
変更前: 2026-05-28T12:00:00
変更後: 2026-07-04T12:00:00
pubDateは初出公開日なので変更しないでください。
記事を書くフローのどこでHookが発火するか
このHookは、記事ファイル(src/content/blog/*.md)をEdit・Writeするたびに毎回発火します。1回きりではなく、書いている途中も含めて何度も動きます。
① 新規記事をWriteで作成
→ 発火(この時点ではgit未追跡=「新規記事」判定)
② 検証・修正のたびにEdit
→ 発火し続ける(まだ未追跡なので「新規記事」判定のまま)
③ git add → commit → push
→ ここでHookは発火しない(Bashコマンドのため)
→ この瞬間から「追跡済み」に切り替わる
④(後日)誤字修正などでEdit
→ 発火(今度は「既存記事」判定でチェック)
pushするまでは「新規記事」ルールがずっと効き続け、pushした瞬間から「既存記事」ルールに切り替わります。git addした直後でcommitがまだの場合は、pubDateの変更チェックだけスルーされる(git show HEADが失敗するため)という細かい抜け道もありますが、いつもの「add・commit・pushを一気にやる」運用ではほぼ気にしなくていいケースです。
まとめ
- Claude Codeに「今日は何日?」と聞いたら、記事のpubDateから逆算した間違った答えが返ってきた
- 「CLAUDE.mdに書いても守られる保証はあるのか」と聞いたら、正直に「ない」という答えが返ってきた
- 単純な「pubDateが今日か」チェックだと、既存記事を修正するたびに誤爆する穴があった
git ls-filesで新規記事と既存記事の修正を判定し分ける設計にしてから、3パターンのテストで動作確認できた- ルールを文書に書くだけでは再発防止にならない。機械的にブロックする仕組みがあって初めて安心できる
CLAUDE.mdは便利ですが、あくまで「守ってくれたらラッキー」な指示書です。絶対に守ってほしいルールがあるなら、Hookで機械的に強制する方が確実だと実感しました。
📖 関連記事
- Claude Codeデスクトップ版でHookを設定する方法 → Hookの基本的な設定方法はこちら
- Hookのexit 2とstderrを基礎から解説 → なぜexit codeとstderrでClaudeに通知が届くのかを深掘り
- CLAUDE.mdの書き方と育て方【実運用ガイド】 → CLAUDE.md自体の育て方はこちら
このブログでは、Claude Codeの使い方やハマったポイントを 実際に試した体験ベース で発信しています。お気に入り登録お願いします 🙌