
疲れていても、急いでいても、うっかりしていても、確実に検証が走る。そういう状態を、今回作る。
第8回で、2体目を立てた。worktreeで作業ツリーを分け、担当ファイルを割り振れば、2体が同じファイルを取り合う事故は防げる。だが体数が増えるほど、別の問題が出てくる。1体ずつ「テストは回した?型チェックは通った?」と聞いて回るのは、2体まではできても、5体・10体になると破綻する。
第7回で検証ループそのものは作った。テスト・ビルド・実行結果を確かめる仕組みは、もう手元にある。今回の課題は少し違う。その仕組みを、Claudeが自分から律儀に使うとは限らないという現実に、どう対処するかだ。
CLAUDE.mdは「お願い」、hookは「強制」
CLAUDE.mdに「必ずテストを実行してから報告してください」と書いておけば、たいていはその通りに動く。だが「たいてい」で止まる。
“Claude treats CLAUDE.md files and auto memory as context, not enforced configuration.”
筆者訳ClaudeはCLAUDE.mdファイルや自動メモリを、強制される設定としてではなく、あくまで文脈として扱う。
「文脈であって、強制設定ではない」。この一文が今回の出発点になる。CLAUDE.mdは、有能な同僚への申し送りメモに近い。読んでもらえることが前提で、絶対に守られる保証はない。
コンテキストが長くなったとき、途中で圧縮されたとき、あるいは単純に見落としたとき。CLAUDE.mdの「お願い」は、条件次第で読み飛ばされる。確実に守らせたいルールは、hookに移す。
hooksの仕組み:いつ・何を実行するか
hookは、ツール呼び出しやセッションの節目で決定的に実行されるコマンドだ。Claudeの判断を経由しない。だから守られなかった、が起きない。
hookの戻り値には意味がある。終了コード2はブロッキングエラー、0は成功。ここを取り違えると、意図せず処理を止め続けたり、逆に何もチェックしていないのに素通りさせたりする。
イベントの種類、終了コードの扱い、設定ファイルでの書き方をまとめた公式ドキュメント。最初の1本を書く前に一読する価値がある。
自己検証ループを作る:「見えている」と「動くと分かっている」
第7回で作った検証ループを、hookで自動起動に変える。Editツールが走ったら型チェックを、コミット前にはテストを。人間が「やって」と頼まなくても、実行された事実だけが残る。
“Testing turns a skill that seems to work into one you know works.”
筆者訳テストをすることで、『動いているように見える』スキルが、『動くと分かっている』スキルに変わる。
この指摘はもともとSkillsの品質について語られたものだが、自己検証全般に当てはまる。「動いているように見える」と「動くと分かっている」の間には、実行して確認したという1手順の差しかない。hooksは、その1手順を省略できなくする仕組みだ。
$(Editツール実行後、hookが自動起動)npm testFAIL test/upload.spec.ts(1 failing)。Claudeへの報告はブロックされました。
hookが強制するのは、テストやlintのような機械的な合否判定だ。もう一段別の視点からの確認を足すなら、作業を完了扱いにする前に、フレッシュなコンテキストのサブエージェントにdiffをレビューさせ、見落としを報告させるとよい。組み込みの /code-review スキルは、この型で動く。
“Before treating a task as done, have a subagent review the diff in a fresh context and report gaps.”
筆者訳完了扱いにする前に、フレッシュなコンテキストのサブエージェントにdiffをレビューさせ、見落としを報告させる。
実装したセッションのまま見直すと、書いた本人としての思い込みが残りやすい。コンテキストを切り替えたサブエージェントに見せることで、その思い込みを外して読める。
ただし、このレビューには注意が要る。欠陥を探せと頼まれたレビュアーは、仕事が健全なときでも、たいてい何かしら見つけてくる。
“A reviewer prompted to find gaps will usually report some, even when the work is sound, because that is what it was asked to do. Chasing every finding leads to over-engineering... Tell the reviewer to flag only gaps that affect correctness or the stated requirements, and treat the rest as optional.”
筆者訳欠陥を探すよう頼まれたレビュアーは、それが役目である以上、仕事が健全なときでもたいてい何かを報告してくる。指摘を全部追いかければ、過剰な作り込みに向かう。correctness(正しく動くか)と明示された要件に影響する欠陥だけを拾うようレビュアーに伝え、それ以外は任意項目として扱う。
見つかった指摘を全部拾おうとしないこと。correctnessと要件に効く指摘だけを採用し、残りは任意項目として扱う、という線引きを先にレビュアーへ伝えておく。
人間とAIに同じ品質バーを適用する
経験や肩書きに関係なく、同じ確認を通す。これが事故を減らす発想の核だ。
離陸前のパイロットのチェックリストを思い出してほしい。操縦士が新人でもベテランでも、チェックリストの項目は同じだけ読み上げる。
視点
チェックリストは信頼の代わりではない
ベテランのほうが間違えないから確認を省いていい、とはならない。むしろ慣れているほど確認を飛ばしたくなるからこそ、チェックリストは全員に等しく適用される。
hooksも同じだ。Claudeが優秀になったから確認は要らない、という理屈は成立しない。確認の有無は、能力の高さと関係のない話として設計する。
- 編集直後に型チェックを自動実行
- 失敗したら報告そのものをブロックする
- 何を確認したか、ログに残す
- チェックを「参考情報」として表示するだけで止めない
- 失敗しても終了コードを返さない
- 範囲が広すぎて、正常な操作まで頻繁にブロックする
よくある誤解と罠
- hookを入れれば検証ループは自動で万全になるという誤解。hookは「強制する手段」であって、何をチェックするかの中身は第7回の検証ループ設計がそのまま生きる
- 終了コードの意味を取り違える。0と2を逆に書いてしまい、通ってほしい操作を止め続けたり、止めたい操作を素通りさせたりする
- CLAUDE.mdに書けば十分だと思い込む。確実に守らせたいルールと、文脈として伝えれば足りるルールを、同じ場所に混ぜて書いてしまう
- サブエージェントによるレビューの指摘は、全部拾わなければならないという思い込み。欠陥を探すよう頼まれたレビュアーは、仕事が健全でも何かしら見つけてくる。correctnessと要件に影響する指摘だけを拾い、残りは任意として扱う
今日のまとめ
- CLAUDE.mdは「お願い」、hooksは「Claudeの判断に関係なく強制実行」される仕組み
- 確実に守らせたい検証は、hookのPreToolUse・PostToolUse・Stopに乗せる
- 人間とAIに同じ品質バーを適用するとは、能力の高さで確認の有無を変えないということ
- diffのレビューはフレッシュなコンテキストのサブエージェントに任せる。ただし指摘は全部拾わず、correctnessと要件に効くものだけを採用する
明日のアクション
自分のリポジトリに、Editツール実行後に型チェックまたはlintを自動で走らせるPostToolUseフックを1つ設計してください。
実際に設定ファイルへ書き込む前に、まず紙かメモで「いつ・何を・失敗したらどうするか」を1行ずつ書き出すこと。終了コードの扱い(0/2)を必ず明記してください。
次は、体数が増えるほど重くなる承認プロンプトそのものを扱う。第10回、許可を設計する。