3 層フィードバックの設計
contextlint は同じ lint エンジンを LSP / MCP / CI という 3 つのプロトコルに乗せて配信します。1 つの実行手段に絞らず、あえて 3 層に分けたのは、ドキュメントが壊れるタイミングと検出するタイミングをできるだけ近づける ための設計判断です。このページでは、その理由を解説します。
3 層の概要 (タイミングと対応ツール) は Get Started に表があります。ここでは なぜ 3 層に分けるのか を中心に扱います。
ドキュメントが壊れるタイミング
Section titled “ドキュメントが壊れるタイミング”ドキュメントの整合性は、ライフサイクルの 3 つの局面で崩れる可能性があります。
| 局面 | 何が起きるか |
|---|---|
| 人間が書いている間 | リネーム、ID 変更、セクション削除など、編集の副作用で参照が壊れる |
| AI が書いている間 | AI が新しいドキュメントを生成・編集するときに、既存ファイルへの参照を壊すことがある |
| 複数の編集が合流するとき | branch をマージした瞬間に、独立して書かれていた 2 つの変更が組み合わさって整合性が崩れる |
3 層フィードバックは、この 3 つの局面それぞれに対応するチェック点 を用意するための設計です。
| 層 | プロトコル | いつ走るか | 対応ツール |
|---|---|---|---|
| 書いている間 | LSP | エディタでの入力中 (debounce) | VS Code / Cursor / Neovim / Helix / JetBrains |
| AI が書いている間 | MCP | AI エージェントが doc を編集した直後 | Claude Code / Cursor Agent / Cline / Codex / Windsurf |
| マージ前 | CLI | PR / pre-commit / push 時 | GitHub Actions / pre-commit / Husky / lint-staged |
3 つは 検出するタイミング が違うだけで、実行するエンジン は同じです。ルール定義、設定ファイル、出力フォーマットも全て共通化されており、層の間でズレが生じない作りになっています。
なぜ 3 層に分けるのか
Section titled “なぜ 3 層に分けるのか”1 つの層だけでは検出が遅すぎる、もしくは早すぎる
Section titled “1 つの層だけでは検出が遅すぎる、もしくは早すぎる”CI のみに頼る場合の問題点: ドキュメントの不整合は、PR を開いて CI が走るまで誰にも見えません。気づいたときには複数のコミットを跨いだ修正が必要になっており、原因の特定にもコストがかかります。
LSP のみに頼る場合の問題点: 編集中の即時フィードバックは強力ですが、それを通り抜けたまま PR がマージされる可能性があります。最終ガードが無い状態で main ブランチが壊れるリスクが残ります。
MCP のみに頼る場合の問題点: AI エージェント経由の編集はカバーできますが、人間が直接エディタで書いた変更や、CI で検出すべき副作用は拾えません。
3 層を組み合わせることで、それぞれの「漏れ」を別の層で受け止められます。
検出が早いほど修正コストが下がる
Section titled “検出が早いほど修正コストが下がる”不整合は検出が早いほど直しやすく、遅いほど影響範囲が広がります。
| 検出タイミング | 修正コストの目安 |
|---|---|
| 入力中 (LSP) | キーストローク 1 個分。波線が出た瞬間に直る |
| AI 編集直後 (MCP) | AI が自分で修正案を提示する。人間は確認するだけ |
| PR 時 (CI) | コミットを跨いで修正が必要。レビュー往復が増える |
| マージ後 | 別 PR で追加修正。main が一時的に壊れた状態になる |
LSP は「壊さない」、MCP は「壊れたらすぐ直す」、CI は「壊れたものを通さない」 — それぞれ役割が異なり、同じツールが 3 つの形で配信される意義があります。
標準プロトコルに乗せることで、ツール選択の自由を保つ
Section titled “標準プロトコルに乗せることで、ツール選択の自由を保つ”3 層はそれぞれ オープンな標準プロトコル に対応しています。
- LSP (Language Server Protocol) — エディタを限定しない。VS Code / Cursor / Neovim / Helix / JetBrains で同じ動作
- MCP (Model Context Protocol) — AI ホストを限定しない。Claude Code / Cursor / Cline / Codex / Windsurf で同じ動作
- CLI (シェルコマンド) — CI / Git hook / 手動実行のいずれでも使える
contextlint は標準プロトコルを採用することで、ユーザーの環境にロックインしない ようにしています。エディタを乗り換えても、AI ホストを変えても、CI を別のサービスに移しても、同じリンターが使い続けられます。
同じエンジンを 3 層に乗せる利点
Section titled “同じエンジンを 3 層に乗せる利点”3 層が共通のエンジンを使うことには、運用上の重要な利点があります。
- 結果が一貫する — エディタで OK だったものが CI でエラーになる、という食い違いが起きません
- 設定が 1 か所に集約 —
contextlint.config.jsonをリポジトリ直下に置けば、3 層全てで同じルールが適用されます - ルール追加が即座に全層に反映 — 新しいルールを書けば、その日からエディタ・AI・CI の全てで検出可能になります
実装的には、lint パイプラインを @contextlint/core に集約し、@contextlint/cli / @contextlint/mcp-server / @contextlint/lsp-server がそれぞれ薄いアダプタとして core を呼び出す構造になっています。詳しくは Graph API や各 Integrations のページを参照してください。
次のステップ
Section titled “次のステップ”- Context Graph — 3 層の lint エンジンが共通して使う依存グラフ
- Integrations — 各層を実際に組み込む手順