ADR-25 — プラグインが提供するRBSシグネチャ
ステータス: Accepted、2026-05-21。
プラグインgemが、そのマニフェストを通じてRigorの解析環境にRBSシグネチャディレクトリを提供できるようにする決定を記録する — 今日、RBSのみの「バンドル」gemを手書きのsignature_paths:パスで配線せざるをえないギャップを解消する。WD1(signature_gems:設定キーではなくManifestフィールド)が承認された。却下された設定キー案は、書面上の前提として記録しておく。
コンテキスト
Section titled “コンテキスト”今日のRigorは、3つのソースからRBS環境を構成しており、いずれもRbsLoaderのsignature_pathsに解決される:
signature_paths:(.rigor.yml内)— ディレクトリパスの明示的なリスト。.rigor.ymlはプレーンなYAML.safe_load_file(ERBなし)でパースされるため、エントリーはリテラルなパスである — gem名は解決されず、「インストール済みgem内のsig/」を名指しするポータブルな手段がない。bundler:ディスカバリー(Environment::BundleSigDiscovery)—<bundle_path>/ruby/*/gems/*/sig/を走査する。自動検出するのはvendor/bundle/.bundle/configのBUNDLE_PATHレイアウトのみであり、無差別である:選ばれた集合ではなく、スキップされないすべてのgemのsig/を取り込む。プレーンなbundle install(gemがシステム / rbenvのgemディレクトリにある)を実行するプロジェクトは、明示的なbundle_path:なしでは何も得られない。rbs_collection:—rbs_collection.lock.yamlをパースする。
Plugin::Baseプラグインは、診断(diagnostics_for_file)、呼び出しごとの戻り値型(flow_contribution_for)、マクロサブストレート宣言、型ノードリゾルバ、プラグインをまたぐファクト(fact)を提供できる — しかしRBSは提供できない。「このプラグインがロードされたときにシグネチャ環境を拡張する」というマニフェストフックが存在しない。
具体的な被害者はrigor-activesupport-core-extである。これは、ActiveSupportのcore_extセレクタ(Railsプロジェクトで最大の単一call.undefined-methodクラスタ — 実測されたMastodonの実行では488件中≈365件の診断)向けにsig/を同梱することだけを目的としたgemである。プラグイン側のRBSフックが存在しないため、これはそもそもプラグインではない — そのlib/rigor-activesupport-core-ext.rbは素のコメントであり、ユーザーはsignature_paths:をそのgemのsig/へ手で配線しなければならない。設定にERBがないため、機能する形式は、絶対かつバージョン固定のパス(bundle show …)か、ディレクトリのベンダリングコピーのみである。そのgem自身のREADMEは、この手作業のステップを既知の欠点として文書化し、欠けているメカニズムを名指ししている:「Rigorが『ロード時にsignature_pathsを拡張する』というプラグインマニフェストエントリーを備えるまでは、最もシンプルなパッケージングは、ユーザーが手で配線するsig/ディレクトリである。」
同じギャップは、将来のあらゆるプラグインが、アナライザーコードと並んでRBSオーバーレイを同梱することを妨げる — たとえば、診断と、認識するフレームワーククラス向けのsig/オーバーレイの両方を同梱したいRailsプラグインのように。
このADRはそのギャップを解消する。
プラグインのManifestにオプショナルなsignature_paths:フィールドを追加する。プラグインは、自身が同梱するRBSディレクトリを、自身のgemルートからの相対パスとして宣言する。プラグインが.rigor.ymlのplugins:に列挙され、正常にロードされると、Plugin::Loaderは宣言された各ディレクトリを絶対パスに解決し、解決された集合が、設定のsignature_paths:およびbundler: / rbs_collection:ディスカバリーの出力と並んでRBS環境にマージされる。
純粋なRBSバンドルは些細なプラグインになる:signature_paths:を宣言し、それ以外には何も宣言しない(diagnostics_for_fileなし、flow_contribution_forなし)マニフェストを持つPlugin::Baseサブクラスである。rigor-activesupport-core-extはまさにこの形に変換され、プロジェクトはあらゆるプラグインを有効化するのと同じ方法で — plugins: [rigor-activesupport-core-ext] — パスなし、ベンダリングなし、絶対参照なしで有効化する。プラグインgemは自身のsig/の場所を内部で解決する。
作業上の決定
Section titled “作業上の決定”WD1 — 設定キーではなくマニフェストフィールド
Section titled “WD1 — 設定キーではなくマニフェストフィールド”メカニズムはManifestフィールドであり、新しい.rigor.ymlのトップレベルキー(たとえば却下されたsignature_gems:)ではない。
- マニフェストはすでに、他のすべてのプラグイン提供宣言を担っている —
config_schema、produces/consumes、owns_receivers、type_node_resolvers、block_as_methods、heredoc_templates、trait_registries、external_files、hkt_registrations。RBSの提供も同じ場所に属する;それはプラグインの性質であり、作者によって一度だけ宣言されるものであって、プロジェクトごとの設定ではない。 signature_gems:設定キーは、ユーザーが学ぶべき2つ目のメカニズムになる。プラグインがRBSを提供できる以上、それは冗長である — gemのsig/が欲しいプロジェクトは、そのgemをplugins:の下に列挙するだけでよい。- 有効化はひとつのリストにとどまる。
.rigor.ymlを読むレビュアーは、すべてのRigor拡張 — 診断プラグインもRBSバンドルも同様に — をplugins:の下に見る。
WD2 — パスはプラグインのgemルートからの相対
Section titled “WD2 — パスはプラグインのgemルートからの相対”このマニフェストフィールドは相対的なディレクトリ文字列(一般的なケースでは["sig"])を保持する。公開されたgem内の絶対パスは、別のマシンでは無意味である。
Plugin::Loaderはすでに各プラグインgemをrequireし、それが登録するプラグインクラスを観測している。ローダーはそのクラスの定義ファイル(Object.const_source_location)を記録し、gemルート(lib/を含むディレクトリ)まで遡り、各マニフェストのsignature_paths:エントリーをそれに対して解決する。存在しないディレクトリが宣言されている場合、そのプラグインに対するロード時のLoadErrorになる — サイレントではなく大声で、なぜならsig/の欠落はバンドルgemが壊れていることを意味するからである。
WD3 — 純粋なRBSバンドルもプラグインである
Section titled “WD3 — 純粋なRBSバンドルもプラグインである”RBSのみのgem(アナライザーコードなし)は、signature_paths:を宣言しフックを何もオーバーライドしないマニフェストを持つPlugin::Baseサブクラスとしてパッケージングされる。diagnostics_for_fileはベースのno-opを継承する。これは意図的でわずかなセレモニー — ≈10行のプラグインクラスひとつ — であり、その対価として、ポータブルな有効化、単一のplugins:リスト、他のすべてのRigor拡張との統一性を得る。
却下された案 — 独自のローダーパスを持つ別個の「RBSバンドル」アーティファクト型:それはローダーのgem-require / 登録 / id一意性の機構を、振る舞い上の利得なしに重複させることになる。
WD4 — プラグインのシグネチャは加法的;衝突は穏やかに縮退する
Section titled “WD4 — プラグインのシグネチャは加法的;衝突は穏やかに縮退する”プラグインが提供するsignature_paths:は、設定のsignature_paths:、bundler:ディスカバリーの出力、rbs_collection:とマージされる — 決して置き換えない。重複宣言の衝突(プラグインのsig/が、別のソースがすでに定義している定数を再宣言する)は、BundleSigDiscoveryの衝突がすでに使っているRbsLoader#env内の同じO7フェイラーメモパスを通じて縮退する:問題のファイルを名指しする警告がひとつ出て、解析は続行する。新しい衝突処理サーフェス(surface)はない。
WD5 — ターゲットを絞った提供 対 広範なディスカバリー
Section titled “WD5 — ターゲットを絞った提供 対 広範なディスカバリー”BundleSigDiscovery(bundler:設定)は残る。両者は補完的である:
| プラグイン提供(このADR) | BundleSigDiscovery | |
|---|---|---|
| 対象範囲 | plugins:の下にあるプラグインそのもの | vendor/bundle内のスキップされないすべてのgem |
| 意図 | 作者が「このgemはRBSソースである」と宣言する | 機会主義的 — sig/を同梱するものを何でも拾う |
| レイアウト | 任意 — プラグインが自身のパスを解決する | vendor/bundle / .bundle/configのみ |
プラグイン提供は意図的でポータブルなパスであり、バンドルディスカバリーは機会主義的な総合受け皿のままである。BundleSigDiscoveryをデフォルトのbundle installレイアウトに拡張することは、別個でより小さなフォローアップである(§「対象外」に記載)。
WD6 — 1.0以前のプラグイン契約に加法的
Section titled “WD6 — 1.0以前のプラグイン契約に加法的”プラグイン契約(contract)(ADR-2)は1.0以前であり、v0.2.0で安定化する。この変更は新しいオプショナルなマニフェストフィールドである — signature_paths:を宣言しないプラグインは影響を受けず、既存のプラグインは何も壊れない。v0.1.x系統内で投入しても安全であり、v0.2.0が凍結する契約サーフェスの一部であるべきである。
rigor-activesupport-core-extがポータブルな1行の有効化を得る。plugins: [rigor-activesupport-core-ext]—bundle showなし、絶対パスなし、ベンダリングなし。READMEの手作業による配線セクションと、そのv0.1.xの欠点が消える。rigor-project-initSKILLが単純化する。ASバンドル向けのsignature_paths:ステップ(現在はディレクトリをベンダリングする指示)が、通常のプラグイン選択ステップに畳み込まれる。- 将来のプラグインがRBSオーバーレイを同梱できる。Railsプラグインは、診断と、モデル化するクラス向けの
sig/オーバーレイの両方を、ひとつのgem内に同梱し、ひとつのマニフェストで宣言できる。 - 有効化サーフェスがひとつ。すべてのRigor拡張 — 診断プラグインもRBSバンドルも — が
plugins:エントリーである。
- 純粋なRBSバンドルが今やプラグインクラスを抱える。今日は素のgemである場所に≈10行のセレモニー。ポータビリティと統一性のため、それに見合うと判断した(WD3)。
- マニフェストフィールドがひとつ増える — 1.0以前の契約に。加法的かつ低リスク(WD6)。
BundleSigDiscoveryの自動検出をデフォルトのbundle installgemレイアウトに拡張することは、ここでは解決されていない — 機会主義的ディスカバリーの改善のままであり、別途キューに入れる。
実装のスライス分け(提案)
Section titled “実装のスライス分け(提案)”スライス1 — マニフェストフィールド + ローダー解決 + 環境マージ
Section titled “スライス1 — マニフェストフィールド + ローダー解決 + 環境マージ”Manifestがオプショナルなsignature_paths:フィールド(相対文字列の配列、フリーズ済み)を得る。Plugin::Loaderが、ロードされた各プラグインの宣言ディレクトリを、プラグインgemルートに対して絶対パスに解決する(WD2);欠落したディレクトリはそのプラグインに対するLoadErrorになる。Runner/Environment.for_projectが、レジストリの解決済みプラグインシグネチャディレクトリを、RbsLoaderに渡されるシグネチャパス集合にマージする(レジストリはすでにEnvironment.for_projectに通されている)。- spec:マニフェストフィールドの配線、ローダー解決 + ディレクトリ欠落エラー、エンドツーエンドの
Environment取り込み。
スライス2 — rigor-activesupport-core-extをプラグインに変換
Section titled “スライス2 — rigor-activesupport-core-extをプラグインに変換”signature_paths: ["sig"]を宣言するマニフェストを持つRigor::Plugin::ActivesupportCoreExt < Plugin::Baseクラスを追加し、登録する。- READMEの「Usage」を
plugins:有効化を中心に書き直し、手作業のsignature_paths:/ ベンダリングの指示を削除する。
スライス3 — オンボーディングのフォロースルー
Section titled “スライス3 — オンボーディングのフォロースルー”rigor-project-initSKILLを更新する:ASバンドルがsignature_paths:配線ステップから通常のプラグイン選択へ移る。bundler:/rbs_collection:設定ドキュメントから相互参照する。
- ADR-2 — このADRが拡張するプラグイン契約。
plugins/rigor-activesupport-core-ext/README.md§「なぜRigor::Plugin::Baseサブクラスではなくsig/バンドルなのか」 — このADRが解消するギャップであり、バンドル自身によって名指しされている。docs/notes/20260521-mastodon-v4.5-regression-sweep.mdおよびdocs/notes/20260515-real-world-rails-survey.md— ActiveSupportのcore_extクラスタがRailsの支配的な診断ソースであることを確立したサーベイ。lib/rigor/environment/bundle_sig_discovery.rb— このADRのターゲットを絞ったメカニズムが補完する、機会主義的ディスカバリー。
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.