本文へスキップ
ガイドの目次

ドキュメント管理ワークフロー

← ガイド目次

このページがガイドの本体。4 層構造・front matter・鮮度判定・日々の運用・AI 連携・CI までを通して説明する。

1. 全体像

docs-kit の運用は、コード変更 1 回ごとに回る小さなループになる。

要点は 2 つだけ。

  1. 着手前に which — 関連文書を機械に選ばせ、その文書だけを読む。全部読まない
  2. 完了時に verify — 実装と突き合わせた文書に「いま突き合わせた」という印を付ける

この 2 つを守っていれば、あとは CI が破綻を検出してくれる。

2. 4 つの層 — 何をどこに書くか

文書は 4 層のいずれかに置く。層は増やせない。

役割寿命書き換え鮮度判定
adr/決定記録。なぜその方式を選んだか、何を捨てたか永久しない (追記のみ)scope があれば対象
spec/現在の仕様。いまどう動くのが正しいか実装と同期する対象
arch/アーキテクチャ。構成・依存方向・データフロー実装と同期する対象
plan/一時的な作業計画。手順とゴール使い捨てする常に対象外

どの層に書くか

層ごとの書き方の指針

adr/ — 決定記録

1 決定 1 ファイル。「状況 → 決定 → 帰結」の 3 節で書く。採用しなかった案とその理由を必ず残す。これが無いと、半年後に同じ議論を最初からやり直すことになる。

方針が変わったときは既存の ADR を書き換えず、新しい ADR を追加して古いほうに status: supersededsuperseded_by: ADR-00NN を付ける。

spec/ — 現在の仕様

「いま何が正しいか」だけを書く。経緯は ADR にリンクする。scope を必ず書く — scope の無い spec は鮮度判定の対象外になり、docs-kit を使う意味がほとんどなくなる。

arch/ — アーキテクチャ

パッケージ構成・依存方向・データフロー。多くのプロジェクトでは arch/overview.md の 1 本で足りる。

plan/ — 作業計画

一時的な足場。マージしたら削除する。verified_at を持てず、鮮度判定にもかからない。進行状態 (done/doing のチェックボックス) は書かない — Issue トラッカーへ。

3. front matter — 文書のメタデータ

ファイル配置と id

管理対象は <root>/<layer>/ 直下*.md だけ。サブディレクトリは走査されないので、画像などの資産置き場に自由に使える。<root>/INDEX.md は生成物。

docs/
├── INDEX.md              ← 生成物。手で編集しない
├── adr/
│   ├── 0001-markdown-as-source-of-truth.md    → ADR-0001
│   └── 0002-four-layer-structure.md           → ADR-0002
├── spec/
│   ├── cli.md                                 → SPEC-cli
│   └── staleness.md                           → SPEC-staleness
├── arch/
│   └── overview.md                            → ARCH-overview
├── plan/
│   └── phase1-core-cli.md                     → PLAN-phase1-core-cli
└── assets/                ← 走査されない。画像等はここに置ける
    └── diagram.png

id とファイル名は 1 対 1 で対応させる。ずれると lint エラー (E004)。

id 形式ファイル名
adr/ADR- + 4 桁数字NNNN-<slug>.mdADR-0003adr/0003-sha-based-staleness.md
spec/SPEC-<slug><slug>.mdSPEC-clispec/cli.md
arch/ARCH-<slug><slug>.mdARCH-overviewarch/overview.md
plan/PLAN-<slug><slug>.mdPLAN-phase1plan/phase1.md

slug は [a-z0-9]+(-[a-z0-9]+)* (小文字英数とハイフン)。id はリポジトリ内で一意。

フィールド一覧

---
id: SPEC-editing-model                                # 必須
title: 編集モデル                                      # 必須
status: current                                       # 必須
scope:                                                # 任意 (spec/arch は推奨)
  - src/core/editing/**
depends_on: [ADR-0003]                                # 任意
verified_at: 8f3a1c2e0b7d4a91c3f6e2d8b5a704c1e9f3d602 # 任意 (40 桁)
x-owner: platform-team                                # 任意 (自由)
---
フィールド必須説明
idstring上表の形式。リポジトリ内で一意
titlestring空文字は不可
statusenumcurrent / draft / stale / superseded の 4 値のみ
scopestring[]この文書が責任を持つコード範囲。リポジトリルート相対・/ 区切りのグロブ
depends_onstring[]前提とする文書の id。存在しない id を指すとエラー
verified_atstring40 桁 hex の完全 commit SHA。docs-kit verify が書き込む
superseded_bystringsuperseded 時後継文書の id
x-*任意プロジェクト固有の拡張。ツールは解釈せず、そのまま保持する

x- 以外の未知フィールドは lint エラー (E007)。 チーム固有のメタデータ (担当・Jira キー等) を足したいときは x- を付ける。

本文の規約

  • front matter の直後に # <id>: <title> の見出しを置くことを推奨 (lint は強制しない)
  • 要約フィールドは無い。 which や MCP が返す 200 文字の要約は、本文の最初の非見出し・非空段落から自動で導出する。要約を別フィールドに持つと本文とドリフトするため
  • したがって、本文の 1 段落目に「この文書は何か」を書くと検索性が上がる

4. scope の書き方

scope は docs-kit の精度を決める唯一のパラメータ。ここだけは丁寧に書く価値がある。

scope:
  - src/core/editing/**       # ディレクトリ配下すべて
  - src/api/editing.ts        # 単一ファイル
  - "src/**/*.editing.ts"     # 名前パターン
  • 構文は doublestar (** サポート)
  • リポジトリルート相対・/ 区切り (Windows でも /)
  • 先頭 /./.. を含むパターンは不正 (E012)
  • 判定対象は git 管理下のファイルのみ

よくある失敗

失敗症状直し方
広すぎる (src/**)何をしても stale になる。やがて誰も見なくなる文書が実際に説明している範囲まで絞る。1 文書 1 モジュールが目安
狭すぎる (単一ファイルのみ)隣のファイルが変わっても気づけない「この文書の記述が嘘になる変更が起きうる範囲」まで広げる
テストを含めているテストを直すたびに staledocs.config.yamlignore**/*_test.go 等を入れる
存在しないパスを書いたlint 警告 W002。判定は broken (dead-scope)。draft の間は対象外パスのタイポを直す。まだコードを書いていないだけなら draft のままでよい (下記)
codeRoots の外を指しているlint 警告 W003codeRoots を実態に合わせるか、scope を直す

判断の基準は 「このファイルが変わったとき、この文書を読み直す必要があるか?」。あるなら scope に含める。

コードより先に文書を書く

draft の間は、scope にまだ存在しないファイルを書いてよい。W002 は出ない。これは意図した挙動で、draft の scope は「これから書くコードの宣言」だからである。

status: draft
scope:
  - Sources/Ingest/**      # まだ書いていない。これでよい

グリーンフィールドで作業開始プロトコルが機能するのはこれによる。docs-kit which Sources/Ingest/Pipeline.swift は、そのファイルが存在する前から担当する spec を返す。「which が返したものを読め」と指示された AI に、読むものが存在する状態になる。

verifycurrent に昇格した瞬間に W002 と dead-scope が両方発火するので、本物のタイポは実害を持つ瞬間に必ず表面化する。W003 (codeRoots の外) は draft の間も出続ける。

scope を持つ文書が 1 つも無い場合、which は空を返しつつ docs/INDEX.md を指すヒントを出す。「読むものが無い」ように黙って見えることはない。

5. status のライフサイクル

status意味AI の扱い誰が動かすか
draft実装と突き合わせていない参考情報。コードを正とするnew / adopt が付与
current突き合わせ済み。仕様として有効仕様として信じるverify が昇格させる
stale古いと分かっている参考情報。コードを正とする人間が手で宣言
superseded後継文書に置き換えられた読まない人間が手で宣言 (superseded_by 必須)

押さえておくポイント。

  • scope を持つ文書の draft → currentverify を通す。 手で current に書き換えても verified_at が無ければ lint E008 で落ちるので、実質的に verify が強制される
  • scope を持たない文書 (多くの ADR) は手で current にしてよい。 鮮度保証の対象外なので verify する意味がない
  • stale は人間による宣言。 docs-kit stale コマンドの判定結果は front matter に書き戻されない。コマンドの判定 (一時的な事実) と、文書の status (人間の意思表示) を混ぜないため
  • superseded は終端。 verify するとエラーになる

6. 鮮度判定の仕組み

判定対象

判定されるのは status: current かつ scope が非空かつ verified_at を持つ文書だけ。それ以外は skipped (判定せず、exit code にも影響しない)。

  • draft / stale / superseded — 鮮度保証を宣言していないので対象外
  • plan 層 — 常に対象外
  • current なのに scope や verified_at が無い文書 — lint が E008 で捕まえる

判定の流れ

全文書の判定後、exit code は最悪値になる (broken > stale > ok)。

broken の原因と対処

broken は「判定の前提が壊れている」状態で、文書の中身以前の問題。

理由コード何が起きたか対処
unknown-shaverified_at の SHA がリポジトリに無いshallow clone なら fetch-depth: 0 にする。履歴が消えたなら再 verify
not-ancestorrebase / force push で検証時点が現在の履歴から外れた実装と突き合わせ直して再 verify
dead-scopescope グロブがどのファイルにも一致しない (コードが消えた/移動した)scope を実態に合わせる。機能ごと消えたなら文書を superseded にする
missing-dependencydepends_on の参照先 id が存在しない参照先のタイポを直すか、参照を外す

stale の原因

理由コード何が起きたか
code-changedscope 内のコードが verified_at 以降に変わった (最も一般的)
premise-supersededdepends_on で前提にしていた文書が superseded になった
scope-driftリネームで、ファイルが黙って scope の外へ出た

premise-superseded は地味に効く。「コードは変わっていないが、前提にしていた決定が覆った」という、人間が最も見落としやすい種類の腐り方を拾う。

自動で吸収される差分

「scope 内が変わったら常に stale」では運用が回らないので、明らかに仕様に影響しない差分は判定から除外する。

除外規則吸収される差分
docs.config.yamlignore グロブテスト・スナップショットなど、仕様に影響しない変更
verify コミットの除外実装 + 文書更新 + verify を同一コミットで行ったときの自己言及的な差分
tree diff との積範囲内で加えて範囲内で打ち消された変更 (net-zero)
純リネーム (R100) の吸収内容変更を伴わないファイル移動

「verify コミットの除外」は、そのコミットが verified_at 行を書き換えているかで判定する。「文書を最後に触ったコミットを一律で除外」ではないので、文書だけ直して verify しなかったコミットはちゃんと差分として残る。

未コミット変更 (dirty)

判定は常に HEAD 基準。scope 内に未コミットの変更があると、判定結果に dirty 警告が添えられる (レベルは変わらない)。verify も同様に警告する — HEAD に入っていない変更を「検証済み」にはできないため。

出力例

docs-kit stale
SPEC-editing-model  stale    code-changed: 3 files changed since 8f3a1c2
  - src/core/editing/model.ts (2 commits)
  - src/core/editing/undo.ts
ARCH-overview       ok       (verified at 8f3a1c2)
SPEC-mcp-tools      skipped  (draft)

ok 1 / stale 1 / broken 0 / skipped 1

判定行の末尾に [uncommitted changes inside scope] が付くことがある (dirty 警告)。

CI や AI 向けには JSON も出せる。

docs-kit stale --format json
{
  "results": [
    {
      "id": "SPEC-editing-model",
      "path": "docs/spec/editing-model.md",
      "level": "stale",
      "verified_at_short": "8f3a1c2",
      "reasons": [{ "code": "code-changed", "detail": "3 files changed" }],
      "evidence": {
        "files": ["src/core/editing/model.ts", "src/core/editing/undo.ts"],
        "commits": ["...", "..."],
        "absorbed": [{ "kind": "rename", "from": "src/a.ts", "to": "src/b.ts" }]
      },
      "dirty": false
    }
  ],
  "summary": { "ok": 1, "stale": 1, "broken": 0, "skipped": 1 },
  "exit_code": 1
}

evidence に根拠 (変更ファイルとコミット) が入るので、AI はそのまま diff を読みに行ける。

7. 日々の運用

7.1 作業開始時 — which で読む文書を絞る

これから触るパスを渡すと、関連文書が返る。

docs-kit which src/core/editing/model.ts
primary:
  SPEC-editing-model  current  8f3a1c2  docs/spec/editing-model.md
related:
  ADR-0003            current  -        docs/adr/0003-undo-stack.md
  • primary — 引数パスが scope グロブに一致した文書
  • related — primary の depends_on を 1 ホップ展開したもの

複数パス・ディレクトリも渡せる (ディレクトリは <dir>/** として展開される)。

docs-kit which src/core/editing src/api/editing.ts

一致が無ければ空出力で exit 0 (「関連文書なし」は正常な情報であってエラーではない)。

返ってきた文書だけを読む。 これが docs-kit の検索性側の価値で、docs/ 全体を読ませないための仕組み。

7.2 作業中 — status を信じる基準

statusどう扱うか
current仕様として信じてよい
draft / stale参考情報。実装コードを正とする
superseded読まない (superseded_by の先を読む)

current の文書と実装が矛盾していたら、勝手にどちらかへ合わせない。 矛盾を報告して判断を仰ぐ。docs-kit が検出できるのは「scope 内が変わったこと」までで、「文書と実装のどちらが正しいか」は人間の判断になる。

7.3 作業完了時 — 更新して verify する

verify は front matter の verified_at を HEAD に更新し、draft / stale なら current に昇格させる。

docs-kit verify SPEC-editing-model ARCH-overview
SPEC-editing-model: verified_at → 8f3a1c2
ARCH-overview: verified_at → 8f3a1c2 (promoted to current)

verify が書き換えるのは front matter の該当行だけ。 YAML の再シリアライズはしないので、x- フィールドもコメントも本文もバイト単位で保たれる。

前提条件を満たさないとエラー (exit 3) になる。

  • id が存在すること
  • scope を持つこと
  • plan 層でないこと
  • status: superseded でないこと

7.4 コミットの粒度

判定の仕組み上、verify コミットに無関係な実装変更を同居させると、その変更は検出されない (verify コミットは差分計算から除外されるため)。

推奨する形:

コミット 1: 実装の変更
コミット 2: 設計書の更新 + verify   ← ここに他の実装変更を混ぜない

1 コミットにまとめる場合も、そのコミットに入れる実装変更は、更新した文書が説明している範囲のものだけにする。

また、マージコミット経由でのみ入った変更は判定から落ちるため、squash マージを推奨する。

8. AI から使う (Claude Code)

MCP サーバ

claude mcp add docs-kit -- docs-kit mcp

登録は PATH 上の docs-kit を指すので、バイナリを差し替えても再登録は不要。セッションを開き直せば新しいツール定義が反映される。

提供されるツールは 4 つ。

ツール対応する CLI用途
docs_whichwhichパス → 読むべき文書の逆引き
docs_stalestale鮮度判定
docs_verifyverify突き合わせ済みの宣言 (front matter のみ)
docs_newnewテンプレートから新規文書を作成

このサーバは本文を返さないし書かない。 返すのは絶対パス・メタデータ・200 文字の要約だけ。本文の読み書きは AI 自身の Read / Edit ツールが担当する。おかげで diff 表示も許可プロンプトも部分編集もそのまま使える。

CLI との違いで実用上効くのは 2 点。

  • docs_stale は既定で skipped の文書を結果に含めない (件数だけ summary に出る)。判定対象外の文書が結果の大半を占めるため、これだけでペイロードが大きく減る
  • docs_verify は複数 id を受け、件別に結果を返す。前提条件を満たさない id があっても残りは処理を続けるので、AI は失敗した分だけ自己修復できる

CLAUDE.md 管理ブロック

docs-kit init が CLAUDE.md に挿入するブロック。全リポジトリで一字一句同じ内容が入る。

<!-- docs-kit:begin v1 -->
## docs-kit protocol

Design docs live under docs/ (adr = decision records, spec = current specification, arch = architecture, plan = temporary work plans).

Before starting work:
1. Run `docs-kit which <path you are changing>` to identify the relevant documents, and read only those
2. Trust documents with `status: current` as specification. Treat draft / stale as reference material and trust the implementation code instead
3. If a current document contradicts the implementation, do not quietly pick a side — report the contradiction and ask

When finishing work:
1. If the specification changed, update spec/. Record new design decisions as an ADR with `docs-kit new adr <slug>` (never rewrite an existing ADR — supersede it instead)
2. Run `docs-kit verify <id>` for every document you reconciled against the implementation
3. Confirm `docs-kit lint && docs-kit stale && docs-kit index --check` all pass before calling the work done

Conventions:
- Do not record progress state (done/doing) in docs. That belongs in an issue tracker
- docs/INDEX.md is generated. Never edit it by hand (regenerate with `docs-kit index`)
<!-- docs-kit:end -->
  • マーカーの v1 はブロック仕様のバージョン。init を再実行すると、このブロックだけが最新版に置換される
  • ブロック外の CLAUDE.md には一切触れないので、既存の記述と共存できる

スラッシュコマンド

init.claude/commands/ に 2 つのコマンドも置く。

コマンド用途
/docs-verify作業完了時の締め手順 (spec 反映 → verify → lint/stale 確認)
/docs-catchupstale / broken になった文書を実装と突き合わせて解消する

実際の読み分け

docs-kit 自身の開発セッションで実測した挙動。

  • docs_which を 1 回呼んで primary 3 + related 12 = 15 文書が返り、本文を読んだのは 4 文書。残り 11 件はタイトルと要約だけで「今回は関係ない」と判断でき、開かずに済んだ
  • ただし 読むと決めた 4 文書はいずれも全文が必要だった。200 文字の要約は「読むか読まないか」の判定には十分だが、「何が書いてあるか」には足りない
  • まだ存在しないパスを混ぜても静かに無視される。 「これから作る予定のパス」を着手前に投機的に渡せるのは実用上効く

9. CI で守る

docs-kit init が生成するワークフロー。

name: docs-kit
on:
  pull_request:
  push:
    branches: [main]
jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # stale は全履歴を必要とする
      - name: Install docs-kit
        run: |
          curl -fsSL "https://downloads.noop.co.jp/docs-kit/${DOCS_KIT_VERSION}/docs-kit_linux_amd64.tar.gz" | tar -xz
          sudo install -m 755 docs-kit /usr/local/bin/docs-kit
        env:
          DOCS_KIT_VERSION: v1.0.0
      - run: docs-kit lint
      - run: docs-kit stale
      - run: docs-kit index --check

fetch-depth: 0 は必須。 shallow clone だと verified_at の SHA が履歴に無く、全文書が unknown-shabroken になる。

3 つのコマンドの役割は分かれている。

コマンド見るものgit 履歴
lintfront matter のスキーマ違反不要
stale実装との鮮度のずれ必要
index --checkINDEX.md が最新か (gofmt 方式)不要

導入直後で stale がたくさん出る場合は、段階的に厳しくするとよい (既存プロジェクトへの導入 参照)。

10. コマンド早見表

docs-kit init                雛形 + config + CLAUDE.md ブロック + CI を生成
docs-kit adopt               既存 docs に front matter スタブを付与 (status: draft)
docs-kit lint                front matter のスキーマ検証
docs-kit stale [id...]       鮮度判定
docs-kit index [--check]     INDEX.md を生成 (--check は CI 用の同期検証)
docs-kit which <path...>     パス → 関連文書の逆引き
docs-kit new <layer> <slug>  テンプレートから新規文書を作成 (--title)
docs-kit schema              front matter のスキーマを出力 (フィールド / enum / 層ごとの規則)
docs-kit verify <id...>      verified_at を HEAD に更新し current へ昇格
docs-kit mcp                 MCP サーバを stdio で起動

schema は config も git も読まないので、文書が 1 つも無いリポジトリを含め、どのディレクトリからでも答えられる。

共通フラグ:

フラグ説明
-C <dir>実行ディレクトリを変更 (git と同様)
--format text|jsonlint / stale / which / schema の出力形式。既定は text
--versionバージョン表示
--forceinit のみ。生成 CI と .claude/commands を上書き

exit code:

code意味
0成功 (警告のみを含む)
1検出あり (lint エラー / stale)
2broken 検出 (stale のみ)
3実行エラー (config 不正・git 不在・引数エラー等)

出力パスはすべてリポジトリルート相対・/ 区切り。

設定ファイル

docs.config.yaml はリポジトリルート直下に 1 本だけ。これが設定の全面積。

schemaVersion: 1              # 必須。現在は 1 のみ
root: docs                    # 任意。層ディレクトリの親。既定 "docs"
codeRoots: [src]              # 任意。コードのルート群。既定 ["src"]
ignore:                       # 任意。stale 判定で無視するグロブ。既定 []
  - "**/*.test.ts"
  - "**/__snapshots__/**"
キー用途
schemaVersion互換性管理。未知の値は exit 3
root層ディレクトリ (adr/ 等) の親
codeRootslint W003 (scope が域外を指す警告) の基準
ignorestale の変更集合から除外するグロブ (scope 一致後に適用)

未知のトップレベルキーはエラー。config に x- 名前空間は無い。設定が壊れた状態で lint/stale の結果を返すと誤った green を出すため、config 不正はすべて exit 3 で即座に落ちる。

11. lint エラーコード早見表

エラー (exit 1):

コード内容
E001front matter が無い、または YAML として解析不能
E002必須フィールド欠落 (id / title / status)
E003id の形式不正 (層プレフィックス・文字種)
E004id とファイル名の不整合
E005id の重複
E006status が enum 外
E007未知のトップレベルフィールド (x- 以外)
E008status: current かつ scope ありなのに verified_at が無い
E009verified_at が 40 桁 hex でない
E010status: superseded なのに superseded_by が無い
E011depends_on / superseded_by の参照先 id が存在しない
E012scope グロブの構文エラー (絶対パス・.. を含む場合も)

警告 (exit 0):

コード内容
W001plan 層の文書に verified_at がある (無意味)
W002scope グロブが git 管理下のファイルに 1 件も一致しない。status: draft の間は報告しない
W003scope が codeRoots の外を指す (タイポの疑い)

lint は git 履歴に依存しない静的検証のみを行う。SHA の解決可能性など履歴に依存する検証は stale の管轄。

次に読む