この記事について
Windows / Git / Linux / AI Agentをまたぐ開発環境で、同じ内容のはずのテキストファイルのSHA-256が一致しないことがありました。原因はCRLFとLFの違いという、Gitを使う人にはおなじみの話です。
ただ今回問題になったのは、そのraw hash mismatchをAI Agentがどう解釈すべきかという契約が、どこにも書かれていなかったことでした。Repository側の正規化は正常に機能していました。欠けていたのはAgent側の意味論です。
本稿は、私が最初に立てた仮説がGit履歴調査によって反証され、問題を再定義し、最小限のAgent契約として実装するまでの記録です。
1. 導入
Gitを日常的に使っていれば、CRLFとLFの違いでdiffが荒れたり、hashが変わったりする経験は一度はあるはずです。多くの場合、.gitattributes で改行コードを正規化しておけば解決する、という話で終わります。
しかし今回、Windows / Git / Linux / AI Coding Agentが混在する環境で起きた問題は、その一段階先にありました。
.gitattributes によるLF正規化そのものは正常に機能していたにもかかわらず、AI Agentが「hashが一致しない」という事実をどう解釈すべきかについての取り決めがなく、不要な再確認や調査の分岐が発生し得る状態になっていたのです。
この記事は、CRLF/LFの一般論ではありません。AI支援開発において、この種の「観測された差」をどう扱うべきかという運用上の問題と、その対策の記録です。
2. 何が起きたか
同じ内容だと考えていたテキストファイルについて、SHA-256のraw hashが一致しないケースが発生しました。
raw byte列で見れば、LFとCRLFは異なります。内容として同一に見えてもSHA-256が一致しないのは当然の挙動で、ここまでは技術的に何も不思議なことはありません。
問題は、hash mismatchそのものではなく、AI Agent側でその意味が定義されていなかったことでした。
3. なぜhash mismatchがAI開発でコストになりうるのか
人間がhash mismatchを見た場合、経験から「ああ、改行コードの差かもしれない」と早い段階で候補を絞れることがあります。
一方、AI Agentは、明示的な判断規則がなければ、改行差を優先的に除外するとは限りません。観測した事実に対して、複数の可能性を追加確認の対象として扱うことがあります。
hash mismatchという事実だけでは、
- 内容が実際に変更された
- remoteとlocalがずれている
- checkoutが古い
- そもそも別ファイルを見ている
- 改行コードだけが異なる
- 再取得が必要
といった可能性を区別できません。
Agentの判断経路が明示されていなければ、次のような探索経路が発生し得ます。
hash mismatch
→ 再確認
→ ファイル再読込
→ diff
→ Git確認
→ 全文比較
→ 人間確認
→ 再実行
これはトークン、時間、そして人間の注意力を消費する可能性があります。CRLF/LFという本質的には小さな表現差が、AI支援開発では相対的に大きなコストへ発展し得るのは、この「意味づけの不在」によって確認経路が増える余地があるためです。
ここで述べているのは、Agent一般の確定的な性質ではありません。今回のケースで実際に発生した探索量や頻度を、定量的に測定したわけでもありません。
4. 「LFルールが消えた」という最初の仮説
問題に気づいた時点で、私が最初に立てた仮説は次のようなものでした。
以前どこかの時点でLF統一ルールを固定したはずだが、Agent運用契約やテンプレートの更新過程で、そのルールが失われたのではないか。
実際、現行のAgent運用契約を確認すると、
- Windows local repository / local hashはPowerShellで確認する
という「どこで実行するか」の規則は存在していました。
しかし、
- canonical line ending = LF
- raw hash mismatch ≠ content mismatch
- CRLF/LF-only differenceの扱い
- normalized comparison
- hash mismatch時の診断順序
といった、差分の意味づけに関わる記述は見当たりませんでした。
この時点では、「LF policyそのものが過去のどこかで失われたのではないか」という仮説が自然に見えました。
5. Git履歴を調べる
仮説を検証するため、AI支援開発のルールやテンプレートを管理している独自の文書運用基盤について、Git履歴をread-onlyで調査しました。
確認できたのは、次のことです。
現在の状態
.gitattributes には、
* text=auto eol=lf- 各種拡張子に対する
text eol=lf
という、repository全体でLFを維持するpolicyが現在も存在していました。
git ls-files --eol で確認した対象tracked filesも、
i/lf w/lf
という状態でした。
一方、確認したAgent運用契約とテンプレートには、CRLF/LF差やhash mismatchの解釈に関する規則は存在しませんでした。
履歴上の事実
過去のGit履歴を確認すると、2026年7月に .gitattributes へLF固定ルールを導入し、tracked text filesをLFへ正規化していました。
その後、この改行ルール自体を変更・削除した履歴は確認されませんでした。
6. 仮説は外れていた
つまり、LF統一ルールが導入後に消えた、という最初の仮説は誤りでした。
.gitattributes によるLF正規化は導入時点から現在まで存在し、今回確認した履歴の範囲では変更・削除されていませんでした。
これは重要な反証でした。「ルールが失われたから直す」という単純な話であれば、対応は .gitattributes を復元するだけで済んだはずです。
しかし実際には、直すべき対象は別の場所にありました。
7. 本当の問題:正規化と意味論の分離不足
調査を経て、問題を次のように再定義しました。
Repository側の機械的正規化、つまり .gitattributes が担当する「ファイル表現をLFに揃える」という役割は、正常に機能していました。
一方で、Agent側の意味論、つまりAgent運用契約が担うべき「観測した差異をどう解釈するか」という役割は、定義されていませんでした。
図にすると、次のような状態です。
Repository
LF canonicalization (.gitattributes)
→ 正常に機能
Agent
raw hash mismatch interpretation
→ 未定義
正規化ルールが「消えた」のではありません。正規化とは別のレイヤーで必要な取り決めが、そもそも存在していなかった、というのが調査後の整理です。
8. Canonicalization / Observation / Interpretation
この問題を整理するため、扱う対象を3つの層に分けて考えました。
Layer 1: Canonicalization
何を正規形とするかを定める層です。今回の主対象は、LFをcanonical line endingとすることでした。この役割は .gitattributes が担っていました。
Layer 2: Observation
実際に何が違うかを測る層です。raw SHA、EOLの違い、LF正規化後の比較、diffなどが該当します。
Layer 3: Interpretation
観測された差異を、Agentがどう意味付けするかを定める層です。IDENTICAL / REPRESENTATION_ONLY / CONTENT_DIFFERENCE / UNKNOWN といった分類が考えられます。
今回不足していたのは、このLayer 3でした。
重要なのは、観測された事実とその解釈を分けることです。
Observed:
raw SHA256 differs.
Inference:
content may have changed.
Additional observation:
LF-normalized content is identical.
Conclusion:
representation-only difference.
raw hashが違うことは観測事実です。しかし「内容が変更された」は、その時点ではまだ推論にすぎません。
追加観測によってLF正規化後の内容が一致すると分かれば、「表現上の差だけだった」という別の結論へ進めます。
この分離によって、Agentに必要以上の推測をさせず、異常時の判断経路を限定しやすくなります。
9. 過剰なガードレールも問題になりうる
対策を検討する過程では、より徹底した検証も候補に上がりました。
例えば全ファイルについて毎回、raw hash、normalized hash、BOM、encoding、EOL、diff、必要に応じたGit履歴まで確認する方法です。
しかし、この方法は採用しませんでした。ガードレールそのものを実行するコストが、削減したいコストを上回りかねないためです。
すべてのファイル操作へ重い検証を課せば、hash mismatchによる探索の発散を防げる可能性はあります。その代わり、問題が起きていない通常作業にも、検証コストが常時発生してしまいます。
そこで採用したのは、Fast Path / Exception Path型の考え方でした。
通常時
→ 追加確認なし
raw hash mismatch発生時
→ 最小診断
normalized content mismatch時
→ deeper investigation
目標は、チェックの総量を増やすことではありません。異常が起きたときの分岐を決定論的にすることです。
10. 最小契約という対策
以上を踏まえ、Agent運用契約へ追加する意味論は最小限に絞りました。内容は次の通りです。
Repository-managed text files use LF as canonical line endings.
A raw hash mismatch alone does not establish a content mismatch.
For text files, first compare LF-normalized content.
If normalized content matches, treat the difference as representation-only
and continue.
Perform deeper investigation only when normalized content differs
or the cause remains unknown.
この契約が意味するのは、次の点です。
- Git管理下のテキストファイルにおけるcanonical line endingはLFである。
- raw hash mismatchだけをもって「内容が変わった」と断定しない。
- テキストファイルでは、まずLF正規化後の内容を比較する。
- 正規化後の内容が一致すれば、representation-onlyとして扱う。
- CRLF/LFのみの差を理由に、再生成、作業停止、人間承認を要求しない。
- 正規化後の内容が異なる場合、または原因が不明な場合だけ追加調査する。
- 通常のwork unitごとにEOLやhash確認を義務づけない。
この契約は「毎回何を確認するか」を増やしたものではありません。異常が観測されたときに、どう解釈し、どこまで調査するかを限定した契約です。
11. 文書運用基盤への反映
この方針を、私が普段使っている独自の文書運用基盤へ反映しました。
変更対象は、
- 現行のAgent運用契約
- 新規環境向けテンプレート
- 変更履歴
の3点に限定しました。既存の .gitattributes は変更せず、tracked filesの再正規化も行っていません。
検証では、mainブランチかつclean baselineであること、ローカルとリモートの参照が一致していること、git diff --check とcached checkがPASSであること、staged setが意図した変更対象と一致すること、Agent契約文の内容、Markdown safety、全文diff、push後のローカル・リモート参照一致、worktree cleanを確認しています。
なお、一部の自動比較経路は環境要因により未実行でしたが、契約文の同一性自体は全文diffで確認済みでした。このため、自動比較経路の未実施を、作業完了を阻害する未確認事項としては扱っていません。
12. 現状と未確認事項
ここまでで確認できているのは、次の点です。
- LF canonicalizationは以前から存在していた。
.gitattributesの改行ルールは、その後変更・削除されていなかった。- 今回の問題はLF policyの消失ではなかった。
- Agent側にはhash mismatchをどう解釈するかという契約がなかった。
- 最小契約をAgent運用契約とテンプレートへ反映した。
.gitattributesは変更していない。- renormalizeは実施していない。
- 常時hash/EOL validationも導入していない。
一方、まだ確認できていないこともあります。
この契約導入後、hash mismatchが自然発生した際に、再読込、再調査、人間確認、作業停止、トークン消費、作業時間が実際にどれだけ減るかは、まだ観測していません。
したがって、「この契約によってトークン消費が○%削減された」といった定量的効果を、現時点で主張することはできません。
言えるのは、無駄な探索分岐を減らすことを目的とした設計と実装まで完了した、というところまでです。
13. AI Agent運用から得られた教訓
今回の一連の作業から得られた教訓は、大きく2つあります。
一つ目は、Repositoryの状態を正規化するだけでは、Agentがその差異をどう扱うかまでは決まらない、ということです。.gitattributes はファイル表現を制御しますが、「raw hashが違った場合、それを何と解釈するのか」までは制御しません。CanonicalizationとInterpretationは、別の問題です。
二つ目は、最初に立てた仮説を、そのまま対策へ変換しないことです。今回、「LFルールが消えたのではないか」という仮説は自然に見えました。しかしGit履歴を確認すると、それは誤りでした。
もし仮説を検証せずに修正へ進んでいたら、正常に存在している .gitattributes を無意味に再整備し、本当に不足していたAgent側の判断規則には手をつけなかったかもしれません。
今回のケースから、Agentを賢く推測させるより、推測する必要そのものを減らす方が有効な場合があるという考え方が得られました。
14. まとめ
CRLFとLFの違いそのものは、新しい問題ではありません。しかしAI Agentを開発工程へ組み込むと、その小さな表現差が別の問題を生むことがあります。
raw hash mismatchという観測事実に対して「これは何を意味するのか」という契約がなければ、Agentが追加確認を必要と判断する余地が増えます。
今回のケースでは、Repository側のLF正規化は最初から正常に機能していました。最初に立てた「LFルールが消えた」という仮説は、Git履歴調査によって反証されました。
不足していたのは、正規化そのものではなく、観測した差異をAgentがどう解釈するかという契約でした。
そこで、通常時の検証を増やすのではなく、
raw hash mismatch
→ LF-normalized comparison
→ representation-onlyなら継続
→ content mismatchまたはUNKNOWNなら追加調査
という最小の例外処理を、Agent運用契約へ追加しました。
この対策による定量的な効果は、まだ未確認です。
ただし、少なくとも今回の調査によって、機械的な正規化と、Agentによる差異の意味付けは別々に設計する必要があるという運用上の論点を明確にすることができました。
本稿は個人の開発経験に基づく記録であり、一般規格や唯一の正解を示すものではありません。対象プロジェクトの実名・実インフラ情報(リポジトリ名、commit SHA、内部ディレクトリ構造等)は意図的に含めていません。サイト全体の免責事項はこちらをご覧ください。