Reflection Facade — `Rigor::Reflection`
Status: Public read shape (v0.0.7). This module is the unified
read-side facade over Rigor’s three reflection sources. It is the
substrate the v0.1.0 plugin API will be designed against; per
docs/design/20260505-v0.1.0-readiness.md,
landing the facade was the highest-leverage cold-start slice for
v0.1.0 readiness.
The module is read-only and additive. Existing call sites that
read directly from Rigor::Scope or
Rigor::Environment::RbsLoader continue to work unchanged; they
migrate to the facade at their own pace.
Reflection sources joined
Section titled “Reflection sources joined”| Source | What it provides | Mutability |
|---|---|---|
Rigor::Environment::ClassRegistry | Ruby Class / Module objects (Integer, Float, Set, Pathname, …) registered at boot. | Static during a rigor check run. |
Rigor::Environment::RbsLoader | RBS-side declarations: instance / singleton methods, class hierarchy, constants. | Loaded on demand from the project’s sig/ directory and the bundled stdlib RBS. |
Rigor::Scope discovered facts | Source-side discoveries from Rigor::Inference::ScopeIndexer: user-defined classes / modules, in-source constants, discovered method nodes, class ivar / cvar declarations. | Per-scope; threaded through the inference engine. |
The facade joins these sources without caching; underlying sources
already cache where it matters (RbsLoader memoises class
definitions; ClassRegistry is constant; Scope is an immutable
value object).
Public API (v0.0.7 first pass)
Section titled “Public API (v0.0.7 first pass)”Existence and ordering
Section titled “Existence and ordering”Rigor::Reflection.class_known?(class_name, scope: Scope.empty)—truewhen ANY source recognises the class / module name.Rigor::Reflection.rbs_class_known?(class_name, scope: nil, environment: nil)— RBS-only variant ofclass_known?:trueonly when RBS defines the class, independent of source-discoveredclass Foo; enddeclarations. Use when a rule must walk RBS method tables and so needs an RBS-defined receiver specifically.Rigor::Reflection.class_ordering(lhs, rhs, scope: Scope.empty)—:equal/:subclass/:superclass/:disjoint/:unknownordering between two class names. Delegates toEnvironment#class_ordering.
Type carriers
Section titled “Type carriers”Rigor::Reflection.nominal_for_name(class_name, scope: Scope.empty)—Rigor::Type::Nominalfor the class name, ornilwhen no source knows the class.Rigor::Reflection.singleton_for_name(class_name, scope: Scope.empty)—Rigor::Type::Singletonfor the class name’s class object, ornil.
Constants
Section titled “Constants”Rigor::Reflection.constant_type_for(constant_name, scope: Scope.empty)— type of the named constant. Joins in-source constants (recorded byScopeIndexer) and RBS-side constants. In-source wins on collision because the user’s source is the authoritative declaration.
Methods
Section titled “Methods”Rigor::Reflection.instance_method_definition(class_name, method_name, scope: Scope.empty)— RBSRBS::Definition::Methodfor the instance method, ornilwhen the class or method is not in RBS.Rigor::Reflection.singleton_method_definition(class_name, method_name, scope: Scope.empty)— RBS-side singleton (class-side) method definition, ornil.Rigor::Reflection.instance_definition(class_name, scope: nil, environment: nil)— the full instance-sideRBS::Definition(whole method table / member list), ornil. For callers that walk the class rather than one method.Rigor::Reflection.singleton_definition(class_name, scope: nil, environment: nil)— the full singleton-sideRBS::Definition, ornil.Rigor::Reflection.class_type_param_names(class_name, scope: nil, environment: nil)— the RBS-declared type-parameter names asArray<Symbol>(e.g.[:Elem]forArray[Elem]), or[]for a non-generic or unknown class. Used when binding generic method types to a concrete receiver.
The RBS-consulting methods accept either scope: or environment: (the latter for dispatcher call sites that carry no Scope); when neither is given they fall back to Scope.empty’s environment.
Source-side discoveries
Section titled “Source-side discoveries”Rigor::Reflection.discovered_class?(class_name, scope: Scope.empty)—truewhen the analyzed source contains a class / module declaration. Does NOT consult the RBS loader (useclass_known?for the union).Rigor::Reflection.discovered_method?(class_name, method_name, kind: :instance, scope: Scope.empty)—truewhenScopeIndexerrecorded adeffor the given method on the given class with the matching kind.
Provenance
Section titled “Provenance”The provenance side of the API (which source family contributed each fact) is explicitly out of scope for the v0.0.7 first pass. v0.1.0’s plugin API adds it as a separate concern, per ADR-2 § “Plugin Diagnostic Provenance” and the readiness analysis’s recommendation to keep the facade narrow until plugin authors require provenance for diagnostic explanations.
Stability
Section titled “Stability”The facade’s method signatures are stable as a v0.0.x public read shape. Adding new methods is an additive change; renaming or removing existing methods is a breaking change requiring a major or minor version bump.
The underlying source-of-truth dispatch may change without notice.
For example, the in-source vs RBS preference rule for
constant_type_for is a documented contract and stays stable;
how each source caches its lookups internally is not.
Future evolution
Section titled “Future evolution”The v0.1.0 plugin API extends this module along three axes,
called out in docs/design/20260505-v0.1.0-readiness.md:
- Provenance — every read returns a
(value, source_family)pair so plugin diagnostics can explain why a fact came from source / RBS / generated / plugin. - Unified
MethodDefinitioncarrier — currentlyinstance_method_definitionreturns the rawRBS::Definition::Method; v0.1.0 introduces a Rigor-side carrier that joins sourcedefnodes, RBS sigs, and plugin dynamic members under one shape. - Cache slice descriptors — each read returns or accepts a cache key derived from the typed-slot schema in ADR-2 § “Cache Invalidation Needs a Declarative API”, so plugin facts that depend on a reflection lookup invalidate cleanly when the underlying source changes.
These are not part of the v0.0.x contract.
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.