AGENTS.mdには、ほとんどの設定ファイルには起きない特有の障害モードがある:何も壊さずに陳腐化するのだ。package.jsonが間違っていればビルドが失敗する。AGENTS.mdが陳腐化してもAIエージェントが古い前提で動き続けるだけで——エージェントが古いテストコマンドを使い続けたり、触らないはずのファイルに手を出したりするまで、誰も気づかない。
CI/CDはこれを解決する適切な場所だ。すべてのプッシュでAGENTS.mdをリンターに通す必要があるわけではないが、ドリフトを検知する場所としてCIは最適だ:pnpm-lock.yamlが変更されたとき、AGENTS.mdのインストールコマンドは更新されたか?新しい保護対象ディレクトリが追加されたとき、そこに記載はあるか?テストランナーがJestからVitestに変わったとき、AGENTS.mdは追いついているか?
このガイドでは、AGENTS.mdを実際のコードベースと同期し続けるためのGitHub Actionsワークフロー、Pre-commitフック、軽量なレビューボット設定を解説する。
核心的な問題:設定ファイルは変わるがAGENTS.mdは変わらない
実際のドリフトパターンはこうだ。チームが2スプリントでJestからVitestへ移行する。誰かがpackage.json、vitest.config.ts、全テストファイルを更新する。PRはCIを通過する。しかしAGENTS.mdには依然としてnpx jest --testPathPattern=と書いてあり、AIエージェントはその誤った情報を受け継ぐ。
修正策は、重要な設定ファイルが変更されたときにAGENTS.mdも更新されているか確認するCIチェックだ。AGENTS.mdの内容を検証するのではなく、更新が必要そうなときに検知する。
GitHub Actions:ドリフト検知
最も役立つチェックは驚くほど単純だ。PRがウォッチリスト上のファイルを変更するとき、AGENTS.mdも更新されているか確認する。されていなければ、著者にAGENTS.mdの更新か更新不要の確認コメントを求める。
# .github/workflows/agents-md-check.yml
name: AGENTS.md ドリフトチェック
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
agents-md-drift:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: ドリフトを引き起こす変更をチェック
id: drift-check
run: |
BASE=${{ github.event.pull_request.base.sha }}
HEAD=${{ github.event.pull_request.head.sha }}
# 変更時にAGENTS.mdのレビューをトリガーすべきファイル
WATCHED_PATTERNS=(
"package.json"
"pnpm-lock.yaml"
"package-lock.json"
"yarn.lock"
"Makefile"
"*.config.ts"
"*.config.js"
"docker-compose*.yml"
".env.example"
".env.ci"
)
PATTERN=$(IFS="|"; echo "${WATCHED_PATTERNS[*]}")
CHANGED=$(git diff --name-only "$BASE" "$HEAD")
AGENTS_CHANGED=$(echo "$CHANGED" | grep -c "AGENTS.md" || true)
CONFIG_CHANGED=$(echo "$CHANGED" | grep -E "$PATTERN" | grep -v "node_modules" || true)
if [ -n "$CONFIG_CHANGED" ] && [ "$AGENTS_CHANGED" -eq 0 ]; then
echo "drift_detected=true" >> $GITHUB_OUTPUT
echo "changed_files<<EOF" >> $GITHUB_OUTPUT
echo "$CONFIG_CHANGED" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "drift_detected=false" >> $GITHUB_OUTPUT
fi
- name: ドリフト検知時にPRにコメント
if: steps.drift-check.outputs.drift_detected == 'true'
uses: actions/github-script@v7
with:
script: |
const changedFiles = `${{ steps.drift-check.outputs.changed_files }}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## AGENTS.mdのレビューが必要です
このPRで以下の設定ファイルが変更されましたが、\`AGENTS.md\`は更新されていません:
\`\`\`
${changedFiles}
\`\`\`
**要対応:** 変更されたコマンド・パス・規則を反映するよう\`AGENTS.md\`を更新するか、更新が不要な理由をコメントに記載してください。`
});
これはハードな失敗ではなくコメントによる促しを提供する——AGENTS.mdには適切な判断だ。設定ファイルとAGENTS.mdの内容の関係は構文的ではなく意味的であり、更新が必要かどうかは人間が判断する必要があるからだ。
Pre-commitフック:構文と構造の検証
構造検証には、素早く動作して大きな声で失敗するPre-commitフックが、CIより役立つ。コミットが入る前にAGENTS.mdに期待されるセクションがあるかチェックする:
#!/bin/bash
# .git/hooks/pre-commit(またはpre-commitフレームワーク経由)
# scripts/validate-agents-md.sh としても利用可能
set -e
AGENTS_FILE="AGENTS.md"
if [ ! -f "$AGENTS_FILE" ]; then
echo "エラー: リポジトリルートにAGENTS.mdが見つかりません"
exit 1
fi
# 必須セクションのチェック
REQUIRED_SECTIONS=(
"## Commands"
"## Code Style"
)
MISSING=()
for section in "${REQUIRED_SECTIONS[@]}"; do
if ! grep -q "^${section}$" "$AGENTS_FILE"; then
MISSING+=("$section")
fi
done
if [ ${#MISSING[@]} -gt 0 ]; then
echo "エラー: AGENTS.mdに必須セクションがありません:"
printf ' %s\n' "${MISSING[@]}"
echo ""
echo "これらのセクションを追加するか、.git/hooks/pre-commitのREQUIRED_SECTIONSを更新してください"
exit 1
fi
# 明らかな陳腐化マーカーをチェック
STALE_PATTERNS=(
"npm install" # pnpmを使っている場合
"yarn add" # pnpmを使っている場合
"TODO:"
"FIXME:"
)
for pattern in "${STALE_PATTERNS[@]}"; do
if grep -qi "$pattern" "$AGENTS_FILE"; then
echo "警告: AGENTS.mdに陳腐化している可能性のある内容: '$pattern'"
echo "コミット前にレビューして更新または削除してください。"
# 警告のみ——これではコミットをブロックしない
fi
done
echo "AGENTS.mdの検証を通過しました"
exit 0
pre-commitフレームワーク(生フックよりも管理しやすい)で使う場合:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: agents-md-validate
name: AGENTS.md構造を検証
entry: scripts/validate-agents-md.sh
language: script
files: AGENTS.md
pass_filenames: false
CI:コマンドの実際動作を検証
最も信頼できるAGENTS.mdチェックは、最もシンプルでもある:実際に正しいと主張しているコマンドを実行する。
# .github/workflows/verify-agents-md-commands.yml
name: AGENTS.mdコマンドを検証
on:
push:
paths:
- 'AGENTS.md'
schedule:
- cron: '0 6 * * 1' # 毎週月曜——他のマージによるドリフトを検知
jobs:
verify-commands:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: AGENTS.mdからビルドコマンドを解析して検証
run: |
# AGENTS.mdからビルドコマンドを抽出
BUILD_CMD=$(grep -A1 "^\- \*\*Build\*\*:" AGENTS.md | head -1 | sed 's/.*`\(.*\)`.*/\1/')
if [ -z "$BUILD_CMD" ]; then
echo "AGENTS.mdからビルドコマンドを解析できませんでした"
exit 1
fi
echo "AGENTS.mdのビルドコマンドをテスト: $BUILD_CMD"
pnpm install --frozen-lockfile
eval "$BUILD_CMD"
- name: AGENTS.mdからテストコマンドを解析して検証
run: |
TEST_CMD=$(grep -A1 "^\- \*\*Test\*\*:" AGENTS.md | head -1 | sed 's/.*`\(.*\)`.*/\1/')
if [ -z "$TEST_CMD" ]; then
echo "AGENTS.mdからテストコマンドを解析できませんでした"
exit 1
fi
echo "AGENTS.mdのテストコマンドをテスト: $TEST_CMD"
eval "$TEST_CMD"
このアプローチは、AGENTS.mdのコマンドを一貫したフォーマット(- **Build**: `cmd`)で記載することを前提とするが、それ自体は良いプラクティスだ。週次スケジュールは、コードベースが変更されたが同じPRではAGENTS.mdが更新されなかったケースを検知する。
保護パスの検証
AGENTS.mdがエージェントが触ってはいけないディレクトリを列挙しているなら、それらのパスが実際に存在するか検証する:
#!/bin/bash
# scripts/verify-agents-md-paths.sh
AGENTS_FILE="AGENTS.md"
# "Never modify"や"Do not touch"パターンの行からパスを抽出
PROTECTED_PATHS=$(grep -oE '`[^`]+/[^`]+`' "$AGENTS_FILE" | tr -d '`' | grep "/" | sort -u)
MISSING_PATHS=()
for path in $PROTECTED_PATHS; do
# コマンドパターンに見える場合はスキップ
if [[ "$path" == *"$"* ]] || [[ "$path" == *"*"* ]]; then
continue
fi
if [ ! -e "$path" ]; then
MISSING_PATHS+=("$path")
fi
done
if [ ${#MISSING_PATHS[@]} -gt 0 ]; then
echo "警告: AGENTS.mdが存在しないパスを参照しています:"
printf ' %s\n' "${MISSING_PATHS[@]}"
echo ""
echo "これらは陳腐化した参照かもしれません。AGENTS.mdを確認して削除または更新してください。"
exit 1
fi
echo "AGENTS.mdで参照されているすべてのパスが存在します。"
スケジュールレビュー:週次の鮮度チェック
PRタイムのチェックに加え、週次のスケジュールワークフローでコマンド検証を実行すると、複数の小さいPRが蓄積することで生じるドリフトを検知できる。個々のPRは設定ファイル1つしか変更せず、AGENTS.mdの更新が不要に見える。しかし4つのPRの後、ドキュメントは明らかに間違いだらけになっている。
# .github/workflows/agents-md-weekly-review.yml
name: AGENTS.md週次ヘルスチェック
on:
schedule:
- cron: '0 9 * * 1' # 月曜9時UTC
workflow_dispatch:
jobs:
health-check:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- uses: actions/checkout@v4
- name: AGENTS.md完全検証を実行
id: verify
run: |
bash scripts/validate-agents-md.sh 2>&1 | tee verify-output.txt
echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: 検証失敗時にイシューを作成
if: steps.verify.outputs.exit_code != '0'
uses: actions/github-script@v7
with:
script: |
const output = require('fs').readFileSync('verify-output.txt', 'utf8');
// 同件のイシューが既に開いているか確認
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'agents-md-stale'
});
if (issues.data.length > 0) {
console.log('陳腐化AGENTS.mdのイシューは既に開いています');
return;
}
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'AGENTS.mdが陳腐化している可能性——週次チェックが失敗',
body: `週次のAGENTS.mdヘルスチェックが失敗しました。\n\n\`\`\`\n${output}\n\`\`\`\n\nAGENTS.mdを確認して必要に応じて更新してください。`,
labels: ['agents-md-stale', 'maintenance']
});
自動化すべきことの判断
AGENTS.mdメンテナンスのすべてを自動化すべきではない。判断表:
| チェック | 自動化すべきか | アプローチ |
|---|---|---|
| 必須セクションが存在するか | Yes | Pre-commitフック |
| コマンドが実際に動作するか | Yes | CIワークフロー(AGENTS.mdプッシュ時 + 週次) |
| 設定のドリフトが検知されるか | Yes | PRコメントボット |
| 参照パスが存在するか | Yes | CIスクリプト |
| 内容が正確で完全か | No | 設定PRでの人間によるレビュー |
| AIエージェントにとって有益なコンテキストか | No | 開発者の判断 |
自動化は機械的な正確さを担当する。コンテンツ品質は依然として人間の仕事だ。目標は「AGENTS.mdの更新を忘れた」という障害モードを排除すること——ファイルに何を書くべきかの判断を置き換えることではない。