ブログ一覧

changesets を AI エージェントに書かせると変更履歴が読める物になる

8 つのサイトを 1 つのモノレポで運用していて、変更のほとんどを AI エージェント(Claude Code)に PR として作らせています。エージェントの PR は 1 日に何本も merge されるので、しばらく経つと「あのサイトはいつ何が変わったのか」を私自身が追えなくなりました。git log はあります。ただ、commit のメッセージは「何をしたか」の一行で、「なぜ、何が壊れていて、何を捨てたか」はそこに無い。この記事は、changesets を入れて、エージェントに変更記録を書かせることで、変更履歴が人間の読める物になった記録です。別の bot のリポジトリでも同じ形にしていて、そちらで踏んだ罠も併記します。

changesets が何をする道具か

changesets は、パッケージのバージョンと CHANGELOG を管理する道具です。PR に .changeset/<名前>.md という小さなファイルを添え、そこに「どのパッケージが、どの種類の変更(patch / minor / major)で、何が変わったか」を書きます。main に merge されたこのファイルが溜まると、GitHub Actions(`changesets/action`)が「Version Packages」という PR を自動で立て、それを merge した時点で各パッケージの version が上がり、CHANGELOG.md に記録が集約されます。

.changeset/
  config.json                 # changelog の生成に @changesets/changelog-github(PR と commit へのリンク付き)
  restore-dropped-emdash-patch-hunks.md   # PR に添える変更記録(merge 後、Release PR で消える)
.github/workflows/
  release.yml                 # main への push で changesets/action を実行。Release PR を作る
apps/
  <site>/CHANGELOG.md         # サイトごとに集約された履歴
  <site>/package.json         # version が上がる。全部 private なので npm には出さない

モノレポ側では、apps/* はすべて private: true で npm には公開しません。それでも使う理由は、バージョン番号ではなく「サイトごとの CHANGELOG に、人間が読める変更記録を自動で集める」ことです。2026-04 に入れてから、Release PR は 18 回 merge されています。

エージェントに何を書かせるか

変更記録の書き方を、エージェントの手順書(CLAUDE.md)に 3 行で決めています。

  • 読み手(将来の自分、別のメンバー)が「何が、なぜ変わったか」を追えるレベルで書く
  • commit のメッセージより詳しく、PR の本文より簡潔に
  • 迷ったら書く。履歴を残して困ることはない

changeset を書かなくてよいのは、CI やインフラや文書の変更のように、サイトの挙動に影響しないものだけです。

ここが AI エージェントと相性の良いところです。人間がやると、変更記録は PR を出す直前に「あとで書く」になりがちで、書く頃には文脈が薄れています。エージェントは、PR を作るその瞬間に、Issue と差分と自分の調査の全部を持っている。そこで書かせた記録は、人間があとから書くより濃い。逆に言えば、エージェントの作業の文脈はセッションが終われば消えるので、その場で書かせないと二度と書けません。

実例: 前の記録の誤りを、次の記録が訂正する

2026-09-02 に、使わなくなったプラグインを撤去する PR を merge しました。その変更記録には、emdash 本体に当てているパッチについて「残るのは 404 ログ関連の 2 つだけ」と書いてあります。これが誤りでした。同じファイルに載っていた別目的の 2 つの修正(タクソノミーの先読み停止と、サイトマップの正規表現)も一緒に落ちていて、翌日、監視が D1 の読み取り行数の急増を検知しました。

戻す PR の変更記録には、こう書かせました。

#385 の変更記録にある「残るのは 404 ログ関連の 2 つだけ」はこの取りこぼしを見落としている。

CHANGELOG を開くと、0.9.3 の欄に、誤った記録と、それを訂正する記録が並んでいます。どちらも消していません。git log でこの関係を読み取るには、2 つの commit を開いてパッチの中身を比べる必要がありますが、CHANGELOG では 2 段落を続けて読むだけです。変更履歴が「読める物」になった、というのはこういうことです。

別のリポジトリで踏んだ罠

Slack から X に投稿する bot のリポジトリにも同じ形を入れました。こちらは単一パッケージで、private のまま git のタグと GitHub Release まで自動で作っています(privatePackages: { version: true, tag: true })。3 つ、モノレポ側では出なかった罠がありました。

  • タグの系統が 2 つになった。 それまで「main への push ごとに patch を機械的に上げてタグを切る」ワークフローがあり、changesets のリリースと役割が丸ごと重なっていました。共存させると、通常の PR の merge のたびに patch タグが切られ、Version Packages PR の merge では Release が 2 つできます。古い方を消し、バージョン番号の意味付け(patch / minor / major)は changesets だけが持つことにしました
  • changeset の忘れ検知は、commit 済みのファイルしか数えない。 CI に changeset status --since=origin/main を入れて、ソースの変更があるのに changeset が無い PR を検知しています。このコマンドは、作業ツリーにある未 commit の changeset を数えません。手元で「あるのに無いと言われる」を踏んで分かりました。運用の初期は continue-on-error: true の警告扱いにし、changeset が要らない PR は skip-changeset のラベルで除外しています。定着したら警告を外すだけで必須にできます
  • Version Packages PR の merge も本番デプロイになる。 このリポジトリは main への push でデプロイが走ります。Version Packages PR の差分は package.json の version と CHANGELOG.md と changeset ファイルの削除だけで、コードの実体は変わらず、Terraform も no-op です。無害ですが、「Release PR の merge でデプロイが 1 回走る」ことは知っておく必要があります

境界: 3 つの場所に何を書くか

commit メッセージ、PR の本文、changeset の 3 つは役割が違います。

  • commit: 何をしたか。1 行
  • PR の本文: なぜやったか、どう確かめたか、何に詰まったか。レビューの材料。merge されれば PR の画面にしか残らない
  • changeset: 使う側から見て何が変わったか。CHANGELOG に永久に残り、あとから読まれる前提で書く

changeset に PR の本文を写すと長すぎ、commit のメッセージを写すと短すぎます。「コミットより詳しく、PR 本文より簡潔に」はこの 3 つの間の線です。

どこで止まるか

  • changeset の質は、書かせる指示の質で決まります。「変更を記録して」だけだと、commit メッセージの言い換えが出てきます。「何が、なぜ、何を捨てたか」と、読み手を指定して初めて読める物になります
  • CHANGELOG はパッケージ単位に集約されます。モノレポで 8 つのサイトに同じ修正を当てたときは、8 つの CHANGELOG に同じ段落が入ります。1 か所で読みたい場合は Release PR の本文を見る方が早い
  • 誤った記録は残ります。それが良いところでもあり、読み返す人は「訂正が続いていないか」まで読む必要があります

この構成で気をつけること

  • 変更記録は、文脈が濃い瞬間に、その場で書かせる。エージェントの文脈はセッションが終わると消える
  • 読み手を指定する。「将来の自分と別のメンバーが、何が、なぜ変わったかを追える」
  • 迷ったら書く。書かない条件(挙動に影響しない変更)だけを決めておく
  • リリースの自動化を 2 系統持たない。タグを切る主体は 1 つ
  • 誤った記録は消さず、次の記録で訂正する

参考リンク

関連

この記事をシェア