コンテンツにスキップ

ADR-25 — プラグインが提供するRBSシグネチャ

ステータス: Accepted、2026-05-21

プラグインgemが、そのマニフェストを通じてRigorの解析環境にRBSシグネチャディレクトリを提供できるようにする決定を記録する — 今日、RBSのみの「バンドル」gemを手書きのsignature_paths:パスで配線せざるをえないギャップを解消する。WD1(signature_gems:設定キーではなくManifestフィールド)が承認された。却下された設定キー案は、書面上の前提として記録しておく。

今日のRigorは、3つのソースからRBS環境を構成しており、いずれもRbsLoadersignature_pathsに解決される:

  1. signature_paths:.rigor.yml内)— ディレクトリパスの明示的なリスト。.rigor.ymlはプレーンなYAML.safe_load_file(ERBなし)でパースされるため、エントリーはリテラルなパスである — gem名は解決されず、「インストール済みgem内のsig/」を名指しするポータブルな手段がない。
  2. bundler:ディスカバリーEnvironment::BundleSigDiscovery)— <bundle_path>/ruby/*/gems/*/sig/を走査する。自動検出するのはvendor/bundle / .bundle/configBUNDLE_PATHレイアウトのみであり、無差別である:選ばれた集合ではなく、スキップされないすべてのgemのsig/を取り込む。プレーンなbundle install(gemがシステム / rbenvのgemディレクトリにある)を実行するプロジェクトは、明示的なbundle_path:なしでは何も得られない。
  3. 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.ymlplugins:に列挙され、正常にロードされると、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/の場所を内部で解決する。

WD1 — 設定キーではなくマニフェストフィールド

Section titled “WD1 — 設定キーではなくマニフェストフィールド”

メカニズムはManifestフィールドであり、新しい.rigor.ymlのトップレベルキー(たとえば却下されたsignature_gems:)ではない。

  • マニフェストはすでに、他のすべてのプラグイン提供宣言を担っている — config_schemaproduces / consumesowns_receiverstype_node_resolversblock_as_methodsheredoc_templatestrait_registriesexternal_fileshkt_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 — ターゲットを絞った提供 対 広範なディスカバリー”

BundleSigDiscoverybundler:設定)は残る。両者は補完的である:

プラグイン提供(この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-init SKILLが単純化する。ASバンドル向けのsignature_paths:ステップ(現在はディレクトリをベンダリングする指示)が、通常のプラグイン選択ステップに畳み込まれる。
  • 将来のプラグインがRBSオーバーレイを同梱できる。Railsプラグインは、診断、モデル化するクラス向けのsig/オーバーレイの両方を、ひとつのgem内に同梱し、ひとつのマニフェストで宣言できる。
  • 有効化サーフェスがひとつ。すべてのRigor拡張 — 診断プラグインもRBSバンドルも — がplugins:エントリーである。
  • 純粋なRBSバンドルが今やプラグインクラスを抱える。今日は素のgemである場所に≈10行のセレモニー。ポータビリティと統一性のため、それに見合うと判断した(WD3)。
  • マニフェストフィールドがひとつ増える — 1.0以前の契約に。加法的かつ低リスク(WD6)。
  • BundleSigDiscoveryの自動検出をデフォルトのbundle install gemレイアウトに拡張することは、ここでは解決されていない — 機会主義的ディスカバリーの改善のままであり、別途キューに入れる。

スライス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-init SKILLを更新する: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.