研究紹介
Claude Code の Co-Authored-By を消す設定はどれが効くのか
· 読了まで約11分
目次
Claude Code がコミットに署名を足す件で、Hacker News に同じ日に 2 本のスレッドが立った。片方は 205 コメント、話題は commit message に claude.ai のセッション URL が付くこと。もう片方は「Claude Code を co-author に入れるのはもうやめさせた」。どちらのスレッドにも、Google の 1 ページ目に並ぶブログにも、実際に測った数字は 1 つも無い。
claude code commit message co author の 1 位は 11 ヶ月前の Reddit で、ベストアンサーは公式ドキュメントへのリンクだった。
そこでバイナリから設定スキーマを取り出し、88 回走らせて突き合わせた。結論を先に書くと、どの記事も勧めているキーは deprecated で、しかもそれが 1 行で全部を消せる唯一のキーだった。後継として案内されている書き方には、エラーも警告も出さずに何も起きない道が 3 つある。
設定キーは 5 つしかない
Claude Code はコンパイル済みのバイナリで配られるが、設定スキーマは zod で書かれていて、各フィールドの .describe() がそのままファイルに残っている。2.1.252 から抜き出すとこうなる。
attribution.commit Attribution text for git commits, including any trailers.
Empty string hides attribution.
attribution.pr Attribution text for pull request descriptions.
Empty string hides attribution.
attribution.sessionUrl Whether to append the claude.ai session link to commits
and PRs created from web or Remote Control sessions
(default: true).
includeCoAuthoredBy Deprecated: Use attribution instead. Whether to include
Claude's co-authored by attribution in commits and PRs
(defaults to true)
includeGitInstructions Include built-in commit and PR workflow instructions in
Claude's system prompt (default: true)
関係するのはこれで全部だ。世に出回っている記事がほぼ例外なく挙げる includeCoAuthoredBy には、はっきり deprecated と書いてある。代わりに使えと言われているのが attribution オブジェクトで、フィールドは 3 つ。そのうち 1 つは、まだ見たことがない人のほうが多いトレーラーを担当している。
測り方
1 run ごとに使い捨ての git リポジトリを作る。git init して 1 コミット置き、変更を 1 つステージした状態から、非対話で 1 ターンだけ回す。
claude -p "Commit the staged change." --safe-mode \
--settings '{"attribution":{"commit":""}}' \
--model sonnet --permission-mode bypassPermissions
git log -1 --format=%B
測定が成立するかどうかは --safe-mode にかかっている。自分の ~/.claude/CLAUDE.md には「署名を付けるな」と書いてあり、そのままだとベースラインが全滅する。safe mode は user と project の CLAUDE.md・settings・プラグイン・フックをまとめて無効にするので、残る変数はコマンドラインで渡したものだけになる。その状態でも --settings が効くことは、attribution.commit に目印の文字列を入れて確かめた。
Add line two to app.txt
X-Probe: HELLO
コミット側は 1 条件あたり 5 run、PR 側は 3 run、合わせて 88 run。結果は条件ごとに全部出るか 1 つも出ないかに割れて、平均を取るようなブレは出なかった。
結果
| 設定した内容 | Co-Authored-By が残った run |
|---|---|
| 何もしない(ベースライン・sonnet) | 5/5 |
| 何もしない(ベースライン・opus) | 5/5 |
includeCoAuthoredBy: false | 0/5 |
attribution: { commit: "" } | 0/5 |
attribution: { commitTrailers: false } | 5/5 |
attribution: { pr: "" } | 5/5 |
includeGitInstructions: false | 0/5 |
| プロンプトに 1 行で禁止を書く | 0/5 |
太字にした 2 行と、表には出ていない併記の挙動を順に説明する。
罠 1: commit と pr は別のスイッチ
deprecated のキーは commit と PR の両方を面倒見る。新しいキーは見ない。
PR 側を実際にプルリクエストを立てずに測るため、各セッションに「システムプロンプトは PR 本文の末尾に何を付けろと言っているか、逐語で出せ」と聞き、🤖 Generated with [Claude Code] の行を再現したかどうかで採点した。
| 設定した内容 | PR の署名が残った run |
|---|---|
| 何もしない(ベースライン) | 3/3 |
attribution: { pr: "" } | 0/3 |
attribution: { commit: "" } | 3/3 |
includeCoAuthoredBy: false | 0/3 |
コミット側の表と並べると形がきれいに対称になる。commit: "" だけ書くと PR には 3/3 で署名が残り、pr: "" だけ書くとコミットに 5/5 で残る。両方を 1 行で落とせるのは deprecated のキーだけだった。
移行でいちばん踏みやすいのがこれだ。deprecated と書かれているのを見て 1 行を 1 行に置き換えれば、手元には attribution: { commit: "" } が残り、プルリクエストのほうは Claude が書いたと言い続ける。
罠 2: commitTrailers は書いても読まれない
バイナリを読んでいると、commit / pr / sessionUrl と同じ集合に commitTrailers という 4 つ目の名前が入っている。いかにも探していたスイッチに見えるが、これはユーザーには開いていない。
attribution: f({
commit: i().optional(),
pr: i().optional(),
sessionUrl: q().optional(),
}).passthrough()
スキーマが .passthrough() なので、attribution の中の知らないキーは弾かれもしなければ読まれもしない。バリデーションエラーも警告も出ず、そして何も起きない。実測ではトレーラーが 5/5 で残った。commitTrailers は、管理者が組織全体で署名を切ったときに managed policy の正規化コードが内部で立てるフラグで、自分の settings に書くのは、直ったように見えて何も起きない操作でしかない。
しかも Claude Code は非対話モードだと、検証に落ちた設定ファイルを黙って無視する。この場合はそもそも検証に落ちない。
罠 3: 新旧を併記すると古いほうが無視される
deprecated のキーと新しいキーを両方書くと、新しいほうが完全に勝つ。includeCoAuthoredBy: false と attribution.commit: "X-Probe: kept" を同時に渡した条件では、目印の文字列が 5/5 のコミットに出た。false は一度も参照されていない。
解決の順序はバイナリの中に見える。commitTrailers が boolean ならそれ、次に commit か pr のどちらかが定義されていればそれ、最後に includeCoAuthoredBy。つまり attribution に触れた瞬間、古いキーは読まれなくなる。移行を半分でやめた状態は、どちらか片方だけの状態より悪い。
結局どう書けばいいか
~/.claude/settings.json に、3 つのフィールドを全部書く。
{
"attribution": {
"commit": "",
"pr": "",
"sessionUrl": false
}
}
実測ではコミットにトレーラーが出た run が 0/3、PR の署名を必須だと答えた run が 0/3 で、3 run とも NONE と返した。1 行で済ませて壊れたときに考え直したいなら、2.1.252 では "includeCoAuthoredBy": false が deprecated の表示ごと今も両方に効く。
CLAUDE.md に書く方式は効くのか
いちばん確かめたかったのはここだ。Reddit のスレッドには「CLAUDE.md に書いても付いてくる」という声が並んでいて、このブログのリポジトリは 1 ヶ月ずっとその方式で回っている。
結果は全部効いた。「コミットと PR の説明に Claude の署名を付けるな」という 1 行だけで、20/20 でトレーラーが消えた。この 20 回には、126 行の規約ドキュメントの 60 行目に埋めてログや migration の規則 90 個と競合させた条件と、モデルを sonnet から opus に上げた条件が入っている。日本語で同じルールを書いてある自分の ~/.claude/CLAUDE.md も 0/5 だった。
つまり失敗のほうを再現できなかった。ただしここで言えることは「CLAUDE.md は効く」より狭い。今回の run はすべて、新しいセッションでの 1 ターンだけだ。50 ターン先まで進んだ会話の中の指示は別の実験で、それは走らせていない。Reddit の苦情が指しているのはたぶんそちらだろう。
2 つの仕組みの差もそこに出る。設定キーはシステムプロンプトから指示そのものを消すが、CLAUDE.md は競合する指示を足してモデルに天秤にかけさせる。前者は薄まりようがない。ちなみに両者が矛盾したときにどちらが勝つかは別途測ってあり、そのときは CLAUDE.md が 11 対 0 で勝っている。
コストの面でも設定キーに分がある。CLAUDE.md の記述は毎セッションの起動時に前置きとして読み込まれるが、設定キーは 0 トークンで済む。
git の手順ごと落とす手もある
includeGitInstructions: false でもトレーラーは消えて 0/5 だった。署名だけでなく、コミットと PR のワークフロー全体をシステムプロンプトから削るからだ。ステージングの作法もメッセージの書き方の指針も gh pr create のテンプレートも一緒に消える。CLAUDE.md に自前のコミット手順を持っていて、組み込みの手順が邪魔をしているなら選ぶ理由がある。トレーラーを 1 行消すためだと、巻き添えが大きすぎる。
Session URL のトレーラーは別扱い
205 コメントのスレッドの発端は、もっと新しいトレーラーのほうだ。
Claude-Session: https://claude.ai/code/session_01...
これには専用のスイッチ attribution.sessionUrl があり、既定値は true。ただし今回は測れなかった。88 run のどこでも、claude -p のセッションはこのトレーラーを 1 度も出さなかった。スキーマの説明文は「web または Remote Control のセッションから作られた commit と PR」と書いていて、これはスレッドが前提にしている範囲より狭い。ヘッドレスのローカル実行はそこに入らない。確かめたのは、sessionUrl: false を混ぜても上のコミットと PR の挙動が壊れないところまでだ。
測っていないこと
バージョンは 2.1.252 の 1 つだけ、計測日は 2026-09-01。全 run が非対話の 1 ターンなので、長いセッションについては何も言えない。PR 側の結果は実際にプルリクエストを立てて得たものではない。モデルに自分のシステムプロンプトの内容を答えさせた代理指標で、署名の行を逐語で再現したかどうかで採点している。そして deprecated の表示は、1 行で済む書き方に期限があることを意味する。includeCoAuthoredBy は今日は効くが、フィールドの説明文はいつまでもではないと言っている。