• Status: Accepted
  • Mode: Standard
  • Scope: platform
  • Kruchten Type: Existence/Property
  • Implementation Status: In Progress
  • 起案者: [email protected]
  • 起案日時 (JST): 2026-05-18 23:54
  • 承認日時 (JST): 2026-05-19
  • Deciders: [email protected] (単独)

1. コンテキスト

1.1 背景 (Background)

新規 .mddocs/ 配下に追加した際、docs/_config.json への nav 登録漏れに気づかずビルド対象外のまま放置されるケースが構造的に発生する。CLAUDE.md に「新しい .md ファイル追加時は必ず docs/_config.json の nav 配列に登録」と運用ルールは明記されているが、機械チェックがないため人間/Claude の意識頼りであり、同種の漏れが再発するリスクが構造的に残存している。

1.2 現状 (Current State / As-Is)

実例 (2026-05-18 セッション中に発覚):

  • 2026-05-16: PR #796 で ref_smb_solo_accountant_jtbd_hypotheses.md を main マージしたが nav 未登録
  • 影響期間: 2026-05-16 (mtime) → 2026-05-18 (登録 PR #827) = 2 日間
  • ユーザーがリンク要求するまで誰も気づかず Cloudflare Pages 上で 404 状態
  • 現状の docs ビルド対象 .md は 459 ファイル / リポジトリ内 .md 総数とは差分あり (調査時点で構造的 orphan が残存している可能性 — 初回 lint 実行で確定)

1.3 課題 (Problem)

PR #796/#827 型事故は 2 日間 404 状態を発生させた実害がある。さらに Jr 引き継ぎコスト試算 (2026-10 入社予定 1 名):

  • 引き継ぎ説明 30 分 × 質問 5 回 = **2.5 時間**を毎回繰り返す (属人運用継続)
  • vs. lint 導入後: nav 登録手順を Jr に「lint が教えてくれる」と説明 5 分 + 自動 fail で学習 → 累計 ~10 分

機械チェック不在 = 属人運用 = Jr 入社 (2026-10) で崩壊リスクが構造的に存在。

1.4 制約・要件 (Constraints & Requirements)

  • 既存進行中 PR をブロックしないグレースピリオド設計が必要 (移行期間 2 週間)
  • 初期導入工数は Standard 想定の 5-8 時間レンジ内
  • GitHub Actions 無料枠 (2,000 分/月) 内で運用
  • 外部ライブラリ不要 (Node.js + JSON parse のみ)
  • allowlist による例外管理が可能であること

1.5 目標 (Goals / To-Be)

scripts/docs-nav-lint.mjs を新設し、3 ルール (R1〜R3) を CI 必須チェックとして強制することで PR #796/#827 型事故をゼロ化する。Non-Goals: R4-R6 (プレフィックス規約整合 / 階層深度違反 / 番号重複) は将来拡張とし、本 ADR では実装しない。

2. 決定

scripts/docs-nav-lint.mjs を新設し、3 ルール (R1〜R3) を実装、CI (GitHub Actions) で必須チェックとして実行する。具体:

  • R1 (orphan-md): docs/**/*.md のうち _config.json の nav 配列に登録されていないファイルを検出。初期は WARN、2 週間の grace period 後に FAIL に昇格 (allowlist 設定可: CLAUDE.md / README.md / _template.md 等)
  • R2 (broken-nav-entry): _config.jsonfile フィールドが指す実体ファイルが存在しない場合 FAIL
  • R3 (duplicate-nav-entry): 同一ファイルが複数 nav エントリに登録されている場合 FAIL

配置: .github/workflows/docs-nav-lint.yml で PR 時に実行、npm run docs:lint でローカル実行可能化。本 ADR は ADR-0042 (Prompt Lifecycle 管理) と同等の運用ガードレール拡張の位置付け — ADR-0042 が「プロンプト本文の SSoTKV に置き fallback を hardcoded で持つ + 機械チェックは別途」というパターンで品質を担保しているのに対し、本 ADR は「文書の SSoT を _config.json (nav) に置き、機械チェック (lint) を CI で強制 + 運用ルールを CLAUDE.md に明記」する 2 層構造で品質を担保する。

3. 検討した代替案 (Alternatives Considered)

案 A〜D 評価軸テーブル (明示比較)

評価軸案A CI lint案B build warn案C 何もしない案D 包括的 lint
CI gated?
再発防止性◎ 機械強制△ ログ流れる×◎+ より厳格
初期導入工数~6h~1h0~15-20h
allowlist 整備コスト中 (初回 1h、現状 orphan 数 nav 比 5-15 件想定)低 (なし)0高 (R4-R6 規約整合分含む)
誤検知リスク低 (3 ルール明確、規則ベース)n/an/a中 (規約整合は揺れる、F.X.NN プレフィックス変更等で誤発火)
起案者手戻り頻度月 1-2 件想定 (FAIL は明示ガード)0 (ブロックなし)0月 5-10 件想定 (規約違反検出が多い)
Jr 引き継ぎ○ 自動ガード△ ログ依存× 完全属人○ 自動ガード
拡張余地◎ R4〜R6 段階追加可×× 既に MVP 過剰
  • 案 A (採用): CI lint 新設 + 3 ルール R1-R3。機械的に再発防止、CI ベルトコンベアに乗る、誤検知リスク低、段階拡張可能。
  • 案 B: ローカルビルド warn のみ — 不採用理由: ローカルビルドのログは流れて見過ごされやすく、CI gate していないとすり抜ける (案 A の劣化版)。
  • 案 C: 何もしない (属人運用継続) — 不採用理由: PR #796/#827 で 1 度発生済、Jr 入社で属人運用は崩壊する想定。
  • 案 D: 包括的 lint (R1-R6 一気導入) — 部分採用: 案 A の R1-R3 を MVP とし、R4 (プレフィックス規約 F.X.NN 整合性) / R5 (階層深度違反) / R6 (番号重複) は将来拡張とする。一気に作ると初回手戻り過多 + allowlist 整備コスト過大。

4. コスト試算

実装工数 (Standard 想定の 5-8 時間に収まる)

  • scripts/docs-nav-lint.mjs 実装: ~3h (R1 list diff / R2 file existence / R3 dup detection、Node.js + JSON parse のみ、外部ライブラリ不要)
  • .github/workflows/docs-nav-lint.yml 作成: ~0.5h (既存 adr-lint.yml をテンプレ流用)
  • npm run docs:lint スクリプト追加: ~0.1h (package.json 1 行)
  • 初期 allowlist 整備: ~1h (現状 orphan を grep して allowlist 一括追加、想定 5-15 files)
  • 運用ガイド (docs/_internal/05_how-to/nav-lint-guide.md) 執筆: ~1h (Jr 引き継ぎ可能化)
  • 合計: ~5.6h ≈ 6h (Standard 想定の中央値)

運用コスト (継続)

  • CI 実行: GitHub Actions 無料枠で十分。lint 実行 < 30s × 月 ~20 PR = 月 ~10 分 (Actions 無料枠 2,000 分/月の 0.5%)
  • allowlist メンテ: .md 構造変更時のみ (年 1-2 回想定、各 ~10 分)
  • 撤退条件モニタリング: 月初 5 分 (gh run list 1 コマンド)

Jr 引き継ぎコスト削減効果

  • 削減見込み: 引き継ぎ説明 2.5h → ~10 分 = **2 時間の削減** (1 回切り、ROI は半年で回収)

5. 影響 (Consequences)

5.1 正の影響 (Good)

  • ドキュメント nav 漏れの再発防止 (機械チェック化、PR #796/#827 型事故ゼロ化)
  • PR レビュー時の「nav 登録漏れ確認」観点が自動化され人間レビュアーの認知負荷削減
  • 将来 Jr (2026-10 入社予定) が引き継ぐ際の品質ガードレール (引き継ぎコスト ~2.5h → ~10 分)

5.2 負の影響 (Bad)

  • lint 失敗で PR がブロックされ起案者の修正手戻りが発生 (案 D 一気導入を避け 3 ルールに絞ることで月 1-2 件想定に抑制)
  • 例外管理 (lint 対象外ファイルの allowlist) のメンテコスト (初回 1h、以降 .md 構造変更時のみ)

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

  • 既存 nav 未登録ファイル多数 (PDF / _assets/ / dev/_quarantine/ 等) が初回実行で大量検出 → 初回 PR で allowlist 込みで導入 (下記 grace period 設計で対処)

進行中 PR の救済 (Grace Period 設計)

  • 初回 PR: 既存の全 nav 未登録 .md を allowlist に一括登録 (現状凍結、初回 lint 実行でカウント確定後 scripts/docs-nav-lint.mjs --add-current-orphans で自動生成)
  • 移行期間 (2 週間): R1 を WARN-only で運用 → 起案者・レビュアーの慣熟期間、PR ブロックなし
  • 2 週間後: R1 を WARN → FAIL に切り替え (.github/workflows/docs-nav-lint.ymlseverity: error への 1 行変更)
  • 既存進行中 PR が allowlist 範囲外の orphan を持つ場合は [skip-nav-lint] ラベルで bypass 可 (後述ロールバック §)

6. 検証可能な完了条件

  • scripts/docs-nav-lint.mjs が R1〜R3 を実装、以下 6 テストケースで動作確認:
    • R1 OK: docs/_test_sample.md を作成し nav 未登録 → WARN 出力確認
    • R1 NG: 同 file を _config.json nav に登録 → WARN 出力なし確認
    • R1 allowlist マッチ: CLAUDE.md (allowlist 登録済) → WARN 出力なし確認
    • R2 OK: nav に存在しないファイルパス指定 → FAIL 確認
    • R3 OK: 同一 file パスを 2 つの nav エントリに登録 → FAIL 確認
    • 全 OK: 現状の docs/ 全体で R1/R2/R3 lint pass (allowlist 整備後)
  • .github/workflows/docs-nav-lint.yml で PR 必須チェック (status check に表示確認)
  • npm run docs:lint でローカル実行可能 (exit code 0/1 で CI 互換)
  • 初回 PR で allowlist (現状の例外ファイル群) を明示登録
  • docs/_internal/05_how-to/nav-lint-guide.md で Jr 引き継ぎ可能化 (上記コマンド・撤退手順を網羅)

Confirmation (準拠確認 / Fitness Function)

本 ADR の決定は実装そのものが fitness function として機能する自己検証構造を持つ:

  • 検証手段:
    1. 自動検証 (主): .github/workflows/docs-nav-lint.yml で PR 毎に scripts/docs-nav-lint.mjs を実行、R1/R2/R3 違反を検出。GitHub Actions の status check として PR UI に常時表示
    2. ローカル検証: npm run docs:lint で開発者がコミット前にチェック可能 (exit code 0/1)
    3. 撤退条件モニタリング: 月初に gh run list --workflow=docs-nav-lint.yml --status=failure --created-after=$(date -v-1m +%Y-%m-%d) --json conclusion | jq 'length' で手戻り件数を集計 (BSD date / macOS 構文。Linux/CI では date --date='1 month ago' に置換)
  • 実行頻度:
    • PR 作成・更新時 (自動、毎回必須)
    • 月 1 回の撤退条件レビュー (起案者が月初に実施、5 分)
    • Review After (2026-11-18 / 2027-05-18) で総合再評価
  • 違反時の対応:
    • R1 WARN (移行期間中、〜2026-06-01): PR template チェックボックスで起案者の自認を強制、レビュアーが status check 黄色マークを確認
    • R1 FAIL / R2 / R3 (2026-06-01 以降): PR マージブロック → 起案者が _config.json 更新 or allowlist 追加 or [skip-nav-lint] ラベル付与で bypass
    • 撤退条件抵触 (月 5 件超 × 2 ヶ月連続): R1 を WARN-only に格下げ判断、別 ADR で再評価

7. 撤退条件 (Rollback Plan)

R1 (orphan-md) 撤退条件

以下のいずれかが発生したら R1 を WARN-only に格下げ or 撤去:

  • 起案者の手戻り頻度が月 5 件超
    • 計測方法: gh run list --workflow=docs-nav-lint.yml --status=failure --created-after=$(date -v-1m +%Y-%m-%d) --json conclusion | jq 'length' を月初に実行 (or scripts/docs-nav-lint-stats.mjs を別途用意し集計自動化)
    • 5 件超を 2 ヶ月連続 → 撤退判断
  • allowlist メンテコストが nav 登録手数を上回る (本末転倒)
    • 計測: allowlist 編集 PR 数 ≥ nav 新規エントリ PR 数 を 1 ヶ月継続
  • 同種の漏れ検出に Cloudflare Pages ビルドエラー等の代替メカニズムが整備された場合

R2 / R3 撤退

R2 / R3 は撤退しない (FAIL のみで運用、誤検知リスクが低い、機械的にチェック可能な仕様レベル)。

CI 撤去手順 (実施判断後)

対象具体手順影響範囲
GitHub Actions workflow.github/workflows/docs-nav-lint.yml.yml.disabled にリネーム1 コミットで CI 無効化、再起動容易
npm scriptnpm run docs:lint は npm script に残存ローカル任意実行のみ可
最終 PR撤退時の最終 PR にレビュアー sign-off 必須撤退根拠の記録保持

ブロック中 PR の救済手順

  • 当該 PR に [skip-nav-lint] ラベル付与 → workflow が if: !contains(github.event.pull_request.labels.*.name, 'skip-nav-lint') で条件付きスキップ
  • 緊急時の allowlist 一括追加: npm run docs:lint -- --add-current-orphans → 現存 orphan を allowlist (.docs-nav-lint-allowlist.json 等) に一括追記

R1 WARN 見落とし対策

  • GitHub Actions の status check として表示 (PR UI で必ず見える、緑/黄/赤マーク)
  • PR template (.github/pull_request_template.md) に「nav-lint WARN を確認した」チェックボックスを追加 → 別 PR で対応

8. 半年〜1 年後の負債化リスク・Review After

Review After (再評価期限)

  • 2026-11-18 (6 ヶ月後): 撤退条件閾値 (手戻り月 5 件) の実態と R4-R6 拡張可否を判断
  • 2027-05-18 (1 年後): 想定外の運用パターン累積を再評価、規約変更の必要性確認

R4-R6 拡張起動条件

  • 起動条件 (a): R1-R3 で月 3 件以上の事故検出が 2 ヶ月連続 (オペレーション疲弊サイン)
  • 起動条件 (b): Jr 入社 (2026-10) 後の品質改善ニーズが明示的に表面化 (Jr または代表取締役氏からの要望)
  • 起動タイミング: 上記条件を満たした時点 or Review After (2026-11-18) のいずれか早い方

負債化リスク

  • 案 D の R4-R6 (規約整合チェック) を将来 MUST にすると、F.X.NN プレフィックス変更時に大量の手戻りが発生 → 段階導入 + grace period を必ず踏襲
  • allowlist の規模膨張: 上限 30 件を目安、超えたら nav 構造そのものの見直しを起案 (allowlist が肥大化する = 「nav 登録すべきだが運用上できないファイル群」の増加 = 設計上の歪み)
  • gh CLI 依存: 撤退条件計測コマンドが gh に依存、将来 GitHub Actions API 直叩きへの移行余地

参照 (References)

  • 関連 ADR:
    • ADR-0042 (Prompt Lifecycle 管理) との具体整合: ADR-0042 は「プロンプト本文の SSoT を KV に置く + fallback を hardcoded で持つ + 変更時は同一 PR で両方更新」という 「機械的に検証可能な SSoT + ドキュメントによる運用ガード」 のパターンを確立。本 ADR は同じパターンを「文書 nav の SSoT = _config.json + 運用ガード = CLAUDE.md + 機械チェック = docs-nav-lint」に展開、運用ガードレールの整合
    • RQ-051 (Lint Rule Reference 文書構造) — 別 ADR、並行依存ではない。RQ-051 採択後の規約に従って本 lint のドキュメント形式を整える想定だが、現時点では非ブロッキング
  • 関連 PR/Issue: PR #796 / PR #827 — 本起案の動機となった具体事故事例
  • 外部資料: -