Claude Codeとgraphql-inspectorで、スキーマの破壊的変更をPRで止める
graphql-inspectorのdiffがBreakingと判定したPRを止め、@claudeにフィールド削除を@deprecated経由へ直させる流れを、GitHub Actionの設定とCLAUDE.mdの規約で組みます。
GraphQLのスキーマからフィールドを消すと、そのフィールドを使っているクライアントが壊れます。Claude Codeにスキーマを触らせる場合、型エラーを消す一番近道は「邪魔なフィールドを削除する」ことなので、この事故は起きやすい部類です。
graphql-inspectorは、旧スキーマと新スキーマを比べて、変更を破壊的(Breaking)・危険(Dangerous)・安全(Safe)に分類するツールです。Breakingが1件でもあれば、CLIのプロセスは失敗で終わります。これをGitHub ActionでPRの必須チェックにすれば、削除を含む変更はマージ前に止まります。
この記事では、止まったPRをClaude Codeに直させるところまでを組みます。直し方の方針は「削除せず、まず@deprecatedを付ける」です。
破壊的変更の判定はgraphql-inspector、直す役はClaude
役割は2つに分けます。判定は決定論的なツールに任せ、Claudeには判定結果を材料にした修正だけをさせます。
役割の分担
graphql-inspector
旧スキーマと新スキーマを比べ、変更を3種類に分類します。Breakingがあればチェックを失敗させます。
Claude Code GitHub Action
PRコメントの
@claudeを受けて、失敗の中身を読み、スキーマを直してpushします。CLAUDE.md
「フィールドは削除せず
@deprecatedを付ける」という規約を、Claudeの修正方針として固定します。
スキーマから型を生成する側の設定は、GraphQL Codegenの生成物を触らせないdenyとhook設定が扱っています。こちらは生成物の保護で、本記事は「スキーマそのものの互換性」を検査する別の層です。
diffコマンドが出す3分類を確認する
CLIは@graphql-inspector/cliとgraphqlをグローバルに入れて使います。インストール手順では、graphqlはpeer dependencyとして合わせて入れる形で案内されています。
npm i --global @graphql-inspector/cli graphql
graphql-inspector diff OLD_SCHEMA NEW_SCHEMAOLD_SCHEMAとNEW_SCHEMAには、ファイル、URL、github:user/repo#master:schema.graphqlのような参照を渡せます。PRの検査では、基準ブランチ側のスキーマが旧、手元のファイルが新です。
公式サイトにある出力例を見ると、分類の感覚がつかめます。次のスキーマの組が例として載っています。
# 旧
type Post {
id: ID!
title: String
createdAt: String
modifiedAt: String
}
type Query {
post: Post!
posts: [Post!]
}
# 新
type Post {
id: ID!
title: String!
createdAt: String
}
type Query {
post: Post!
}この組に対するdiffは、3件の変更を検出します。Post.modifiedAtの削除とQuery.postsの削除がBreaking、Post.titleがStringからString!になる変更がSafeです。フィールドの削除は、使っているクライアントがあれば確実に壊れるためBreakingになります。
出力の読み方は、次の3点で足ります。
- Breaking: クライアントが壊れうる変更。1件でもあれば失敗
- Dangerous: 壊れるとは限らないが注意が要る変更
- Safe: 互換性を保つ変更
@deprecatedを経由させるルールを知っておく
ここで押さえたいのが、@deprecatedが付いたフィールドの扱いです。公式のルール説明では、非推奨フィールドの削除も、既定ではBreakingとして数えられます。suppressRemovalOfDeprecatedFieldルールを有効にすると、それがDangerousに下がり、チェックを失敗させなくなります。
つまり、スキーマ変更は2回のPRに分かれます。
フィールドを消すまでの2段階
- 1
PR1: @deprecatedを付ける
フィールドは残したまま、
@deprecated(reason: "...")を付けます。クライアントが移行する猶予が生まれます。 - 2
PR2: 移行後に削除する
suppressRemovalOfDeprecatedFieldを有効にしたチェックでは、非推奨フィールドの削除はDangerous扱いになり、チェックが通ります。
ルールを有効にしないチェックのままだと、PR2も止まります。非推奨を経由した削除だけを通したいなら、このルールがその合図になります。CLIでは--ruleで指定します。
graphql-inspector diff \
"git:origin/main:./schema.graphql" schema.graphql \
--rule suppressRemovalOfDeprecatedFieldgit:origin/master:./schema.graphqlの形は、CI用の案内ページに載っている書き方です。ブランチ名は自分のリポジトリの基準ブランチに合わせます。
@deprecatedを付けた時点の変更が、手元の環境でどの分類になるかは、公式ページに例がありません。PR1の想定結果を覚えるより、一度手元で旧新の組を作ってdiffを流す方が確実です。
PRの必須チェックにするワークフロー
GitHub Action側は、schema入力に基準ブランチのスキーマをブランチ名:ファイルパスの形で渡します。
name: Schema Check
on:
pull_request:
jobs:
inspector:
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@v6
- uses: graphql-hive/graphql-inspector@master
with:
schema: 'main:schema.graphql'
rules: |
suppressRemovalOfDeprecatedField
annotation-level: breaking公式の例はuses: graphql-hive/graphql-inspector@masterで書かれています。実際に使う際はタグ指定などをチームの方針に合わせます。checksの権限に書き込みが要る点は、公式が明記しています。
使える入力のうち、運用に効くものを挙げます。
| 入力 | 役割 | 既定値 |
|---|---|---|
schema | 役割旧スキーマの参照 | 既定値なし |
fail-on-breaking | 役割Breakingで失敗させる | 既定値true |
approve-label | 役割破壊的変更を承認済みとして通すラベル | 既定値approved-breaking-change |
annotation-level | 役割PR差分に出す注釈の最小重大度 | 既定値all |
rules | 役割組み込みルールや自作ルール | 既定値なし |
approve-labelは、意図した破壊的変更を人が承認して通すための出口です。ここは人間が付ける前提にして、Claudeにラベルを付けさせない運用が安全です。
annotation-levelをbreakingにすると、インライン注釈がBreakingだけになります。チェックのサマリには全変更が出るので、情報は減りません。
出力にはbreaking-changes、dangerous-changes、safe-changes、changes(総数)があり、後続のステップで使えます。この出力を次節のClaudeへの依頼に渡す方法もあります。
止まったPRを@claudeに直させる
Claude Code GitHub Actionは、ワークフローにpromptが無いとき、コメントの@claudeを待つ対話モードで動きます。公式の例は、issue_commentとpull_request_review_commentで起動する形です。
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--max-turns 8"actions: readがあるので、Claudeはチェックの結果をPR上で読めます。--max-turnsは1回の実行の上限で、コスト面の保険になります。
Inspectorのチェックが失敗したら、PRにこう書きます。
@claude graphql-inspectorのチェックがBreakingで失敗しています。
削除したフィールドを元に戻し、代わりに @deprecated(reason: "...") を付けてください。
スキーマ以外のファイルは変更しないでください。Claudeは同じブランチにコミットを積みます。チェックが再実行され、Breakingが消えれば通ります。ここで1つ落とし穴があります。github_tokenにsecrets.GITHUB_TOKENを渡すと、その名義のコミットではワークフローが動きません。公式のトラブルシューティングは、渡している場合は外してClaude GitHub Appとして動かすか、カスタムのアプリトークンを使うよう案内しています。外さないと、Claudeが直しても再検査が走らず、チェックは赤いままです。
もう1つ、@claudeを人が書く運用にしておくのが無難です。ボットのアクターは、allowed_botsに入れない限りActionが拒否します。Inspectorのチェック失敗を契機に別のボットが@claudeと書く構成は、この制約で止まります。
CLAUDE.mdに互換性の規約を書く
毎回コメントで方針を伝えるより、CLAUDE.mdに置く方が再現性があります。ClaudeはリポジトリのルートのCLAUDE.mdを読んで、PR作成時や依頼への対応時に従います。
## GraphQLスキーマの互換性
- `schema.graphql`のフィールド・引数・enum値は、削除も型の変更もしない
- 不要になったフィールドには `@deprecated(reason: "代替と移行先を書く")` を付けて残す
- 削除は、非推奨化から移行が済んだあとの別PRで行う
- スキーマを変更したら `graphql-inspector diff` を実行し、Breakingが0件であることを確認する
- 承認ラベル `approved-breaking-change` は付けない(人が付ける)最後の2行が運用の要です。Claude自身がローカルでdiffを実行し、Breakingが0件になるまで直す反復が、CI待ちの往復を減らします。CLAUDE.mdを短く保つコツは、VitestのCLAUDE.mdの書き方にも通じます。公式の指針でも、CLAUDE.mdは毎回読まれるため簡潔さが勧められています。
運用で迷う分岐
Breakingを意図的に出したいとき
メジャー変更や、利用者がいないと分かっているフィールドの削除では、承認ラベルが出口です。fail-on-breaking: falseで全体を甘くするより、PR単位でラベルを付ける方が記録も残ります。
実際に使われているかで判定したいとき
considerUsageルールとonUsageに渡すスクリプトで、「直近30日で使われたか」のような独自判定を組めます。公式のサンプルは、使用実績を調べる関数をtype・field・argumentごとに呼ぶ形です。使用状況の取得先は自前で用意する必要があります。
破壊的変更の中身をClaudeに渡したいとき
Actionのbreaking-changes出力は、メッセージ文字列のリストです。たとえば「Field Xyz was removed from object type Abc」という形で出ます。これをプロンプトに差し込めば、@claudeのコメントを人が書かなくても依頼文を組めます。ただし自動化モードでは、Claudeが使えるツールを--allowedToolsで明示する必要があります。
GraphQL APIをClaudeに叩かせたい場合
スキーマの互換性検査とは別の話で、実行時にClaudeがGraphQLのAPIを呼ぶ構成はGraphQL MCPサーバーの使い方にあります。
設定の前に確認すること
- 基準ブランチに
schema.graphqlのようなファイルがあること。コードファーストの場合は、graphql-inspector introspectなどでスキーマファイルを出力する工程が要ります - Inspectorのチェックをブランチ保護の必須チェックに登録していること。登録しないと、Breakingが出てもマージできてしまいます
- Claudeに触らせるのはスキーマと呼び出し側の修正までにし、承認ラベルの付与と本番デプロイは人間の操作に残すこと
GitHub Actionsのワークフロー自体をClaude Codeに書かせる手順は、ワークフローYAMLの作り方が詳しいです。
まとめ
判定をInspectorに、修正をClaudeに分けると、「消して型エラーを黙らせる」変更はPRの時点で赤になります。@deprecatedを挟む2段階の流れは、suppressRemovalOfDeprecatedFieldルールを有効にしたときだけ、PR2が通る形になります。承認ラベルだけは人の手に残してください。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。