メインリポジトリに溜まった未追跡ファイルの棚卸し(ドキュメントの相対パス指定が生んだ誤出力)
今回やったこと / この記事について
複数のAIエージェントと人間が同一リポジトリで作業を進める環境において、リポジトリルートに溜まっていた未追跡ファイル(untracked files)の棚卸しと恒久的な再発防止策を実施した。
今回の棚卸しでは、以下の3点に対処した。
- 全媒体横断モードのGA4計測ログ(4ファイル・計2,586行)が
.gitignoreの記述漏れによりリポジトリ直下のdocs/ga4/に誤ってコミットされていた問題の是正(git rm --cachedによる追跡解除)。 - 旧CMS(WordPress)からのデータ救出ディレクトリ(約121MB)のうち、再生成可能な中間物(約110MB)のみを
.gitignoreで除外指定し、再取得できない一次資料(147本の下書き記事等、約12MB)を確実に追跡下に残す仕分け。 - リポジトリ直下に突如現れていた謎の
/runtime/ディレクトリの原因究明。スクリプトの不具合ではなく、運用ドキュメントが引数例として--output-dir runtime/と相対パスで記述していたことが原因と判明したため、ドキュメント側のパス指定を正規のアセットパスへ修正。
特に3点目の「手順書ドキュメントの相対パス表記が、自律的に動くAIエージェントによる誤出力を引き起こしていた」という事象は、AIエージェント主体の開発・運用パイプラインにおいて見落とされやすい盲点である。その経緯と設計判断を記録する。
未追跡ファイルを放置できない理由
本リポジトリでは、「未追跡ファイルを main ブランチに放置しない」というgit衛生ルールを運用原則として定めている。
単一の開発者が作業する環境であれば、「手元に一時ファイルが散らかっていてもコミット時に選択しなければよい」で済むかもしれない。しかし、常時複数のAIエージェントが自律的にブランチを作成し、検証スクリプトを実行し、PRを作成して自動マージする無人運用環境では、未追跡ファイルの放置は即座に以下のような障害を引き起こす。
- 意図しないコミットへの巻き込み: エージェントが作業変更を
git addする際、関連ファイルを網羅しようとして未追跡の一時ファイルや大容量データを誤ってコミットに含めてしまうリスク。 - 異常検知の麻痺:
git status --porcelainの出力行数が常に2桁・3桁に達していると、エージェントや監視スクリプトが「今どのファイルが新規に生成されたか」を検知できなくなる。 - ディスク領域と転送量の圧迫: 巨大なキャッシュや中間生成物が追跡対象に紛れ込むと、worktreeの展開やリモートリポジトリとの通信コストが跳ね上がる。
そのため、未追跡ファイルが発生した際は「単に無視する」のではなく、発生源を特定して追跡すべきか除外すべきかを峻別し、恒久的なルールに落とし込む必要がある。
GA4ログの取りこぼしと救出データ中間物の仕分け
1. docs/ga4/ の除外漏れ
リポジトリ内の .gitignore には当初、各個別媒体用のパスとして以下が記述されていた。
sites/*/docs/ga4/
しかし、全媒体のアクセス状況を横断集計するスクリプト(ga4_fetch.py)は、リポジトリ直下の docs/ga4/ に集計JSONを出力する仕様となっていた。このルート直下のディレクトリが除外パターンから漏れていたため、過去の集計ログ4件(約2,586行)がGitの追跡対象に紛れ込んでしまっていた。
これらは一度コミットされてしまっていたため、手元ファイルを保持したままインデックスからのみ削除し、.gitignore にルート直下のパスを追加した。
git rm --cached docs/ga4/*.json
# GA4計測ログ(ルート直下および各媒体)
docs/ga4/
sites/*/docs/ga4/
2. CMS救出データ(121MB)の精密な切り分け
別ブログの旧CMS撤収に伴い退避していた救出ディレクトリ(sites/<site>/drafts/wp-rescue/)には、合計121MBのデータが存在していた。
このディレクトリ全体を .gitignore に追加してしまうと、旧環境の停止後に二度と再取得できない貴重な資産が散逸・欠損するリスクがある。そこで内訳を精査し、以下のように二分した。
| 分類 | サイズ | 主な内容 | 処置 |
|---|---|---|---|
| 再生成可能な中間物 | 約110MB | _scan_*.txt, _livedoor_index.json, *.ckpt, *.bak, __pycache__ | .gitignore で除外 |
| 再取得不可能な一次資料 | 約12MB | 救出した下書き記事147本、救出用パーサースクリプト、CMSスキーマ定義 | Git追跡対象として保全 |
単にディレクトリごと丸ごと除外するのではなく、中間生成物のパターン(_scan_*.txt や拡張子 *.ckpt 等)のみを除外ルールに設定した。なお、最終的な救出成否を記録したレポートファイル _scan_final.txt(8KB)のみは、検証記録として例外的に追跡下に含める運用とした。
# wp-rescue の中間物(一次資料12MBは追跡し、中間物110MBのみ除外)
sites/*/drafts/wp-rescue/**/_scan_*.txt
sites/*/drafts/wp-rescue/**/_livedoor_index.json
sites/*/drafts/wp-rescue/**/*.ckpt
sites/*/drafts/wp-rescue/**/*.bak
sites/*/drafts/wp-rescue/**/__pycache__/
!sites/*/drafts/wp-rescue/**/_scan_final.txt
/runtime/ 誤出力の調査:スクリプトではなくドキュメントの罠
今回の棚卸しの中で最も興味深い事象が、リポジトリ直下に突然生成されていた /runtime/ ディレクトリであった。
中身を確認すると、記事に挿入するための画像アセットをWebPへ変換した中間ファイル群が格納されていた。画像処理スクリプト(prepare_images.py)のバグによる誤出力が最初に疑われたが、スクリプトのソースコードを確認したところ、スクリプト自体には既定の出力ディレクトリ(デフォルト値としての runtime/)は一切ハードコードされていなかった。
では、なぜリポジトリルートに /runtime/ が生えていたのか。
コミットログとエージェントへの指示手順書を調査した結果、開発者向けドキュメント(TOOLS.md や各パイプラインの設計仕様書)の用例記述に原因があった。
<!-- 修正前のドキュメント記述 -->
**後処理**: `python3 scripts/prepare_images.py <raw画像> --output-dir runtime/` で WebP 化
タスクを委任されたAIエージェントは、このドキュメントに記載されたコマンド例をそのまま参照し、リポジトリルートを作業ディレクトリとして実行していた。その結果、指定された相対パス runtime/ がカレントディレクトリ(リポジトリ直下)にそのまま解釈され、リポジトリ直下に /runtime/ が作られていたのである。
なぜ /runtime/ を .gitignore に追加しなかったのか
この問題に対する安易な解決策は、.gitignore に /runtime/ を1行追加することである。そうすれば、とりあえず git status 上は綺麗になり、未追跡ファイルの一覧からは消える。
しかし、その対応は採用しなかった。あえて /runtime/ 自体は gitignore しない 判断を下した。
理由は明快である。もし /runtime/ を除外リストに入れてしまうと、将来別のエージェントや新規スクリプトが同じように誤ってリポジトリ直下に出力してしまった場合、Git上で完全に見えなくなってしまうからである。未追跡ファイルとして警告されないということは、誰も誤出力に気づけず、ディスクを圧迫し続けることを意味する。
根本的な対処は、誤出力を隠すことではなく、誤出力の発生源を断つことである。
そのため、該当する3箇所のドキュメント(TOOLS.md、PROJECT_BRIEFING.md、パイプライン設計書)において、相対パス表記を各媒体の記事アセット用パスへと書き換えた。
<!-- 修正後のドキュメント記述 -->
**後処理**: `python3 scripts/prepare_images.py <raw画像> --output-dir sites/<site>/assets/articles/<slug>/runtime/` で WebP 化
ドキュメント側で「--output-dir runtime/ と相対パスで書くとリポジトリ直下に生えるため、必ず記事アセットまでのパスで指定すること」という注意書きを明記することで、AIエージェントが生成するコマンドの誤出力を抑止した。
得られた知見とまとめ
- ドキュメントの記述精度が作業環境の衛生に直結する: 自律型AIエージェントはドキュメントに書かれたコマンド例を極めて忠実に再現する。人間であれば暗黙のうちに「適切なディレクトリに読み替える」ような相対パス表記も、エージェントは指定された文字列のままリポジトリルートで実行する。エージェント向けの手順書やREADMEには、曖昧な相対パスを残さず、展開可能な正規パスの形式で記述する必要がある。
- 「gitignoreで隠す」と「発生源を断つ」を混同しない: 想定外のディレクトリが生成された際、場当たり的に除外リストへ追加することは、異常の早期検知機能を自ら殺すことに等しい。正常系として許可された出力先以外は、あえて未追跡のまま検知に引っかかる状態を保ち、生成ロジックや指示ドキュメント側を是正するのが正しい運用設計である。
- 退避データの中間物と一次資料は厳密に切り分ける: 大容量の移行データであっても、再生成が可能な中間ファイルと、二度と手に入らない一次コンテンツを仕分けることで、リポジトリの容量肥大化を防ぎつつ資産を保全できる。
更新履歴
- 2026-09-07: 初版起稿(未追跡ファイル棚卸しおよび誤出力防止の実績に基づく)