コンテンツにスキップ

GRP-002 循環参照

ドキュメント間の Markdown リンクをグラフとみなして、循環(A → B → C → A のような閉路)が発生していないかを検証します。循環が見つかると error で報告されます。プロジェクトスコープ のルールで、include で読み込んだ全ドキュメントを横断して評価されます。

検出対象は相対パスで .md ファイルを指すリンクのみです。アンカーだけのリンク(#section)や外部 URL は無視されます。

ドキュメント A が B を参照し、B が C を参照し、C が A を参照する、というような循環は、人間が単独のファイルを見ているだけでは絶対に気づけません。整合性のある依存関係を保つには、ドキュメントグラフが DAG(有向非巡回グラフ)であることが望ましく、循環は読者の混乱と更新時の影響範囲の追跡困難を生みます。AI で大量にドキュメントを生成すると、リンクが意図せず双方向になって循環が発生することもあります。このルールはそうした循環を検出します。

フィールド必須説明
filesstring循環検出の対象とするファイルの glob。未指定なら全ドキュメント
excludestring[]グラフから除外するファイルの glob 配列
siteRouterobjectStarlight など SSG が生成する routed URL(例: /docs/x/)を解決し、循環検出がそれらのリンクを辿れるようにする

オプション全体を省略しても動作します。exclude は、目次ファイルや index ファイルのように複数方向のリンクを意図的に持つファイルを除外する用途で使います。

siteRouter を指定しないと、/docs/x/ のような絶対 URL はグラフ構築時にスキップされ、SSG が生成するリンクを経由する循環は検出されません。指定可能なフィールドは REF-001siteRouter と同一です。

サブフィールド必須説明
presetstring既知の SSG プリセット。現在は "starlight" のみ対応
contentDirstringコンテンツディレクトリ(例: "packages/site/src/content/docs"
defaultLocalestringデフォルトロケール。Starlight の prefix なし root ロケールには "root" を指定
localesstring[]サポートするロケールのリスト(例: ["root", "ja", "ko", "zh"]
urlPrefixstring(preset を使わない汎用設定) URL から取り除くプレフィックス
indexFilestring最初に試す index ファイル名(既定: "index.md"

Starlight (i18n) の例:

{
"rule": "grp002",
"options": {
"siteRouter": {
"preset": "starlight",
"contentDir": "packages/site/src/content/docs",
"defaultLocale": "root",
"locales": ["root", "ja", "ko", "zh"]
}
}
}
docs/a.md:
# A
See [B](./b.md).
docs/b.md:
# B
See [C](./c.md).
docs/c.md:
# C
See [A](./a.md).

a.md → b.md → c.md → a.md の循環が発生しているので違反になります。

docs/a.md
line 2 error Circular reference detected: docs/a.md -> docs/b.md -> docs/c.md -> docs/a.md GRP-002

循環の中で「最も依存の弱いリンク」 を 1 本切ります。たとえば c.md → a.md のリンクを別の表現に変えます。

docs/c.md:
# C
This module is consumed by upstream documents.
{
"rule": "grp002",
"options": {
"files": "docs/**/*.md",
"exclude": ["docs/index.md", "docs/sitemap.md"]
}
}

exclude で指定したファイルはグラフからノードごと除外されます。それらを介した循環は検出対象外になるので、双方向リンクが必要な目次系ファイルだけに使うのが推奨です。