古いmainへの直コミットで消えかけた成果を、PR運用へ戻した

  • #Git
  • #PR
  • #worktree
  • #自動化
  • #失敗復旧
古いmainへの直コミットで消えかけた成果を、PR運用へ戻した

今回やったこと

AIエージェントが古いローカルmainへ直接コミットし、pushを拒否された後に成果を放置していた問題を、PRを経由する運用へ戻した。実例では、ローカルmainがorigin/mainに対して先行と遅れを同時に持つ分岐状態になり、滞留したコミットの中には既に同じ内容がoriginへ入っているものもあった。必要だったのは、古いブランチをそのまま運ぶことではなく、まだ存在しない差分だけを救出する作業である。

先に「成果が残っている場所」を分ける

古いmainをそのままrebaseしてPRにしようとすると、すでにマージ済みの変更と未反映の変更を切り分けにくくなる。そこで、origin/mainを基準に新しい作業場所を用意し、現在の基準に対する実質的なファイル差分でローカル固有の内容を確かめる。過去のコミット履歴の並びではなく、現在のorigin/main先端に対する不足分を見るためである。

古いローカルmain
   ├─ origin/mainにも存在する変更
   └─ origin/mainに無い変更  ← ここだけを救出

この分離ができれば、「コミットがある」ことと「成果が失われている」ことを混同しないで済む。コミットが数件滞留しているように見えても、全件を復旧対象にするとは限らない。既存のorigin側と内容が重複しているものを除き、真の差分だけを新ブランチへ移す。

自動化の入口をPRに固定する

今回の再発防止では、エージェントに自由なGit操作を期待せず、ランナー側にworktree作成、commit、push、PR、CI通過後のマージの責務を置いた。エージェントは専用worktree内でファイルを編集するだけにし、成果の有無は作業後の差分で判定する。

実行ランナーは実行前にorigin/mainからworktreeを作り、処理後にランナー側のfinish処理へ渡す。エージェントが自分でmainをcheckoutしたり、originのpush先を変更したりする余地を減らすことが目的である。作業ディレクトリと統合経路を分けることで、古いローカルmainへ書き込む事故を経路として起こしにくくする。

origin/main

専用worktreeで編集

ランナーが差分確認

commit → push → PR → merge

重要なのは、PRを作ること自体ではない。失敗したときに、何が生成され、何がoriginへ届き、何が未処理なのかを追えることだ。ランナーは未コミットの成果物を退避し、コミット済みだがpush前の変更も救出できるようにする。成果物が消えたように見える時間を短くするための設計である。

うまくいかなかった復旧方法

古いブランチを最新mainへ丸ごとrebaseする方法は、一見すると自然に見える。しかし、古いブランチには「既にoriginへ入った変更」と「まだ入っていない変更」が混在する。前者まで持ち込むと、squash mergeなどでパッチIDが一致しない場合に、重複PRや意図しない巻き戻しにつながることがある。

また、pushが拒否された時点で処理を終えるだけでは、ローカルに残る成果を次の担当へ渡せない。実行ログに失敗だけを記録しても、未追跡ファイルや孤立コミットの位置までは伝わらない。復旧対象を明示し、origin/mainとの差分として扱う工程が必要になる。

やってみてわかったこと

AIエージェントの成果を守るには、生成品質だけでなく書き込み経路を設計する必要がある。mainを直接触らせない、origin/mainから毎回作業場所を切る、統合はランナーへ集約するという3点は、エージェントの判断に依存しないガードになる。

この方式でも、差分の内容が正しいかというレビューは残る。ただし、レビュー前に成果が消える問題と、履歴が分岐して何を採用すべきか分からなくなる問題を分離できる。自動化では、失敗をゼロにするより、失敗しても成果と原因を回収できる境界を作る方が現実的である。

この記事について

参照したのは、エージェント実行ランナーの実装概要とタスクキューの記録である。固有の実名や内部接続情報は扱わず、分岐した履歴から差分を救出し、PR経由へ戻す設計上の判断に絞った。

更新履歴

  • 2026-09-20: 初稿。

訂正履歴

なし。