PJR-A99J agentのgit操作が実リポジトリを破壊する事象が再発した
1. 課題内容
PJR-5YW6 の実行で、メインリポジトリの core.bare が true に変更され作業ツリーが操作不能になった。あわせて worktree へテスト用フィクスチャのコミット2件が混入し、docs/register-item.md という実在しないファイルが追加された。実装の成果はなく worktree は破棄した。同種の事象は PJR-X3E8 で一度発生しており、テストで git を扱う際は隔離した環境変数を渡す規約を設けたが、再発を防げていない。agent の git 操作が実リポジトリの設定と履歴へ影響しないようにする。
2. 影響範囲
| 観点 | 影響 |
|---|---|
| スコープ | agent が git を扱う全タスク。実リポジトリの設定と履歴に及ぶ |
| スケジュール | 事故のたびに実行が失敗し、復旧と原因調査が要る。今回は成果が得られず worktree を破棄した |
| コスト | 実行時間と agent 利用量が無駄になる。復旧に人手が要る |
| 品質 | 実リポジトリの設定変更に気づかないまま作業を続けると、後続の操作が想定外の結果になる。履歴への混入は追跡を困難にする |
| 関係者 | git を操作するタスクを実行する全 agent |
3. 対応方針
| 項目 | 内容 |
|---|---|
| 原因 | exec trialを含むagent起動経路が親のprocess.envをそのまま渡し、Git hook由来のGIT_DIRをagentとテスト子プロセスへ継承していた。fixtureのgit initとgit commitが一時directoryではなくlinked worktreeのgitdirへ向いたため、共有configのcore.bare変更とexec branchへのfixture commit混入が同時に発生した |
| 対応策 | 全agent起動経路と親検証からrepository固有Git環境変数を除去する。加えてagent前後のHEADとlocal configを比較し、変更時は親検証・reporter・統合前にblockする |
| 依存事項 | worktree による隔離を前提とした実行方式に関わるため、exec の実行経路を変更する場合は影響範囲を確認する |
| 完了条件 | 下記のとおり |
3.1. 完了条件
- agent の実行によって実リポジトリの git 設定が変更されない。特に
core.bareが変わらない。 - agent の実行によって、意図しないコミットが実リポジトリまたはブランチへ混入しない。
- 上記が守られなかった場合に検知できる。実行の前後で git 設定と HEAD を比較するなど、方法は判断してよい。検知したら実行を止めるか、記録に残して報告する。
- PJR-X3E8 で追加した規約(テストから git を実行する際に隔離した環境変数を渡す)が、実際に守られているかを確認する。守られていない箇所があれば是正する。
- 今回の事象の原因が特定されている。特定できない場合は、切り分けのために行った調査と、残る可能性を記録する。
- 復旧手順が記録されている。
core.bareが変更された場合の戻し方と、混入したコミットの扱いを含む。 npm run typecheck、npm run lint:ts、npm run test:unit、npm run test:integrationが成功する。
今回の観測
- メインリポジトリの
core.bareがtrueになり、git statusがfatal: this operation must be run in a work treeで失敗した。git config core.bare falseで復旧した。 - worktree に
register itemとbefore registrationの2コミットが混入し、docs/register-item.mdが追加されていた。実リポジトリの追跡対象には入っておらず、worktree の破棄で解消した。 - 実装の成果はなく、
src/exec-trial.tsへの変更はなかった。親検証3件は passed と記録されたが、実装が無い状態での結果である。 - PJR-5YW6 は未着手として残る。再実行時は完了条件へ「実リポジトリの git 設定・履歴を変更しない」を明記する。
4. 対応結果
解決内容:
src/exec-run.ts、src/exec-trial.ts、src/exec-worktree-command.tsの全agent起動環境をgitEnvironment()経由に変更した。通常worktree、in-place、executor / reporter pipeline、trial、分割worktree commandのいずれでもGIT_DIR、GIT_WORK_TREE、GIT_COMMON_DIRなどを子プロセスへ渡さない。src/exec-agent-git-state.tsを追加し、agentの各試行前後でHEAD(commitとsymbolic ref)およびlocal configを比較するようにした。差分はagent-git-state-write:として記録し、親検証、reporter、commit、mergeへ進む前に失敗させる。- 親検証とworktree依存installにも隔離環境を適用した。残っていた
src/job.tsとtools/e2e/exec-run-e2e.tsの直接Git実行もgitEnvironment()経由へ統一し、テスト・tool・本番コードの直接Git起動を再監査した。 tests/src/exec-agent-git-state.integration.test.tsでagentによるcommitとcore.bare変更の検知を追加し、tests/src/exec-worktree-command.integration.test.tsで危険なGIT_DIR/GIT_WORK_TREEがagentへ継承されないことを確認する回帰ケースを追加した。
原因の切り分け:
- PJR-X3E8は
tests/src/exec-agent-protected-config.test.tsの個別Git呼び出しだけを隔離したため、そのテストの再発は防いだが、上流のagent起動環境は未修正だった。 - 今回事象で
core.bare変更とfixture commit 2件が同時に発生したことは、異なる一時repository用Git操作が同じ継承GIT_DIRへ向いた場合の挙動と一致する。実際にexec trialのworktreeEnvironment()がprocess.envを無加工で複製していたことから、再発経路を特定した。
再発防止と復旧:
- 環境隔離を第一防御、HEAD・local config比較を検知防御、既存のcommit許可リストを統合防御とする。設計上の必須ルールを
docs/ja/product/040-system-design/sysd-cross-cutting-policy.md、運用詳細をdocs/ja/specdojo/guides/exec-config-guide.mdへ反映した。 - 復旧手順を
docs/ja/specdojo/guides/exec-operation-guide.mdへ記録した。core.bareは影響確認後にgit config --local core.bare falseで戻し、git statusとgit fsckで確認する。混入commitは差分を確認して対象worktree / 未統合branchを破棄し、必要な成果だけを新しいworktreeへ適用する。共有済み履歴のresetやforce pushは行わない。
確認結果:
- executor内で
npm run typecheck、npm run lint:ts、変更Markdownのprettier/markdownlint、register build、catalog validate、index buildが成功した。catalog validateの既存未作成文書に対するwarningを除き、errorはない。 npm run test:unit、npm run test:integration、npm run validate:schemaはexecutor / reporter pipelineの親検証として設定されているため、sandbox内では重複実行せずrunnerの検証結果を正本とする。
5. 関連ドキュメント
- 1回目の発生: PJR-X3E8 テストのgit実行でリポジトリがbareになる
- 発生した実行: PJR-5YW6 trialで完了済みtodoを起点に指定しreporterも比較できるようにする
- agent の権限方針: PJR-3S8Q agent が書き込める設定の範囲
- テストでの git 実行規約:
.github/instructions/vitest.instructions.md