• Status: Accepted
  • Mode: Standard
  • Scope: platform
  • Kruchten Type: Existence/Property
  • Implementation Status: In Progress (PR #919 — Phase 1: PoC + allowlist + npm script)
  • 起案者: [email protected]
  • 起案日時 (JST): 2026-05-21 05:32
  • 承認日時 (JST): 2026-05-22 17:00 (Pipeline retroactive validation 47/50 通過)
  • Deciders: [email protected] (単独)

1. コンテキスト

1.1 背景 (Background)

docs/ 配下の markdown link / anchor 参照が壊れた状態で merge され、Cloudflare Pages の navigation で 404 や意味的に壊れた状態が発生する事例が複数発生している。docs/_internal/06_ops/failure_patterns.md #40 として記録された実例として、PR #874 (SUMMARY/index swap) で既存 link [index.md §1.9](../index.md) が swap 後に意味的に壊れたまま merge され、PR #873 (Glossary SSoT) で偶然発覚 → PR #876 (Step 4 audit) で同種事例として確認された。docs/implementation/legacy-dev/dev_mas-090_*.md L336 等にも swap 影響 anchor が残存している。

1.2 現状 (Current State / As-Is)

scripts/docs-build.mjs の build 成功は link 解決成功を保証しない (anchor が missing でも build は通る)。既存 CI (adr-lint.mjs / docs-nav-lint.mjs / adr-lint-doc-consistency.mjs) は link 解決を検証していない。失敗事例は 3 件以上発生済で、手動レビューでは構造的に防げない状態である。

1.3 課題 (Problem)

build success ≠ link 解決成功という乖離により、anchor 壊れ・broken internal link が PR レビューをすり抜けて merge される。Cloudflare Pages preview での目視確認は工数が大きく、レビュー漏れも発生している。新規参加者は anchor 規則を知らないまま編集するため事故が再発しやすい。

痛みの定量化 (proxy 実測):

  • 失敗事例頻度: 2026-04〜05 の 2 ヶ月で 3+ 件 (PR #874 / #873 / #876、failure_patterns #40 系)、月次 ~1-2 件発生 (累計でなく期間あたり)
  • 手動レビュー工数: docs PR ごとに Cloudflare Pages preview で anchor 目視確認 5-10min × ~10 件/月 = **1-1.5h/月の見過ごし得る工数**。本 ADR で CI 自動化により 0min に削減見込
  • 備考: 個人開発のため正確な history は session log proxy のみ。Jr 入社後は実測値に置換可能

1.4 制約・要件 (Constraints & Requirements)

  • scripts/docs-build.mjsslugify ロジックと anchor 解決規則を完全整合させること
  • 既存 .github/markdown-link-check-config.json との重複・矛盾を避けること
  • CI 実行時間は < 30 秒 (allowance 60 秒) を目標
  • 初期 false-positive 率 < 10%、allow-list は初期 ≤ 20 件
  • 外部 runtime (Rust 等) の追加導入は避け、Node 環境で完結させる (第一候補)

1.5 目標 (Goals / To-Be)

docs/**/*.md の broken-internal-link / broken-anchor を PR 時 CI で自動検出し、failure_patterns #40 系の事故を恒久的に防止する。Non-Goals: 外部 URL の生存性検証 (既存 markdown-link-check の範疇)、過去 commit 全件遡及修正。

2. 決定

scripts/docs-link-check.mjs を新設し、docs-build.mjsslugify ロジックを流用して L1 (broken-internal-link) / L2 (broken-anchor) を検証する自前 mjs 実装を第一候補として採用する。.github/workflows/docs-link-check.yml で PR ごとに実行し、npm run docs:lint:links でローカル実行可能化する。L3 (orphan-anchor-target) はオプション扱い。PoC 結果次第で markdown-link-check / lychee への切替条件を ADR 内に明示する。

3. 判断基準 (Decision Drivers)

3.1 評価軸 (Q42 9 タグから選定)

#重要度 (係数)案件特有の解釈
1#reliable[Must] (×2.0)anchor 壊れ・broken link を確実に検出できるか (failure_patterns #40 再現で検出率 > 80%)
2#maintainable[Must] (×2.0)docs-build.mjs slugify と整合を保てる構造か、保守工数が許容内か
3#efficient[High] (×1.0)CI 実行時間 < 30 秒 (allowance 60 秒)
4#operable[High] (×1.0)false-positive 率 < 10%、allow-list 運用が現実的か
5#suitable[Medium] (×0.5)既存 lint script 群 (docs-nav-lint.mjs 等) と同じ pattern で運用統一できるか

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

3.2 評価軸 × 案スコア表

係数採択案 (自前 mjs)案 X1 (build 統合)案 X2 (何もしない)案 X3 (pre-commit)案 X4a (markdown-link-check)案 X4b (lychee)
#reliable×2.0541335
#maintainable×2.0435343
#efficient×1.0425445
#operable×1.0432244
#suitable×0.5531243
加重和 (正規化)0.8700.6170.6260.5390.7390.826
K.O. 通過 (Must ≥3)❌ (#reliable=1)

加重和は K.O. 通過候補のタイブレーク用。自前 mjs (0.870) が最高、次点 lychee (0.826)。

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

  • 採用案: 自前 mjs (scripts/docs-link-check.mjs) — failure_patterns #40 の根本対応、docs-build.mjs slugify と完全整合、Node 環境のみで完結
  • 案 X1: docs-build.mjs に link 検証を統合 — 不採用理由: build 失敗が link 起因か content 起因か切り分けにくく、build が遅くなる
  • 案 X2: 何もしない (人間レビュー継続) — 不採用理由: 失敗事例 3+ 件発生済、K.O. 不通過 (#reliable=1)
  • 案 X3: pre-commit hook で local 実行 — 不採用理由: hook bypass 可、CI 化に劣る安全性
  • 案 X4a: markdown-link-check 採用 — 不採用理由 (第一候補としては): anchor 検証が弱め、docs-build.mjs slugify との整合保証なし。ただし自前 mjs が PoC で 15h 超過した場合の切替候補
  • 案 X4b: lychee (Rust) 採用 — 不採用理由 (第一候補としては): Rust runtime 追加依存、CI 環境セットアップ追加工数。ただし anchor 解決バグ nightly 3 回連続発生時の移行候補

5. 影響 (Consequences)

5.1 正の影響 (Good)

  • anchor 壊れ・broken internal link の自動検出により failure_patterns #40 系事故を恒久防止
  • content swap (SUMMARY/index swap 等) 後の Cloudflare Pages preview 目視確認の手作業を不要化
  • 新規参加者が anchor 規則を知らずに編集した際の事故を CI が catch
  • 既存 lint script 群 (docs-nav-lint.mjs / adr-lint-doc-consistency.mjs) と同じ pattern で保守可能

5.2 負の影響 (Bad)

  • CI ランタイム +10-30 秒の増加
  • 初回導入時の false-positive 大量発生リスク (allow-list 整備が必要、初期 ≤ 20 件想定)
  • 自前 mjs の保守責任を継続的に負う (slugify 仕様変更時の追随コスト)
  • 実装工数 AI 支援あり ~5-7h / 手作業 ~12-15h
  • docs PR merge リードタイムへの定量影響:
    • 通常時: 既存 CI (30-60 秒) + 本 ADR 追加 (10-30 秒) = +10-30 秒/PR (誤差レベル)
    • false-positive 発生時: allow-list 追加 PR 起票 + レビュー = +15-30min/件 (初期 ≤ 20 件目処、月次 1-2 件発生想定 → +30min-1h/月)
    • 真の broken link 検出時: 起案者が link 修正 + 再 push = +5-15min/件 (本来支払うべきコスト、リードタイム遅延の正当な理由)
    • 想定総影響: 月次 ~30min-1h の追加レビュー工数 (CI 自動化による §1.3 ~1-1.5h/月 削減で相殺、net で減少)

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

  • 外部ツール採用 (lychee/markdown-link-check) vs 自前実装: 第一候補は自前、PoC 結果次第で切替
  • CI 全件 scan vs PR 差分 scan: 初期は全件、CI 実行時間 > 60 秒なら差分 scan へ
  • L3 (orphan-anchor-target) は git history 比較が必要なため初期はオプション扱い

6. 撤退条件 (Rollback Plan)

6.1 工数見積もり (AI 支援あり / 手作業)

フェーズAI 支援あり手作業 (AI 非対応)
PoC (scripts/docs-link-check.mjs 自前 mjs 試作 + docs-build.mjs slugify 流用、検証データ 5 ファイル)2-3h6-8h
allow-list 整備 (research_prompts/ や legacy-dev/ の意図的 link 群を許容 patterns 化、初期 ~20 件)1-2h3-4h
.github/workflows/docs-link-check.yml workflow 統合 (既存 docs-nav-lint workflow 構造を流用)1h2h
PR template への運用記述 (false-positive 時の allow-list 追加手順)0.5h1h
合計~5-7h~12-15h

6.2 PoC 結果で覆る条件 (撤退・差し替え)

  1. maintenance コスト > markdown-link-check 採用比 +50%: PoC で実装工数が 15h 超なら markdown-link-check (既存 .github/markdown-link-check-config.json 流用) に切替
  2. anchor 解決ロジックのバグが nightly 3 回連続発生: 1 年以内なら lychee (Rust) への移行検討 (anchor 検証が業界最強)
  3. CI 実行時間 > 60 秒/PR: 差分 scan モードへ移行 or lychee 並列処理採用

6.3 再評価トリガー

  • 3 ヶ月後 (2026-08-21): false-positive 率 / CI 失敗率を測定し、運用負荷が高すぎれば WARN 化や差分 scan に降格
  • 6 ヶ月後 (2026-11-21): docs/ 構造が大きく変わった場合の規則見直し
  • 1 年後 (2027-05-21): 採用ツールの maintenance 状況再評価 (lychee/markdown-link-check 等)

6.4 導入完了を判定する観測可能指標

指標閾値測定方法
初回 false-positive 率< 10%既存 docs 全件 scan 時の error 件数のうち、人間レビューで「実は壊れていない」と判定された割合
PR 検出有効率> 80%過去 6 ヶ月の anchor 壊れ事例 (failure_patterns #40 等、~5 件) を再現実行して検出可能件数
CI 実行時間< 30 秒 (allowance 60 秒)GitHub Actions workflow の docs-link-check job 実行時間
allow-list 上限初期 ≤ 20 件、3 ヶ月後 ≤ 30 件.github/docs-link-check-allowlist.json の entry 数
週次 job不要 (PR ごとで十分)nightly schedule は採用しない (PR 時実行 + 月次手動レビューで covering)

導入完了判定: 上記 5 指標すべてが閾値内に収まり、かつ 1 ヶ月 (4 PR 以上) 運用して false-positive 0 件 + true-positive ≥ 1 件 検出された時点。

6.6 単独 Decider 体制下の bus factor / 引き継ぎ手順 (v2 reviewer 推奨対応)

代表取締役単独 Decider 体制で自前 mjs を継続保守するリスク (bus factor = 1) を緩和する手順:

  1. PR template への解説リンク必須化: .github/PULL_REQUEST_TEMPLATE.md の docs 編集 section に「link-check 失敗時の対応手順」リンクを記載
  2. docs/_internal/05_how-to/docs-link-check.md の運用ガイド整備 (新規、本 ADR 実装と同 PR で起案):
    • false-positive の判定基準 (例: research_prompts/ 内の参考 URL 列挙等)
    • allow-list 追加手順 (.github/docs-link-check-allowlist.json への entry 追加)
    • slugify 仕様変更時の docs-link-check.mjs 修正手順
  3. slugify ロジックは docs-build.mjs から関数 export し共有 (DRY 原則、保守責任の集約)
  4. Jr 入社 (2026-10) 時の onboarding tasks: 本 ADR の §6.4 指標を実測してみるドリル (3 ヶ月以内に 1 人で全工程実行可能か検証) を docs/_internal/05_how-to/ に追加
  5. 撤退判断の決裁ルール: 1 年以内に本 ADR を Superseded 化する場合の判断主体は代表取締役 (単独)、Jr 入社後は Jr + 代表取締役の 2 人レビューに昇格

6.7 allow-list 肥大化の月次レビュー運用 (v2 reviewer 推奨対応)

.github/docs-link-check-allowlist.json の継続的肥大化を防ぐ運用ルール:

件数閾値アクション
初期 ≤ 20 件通常運用、月次 review なし
20 〜 30 件月次レビュー必須、各 entry の必要性 (3 ヶ月以上参照 0 件のものは削除候補化) を確認
> 30 件ADR 再評価トリガー発火 (§6.3 連動)、自前 mjs の slugify 適用範囲拡大か、allow-list 縮減 PR か、ツール切替か、いずれかを 1 ヶ月以内に判断
> 50 件自動失敗化 (scripts/docs-link-check.mjs が exit 1)、緊急 ADR 再評価

月次レビューチェックリスト (docs/_internal/06_ops/monthly_review_checklist.md に追加要):

  • allow-list 件数のトレンド (前月比 +N 件、推移グラフ)
  • 3 ヶ月以上未参照 entry の特定 → 削除 PR 起票
  • false-positive と判定された entry の root cause 分類 (slugify バグ / 意図的 / 第三者 URL / その他)
  • root cause が「slugify バグ」なら本 ADR §6.2.2 撤退条件発火

6.8 CI 緊急バイパス手順 (運用障害時)

本番運用中に CI が誤検出大量発火・slugify バグ等で マージブロックが運用継続を阻害 する事態に陥った場合の緊急バイパス手順:

バイパス手段操作影響範囲復旧目処
(A) workflow 一時 skip.github/workflows/docs-link-check.ymlon: 条件に [skip docs-link-check] commit message check を追加し、該当 commit を含む PR では skip該当 PR のみ1 PR 限定の即時バイパス
(B) workflow 全体無効化GitHub UI で Actions タブから docs-link-check を Disable workflowリポジトリ全体バグ修正 PR マージ後に Enable で復旧 (~30min-数時間)
(C) allow-list 全許可.github/docs-link-check-allowlist.json["*"] に一時書き換え (許容パターン 1 件のみ)リポジトリ全体修正後に元の entries を復元 (~10-30min)
(D) continue-on-error: trueworkflow の job step に continue-on-error: true を追加し WARN 化リポジトリ全体バグ修正後に削除 (~10-30min)

判断フロー:

  1. 緊急度: 単一 PR のみ → (A) が最小影響。git commit -m "[skip docs-link-check] urgent fix" で対処
  2. 緊急度: 全 PR ブロック → (D) で WARN 降格、CI fail でも merge 可能に。本ロジックを 24h 以内に root cause 修正 PR
  3. 致命的: slugify が完全に破壊 → (B) で workflow 自体を停止、修正 PR 完了まで CI off

いずれも事後対応必須:

  • 24h 以内に failure_patterns.md #NN として記録
  • 1 週間以内に修正 PR + バイパス取り消し PR
  • 1 ヶ月以内に 本 ADR §6.2 撤退条件 §6.3 再評価トリガーの該当チェック (3 件連続発生で ADR 再評価)

重要: バイパス手順は必ず本 ADR + monthly_review_checklist.md に記録、bus factor=1 でも Jr 入社時に引き継ぎ可能化。

6.5 Confirmation (準拠確認 / Fitness Function)

  • 検証手段: .github/workflows/docs-link-check.yml の CI 実行 (PR 時必須チェック) + npm run docs:lint:links のローカル実行
  • 実行頻度: PR ごと (push trigger)、加えて月次手動レビュー (allow-list 肥大化監視)
  • 違反時の対応: L1 (broken-internal-link) / L2 (broken-anchor) が FAIL なら PR マージブロック、起案者が link を修正。false-positive と判定された場合は .github/docs-link-check-allowlist.json への追加 PR を別途起票 (allow-list 上限 30 件を超える場合は ADR 再評価)

7. 参照 (References)

7.1 関連 ADR・PR・ドキュメント

  • 関連 ADR: ADR-0051 (docs-nav-lint.mjs 前例、本 ADR と同 pattern)、ADR-0054 (adr-lint-doc-consistency.mjs 前例)、ADR-0056 (Pipeline temperature 戦略、本 ADR の Gate 4 採点の信頼区間関与)
  • 関連 PR/Issue: PR #874 (SUMMARY/index swap、anchor 壊れの発端事例)、PR #873 / #876 / #877 (本問題の発覚・部分対応 PR 群)、PR #882 (本 ADR の起案 KV 投入プロンプト merged)
  • 関連ドキュメント: docs/_internal/06_ops/failure_patterns.md #40 (本 ADR 起案根拠)、docs/_internal/02_project/TODO_future.md §7 DOC-OPS-01 (改善案バックログ、本 ADR 採択時に「完了 (PR #NNN)」マーク必要)
  • 外部資料: lychee (Rust markdown link checker)、markdown-link-check (Node)、既存 .github/markdown-link-check-config.json

7.2 ADR-0051 / 0054 との具体的差分と Supersede 関係 (v2 reviewer 推奨対応)

本 ADR は ADR-0051 / 0054 を Supersede しない (3 ADR が並立する補完関係)。各 ADR の責務範囲:

ADR責務範囲 (検証対象)検出する事故タイプ本 ADR との差分
ADR-0051 (docs-nav-lint.mjs)_config.json の nav 整合 (orphan-md / broken-nav-entry / duplicate-nav-entry / prefix-group-consistency)nav 登録漏れ・重複・存在しないファイル参照nav 配列のみを scan、docs/**/*.md の本文 link は scan しない
ADR-0054 (adr-lint-doc-consistency.mjs)adr-lint.mjs rule 群と adr-lint_rules.md 規約 doc の整合rule id / severity / category の SSoT 乖離ADR-lint 規約 SSoT 検証専用、汎用 markdown link は scope 外
本 ADR (0057) (docs-link-check.mjs)docs/**/*.md 内の [text](path.md#anchor) link 解決broken-internal-link (L1) / broken-anchor (L2) / orphan-anchor-target (L3 optional)markdown 本文 link 専用、nav / ADR-lint 規約は触らない

3 ADR の協調動作:

  1. docs-nav-lint.mjs (ADR-0051) で nav 整合性検証 → 「ファイルが存在するか」レベル
  2. 本 ADR docs-link-check.mjs で本文 link 整合性検証 → 「anchor が解決するか」レベル新規追加
  3. adr-lint-doc-consistency.mjs (ADR-0054) で adr-lint 規約 SSoT 整合性 → 「規約 doc と実装の乖離」レベル

Superseded 化の可能性: 1 年後 (2027-05-21) 再評価で 3 script を統合 (例: docs-lint-suite.mjs のような umbrella script) する案が浮上する可能性あり。その時点で本 ADR / ADR-0051 / ADR-0054 を 3 つまとめて Supersede する新 ADR を起案する選択肢 (現状は補完並立で許容)。

8. 参照: Pipeline 審査履歴 (2026-05-22 実行時, First Retroactive Validation)

本セクションは Decision Pipeline (LangGraph TS on Cloudflare Workers) で本 ADR を retroactive validation した結果のログ (ADR-0052 retroactive mode、ADR-0056 で確立した §audit history パターンの 第三適用例、ADR-0058 §11 に続く)。ADR-0057 は merge 前の Pipeline 審査を経由しておらず、本 retroactive が First Pipeline audit。スコア 47/50 (Standard 閾値 40/50 + Critical 閾値 45/50 双方クリア)、3 件の改善余地は本 PR 内で本文に反映済。デフォルトでは折りたたまれており、 をクリックで展開する。

📋 Pipeline 審査履歴 全 Gate 結果 (合格 Score 47 / 50) — クリックで展開

Gate 0 Triage

  • needsAdr: true / triageMode: Standard
  • 理由: failure_patterns #40 で 3+ 件の実害事例 + 78 files への横断的影響 + CI 整合性自動化が必要 + 可逆 (撤退手順 ~1h で復旧可能)

Gate 1 Socratic

  • pass: true (追加質問 0 件、起案 context が十分と判定)

Gate 2 Consistency: verdict PASS (自己参照 + ADR-0051/0054 補完並立認識)

  • ADR-0057 既存 (PR #888 merged) を Gate 2 が検出、retroactive validation モードと認識
  • §7.2 責務範囲表により ADR-0051 (nav 整合) / ADR-0054 (ADR-lint 規約 SSoT) / 本 ADR (本文 link 整合) の 3 ADR 補完並立 が正しく認識され duplicate 警告なし
  • 1 年後の 3 ADR 統合可能性 (docs-lint-suite.mjs umbrella) も Gate 2 で長期視点として認識

Gate 3 Parallel Review

  • 3 vendor (Gemini Flash / Claude Sonnet / GPT-4o) からの詳細指摘は本 retroactive run では取得スコープ外 (Standard mode かつ既 merged のため audit history の主目的は score 取得)
  • 主要指摘の要約: 後続 §改善余地 で記録 (3 件)

Gate 4 Scoring: 47 / 50

#採点項目点数コメント
1問題定義4具体的 PR 番号 (#874/#873/#876) と failure_patterns #40 で個別事例の具体性は高い。ただし「失敗事例 3+ 件」が累計か期間あたりか不明確で、手動レビュー工数の増加など痛みの定量化が弱い。
2代替案5採用案含め 6 案 (自前 mjs / X1〜X4b) を網羅、各却下理由と切替候補としての位置づけ (lychee は anchor バグ nightly 3 回連続時) まで明示。
3判断基準5Q42 9 タグから 5 軸選定、重要度係数 (×2.0/×1.0/×0.5)、K.O. criterion、案ごとの加重和正規化スコアまで完備。
4過去 ADR 整合性5ADR-0051/0054/0056 を参照し責務差分を表で明示。Supersede しない補完並立を理由付きで宣言、1 年後の 3 ADR 統合可能性まで言及。
5影響範囲4スクリプト/workflow/allow-list/PR テンプレ/how-to の対象ファイル群と代表取締役・Jr のステークホルダーは明示。ただし docs PR merge リードタイムへの影響や onboarding 工数増は未定量化。
6運用上の罠5allow-list 肥大化の段階別アクション (20/30/50 件)、bus factor=1 への引き継ぎ手順、月次レビューチェックリストまで具体的に整備されている。
7ロールバック4PoC 失敗時の差し替え条件 3 つと再評価トリガー 3 段階は詳細。一方、本番運用中に CI を緊急停止する必要が生じた際の workflow skip / allow-list 全許可 等の即時バイパス手順は欠落。
8コスト試算5PoC / allow-list / workflow / PR テンプレの 4 フェーズで AI 支援あり 5-7h・手作業 12-15h を明示。撤退条件 §6.2.1 で「15h 超過」の閾値とも連動済。
9完了条件5false-positive 率 / 検出有効率 / CI 実行時間 / allow-list 件数 / 週次 job 要否の 5 指標と測定方法を明記、「1 ヶ月 4PR 以上 + false-positive 0 / true-positive ≥1」の判定式まで観測可能。
10長期的影響53/6/12 ヶ月の Review After を 3 段階設定、bus factor 緩和策、1 年後の採用ツール再評価、3 ADR 統合の選択肢まで長期視点で言及。

合計: 47 / 50 (Standard 閾値 40 / 50 → PASS, Critical 閾値 45 / 50 → PASS)

改善余地 (本 PR で反映済)

#指摘反映先効果
1§1.3 失敗事例「3+ 件」の月次発生頻度と手動レビュー工数の数値化§1.3 に「2 ヶ月で 3+ 件 = 月次 ~1-2 件」「手動レビュー ~1-1.5h/月」を proxy 実測として追加問題定義 4/5 → 5/5 見込
2§5 docs PR の merge リードタイムへの定量影響§5.2 に「+10-30 秒/PR (通常) + 月次 ~30min-1h (false-positive 対応)」を追記、§1.3 ~1-1.5h/月 削減との net 比較影響範囲 4/5 → 5/5 見込
3§6 CI 緊急バイパス手順 (merge 強行時の workflow 一時 skip / allow-list 全許可)§6.8 として 4 手法 (workflow skip / 全体無効化 / allow-list 全許可 / continue-on-error) + 判断フロー + 事後対応必須項を新設ロールバック 4/5 → 5/5 見込

改善効果見込: 47/50 → 50/50 達成可能性 (3 項目満点取得時)。

Status 遷移

  • 起案 (2026-05-21, PR #888) → Pipeline 審査未経由のまま Proposed で merged
  • 2026-05-22 retroactive validation (47/50, Standard 閾値 40 + Critical 閾値 45 双方クリア) → 本 PR で Status: Proposed → Accepted に更新
  • 3 件改善余地を本 PR で本文に同時反映 (ADR-0058 PR #898 と同パターン)