74件のうち、配るのは6件だった「更新日で選ぶ」が壊れた話
NGWの更新を配る仕組みは、とうに完成している。壊れたのは「何を配るか決める」ところだった。20年動いてきた「ファイルの更新日で選ぶ」が、AIと働き始めた途端に無関係なファイルを拾い出す。犯人はAIではなくgitだった。診断から、UPDATE MAKER を内容差分へ、さらに「案件を選ぶ」へ作り替えるまでの一日。
配る仕組みは、もうある
NGW は自治体ごとのサーバで動く。改修は、事務所の開発機で直して終わりではない。それぞれの現場に届けて、はじめて完了する。
届け方は二通りだ。数が少なくて近ければ訪問して更新する。それ以外は Web 経由で更新モジュールを配布し、各現場で当ててもらう。この配布と適用の仕組みはとうに完成していて、何年も問題なく回っている。
今日壊れていたのは、その手前だった。そもそも、何を配ればいいのか。
20年動いてきた「更新日で選ぶ」
NGW には UPDATE MAKER という自作ツールがある。やることは単純だ。ソースの木を上から下まで歩く。ファイルの更新日が指定した範囲に入っているものを拾い上げ、一覧に出す。私がチェックを外して調整し、ボタンを押すと、そのファイルだけを固めて更新モジュールと zip ができる。
これで長らく困らなかった。理屈も明快だ。今日直したファイルは今日の日付になっている。今日の日付で拾えば、今日の仕事が出てくる。
前提を一行で書くとこうなる。
ファイルの更新日 = 私が編集した瞬間
この前提が、静かに崩れていた。
対象外のファイルが、増えた
AI と組んで開発するようになってから、様子がおかしくなった。更新日で抽出すると、触ってもいないファイルが一覧に上がってくる。数件ではない。ずらりと出る。
最初は AI が余計なファイルを触ったのかと疑った。違った。犯人はもっと地味なところにいた。
犯人は git だった
ファイルの更新日は、正確には「私が編集した瞬間」ではない。OS がそのファイルを最後に書いた瞬間だ。両者は長い間一致していた。私がひとつの作業ツリーを直接いじっていたからだ。
git は更新日を保存しない。ブランチを切り替える。マージする。もう一台から取り込む。このとき git は、二つのツリーで内容が違うファイルを片端から書き直す。そして OS がそこに「今」の時刻を打つ。中身が1バイトも変わっていなくても、更新日だけが今日になる。
AI と働くようになって、私はブランチを頻繁に行き来していた。切り替えのたびに、無関係なファイルの更新日が今日に塗り替えられていた。
数えて、はっきりした。
- 更新日が今日のファイル:74件
- 内容が本当に変わったファイル:6件
差の68件は、git が触っただけで中身は何も変わっていない。これがずっと一覧に紛れ込んでいた。
厄介なのは、これがエラーを出さないことだ。壊れたのは道具ではなく前提で、前提はエラーを吐かない。20年正しかった一行が、いつの間にか嘘になっていた。
更新日をやめて、内容差分へ
直し方はひとつしかない。ファイルシステムに「いつ書いたか」を尋ねるのをやめる。git に「何の中身が変わったか」を尋ねる。
何が変わったかを正確に知っているのは git だけだ。混乱の原因も git だった。同じ相手に、今度は正しい質問をする。
git diff --name-only <起点> -- <配布対象のディレクトリ>
ブランチを切り替えようが、もう一台から取り込もうが、この答えは汚れない。ログもバックアップも一時ファイルも、実際に中身が変わらない限り出てこない。UPDATE MAKER に起点の入力を足し、この方式を選べるようにした。従来の更新日モードは既定のまま残した。
これで74件は6件になった。一件落着のはずだった。
同じ件数が、並んでしまった
起点はドロップダウンから選ぶ。どのコミットを起点にすると何件配ることになるか、件数を併記した。これで迷わない。そう思っていた。
開いてみたら、こうなった。
3c53d378 復命タブUI調整… (1件)
16f3e551 設定まわりの修正… (0件)
36462f38 健全性レポートの改善… (7件)
e3a702fd 再起動ツールを版上げ… (7件)
69f8d0bd レポートツールを復帰… (7件)
4273daea 再起動処理の改善… (7件)
ed9fbf44 日次の記録… (7件)
7件が5つ並んでいる。 どれを選べばいいのか分からない。
理由ははっきりしていた。その区間のコミットは、配布対象のディレクトリを1つも触っていない。再起動まわりのツール、技術ブログ、サーバ設定。どれも NGW 本体の外だ。だから件数が動かない。
これは曖昧なのではない。等価だ。 この5つはどれを選んでも配布物は完全に同一になる。間違えようがないという意味では、むしろ安全だ。
AI はそう説明した。説明は正しい。私はこう返した。
同じ件数が複数あり、本当に配布すべきものがはっきりしない
正しいことと、選べることは別だ。 安全でも、選べないなら道具として負けている。
案件を選ぶ、という答え
ここで発想を変えた。そもそも私は、起点など選びたくない。
私がいつもやっているのは「複数の案件をまとめて更新にする」ことだ。この不具合と、あの機能追加と、そっちの調整。頭の中にあるのは案件の並びであって、履歴のどの一点から先か、ではない。境界を推理させられていたのが、そもそもの間違いだった。
だから、やった仕事そのものをチェックで選ぶ形にした。
抽出方法 ○更新日(従来) ◉案件選択 ○起点から一括
────────────────────────────────
☑ 9ce286fe 復命未登録時は操作UIを出さず、登録を促す案内に変更 (1件)
☑ 3c53d378 復命タブUI調整: 実績出張期間・出張取消を右カードに (1件)
☑ 3312f72b 出張日程変更対応: 復命書に実績出張期間を追加 (6件)
────────────────────────────────
選択3件の和集合 → 配布対象 8ファイル
コミットを複数チェックすると、それらが変えたファイルの和集合が配布対象になる。各行には、そのコミット単体で何ファイル配ることになるかを併記した。マージコミットも、取り込んだ内容を正しく数える。
配布対象が0件のコミットは一覧に出さない。 選んでも配布物に寄与しないからだ。実測すると、直近40コミットのうち一覧に出るのは16本。残り24本はブログや周辺ツールで、NGW 本体を触っていない。6割が消えて、選ぶべきものだけが残った。
これが一番わかりやすかった。境界を推理する作業が、まるごと消えたからだ。
選んだ案件が、そのまま説明書きになる
もうひとつ足した。チェックした案件の件名が、更新内容にそのまま入る。
// NGW UPDATE DATE: 2026-07-17 21:30:00
// UPDATE FILES: 案件選択 3コミット
// VERSION: 2026 update 7.17.0
※ 更新内容
・復命未登録時は実績出張期間・出張取消の操作UIを出さず、登録を促す案内に変更
・復命タブUI調整: 実績出張期間・出張取消を右カードに集約、既定値と赤字の不具合修正
・出張日程変更対応: 復命書に実績出張期間を追加し、勤怠は実効期間で出張表示
これが更新モジュールに同梱される説明ファイルにそのまま載る。説明書きの手打ちが不要になった。コミットメッセージを丁寧に書いておけば、それが配布物の説明になる。
ということは、これまで私は同じ内容を二度書いていたわけである。コミットに一度、説明ファイルにもう一度。しかも後者は配布のたびに手で打ち直していた。20年だ。気づいたのが今日でよかったのか、遅すぎたのか、判断がつかない。
ひとつ、諦めたこと
正直に書いておく。案件を選んでも、配られる中身は常に最新のファイルそのものだ。
NGW は PHP で書かれている。コンパイルされないからソースをそのまま配る。全ユーザーが同一のファイル構成で動く。だから「このコミット時点のファイルの姿」だけを配ることはできない。あるファイルを案件Aで直し、後で案件Bでも直したなら、Aだけ選んでもBの修正は付いてくる。
選んでいるのは「どのファイルを含めるか」であって、「いつの版を含めるか」ではない。
これは非コンパイル言語の宿命だ。そして実害はない。私の更新はもともと複数案件の一括で、前回と重複しても同じファイルが上書きされるだけだからだ。害がないものを、精密に作り込む必要はない。 画面にその旨だけ書いて、先へ進んだ。
数字で見る
| 項目 | 値 |
|---|---|
| 更新日で拾えたファイル | 74件 |
| 実際に内容が変わったファイル | 6件 |
| 無関係な混入 | 68件(gitが触っただけ) |
| 抽出モード | 3つ(更新日/案件選択/起点から一括) |
| 案件一覧に出るコミット | 直近40本中 16本(0件の24本は非表示) |
| 起点ごとの件数計算 | 30本で約 0.34秒(実diff基準) |
件数は実際の差分で数えている。コミットの日付順に積み上げる方法も考えたが、マージで入った変更を取りこぼすので採らなかった。日付の順番と履歴の親子関係は別物だ。ここでも日付に足を掬われかけた。
AIとの協働作業での学び
今日いちばん効いたのは、AI の正しい説明を受け入れなかったことだった。
「同じ件数は等価だから、どれを選んでも同じ結果になります。安全です」。これは事実で、反論の余地がない。それでも私は「選べない」と言った。そこから案件選択が生まれた。正しさで会話を終わらせていたら、道具は良くならなかった。
AI は事実の検証がすこぶる速い。74件と6件を数え、どのコミットが本体を触っていないかを並べ、日付順の積み上げがマージで壊れることまで指摘してくる。だが「それで人間が選べるのか」は使う側にしか分からない。測るのは向こうの仕事、困るのはこちらの仕事。
もうひとつ。壊れたのは道具ではなく前提だった。「ファイルの更新日=私の編集の証跡」は20年正しくて、git を日常的に使い始めた瞬間に嘘になった。エラーは出ない。誰も教えてくれない。ただリストが少しずつ長くなっていくだけだ。
AI を入れたことでこの前提が壊れた。同時に、壊れた前提を掘り当てたのも AI との対話だった。道具が増えると、道具が前提を壊す。情報が増えれば判断できるようになる、というのは嘘だ。今日はむしろ材料が多すぎて分からなくなっていた。増えた情報を、人間が考えている単位——ここでは案件——に畳み直して、やっと選べるようになった。
配る仕組みは、とうに完成していた。足りなかったのは、何を配るかを、私の頭の中と同じ形で選ばせてくれる画面の方だった。