NGW開発BLOG
← 記事一覧へ
·#066

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 との対話だった。道具が増えると、道具が前提を壊す。情報が増えれば判断できるようになる、というのは嘘だ。今日はむしろ材料が多すぎて分からなくなっていた。増えた情報を、人間が考えている単位——ここでは案件——に畳み直して、やっと選べるようになった。

配る仕組みは、とうに完成していた。足りなかったのは、何を配るかを、私の頭の中と同じ形で選ばせてくれる画面の方だった。