前置き
この記事は私がClaude Codeで個人開発をするにあたりChatGPTと壁打ちした記録を基にAIライティングしたものです。あくまで私が行き着いた方法に過ぎないことをご容赦ください。
AIエージェントに安全にPR作成まで任せるための実践整理
はじめに
AIエージェントにコードを書かせるだけでなく、commit・push・Pull Request 作成まで一気通貫で任せたい、という場面は増えてきました。実際に Claude Code を DevContainer 上で運用してみると、難しいのはコード生成そのものよりも、認証・署名・権限制御・永続化の設計でした。
この記事では、Claude Code × DevContainer × GitHub の組み合わせで、AI に実装と PR 作成までは任せつつ、最終レビューとマージは人間が握るための構成を整理します。設計上の勘所と、実際にハマりやすかった点の両方をまとめます。
目指す構成
まずは全体像です。今回目指したのは、secret をホスト側に閉じ込めたまま、コンテナ内のエージェントが commit・push・PR 作成まで進められる構成です。
この構成で重要なのは、次の 2 点です。
- secret はホスト環境に保持し、コンテナには実行時だけ渡す
- AI には branch push と PR 作成までは許可し、merge は許可しない
人間とAIの境界
最初に決めるべきなのは、何を AI に任せて、何を人間が保持するかです。ここが曖昧だと、GitHub 権限、Git identity、permission 設計も曖昧になります。
人間が握る領域
- Issue の整理
- 方針判断
- レビュー
- マージ
- 本番反映判断
AIに任せる領域
- 実装
- テスト・ビルド補助
- commit
- branch push
- PR 本文の下書きと作成
この境界を先に決めておくと、後続の設計がかなり整理しやすくなります。
Git identityの分離
同じリポジトリで人間も AI も commit する場合、repo 単位で user.name や user.email を切り替えるだけでは混線しやすいです。人間の手動 commit まで AI 名義になる事故も起こりえます。
そのため、実運用では 人間用の作業コピーと AI 用の作業コピーを分ける方が安定します。
repo/
project/ ← 人間用
project-agent/ ← AI用
AI 用 clone にだけ別の Git identity を設定すれば、人間側の普段の Git 設定を汚さずに済みます。
git config user.name "Agent"
git config user.email "agent@example.com"
clone運用の安定性
git worktree は便利ですが、DevContainer と組み合わせると不安定になることがあります。worktree 側の .git が親リポジトリの .git/worktrees/... を参照するため、ホスト側の絶対パスとコンテナ内パスがずれると壊れやすいためです。
典型的には、次のようなエラーに遭遇します。
fatal: not a git repository: .../.git/worktrees/...
また、以前の worktree 構成で作ったコンテナが残っていると、clone 構成へ切り替えたあとも古い状態を VS Code が再利用しようとして混乱しやすいです。
そのため、DevContainer で安定運用したい場合は、別 clone の方が単純で壊れにくいと感じました。
永続化対象の切り分け
DevContainer は再ビルド・再作成が前提です。したがって、コンテナ内に直接置いた設定はいつか消えます。ここでは、何を永続化すべきかを最初に決めておくのが重要です。
永続化を考える対象
- AI 用の SSH 鍵
- AI 用の Git 設定
- Claude Code のホームディレクトリ相当の状態
bind mount向きの対象
~/.ssh- AI 用 gitconfig
named volume向きの対象
/home/node/.claude
例えば、Claude Code の状態を残したいなら、次のように volume を使うと扱いやすいです。
"source=project-claude-home-${devcontainerId},target=/home/node/.claude,type=volume"
この分離をしておくと、コンテナ再作成時の再設定コストがかなり下がります。
SSH接続先の分離
AI 用 GitHub アカウントを使うなら、SSH 鍵も分けておくと混乱しにくくなります。~/.ssh/config に Host alias を切っておくと、どの鍵で接続しているのかが明確になります。
Host github-agent
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_agent
IdentitiesOnly yes
接続確認は次のように行います。
ssh -T git@github-agent
remote URL も alias 側にそろえます。
git@github-agent:owner/repo.git
こうしておくと、認証は AI 用アカウント、push 先は本来の private repository、という構成を素直に作れます。
private repositoryへの権限付与
AI 用アカウントで private repository に branch push や PR 作成を行うには、そのアカウントに対象リポジトリへのアクセス権が必要です。実務的には、対象リポジトリの collaborator に追加するのが分かりやすいです。
ここで重要なのは、AI 用アカウントで作業するからといって、remote を AI 側所有のリポジトリに変える必要はない点です。必要なのは、本来の owner の repository に対して AI 側アカウントがアクセスできることです。
SSH認証とSSH署名
最初にハマりやすかったのは commit 署名でした。一見すると GPG エラーのように見えても、実際には SSH signing の設定不足だった、ということが起こります。
見落としやすいポイントは次の 2 つです。
- GitHub に署名用途として SSH 鍵を登録していない
- ホスト側で
ssh-addしていない
DevContainer で SSH agent forwarding が効いていても、ホスト側で鍵が agent に積まれていなければコンテナ内からは使えません。
ssh-add ~/.ssh/id_ed25519
ssh-add -l
また、GitHub 側では認証用途だけでなく、署名用途としても鍵を登録しておく必要があります。個人開発や一般的なチーム運用であれば、認証用 SSH 鍵を署名にも使う形でも十分現実的です。
Dockerfileと認証情報の境界
コンテナ内で PR 作成まで進めるなら、必要なツールは最初から入っていた方が運用しやすいです。一方で、認証情報までイメージに含めるべきではありません。
Dockerfileに入れる対象
ghopenssh-client- Claude Code 実行に必要なツール
- build / test に必要な基本ツール
Dockerfileに入れない対象
- Personal Access Token
- 認証済み状態
- 秘密鍵そのもの
方針としては、ツールは image に入れるが、認証情報は起動時に注入するのが分かりやすいです。
GH_TOKEN注入方式
gh auth login をコンテナ内で自動化したくなりますが、今回はその方針を取りませんでした。gh は GH_TOKEN や GITHUB_TOKEN があれば、そちらを優先して認証に使えるためです。
この方式には次の利点があります。
- コンテナ破棄時にログイン状態を持ち越さない
- 資格情報の所在をホスト側に寄せられる
- コンテナを再作成しても復旧が簡単
そのため、今回は ホスト側の GH_TOKEN を direnv で管理し、devcontainer.json の remoteEnv で渡す構成に寄せました。
export GH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxx
{
"remoteEnv": {
"GH_TOKEN": "${localEnv:GH_TOKEN}"
}
}
この構成なら、secret はホストに置いたまま、コンテナ内の gh が必要なときだけ使えます。
direnvとの相性
毎回 export GH_TOKEN=... を手で打つ運用もできますが、これは地味に面倒ですし、シェル全体に token を常駐させるのも避けたいところです。
その点、direnv はかなり相性がよいです。
- リポジトリ単位で環境変数を有効化できる
- ディレクトリを離れると token が無効になる
-
.zshrcや.bashrcに token を書かずに済む - DevContainer の
remoteEnvとつながりやすい
流れとしては、次のようになります。
direnv → host GH_TOKEN → DevContainer → gh CLI
secret を image やリポジトリに置かない、という方針とも整合しやすいです。
Classic PATによる初期安定化
Fine-grained PAT は理想的に見えますが、private repository との組み合わせや gh の動作切り分けまで含めると、最初は少し煩雑です。今回は、まず Classic PAT で安定動作を優先しました。
最低限の scope は基本的に次で足ります。
repo
権限不足のとき、gh は repository が存在しないような GraphQL エラーに見えることがあります。しかし実際には、owner/repo の指定ミスではなく権限不足であることも多いです。
トラブル時は、次を先に確認すると切り分けしやすいです。
gh auth status
git remote -v
permission設計
AI を安全に自走させるうえで、最も効くのは permission 設計です。おすすめは、次の 3 段階で整理することです。
allow対象
- ファイル編集
- 検索系コマンド
- lint / test / build
- branch 作成
-
git diff/git log/git status
ask対象
- PR 作成
- 依存追加
- Docker 操作
- 外部通信
deny対象
- merge
- rebase
- force push
- main / master への direct push
- secrets 読み取り
この設計にすると、日常的な実装ループは AI に任せつつ、危険操作だけを明示的に止められます。
merge操作の遮断
AI に PR 作成まではさせたいが、merge はさせたくない、という要件は自然です。一方で、private repository における branch protection や rulesets の使い勝手は、プランや組織設定に左右されることがあります。
そのため、GitHub 側の制御だけに頼らず、エージェント側で merge 系コマンドを deny するのが現実的です。
-
git commit/git push/gh pr createは許可または確認付き -
gh pr merge/git merge/git rebaseは拒否 - force push は拒否
- main / master への直接 push は拒否
この線引きだけでも、運用の安心感はかなり高まります。
steeringとPR本文の接続
AI 開発では、「何を考えて何を変えたのか」がブラックボックスになりがちです。そのため、requirements / design / tasklist のような記録を .steering/ に残す運用は有効です。
ただし、保存するだけでは弱いです。真価が出るのは、その記録を PR 本文に変換できるときです。
例えば、次のように対応づけられます。
-
requirements.md→ Summary / Background / Scope -
design.md→ Implementation / Design Notes -
tasklist.md→ Validation / Remaining Issues / Reviewer Notes
PR テンプレートも大げさである必要はありません。次のような最小構成で十分運用しやすいです。
## Summary
## Background
## Scope
## Implementation
## Validation
## Remaining Issues
## Reviewer Notes
これだけでも、AI が出した変更のレビュー可能性はかなり上がります。
まとめ
Claude Code × DevContainer × GitHub の運用で重要だったのは、モデルの性能そのものよりも、周辺の境界設計でした。
特に効いたのは次の点です。
- 人間用 clone と AI 用 clone を分ける
- worktree より clone を選ぶ
- secret はホストに置き、コンテナへは実行時注入にする
- SSH 認証と SSH signing を最初に整理する
-
gh auth loginではなくGH_TOKEN注入で割り切る - permission を allow / ask / deny で明確に分ける
- merge は人間が握り、AI は PR 作成までに留める
- steering を PR 本文に接続する
AI に実装だけを任せる構成よりも、PR 作成まで含めて設計した方が、開発フロー全体としての一貫性は高まります。一方で、そのためには認証・署名・権限・永続化の整理が欠かせません。
これから同様の環境を整えるなら、まずは「AI に何を任せ、どこから先を人間が握るか」を決めるところから始めるのがよいと思います。