型付き辺: 出 3 / 入 0
ADR-0098: ドキュメント鮮度の verified-as-of frontmatter と種別別 staleness SLA を確立
- Status: Proposed
- Mode: Standard
- Kruchten Type: Existence/Property
- Scope: platform
- Implementation Status: Not Started
- 起案者: [email protected]
- 起案日時 (JST): 2026-06-01 19:02
- 承認日時 (JST): -
- Deciders: [email protected] (単独)
コンテキスト
§1.1 背景
実装が変わったとき、対応する docs が「まだ正しいか・最後にいつ正しさを確認したか」がどこにも残らない。現状サイトに出るのは git 由来の lastUpdated(最終コミット日時) のみで、これは「git が触られた日」であって「人が実装と突き合わせて正しさを確認した日」ではない。typo 修正でも新しくなり、半年放置でも実装が変わらなければ古いと気づけない。
この欠落をどう埋めるかには複数の選択肢があり、SLA 値・段階導入・可逆性のトレードオフを伴うため、方針の選択として ADR に記録する。
§1.2 現状 (As-Is)
- 監査の as-of: 監査は「いつ時点で正しいと確認したか」を問う。lastUpdated では答えられない。
- ドリフト検知の欠落: 既存 lint 5 種は docs 内部の整合性のみを見て、docs↔実装の鮮度を見るルールは 0 件。
§1.3 課題
- Jr 引き継ぎ(2026-10): 「この docs を信用してよいか」の手がかりが lastUpdated しかないと古い記述を鵜呑みにする(ADR-0039/0051 と同じ引き継ぎ driver)。
- 実装は変わらず確認だけ切れる「放置ドリフト」が能動検知できない。
§1.4 制約・要件
- 既存 frontmatter lint / lastUpdated 描画の上に最小増分で乗せる(新ツール導入ゼロ)。
- ADR-0051(grace+allowlist)・ADR-0058(frontmatter-lint SSoT 3-way)の既存パターンを踏襲。
reviewedは任意キーで開始し、grace period(WARN→FAIL)+ allowlist で段階導入。- 一括 backfill は強制しない。
- 個人開発ゆえ escalation-of-commitment を構造的に外部化する。
§1.5 目標 (To-Be)
「完璧な鮮度管理」でなく「確認が切れた docs に気づける」を狙う。docs に verified-as-of 鮮度コントラクトを導入し、監査 as-of に答える土台と、放置ドリフトの CI 検知・サイト可視化を最小増分で実現する。Non-Goals: P2(パス連動ゲート)/ P3-P5、一括 backfill、reviewed の正しさの技術的強制。
決定
docs に verified-as-of 鮮度コントラクトを導入する。frontmatter に reviewed: {date, by, impl_sha} を構造化オブジェクトとして導入(監査証跡レベルは採択前提として確定: 我々の監査要件は「いつ・誰が・どの実装版と照合したか」の証跡チェーンを要求するため、date 単独では不十分と判断し、確認者(by) と実装 commit SHA(impl_sha) を必須サブフィールドとする — 採択後の法務確認先送りによる reviewed 仕様変更=lint/build/backfill 再設計を構造的に回避)。種別(type)別の staleness SLA を正典(policy)として制定し、SLA 超過を CI(grace→FAIL)とサイト(overdue バッジ)で surface する。reviewed は任意キーで開始し、ADR-0051 と同じ grace period + allowlist で段階導入。
SLA 表(初版、要実測精査):
| type | SLA(reviewed からの上限) | 根拠 |
|---|---|---|
| adr | 検査対象外(不変・supersede で更新) | 決定の記録 |
| runbook / ops / data | 90 日 | 実装追従が最速・スキーマ直結 |
| spec | 120 日 | 仕様は実装と対 |
| how-to / policy | 180 日 | 変化が緩い |
| research 系 | 検査対象外 | 時点スナップショットが本質 |
判断基準 (Decision Drivers)
ドキュメント/規約系アーキタイプ(adr_decision_drivers_guide §4)。重み: Must ×2.0 / High ×1.0 / Medium ×0.5。
3.1 評価軸
| # | 軸 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|
| 1 | #maintainable | Must (×2.0) | 既存 frontmatter lint / lastUpdated 描画の上に最小増分で乗ること。新ツールを乱立させない |
| 2 | #operable | Must (×2.0) | CI が overdue を能動検知し、grace+allowlist が骨抜き化(検知無効化・WARN 無視文化・allowlist 肥大化)せず運用が回ること |
| 3 | #suitable | High (×1.0) | 監査 as-of 要件への適合、既存資産(ADR-0051/0058)との整合 |
| 4 | #usable | Medium (×0.5) | 起案者(reviewed 更新)・閲覧者(バッジ)の負担最小化 |
K.O.: Must 軸(#maintainable/#operable)で score < 3 の案は不採用。案 A(lastUpdated 強化のみ)は #operable、案 C(mailer)は #maintainable で K.O.。
3.2 評価軸 × 案スコア表
| 軸 | 係数 | 採択案 (reviewed+SLA) | 案 A (lastUpdated 強化) | 案 B (review-bot 自動 PR) | 案 C (mailer 通知) |
|---|---|---|---|---|---|
| #maintainable | ×2.0 | 4 | 4 | 3 | 2 |
| #operable | ×2.0 | 4 | 2 | 3 | 3 |
| #suitable | ×1.0 | 5 | 2 | 3 | 2 |
| #usable | ×0.5 | 3 | 5 | 4 | 4 |
| 加重和 (正規化) | 0.809 | 0.581 | 0.638 | 0.500 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ | ✓ | ❌ |
検討した代替案 (Alternatives Considered)
- 案 A: lastUpdated 強化のみ: 現状の git lastUpdated を SLA 判定に流用 — typo コミットでも更新されるため「人が確認した日」を表現できず
#operableK.O.。 - 案 B: review-bot 自動 PR: bot が定期的に reviewed 更新 PR を起票 — 新ツール導入が必要で
#maintainableで本案に劣後。reviewed フィールド自体の設計は本 ADR と共通のため、将来 P2 以降で追加可能。 - 案 C: mailer 通知: SLA 超過時に通知のみ — CI/サイトに surface されず無視されやすく
#maintainable/#operable双方で劣後。
影響 (Consequences)
§5.1 正の影響 (Good)
- 監査 as-of に答える土台ができる。
- 放置ドリフトを CI が能動検知(「実装は変わらず確認が切れている」を捕捉)。
- Jr 引き継ぎ時に信用度がバッジで一目。新ツール導入ゼロ。
§5.2 負の影響 (Bad / トレードオフ)
結論のみ。詳細対策は実装スコープ/撤退条件/完了条件にて。
- 形骸化リスク: reviewed 更新は人手規律が前提 → WARN 無視文化は撤退条件の WARN 無視率トリガー + 責任者/判断日の事前明記で監視。
- allowlist 例外置き場化:
expires:・総数上限・overdue 全数カウントで構造的に封じる。 reviewedの正しさは技術強制できない(日付だけ更新で中身未確認)→ 完了条件で コミットメッセージ規約 + 四半期サンプリングレビューを併用。監査証跡レベルは採択前に確定(reviewed = {date, by, impl_sha})。これにより「監査が日付単独を不十分と判定→reviewed 仕様変更で lint/build/backfill 再設計」という #maintainable 毀損(Gate1 critical #1)を採択前に除去する。- false assurance: 「確認済」バッジの過剰信頼 → オンボーディングにバッジの限界を明記。さらにバッジ描画コード自体に tooltip/注釈「この日付は人が確認した日であり、実装の正しさを保証するものではない」を埋め込む実装にし、表示仕様から注釈が外れない構造にする(Gate1 medium #13)。reviewed.impl_sha と HEAD の差分コミット数の自動表示は P4 で再評価。
- SLA 初版は実測前 → 採択時に git log の type 別変更頻度(P50/P90)をスパイク測定して初版値を data-driven に設定し、実装後 3 ヶ月で再測して SLA 表のみの軽量 PR で見直し(フル ADR 改訂不要)。初版が厳しすぎると allowlist 逃げ込み/緩和枠(通算 1 回)の浪費を招くため、初版はスパイク実測下限に寄せる。type 分布リスク(Gate1 high #11)に対しては、git log スパイクで type 別ボリュームと「実装不変・確認切れ」頻度を別途計測し、SLA 初版確定の前提条件として完了条件 #0 に組み込む。
- 既存 docs の backfill 負担(高鮮度 type ほど重い)。
- impl_sha 腐敗リスク(Gate1 high #2, #6): rebase/squash-merge/force-push/shallow clone で SHA 到達不能になりうる。設計判断: lint では SHA 形式(40 桁 hex)のみ検証し、存在確認は行わない(存在確認を入れると正当 reviewed が永続 FAIL)。代わりに impl_sha は SHA または PR 番号(
#<num>)を許容する規約とし(squash-merge 耐性)、到達可能性は四半期サンプリングレビュー(完了条件 #3)で人手確認。SHA 形式のみ通過の抜け穴は四半期レビューで補完する旨を Non-Goal 周辺に明示。 - 撤退後のデータ残留(Gate1 critical #1): git history に刻まれた reviewed エントリは撤退で消えない。撤退条件に frontmatter クリーンアップ手順(削除スクリプト・適用 PR タイムライン・lint ルール無効化順序)を明記。Supersede ADR テンプレートに「旧 reviewed フィールドの移行・廃棄方針」を必須項目として追加。
- 個人情報リスク(Gate1 high #10):
byフィールドは GitHub ユーザー名を正とし氏名・メールアドレスは禁止とfrontmatter-lint_rules.mdに明記。公開リポジトリ前提のリスク水準として記録。 - 外部 CI/後続 ADR への波及(Gate1 high #3): P3(embedme)/P4(変更可視化)/P5(シンボル参照)が reviewed フィールドを参照・依存するかを本 ADR の インターフェース合意として明記: 「P3-P5 は
reviewedを読み取り専用で参照可、書き換え禁止、parse 失敗時はスキップ(strict parse 禁止)」。既存 GitHub Actions workflow の frontmatter parse 処理は実装着手時に列挙し、reviewed フィールド追加が破壊的でないか確認する。 - PR レビュー待ちでの SLA 違反誘発(Gate1 high #8): reviewed のみ更新の軽量 PR は self-merge 可(lint 通過時 auto-merge)とする運用規約を明記。SLA 計算は PR 作成日でなくマージ日基準のままとするが、grace 14 日内にマージできない場合は allowlist で吸収。
- ビルド/lint 性能劣化(Gate1 medium #9): docs-build は frontmatter のみを事前にキャッシュする中間ファイル(例:
reviewed-index.json)から参照する設計。lint 実行時間の上限目標を完了条件に追加。 - GAS 記載の訂正: 本 ADR の実装は pure Node(adr-lint.mjs / docs-build.mjs)であり GAS は使用しない。コスト試算の「GAS 実行時間: 増分なし」は誤解を招くため「GAS 不使用」に訂正。
- --as-of 設定ミスのサイレント全件パス(Gate1 critical #7): 後述の Confirmation/撤退条件で多層化。
§5.3 中立・トレードオフ
- 実装スコープ(やること):
- (a)
reviewed: {date, by, impl_sha}の規約(3 サブフィールド必須)と SLA 表をfrontmatter-lint_rules.mdに SSoT 化(sub、ADR-0058 3-way パターン)。 - (b)
adr-lint --check-frontmatterにfm-reviewed-slaを追加(main)。日付形式エラーはfm-reviewed-format(WARN 専用・SLA FAIL と分離)/基準日は--as-ofで固定しフレーキー回避/allowlist はexpires:必須・総数上限・overdue は allowlist 込み全数カウント(検知無効化防止)。fm-reviewed-formatは date・by・impl_sha の 3 サブフィールド存在と日付形式・SHA 形式(または PR 番号形式)を検証(WARN 専用)。--as-of基準日は CI ログに必ず出力し、デフォルトを実行日当日に固定。週次 job で--as-of省略実行との差分でサイレント停止を検知。さらに「直近 N 日以内に reviewed が更新された docs の割合がゼロ」をアラート条件として別途 CI ステップに組み込み、週次 job の成否を通知して死活確認ループを閉じる(Gate1 critical #7)。 - (c)
docs-build.mjsに reviewed / overdue バッジ描画(main、既存 lastUpdated 箇所に追記)。中間キャッシュreviewed-index.jsonを生成しバッジ描画はそこから参照。バッジ自体に「人が確認した日であり実装の正しさを保証しない」tooltip を埋め込む。 - (d) grace(WARN→FAIL)+ allowlist 配線(main、ADR-0051 踏襲)。grace 期間 = 14 日。
- (a)
- 段階化: ①規約+SLA 定義(sub)→ ②バッジ描画(main・可視化先行)→ ③
fm-reviewed-slaを WARN 導入 → ④grace 後 FAIL 昇格。 - 役割境界: 規約・SLA 表は sub、lint/build は main(cross-workspace)。
- 過去 ADR との関係:
- ADR-0051(grace+allowlist / Accepted): 段階導入・bypass の前例。allowlist の例外常態化リスクを採択前に確認。
- ADR-0058(frontmatter-lint SSoT 3-way / Accepted): reviewed 規約・SLA 表をこのパターンに乗せる。
- ADR-0039(doc 構造 / Accepted): 「Jr が docs を信用できるか」を共有 driver。本 ADR は信用判断材料を提供。
- P2〜P5 分割: 本 ADR は code↔docs 同期パッケージ P1。P2(パス連動ゲート)/P3(embedme)/P4(変更可視化)/P5(シンボル参照)は別 ADR。P2 先行案の明示的棄却: P2 は実装ファイル変更時にしか発火せず「実装不変・確認だけ切れる放置ドリフト」を捕捉できないため P1(本 ADR)を先行させる。両者は補完関係で、P2 が 6 ヶ月以内に Accepted かつカバレッジ > 90% に達した場合のみ reviewed を機械検知の補助に縮退する(撤退条件参照)。約 1.75 人日の投資は grace 期間内の運用で回収するため P2 待ちにしない。
コスト試算
| 作業 | 工数 | 担当 |
|---|---|---|
| frontmatter-lint_rules.md に規約+SLA 表 | 0.25 人日 | sub |
| adr-lint に fm-reviewed-sla(+形式分離/as-of/allowlist 堅牢化) | 0.5 人日 | main |
| docs-build バッジ描画(中間キャッシュ含む) | 0.5 人日 | main |
| grace+allowlist 配線 | 0.25 人日 | main |
| git log 変更頻度スパイク(SLA 初版の type 別 P50/P90 + type 分布 + 実装不変・確認切れ頻度の実測) | 0.25 人日 | main |
| 合計 | 約 1.75 人日 |
追加 LLM コスト: なし。GAS: 不使用。backfill は grace 期間に分散。
撤退条件 (Rollback Plan)
撤退判断は裁量を排した機械判定とする(個人開発ゆえ責任者は代表取締役だが、escalation-of-commitment を構造的に外部化する)。grace 期間 = 14 日(ADR-0051 の実値を本文に明記。本 ADR 単体で判断日が計算可能)。撤退判断日 = 実装 PR マージ + grace 14 日 + 60 日 = マージから 74 日後を起案時にカレンダー登録して外部コミットメント化する。下表の数値閾値を満たした時点で、起案者の主観(『まだ改善余地がある』等)を介在させず自動的に撤退手続き(SLA 緩和/対象縮小/Supersede)へ移行する。SLA 緩和は通算 1 回まで(緩和ループ防止)。
| 判定指標 | 閾値 | 対応 |
|---|---|---|
| overdue が常態化/無視される | 判断日(マージ+74 日)時点で overdue 実数(allowlist 込み全数)が grace 終了時点を 3 ヶ月連続で下回らない、または PR マージ時の WARN 無視率 ≥ 50% | SLA 緩和(通算 1 回)または「監査対象 docs のみ必須」に縮退する Supersede ADR |
| backfill が進まない | grace 内に対象 type の reviewed 付与率 < 50% | SLA 対象 type を data のみ等に縮小 |
| allowlist が例外置き場化 | 総数 > 対象 docs の 20%、または expires 切れ > 0 | allowlist-expired で FAIL。超過時は SLA 緩和でなく棚卸し PR を必須化 |
| P2 実装で reviewed が冗長化 | P2 Accepted かつ P2 カバレッジ > 90% | reviewed を機械検知の補助に格下げ/廃止 |
撤退判断の外部コミットメント化(Gate1 high #12): 撤退判断日を単なるカレンダー登録にとどめず、判断日に 自動生成される GitHub Issue(スケジュール CI job)として外部化する。Issue には測定値・閾値・判断結果の記入テンプレートを付け、クローズには数値の明記を必須とする。『閾値に達したが緩和しない』判断をする場合も理由を Issue に記録し、同じ Issue を 30 日後に自動再オープンする。
撤退時の frontmatter クリーンアップ手順(Gate1 critical #1、撤退の可逆性確保):
- Supersede ADR 起案と同 PR で reviewed フィールド一括削除スクリプトを準備(dry-run 含む)。
- lint ルール(
fm-reviewed-sla/fm-reviewed-format/allowlist-expired)の無効化を 先に デプロイ(誤検知防止)。 - クリーンアップ PR を Supersede ADR Accepted から 10 営業日以内にマージ。
- impl_sha を「証跡として閲覧者を誤誘導」する状態を残さないため、削除でなく仕様変更で残す場合は Supersede ADR テンプレートの「旧 reviewed フィールドの移行・廃棄方針」を必須記入。
allowlist 上限の動的化検討(Gate1 high #4): 初版は固定値 20% で開始するが、grace 終了時点の overdue 実数を基準とした動的上限への切替を撤退判断日の Issue で再評価する(SLA 緩和 1 回枠とは別枠)。
Confirmation
観測可能 KPI で判定。グッドハート回避のため付与率単独に依存させず #2/#3 を併用。
0. 採択前提(必達):
- 検証手段: 監査証跡レベル
reviewed: {date, by, impl_sha}がfrontmatter-lint_rules.mdに明記され、by形式(GitHub ユーザー名)・impl_sha形式(40 桁 hex または#<PR 番号>)の規約が確定していること。実装着手前に git log スパイク(type 別 P50/P90、type 分布、実装不変・確認切れ頻度)を完了。 - 実行頻度: 採択前 1 回 + 実装着手前 1 回。
- 違反時対応: スパイク未完了で実装着手不可。証跡レベル変更が必要になった場合は本 ADR を Supersede。
1. reviewed 付与率(主 KPI):
- 検証手段: grace 終了時点で高鮮度 type の
reviewed:付与率 ≥ 80% を adr-lint レポートで確認。 - 実行頻度: CI 毎回(grace 期間中)+ 撤退判断日(マージ+74 日)。
- 違反時対応: 撤退条件「backfill が進まない」に該当 → SLA 対象 type を縮小。
2. overdue 検知の実動作:
- 検証手段: SLA 超過 docs がある状態で CI が WARN/FAIL を出すことを実 PR で確認(テストは
--as-of固定日付・検知漏れ 0)。さらに「直近 N 日以内に reviewed が更新された docs の割合がゼロ」のアラート CI ステップが死活確認として機能。 - 実行頻度: CI 毎回 + 週次 job(
--as-of省略実行との差分)。 - 違反時対応: アラート発火時は基準日設定ミスとして即時調査。週次 job 自体の失敗・スキップは通知で検知。
3. 証跡の実効性(四半期サンプリング):
- 検証手段: reviewed 更新コミット 5 件抽出し、突き合わせ参照(
verify ... against impl@<sha>or PR/issue 番号)と impl_sha の到達可能性を人手確認。reviewed 更新 PR テンプレに「突き合わせた実装変更点(無ければ『変更なし・確認済』)」を必須化。 - 実行頻度: 四半期 1 回。
- 違反時対応: 形骸化が確認された場合は WARN 無視率トリガー(撤退条件)と合わせて評価。
4. 仕組み存在 + 性能上限:
- 検証手段:
frontmatter-lint_rules.mdに reviewed 規約、adr-lint.mjsにfm-reviewed-sla/fm-reviewed-format/allowlist-expired、docs-build.mjsに reviewed バッジ +reviewed-index.jsonキャッシュが存在。docs 100/500/1000 件でのビルド時間・lint 時間ベンチマークを取得し、lint 実行時間 ≤ 30 秒(現状 docs 数)/ ビルド時間増分 ≤ 20% を上限目標とする。 - 実行頻度: 実装 PR で 1 回 + 半期 1 回再計測。
- 違反時対応: 上限超過時は中間キャッシュの最適化または対象 type 縮小。
参照 (References)
- 関連 ADR: ADR-0039 / ADR-0051 / ADR-0058 / P2-P5 後続 ADR(未起案)
- 関連 PR/Issue: PR #1251 (本 ADR)。
- 外部資料: -