• Status: Accepted (accepted-with-residual-risks — 末尾の HITL 残余リスク節参照。PR #1401 merge = 受理の規約により 2026-06-04 受理)
  • Mode: Standard
  • Kruchten Type: Executive/Property
  • Scope: platform
  • Implementation Status: Not Started
  • 起案者: [email protected]
  • 起案日時 (JST): 2026-06-04 17:38
  • 承認日時 (JST): 2026-06-04 (PR #1401 merge = HITL 手動受理・CV 4 連続 goalpost のため手動迂回)
  • Deciders: [email protected] (単独)

コンテキスト

§1.1 背景

docs サイトのローカルビルド (scripts/docs-build.mjs) には差分ビルドが無く、nav 登録された全ページを毎回フルビルドしている。docs:dev は nodemon が保存のたびにフルビルドを再実行するため、md を 1 行直すたびに長時間待たされ、ライブリロードが事実上機能していない。

§1.2 現状 (As-Is)

2026-06-04 の実測で 611 ページ / 4 分 47 秒 (286 秒, CPU 34%)。1 ページの修正のために 611 ページ分を再生成しており、理論上の無駄 = 610/611 ≒ 99.8%。遅さの一因は glossary ホバー注入 (ADR-0068) が全ページ × 用語の置換を行うことと推定 (実測ログ: changelog 1 ページで 55 置換)。

§1.3 課題

  • md を 1 行直すたびに約 5 分待ち、ライブリロードが機能していない
  • Cloudflare Preview への push 往復が必要で、書きながらの確認に不向き
  • failure_patterns #40 (build 成功 ≠ anchor 解決) と同種の「成功に見えて壊れている」リスクを伴う差分ビルドを安全に導入する必要

§1.4 制約・要件

  • glossary 注入 (ADR-0068) と両立すること。コード実査 (2026-06-04) で scripts/docs-build.mjs L409-414 の injectGlossarySpansbuildPage 内側で都度実行され、L460 の GLOSSARY = loadGlossary() は起動時 fresh ロードであることを確認済み → 注入パイプラインの改修は不要 (改修コスト 0)
  • Cloudflare Pages (本番/Preview) の挙動は変更しない (毎回クリーン環境=常にフルビルドのまま)
  • 差分判定の脆弱性 (mtime 破壊・CI clone・WSL2/Docker volume・FAT32/exFAT) を構造的に回避すること
  • stale 検知を能動化し、PR 段階で本番 stale を防ぐこと
  • 自工数のみで実装 (金銭支出 0 円)

§1.5 目標 (To-Be)

保存→反映 286 秒 → 5 秒以下 (目標 1〜2 秒、≥98% 短縮) をローカル DX で実現。

Non-Goals:

  • Cloudflare Pages (本番/Preview) の挙動変更 — 毎回クリーン環境のため「出力欠如→フル」則により常にフルビルドのまま不変。将来 Pages がビルドキャッシュで _site を保持する仕様変更を行った場合は前提が崩れるため、撤退条件 #6 として独立項目化
  • glossary 注入アルゴリズム自体の高速化 (O(pages × terms) のフルビルド時総量は構造的に未解決のまま。差分ビルド時は再生成ページ分のみ)。棚上げの永続化を防ぐためページ数 1000 超過 or フルビルド 10 分超過を再評価トリガーとして明記
  • ファイル削除・リネーム時の孤児 html の自動掃除 (検知・警告は --check-orphans で行うが、削除自体は --full + rm -rf _site の運用とし how-to に追記)

決定

docs-build.mjsmtime + コンテンツハッシュ (SHA-256) のハイブリッド判定による差分ビルドを既定で導入する。グローバル入力は GLOBAL_INPUTS 定数として明示宣言してフル fallback、CI=true で強制フル、.build.lock の PID 生存確認・GLOBAL_INPUTS 専用 debounce・30% 超ショートサーキット・閾値ベース黄色警告・孤児検知・JSON Lines ログ・PR diff CI ジョブを初期リリースに同梱する。escape hatch として --full / --only <path> フラグを追加。中期タスク (静的解析 lint・促進 GitHub Actions・Preview 自動確認 CI) は期日付きで撤退条件の判定対象とする。

判断基準 (Decision Drivers)

3.1 評価軸

#重要度 (係数)案件特有の解釈
1#reliable[Must] (×2.0)stale ページが出ない・本番に古い html がデプロイされない (failure_patterns #40 と同種リスクの抑止)
2#efficient[Must] (×2.0)md 1 本変更後の再ビルド ≤ 5 秒 (目標 1〜2 秒)
3#maintainable[High] (×1.0)GLOBAL_INPUTS 追記等の運用負担が継続可能。維持コスト累積時間を 4 週後に定量評価
4#operable[High] (×1.0)docs:dev の保存フローと自然に統合・ゾンビロックや CI 環境で誤動作しない
5#flexible[Medium] (×0.5)--full / --only の escape hatch、将来の並列化・案 E 移行余地

K.O. criterion: Must 軸 (#reliable, #efficient) の score < 3 は不採用。

3.2 評価軸 × 案スコア表

係数採択案 B (mtime+SHA-256 ハイブリッド)案 A (--only のみ)案 C (現状維持)案 D (Eleventy 移行)案 E (chokidar+dep-graph)
#reliable×2.045533
#efficient×2.054155
#maintainable×1.034522
#operable×1.042233
#flexible×0.543244
加重和 (正規化)0.8000.7600.5600.6600.660
K.O. 通過 (Must ≥3)❌ (#efficient=1)

検討した代替案 (Alternatives Considered)

  • 案 A: --only 単一ページフラグのみ — 正しさのリスクはゼロだが毎回パス指定が必要で docs:dev (保存→自動) と相性が悪い。B を作れば A はフラグとしてほぼタダで付くため、撤退条件 #3 発動時の縮退先とする
  • 案 C: 現状維持 + Cloudflare Preview 確認運用 — push 往復が必要で書きながらの確認に不向き。K.O. criterion (#efficient) で不採用
  • 案 D: 差分ビルド内蔵の SSG (Eleventy 等) へ全面移行 — 自前 build の移植コスト粗見積もり: nav 生成 8h / glossary 注入移植 12h / mermaid 4h / 検索連携 6h / 全体動作確認 8h ≒ 38h+ (案 B の約 4 倍) で過剰
  • 案 E: chokidar + dependency-graph 等の差分判定ライブラリのみ部分採用 — mtime 破壊・依存追跡漏れを構造的に解決し得るが、ライブラリ学習・依存追加・常駐プロセス化のコストが本件工数を超過。ページ数 1000 超過時の再評価トリガーで案 E を改めて比較する (その際は維持コストの定量比較を必須とする)
  • (盲点 #2) Worker Threads 4 並列でのフルビルド高速化 — 286 秒の支配項が glossary 置換の CPU 処理 (CPU 34%) であれば、4 並列化で 60〜80 秒台に落ちる可能性がある。実装前事前調査に「Worker Threads 4 並列でのフルビルド計測 (1h 以内)」を追加し、並列化後の時間が 30 秒以内に収まる場合は差分ビルドを先送りする判断基準を採用条件に加える。計測結果を本 ADR に追記する

影響 (Consequences)

§5.1 正の影響 (Good)

  • md 1 本変更時の保存→反映が 286 秒 → ≤ 5 秒 (目標 1〜2 秒、≥98% 短縮)
  • docs:dev のライブリロードが実用化し、書きながらの確認が可能に
  • glossary 注入は差分時に再生成ページ分のみ実行されるため、注入コストも 1/611 まで縮退 (コード実査済み)
  • GLOBAL_INPUTS 定数化により全ページ影響入力が PR 差分に乗り、レビュー時に可視化される
  • JSON Lines ログによりヒット率と維持コストの定量評価基盤が整備される

§5.2 負の影響 (Bad)

  • 差分判定ロジック・manifest・安全装置一式の維持コストが恒常的に発生 (4 週後レビューで定量記録)
  • GLOBAL_INPUTS 追記が新規参加者の見落とし対象となり、リリース初日から未登録の全ページ影響入力があれば stale が発生し得る (盲点 #1)
  • PR diff CI ジョブが追加され、フルビルド 286 秒級ジョブが該当 PR にかかる (パスフィルタで限定)
  • node_modules/.cache 配置は npm ci で消去されローカル初回ビルドは常にフルになる (盲点 #3)
  • 「速さ」の即時フィードバックが現在バイアスを生み、Preview 確認が形骸化するリスク

§5.3 中立・トレードオフ (Neutral / Trade-offs)

  • Cloudflare Pages は仕様により常にフルビルドのまま (本件はローカル DX のみ)
  • 差分ビルドの複雑な安全装置と、並列化による単純なフルビルド高速化 (案 E に近い思想) のトレードオフは事前調査で確定させる
  • 撤退条件 #3 (2 人日超過) は手戻りバッファを含めると発動可能性が高く、設計完了時点での再見積もりも判定対象に拡張する (盲点 #5)

撤退条件 (Rollback Plan)

#判定指標期限/検知アクション
1stale ページの実害報告 ≥ 1 件 (ローカルで古い内容を見て判断を誤った等)発生時既定を旧挙動 (常時フル) に戻す。差分は opt-in フラグに降格
2導入 4 週で差分ヒット率 < 50%。判定は JSON Lines ログの自動集計に基づく定量評価とし体感は補助4 週後のログ集計無効化ルールの見直し or 撤退。維持コスト累積時間も併せて定量記録・評価
3実装が 2 人日を超過。設計完了時点での再見積もりで 2 人日超過が確実な場合も対象に拡張 (盲点 #5)実装中 / 設計完了時案 A (--only のみ) に縮退
4グローバル入力の見落とし由来 stale の発覚 (閾値警告・孤児検知 or PR diff CI で検知)発生時GLOBAL_INPUTS 即時追記。同種再発時は差分既定 OFF に降格 + 静的解析 lint の導入を前倒し
5ページ数 1000 超過 or フルビルド 10 分超過4 週後レビュー + 継続観測glossary 注入アルゴリズム最適化と案 E (ライブラリ部分採用) を再評価。案 E との維持コスト定量比較を必須とする
6Cloudflare Pages がビルドキャッシュで _site を保持する仕様変更を行った場合仕様変更の検知時「出力欠如→フル」前提が崩れるため本番 stale 防止策を再設計 (CI=true 強制フル + diff CI の有効性を再検証)
7中期タスク (静的解析 lint = 2 週間以内 / 促進 GitHub Actions = 2 週間以内 / Preview 自動確認 CI = 4 週間以内) の期日未達4 週後レビューヒット率・維持コストに関わらず撤退条件の判定対象とし、差分既定 OFF への降格を検討
8事前調査で Worker Threads 並列化により素のフルビルドが 30 秒以内に収まることが判明 (盲点 #2)実装前事前調査差分ビルド導入を先送りし、並列化 ADR を別途起案
9事前調査で現行フルビルドの 2 回連続実行 diff がゼロにならない (冪等性不成立、盲点 #4)実装前事前調査diff CI ジョブの設計前提が崩れるため、PR diff CI なしで進めるか差分ビルド自体を見送る

Confirmation

検証手段 / 実行頻度 / 違反時対応:

  • 検証手段:
    • md 1 本変更後の再ビルド時間 ≤ 5 秒 (目標 1〜2 秒) を time で実測
    • _config.json / glossary.md / テンプレート変更時にフルビルドへ落ちることを 3 ケースとも確認
    • git checkout でブランチ切替直後の初回ビルドで「意図しない全スキップ」「無駄な全再生成」のどちらも起きないこと (hash 比較の有効性確認)
    • CI=true で自動フルになること + Cloudflare Pages ビルド設定の CI=true をスクリーンショットで証跡化 (リリース前)
    • manifest を意図的に破損させた場合にフルビルドでなくページ単位の部分再生成で自動復旧すること / schemaVersion 不一致時にフルへ自動フォールバックすること
    • manifest が node_modules/.cache/docs-build/ 配下にあり、本番 URL から到達不能であること
    • .build.lock に死亡 PID を書き込んだ状態で起動し、自動解放されて無音停止しないこと
    • 境界値テスト: 「mtime のみ変化・SHA-256 一致」でスキップされること / 「mtime 不変・SHA-256 不一致 (タイムスタンプ改ざんで再現)」で再生成されること / WSL2 or Docker volume の mtime 丸め挙動を最低 1 ケース実機確認 (再現環境がない場合は残存リスクとして記録)
    • 一括変更ベンチ: 50 / 200 / 611 ファイル同時変更で素のフルビルド 286 秒を上回らないこと・30% 超でショートサーキットが発火すること
    • 孤児検知: ページ A を B にリネームしたケースで _site/A.html が孤児として警告されること (--check-orphans)
    • 611 エントリ相当のダミー manifest で読み書き単体ベンチマークを実施し目標 5 秒に占める割合を記録
    • 差分ビルド後の該当ページをブラウザで開き、サイドバー nav・glossary ホバーが正しいことを目視 (注入がページ単位で走ることの実機確認を兼ねる)
    • rm -rf _site 後の初回がフルになること、--full が常時フルになることを確認
    • ビルド完了時に「再生成 N / 全 M ページ」が毎回出力され、閾値違反時に黄色警告が出ること / JSON Lines ログがローテーション上限 (10MB/7 日) を超えないこと
    • PR diff CI ジョブが初期リリース前に有効化されていること (パスフィルタ + 並列 + 正規化。意味的に等価な出力で false positive が出ないことをリリース前に確認)
    • (盲点 #1) リリース前に現行ビルドスクリプトの全ファイル読み込みパスを手動で網羅的にリストアップし GLOBAL_INPUTS の完全性を確認する。動的 glob 読み込みが存在する場合は静的解析で検出不能な経路を残存リスクとして記録
    • (盲点 #3) manifest 配置先の選定にあたり (1) npm ci での消去有無、(2) CI の node_modules キャッシュ戦略との干渉、(3) パッケージマネージャ変更時の移行コストを検討した記録を残し.build-cache/ 等の代替案との比較を本 ADR に追記
    • (盲点 #4) 現行フルビルドを同一環境で 2 回連続実行し、出力 html 同士の diff がゼロになることを確認 (ビルド冪等性の検証)。冪等性が確認できて初めて diff CI の正規化対象が確定する
  • 実行頻度:
    • リリース前: 上記項目の全数実機確認
    • 通常運用: PR ごとに diff CI ジョブ (パスフィルタ対象 PR のみ)
    • 4 週後レビュー: docs:dev の体感が 5 秒以内で安定 / JSON Lines 集計の差分ヒット率 ≥ 50% / 維持コスト累積時間の定量記録 / 中期タスク完了状況の評価 / ページ数増加トレンドを記録
  • 違反時対応: 撤退条件 #1〜#9 のいずれかに該当するため、当該条件のアクションを発動

事前調査 (実装前に実施し結果を本 ADR に追記)

  • 直近 3 ヶ月の git log から glossary.md / _config.json / テンプレートの変更頻度を集計し、フル fallback 発動率 (差分ヒット率) の事前推定値を得る。推定ヒット率 < 50% の場合は「glossary 注入パスのみ差分対象から分離する中間案」を採用条件として再検討する
  • 611 エントリ相当のダミー manifest で SHA-256 記録の読み書き単体ベンチマークを実施し、目標 5 秒のうち manifest 処理が占める割合を計測・記録する
  • glossary 注入パスのコード実査は実施済み (§1.4 制約・要件参照。注入は buildPage 内側・改修不要)
  • (盲点 #2) Worker Threads 4 並列でのフルビルド計測 (1h 以内) を実施し、30 秒以内に収まる場合は差分ビルドを先送り (撤退条件 #8)
  • (盲点 #4) フルビルド 2 回連続実行の冪等性検証を実施し、diff CI の設計前提を確定 (撤退条件 #9)

中期タスクの期日明示 (永続的先送りの防止)

  • 静的解析 lint (GLOBAL_INPUTS 定数とビルドスクリプトが実際に読み込むファイルパスを比較し未登録を検出) = 初期リリース後 2 週間以内
  • GLOBAL_INPUTS 追記促進 GitHub Actions (docs/ 以下の構造的ファイル追加を検知して PR コメント) = 初期リリース後 2 週間以内
  • Preview URL 自動確認 CI (nav・glossary 存在チェック) = 初期リリース後 4 週間以内
  • PR テンプレートへ「docs/ 構造的ファイル (テンプレート・CSS・設定) を追加・変更した場合は GLOBAL_INPUTS を確認したか」チェックボックス追加 = 初期リリース同時
  • 4 週後レビューの評価項目に「中期タスクの完了状況」を含め、期日未達は撤退条件 #7 の判定対象とする

コスト試算

作業工数 (h)
mtime+SHA-256 ハイブリッド判定 + manifest (アトミック書込・破損フォールバック・schemaVersion・node_modules/.cache 配置)2.5
GLOBAL_INPUTS 明示宣言 + CI=true ガード + .build.lock (PID 生存確認・タイムアウト) + GLOBAL_INPUTS 専用 debounce + 30% 超ショートサーキット1.5
--full / --only (グローバル入力変更警告含む) + docs:dev 配線確認1.0
再生成数出力 + 閾値ベース黄色警告 + 孤児検知 (--check-orphans) + JSON Lines ログ (ローテーション 10MB/7 日)1.0
PR diff CI ジョブ (パスフィルタ + 並列実行 + 非決定的要素の正規化)1.0
動作確認 (基本 9 シナリオ + 境界値 4 種 + 一括変更ベンチ 50/200/611 + 孤児検知。再現環境のないケースは残存リスク記録)2.0
事前調査 (git log 変更頻度→ヒット率推定 + manifest 読み書きベンチ + Worker Threads 並列計測 + フルビルド冪等性検証)0.5
ドキュメント (how-to 追記 + Pages CI=true 証跡 + pre-push リマインダー + PR テンプレートのチェックボックス)0.5
合計 (素見積もり)約 10h (1.25 人日)
(盲点 #5) 発見的リスクによる手戻りバッファ (20〜30%)+2〜3h
合計 (バッファ込み)約 12〜13h (1.5〜1.6 人日)

金銭支出 0 円 (自工数のみ。GitHub Actions 有料プラン利用時は diff CI の月次実行時間を別途試算)。期待効果: 保存→反映 286 秒 → 5 秒以下 (目標 1〜2 秒、≥98% 短縮)。維持コスト (運用後のバグ対応・GLOBAL_INPUTS 追記・stale 調査) は 4 週後レビューで実測を定量記録し本欄に追記する。

WSL2/Docker mtime 丸め実機確認 (+2〜4h)・PR diff false positive 調査 (+1〜3h)・tmp→rename の NTFS/WSL2 確認・process.kill(pid, 0) の Windows/WSL2 差異確認は手戻りバッファ内で吸収するか残存リスク記録に降ろす。

リスク一覧 (盲点検出レポート反映済み)

  • mtime 破壊: SHA-256 ハイブリッド + CI=true 強制フル + WSL2/Docker 注意の how-to 記載
  • manifest 破損・部分書き込み: アトミック書込 + エントリ単位フォールバック + schemaVersion 照合。Confirmation に破損復旧シナリオ
  • ゾンビロック: PID 生存確認 + 10 秒警告/30 秒強制解放
  • GLOBAL_INPUTS 完全性 (盲点 #1): リリース前に手動網羅確認を Confirmation に追加。動的 glob があれば残存リスクとして記録
  • stale 検知の受動性: 閾値ベース黄色警告 + 孤児検知 + JSON Lines ログ + PR diff CI ジョブ
  • diff CI の false positive と冪等性 (盲点 #4): 事前調査でフルビルド 2 回 diff のゼロ確認、不成立時は撤退条件 #9
  • 並列化代替案の未検討 (盲点 #2): 事前調査で Worker Threads 4 並列を計測、30 秒以内なら撤退条件 #8 で先送り
  • manifest 配置と npm/CI ライフサイクル (盲点 #3): npm ci での消去・CI キャッシュ干渉・パッケージマネージャ変更コストを事前検討し代替案 (.build-cache/) を比較記録
  • 動作確認の網羅性限界: 境界値を Confirmation 明示、再現環境のないケースは残存リスク記録
  • Preview 確認の形骸化: 起動時メッセージ + pre-push リマインダー + GLOBAL_INPUTS 変更 PR への必須確認項目自動挿入 + Preview URL 自動確認 CI (4 週以内)
  • 維持コスト過小評価 (盲点 #5): 工数バッファ 20〜30% + 撤退条件 #3 を設計完了時の再見積もりにも拡張
  • Cloudflare Pages の仕様変更: 撤退条件 #6 で独立項目化
  • failure_patterns #40 教訓: Confirmation に stale 検出シナリオを複数明記

残余リスクと受理経緯 (HITL escalate)

本 ADR は審査パイプラインで Gate 4 scoring に 4 回連続合格 (44 → 46 → 47 → 48 / Standard 閾値 40) した一方、Cross-Validation4 回連続差し戻し (critical 盲点が毎回別種に入れ替わる goalpost パターン・[[adr-0109]] が対象とする事象) となったため、ADR-0109 (bounded rounds + HITL 残余リスク受理) の趣旨に基づき人間判断で受理する。chat UI 経路は DRP-371 (draftId=null で loop-breaker 無効) により自動 escalate (PR 自動起票) が発火しないため、本 PR は手動起票である。merge = 受理 / close = 却下

各 round の critical と対応:

roundscoringcritical 盲点対応
144/50mtime 単独判定の信頼性 (git checkout / CI / Docker で破壊)生 context v2 で mtime+SHA-256 ハイブリッドを起案者決定に昇格し解消
246/50diff CI が「導入後フォローアップ」扱い + Pages CI=true 証跡なしv3 で初期リリース前必須化 + 証跡化し解消
347/50glossary 注入粒度がページ単位差分と非両立の疑いコード実査で反証 (scripts/docs-build.mjs L409-414 = buildPage 内側注入 / L460 = 起動時 fresh ロード)・v4 に反映し解消
448/50GLOBAL_INPUTS 完全性のリリース前確認が lint でなく手動本節の残余リスク 1 として受理

受理時の残余リスク:

  1. GLOBAL_INPUTS 完全性のリリース前保証は手動である: 静的解析 lint は初期リリース後 2 週間以内の期日付き中期タスクであり、リリース前の防壁は「ビルドスクリプトの全ファイル読み込みパスの手動網羅リストアップ」(Confirmation 記載)。動的 glob 読み込みが存在する場合は静的解析でも原理的に検出不能な経路が残る。緩和 = 撤退条件 #4 (発覚時の即時追記 + 同種再発時の既定 OFF 降格) + 閾値警告・孤児検知・PR diff CI の三重の事後検知
  2. 再現環境のない境界値ケースの実機未確認: WSL2 / Docker volume の mtime 丸め・NTFS の rename 挙動・process.kill(pid, 0) の OS 差異は、再現環境が用意できない場合に残存リスクとして記録して進める (Confirmation に明記済み)

参照 (References)

  • 関連 ADR: ADR-0068 (glossary ホバー注入 = 全ページ依存の根拠) / [[adr-0109]] (bounded rounds + HITL 受理 = 本 ADR の受理根拠)
  • 関連 PR/Issue: failure_patterns #40 (build 成功 ≠ anchor 解決) / DRP-371 (chat UI 経路の自動 escalate 不発 = 手動起票の理由)
  • 実測: 2026-06-04 フルビルド 611 ページ 286 秒 (本起案の根拠データ)
  • コード実査 (2026-06-04): scripts/docs-build.mjs L409-414 (buildPage 内の glossary 注入)・L460 (GLOSSARY = loadGlossary() 起動時ロード) — glossary 注入がページ単位差分と両立する根拠
  • 外部資料: make / ninja / Bazel が mtime + コンテンツハッシュを併用する設計慣行 (mtime 単独判定の既知脆弱性への業界標準対処)
  • リリース前取得: Cloudflare Pages ビルド設定の CI=true 証跡スクリーンショット (docs へ)
  • 実装前取得: 直近 3 ヶ月 git log の glossary.md / _config.json / テンプレート変更頻度集計 / Worker Threads 4 並列ベンチ / フルビルド冪等性検証
  • 実装は scripts/ のため main ワークスペース領分 (本起案は sub から)