Claude Codeの禁止事項を番号で管理する — ルールIDシステム入門
Claude Codeの禁止事項を番号で管理する — ルールIDシステム入門
「禁止」は消える
Claude Codeに「git pushはしないで」と伝えた。しばらくは守ってくれた。しかし3日後、別のタスクの流れで実行されてしまった。
指示の「禁止」は、セッションをまたぐと薄れる。CLAUDE.mdに書いておいても、長いセッションの中で埋もれる。
この問題には2つのアプローチがある:
- 機械的に強制する:PreToolUseフックで物理的にブロックする
- 番号で管理する:ルールにIDをつけて例外処理を簡潔にする
どちらか一方ではなく、組み合わせることで「重要なルールは絶対に守られ、軽微なルールは柔軟に例外を認められる」状態になる。
アプローチ1:PreToolUseフックで機械的にブロック
最も重要な禁止事項(「絶対にやってはいけない操作」)は、フックで機械的にブロックする。Claudeの指示理解に依存しない。
フックの仕組み
実装例
// ~/.claude/hooks/safety-guard.js
const chunks = [];
process.stdin.on('data', d => chunks.push(d));
process.stdin.on('end', () => {
const input = JSON.parse(Buffer.concat(chunks).toString() || '{}');
const command = input?.tool_input?.command || '';
const BLOCKED = [
{ pattern: /\bgit\s+push\b/, reason: 'ポリシー違反: git pushはローカル専用' },
{ pattern: /rm\s+-rf\s+\/(etc|usr|var|bin)/, reason: 'システムパス削除禁止' },
{ pattern: /curl[^|]*\|\s*bash/, reason: '未検証スクリプト実行禁止' },
];
for (const { pattern, reason } of BLOCKED) {
if (pattern.test(command)) {
process.stdout.write(JSON.stringify({ decision: 'block', reason }));
return;
}
}
process.stdout.write(JSON.stringify({ decision: 'allow' }));
});
// settings.json
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "node %USERPROFILE%\\.claude\\hooks\\safety-guard.js"
}]
}]
}
}
フックでブロックされるルールは「例外なし」のもの。ここに追加するのは慎重にする。
アプローチ2:ルールに番号をつけて例外処理を簡潔に
フックで強制できないルール(コードスタイル・設計方針・作業フロー)は、CLAUDE.mdやrules/に書く。このとき、ルールに番号をつけると例外処理が格段に楽になる。
番号なしのルール管理(Before)
# CLAUDE.md
- テストは必ずtestcontainersを使う。モックDBは使わない
- APIキーはos.environ.getに依存せず、.envを直読みしてapi_key=に渡す
- 外部API呼び出しはtry/exceptで囲んでログを残す
例外を認めるとき:
ユーザー: 「さっきのDBの件だけど、今回のユニットテストはインメモリSQLiteでいい」
Claude: (どのルールへの例外か不明確。テストの方針なのか、DBの使い方なのか)
番号ありのルール管理(After)
# CLAUDE.md
## 開発方針(番号で参照可能)
- T01: テストは必ずtestcontainersを使う。モックDBは使わない
- T02: APIキーは.envを直読みしてapi_key=に明示的に渡す
- T03: 外部API呼び出しはtry/exceptで囲んでログを残す
例外を認めるとき:
ユーザー: 「T01を今回のユニットテストだけ例外にして。インメモリSQLiteでいい」
Claude: (T01が何かを正確に把握。このテストに限ってtestcontainersを使わなくていいと理解)
番号管理の効果
| ケース | 番号なし | 番号あり |
|---|---|---|
| ルールの参照 | 「さっき言ったDB関係のルール」 | 「T01」 |
| 例外の許可 | 「今回はあのルールは除外して」 | 「T01を今回だけ例外として」 |
| 例外の範囲 | 曖昧(全体?今回だけ?) | 明確(指定したルールのみ) |
| ルールの変更 | 「前に言ったことを変えて」 | 「T02を更新して」 |
特に例外処理が変わる。番号がないと「どのルールに対する例外か」をClaudeが文脈から推測しなければならない。番号があれば一対一に対応する。
番号の設計方針
ルールの性質でプレフィックスを分けると管理しやすい。
| プレフィックス | 対象 | 例 |
|---|---|---|
T |
テスト方針 | T01: testcontainers使用 |
G |
Git操作 | G01: featureブランチからPR |
A |
API・外部連携 | A01: .envを直読み |
C |
コードスタイル | C01: 型ヒント必須 |
プレフィックスは自分のプロジェクトに合わせて決める。重要なのは「同じ文字+番号で一意に参照できること」だ。
どちらをどちらに使うか
| ルールの性質 | 適切な手段 |
|---|---|
| 「絶対に実行してはいけない操作」 | PreToolUseフックで機械的ブロック |
| 「方針・スタイル(例外あり)」 | CLAUDE.mdに番号付きで記載 |
| 「プロジェクト固有の制約」 | プロジェクトCLAUDE.mdに番号付きで記載 |
フックは「例外なし」のルールだけに使う。例外を認める可能性があるルールをフックに入れると、フックを無効化しないと作業できなくなる。
まとめ
- PreToolUseフックは「絶対禁止」操作の機械的強制に使う
- CLAUDE.mdのルールには番号をつけると例外処理が一言で済む
- 番号はプレフィックスで性質を分類すると参照しやすい
- フック(機械的強制)と番号管理(柔軟な例外)は補完関係
この仕組みを5層ハーネス全体に組み込む設計は有料Book「Claude Code ハーネスエンジニアリング 実践Playbook」で解説している。
→ Claude Code ハーネスエンジニアリング 実践Playbook(Zenn)
感想や「このケースはどうする?」みたいな質問は、コメントに気軽に書いてもらえると嬉しいです。いいねも励みになります。
Discussion