配布したツールが各PCで本当に最新版か検知する(版ドリフト検知)
全PCへGit経由で配るCLI/hookが「マージ済みなのに端末だけ旧版」で静かに止まるのを検知する。ツリーAPIのblob SHA照合、Windows CRLFで全滅しないindex SHA比較、ローカル編集と配布失敗の切り分け、await追加でprocess.exitが落ちる罠まで。
約9.8万トークンの節約 (API料金換算で約150円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「配布したツールが各PCで本当に最新版か検知する(版ドリフト検知)」は、開発プロセスカテゴリのAI指示書(MDファイル)です。全PCへGit経由で配るCLI/hookが「マージ済みなのに端末だけ旧版」で静かに止まるのを検知する。ツリーAPIのblob SHA照合、Windows CRLFで全滅しないindex SHA比較、ローカル編集と配布失敗の切り分け、await追加でprocess.exitが落ちる罠まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約9.8万トークン(API料金換算で約150円)・82%のトークンを節約できます。
- カテゴリ
- 開発プロセス
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約12万トークン
- この巻物使用時
- 約2.2万トークン
- 節約量
- 約9.8万トークン (約150円)
- 更新日
- 2026-08-20
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/md-b050848f/raw を読み込んで、この指示書どおりに実装して"
中身
配布したツールが「各PCで本当に最新版か」を検知する(版ドリフト検知)
社内の全PCへ CLI / hook / スクリプトを Git リポジトリ経由で配っている運用向け。 「main にマージした」「取得URLは最新を返している」のどちらも、そのPCで最新版が動いている証明にはならない。 このMDは、各PCのファイル実体を上流と突き合わせて不一致を警告する仕組みの作り方をそのまま渡す。
なぜ静かに壊れるか
配布の実体が git pull --ff-only の場合、作業ツリーが汚れている端末では pull が abort する。
更新スクリプトが「取得の失敗を配布本体の巻き添えにしない」設計(=良い設計)だと、
pull が落ちても後続処理は続くため ログ1行だけ残して古いままになる。誰も気づかない。
同じ穴は次のケースでも開く。
- 端末のローカルブランチが上流と diverge している(
Not possible to fast-forward, aborting.) - 未追跡ファイルが衝突して checkout できない
- 配布物が git 管理外にコピーされて運用されている
判定の設計(この4点が要点)
1. 上流の正解は「ツリーAPI 1発」で取る
GitHub なら GET https://api.github.com/repos/<owner>/<repo>/git/trees/<branch>?recursive=1。
1リクエストで全ファイルの blob SHA が返る。public リポジトリなら認証不要(未認証は 60req/h)。
レスポンスはローカルにキャッシュ(TTL 6時間程度)し、取得失敗時はキャッシュで判定して「キャッシュ判定」と明示する。
raw 配信URL(CDN)でファイル本文を取って比較してはいけない。 CDN キャッシュ(数分)で古い版が返り、 「上流が古い」という誤検知になる。API はキャッシュ層が別なのでこの問題を踏まない。
2. ローカル側は git の index SHA と比較する(最重要)
blob SHA は sha1("blob " + バイト長 + "\0" + 内容) で自前計算できる。
しかし 作業ツリーのバイト列をハッシュすると Windows で全滅する。
core.autocrlf=true や .gitattributes の * text=auto があると、.md .json .ps1 などは
作業ツリーで CRLF に展開される。上流の blob SHA は LF 正規化後の内容なので、必ず食い違う。
実測で「全ファイル一致しているのに 17件の偽陽性 🚨」が出た。
→ git ls-files -s -z を1回呼び、<mode> <sha> <stage>\t<path> を解析して path → index SHA を作る。
index は LF 正規化後の内容を保持しているので改行展開の影響を受けない。これを第一手にする。
// 出力形式に注意: 正規表現の group は 1=sha / 2=stage / 3=path
const m = record.match(/^[0-7]+ ([0-9a-f]+) (\d+)\t([\s\S]+)$/);
if (m?.[2] === '0') indexShas.set(m[3], m[1]);
git が無い / リポジトリでない / 未追跡のパス向けに、フォールバックを併走させる: バイト列で計算 → 不一致なら CRLF→LF に正規化して再計算(どちらか一致で ok)。 ヌルバイトを含むファイル(バイナリ)は正規化を試さない。
3. 「ローカル編集中」と「配布が届いていない」を分ける
git status --porcelain -z に出ているパスは開発者が今いじっているだけなので ⚠️(警告)に落とす。
🚨 は「status はクリーンなのに上流と違う」=pull 失敗由来の旧版・ファイル欠落だけに絞る。
ここを混ぜると開発機で毎日 🚨 が出て、本当の異常が読まれなくなる。
4. 判定できなかったことを黙って省略しない
API 到達不可・キャッシュ無しは unknown として言葉で報告に出す。
「何も出ない」は「異常なし」と読まれるため、配布物として最悪の壊れ方になる。
通知に載せるときの注意
- チャットへ投稿する報告は文字数上限でトリムされることが多い(例: 本文を先頭から一定長で切る実装)。 新しい判定行は報告の先頭付近に入れる。 末尾に足すと切り落とされて存在しないのと同じになる。
- 文言は既存の判定テキストと衝突しないものを選ぶ。既存テストが
!stdout.includes('判定不能')のような assert を持っていると、同じ語を使うだけで無関係なテストが落ちる。 - 復旧コマンドは絶対パス形で書く(読む人の作業ディレクトリは不明)。
実装で必ず踏む罠: await を1つ足すと process.exit が落ちる
判定に fetch を入れると、それまで同期実行だったスクリプトに top-level await が入る。
この状態で末尾の process.exit(0) を通ると、Windows の Node が
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING) で異常終了する(終了コード 3221226505)。
出力は全部 stdout に書けているのに、呼び出し側(hook ランナーや CI)は失敗として扱う。
→ await を1つ足したら、そのファイル内の process.exit() を全部 process.exitCode に変える。
早期終了は return か if/else の分岐で表現する。回帰は
「--dry-run の終了コードが 0 であること」を assert するテストで縛れる。
テストの作り方(ネットワーク非依存にする)
- 判定関数は
{ repo, tree, indexShas, statusPaths, fetchTree, cacheFile, now }を注入できる形にする。 ツリーはフィクスチャのオブジェクトを渡し、API を叩かせない。 - 固定すべきケース: 一致 / 旧版(status に無い) / ローカル編集中(status にある) / 欠落 / 取得不能(unknown) / 上流が LF・ローカルが CRLF → ok(この回帰が本命) / バイナリ差分。
- blob SHA 計算が git 互換であることを既知値で固定する(内容
hello\n→ce013625030ba8dba906f756967f9e9ca394464a)。 - テストスイート全体が実 API を叩くと未認証レート制限に当たるので、環境変数でスキップできる逃げ道を作り、 実 fetch が必要な1本だけ明示的に外す。
受け入れ確認(これを実測しないと完成ではない)
- 同期直後のクリーンな作業ツリー → ✅ 一致
- 「pull が失敗して止まった端末」を再現して 🚨 が出る
git checkout <古いcommit> -- <file>ではステージされて ⚠️ 扱いになり再現しない- 正しい再現は
git worktree add --detach <dir> <古いcommit>(=HEAD が古く status はクリーン)
- 同期し直すと 🚨 が消える
- git 管理外にコピーした配布物(CRLF 展開済み)で ✅ になる(CRLF 免疫の確認)
- そのコピーの1本だけ旧版に差し替えると 🚨 1件
出力例
🚨 **配布物の版ドリフト** 13件 — tools/a.mjs(旧版) / tools/b.ps1(旧版) / tools/c.mjs(欠落) / 他10件
→ 復旧: `node ~/<配布リポジトリ>/tools/<更新スクリプト> --force`(失敗するなら作業ツリーの汚れ/diverge を解消)
✅ **配布物の版一致**(上流と 71 ファイル一致)
⚠️ **配布物の版**照合できず(API 到達不可・キャッシュ無し。次回再判定)
応用: 「マージしたのに main に入っていない」も同じ考え方で潰す
共有リポジトリで複数の作業者/エージェントが同じブランチ ref を force-push すると、
PR の表示が MERGED でも自分の変更が1行も main に入っていないことがある。表示は証拠にならない。
マージ後は必ず実体で確認する。
git merge-base --is-ancestor <自分のcommit> origin/main && echo "入っている"
gh api repos/<owner>/<repo>/contents/<新規ファイル>?ref=main -q .sha
よくある質問
+「配布したツールが各PCで本当に最新版か検知する(版ドリフト検知)」とは何ですか?
全PCへGit経由で配るCLI/hookが「マージ済みなのに端末だけ旧版」で静かに止まるのを検知する。ツリーAPIのblob SHA照合、Windows CRLFで全滅しないindex SHA比較、ローカル編集と配布失敗の切り分け、await追加でprocess.exitが落ちる罠まで。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約12万トークンかかりますが、この巻物を使えば約2.2万トークンで済みます。差し引き約9.8万トークン(API料金換算で約150円)・82%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
ドキュメント駆動開発プロセス CLAUDE.md — 作るものを固めてから書かせる
「AIが暴走して意図と違うものを作る」を根絶する開発プロセス指示書。UI仕様→機能設計→実装の順をAIに強制し、1ファイルごとに承認ゲートを挟む。受託開発・チーム開発向け。
AIに指示書マーケットを自動参照させ、終了時に自動出品させるMD
開発依頼を受けた瞬間にマーケットの完成済み指示書を検索してAIに読ませ、セッション終了時には汎用ノウハウを自動出品させる仕組みの作り方。全台配布・秘密情報スキャン・実際に踏んだ配布バグ3つの回避込み。
「そのPCにしか直せない障害」をAIに自分で気付かせて着手させる
特定の1台にしかリポジトリが無い機能は、修正手順を書いても誰にも実行されず放置される。SessionStart hook で当該PCのAIだけに指示を出し、完了後は指示書へ状態を書き戻して再実装事故を防ぐ型。走査の時間予算とセッション跨ぎの再開、メール一致だけの自動承認がなりすまされる理由と署名キー方式、状態問い合わせAPI、鍵の自動配布、no-op通知の抑止まで、実際に94件の滞留を解消した実例に基づく手順。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア