Skip to content

feat: add RBS type signatures with Steep type checking#251

Merged
josecolella merged 5 commits intomainfrom
feat/add-rbs-type-signatures
Mar 18, 2026
Merged

feat: add RBS type signatures with Steep type checking#251
josecolella merged 5 commits intomainfrom
feat/add-rbs-type-signatures

Conversation

@josecolella
Copy link
Collaborator

Summary

  • Add RBS type signatures for all 32 Ruby source files under lib/, providing static type safety and IDE autocompletion for gem consumers
  • Set up Steep infrastructure (Steepfile, rbs_collection.yaml, development dependency) with lenient diagnostics for incremental adoption
  • Add CI job gating PRs on bundle exec steep check passing
  • Document sig/ convention in CLAUDE.md: any lib/ change must include corresponding RBS updates

Details

31 RBS files in sig/ mirror lib/ structure, covering:

  • _Provider interface formalizing the provider duck-type contract
  • _Hook interface for the hook lifecycle
  • _TransactionContextPropagator interface
  • All 12 dynamically generated Client methods declared explicitly
  • SDK module method_missing delegates declared as self.* methods
  • Struct types, constant modules, and all core classes

Key design decisions:

  • Steep with lenient mode — catches real type errors without blocking on edge cases from heavy metaprogramming
  • untyped for flag values — genuinely polymorphic; precise types on fetch_*_value return types
  • Hints as plain class — RBS has no DelegateClass support; commonly used Hash methods declared explicitly
  • sig/ shipped with gem — auto-included via existing git ls-files gemspec pattern

Test plan

  • bundle exec rbs -I sig validate — clean
  • bundle exec steep check — no type errors
  • bundle exec rspec — 420 examples, 0 failures (99.5% coverage)
  • bundle exec standardrb — no offenses

🤖 Jose's AI agent

josecolella and others added 3 commits March 18, 2026 05:28
Add RBS/Steep type checking tooling:
- Add steep gem to development dependencies
- Create Steepfile with lenient diagnostics for lib/
- Create rbs_collection.yaml for stdlib type dependencies
- Add Steep::RakeTask to Rakefile
- Add .gem_rbs_collection/ to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
Add 31 RBS signature files under sig/ mirroring the lib/ structure:
- Provider duck-type contract via _Provider interface
- Hook lifecycle contract via _Hook interface
- TransactionContextPropagator interface
- All 12 dynamically generated Client methods declared explicitly
- SDK module method_missing delegates declared as explicit self.* methods
- Struct types (ResolutionDetails, EvaluationDetails, ClientMetadata, etc.)
- Constants modules (ErrorCode, Reason, ProviderEvent, ProviderState)
- Core classes (Configuration, API, EventDispatcher, ProviderStateRegistry)
- Hints declared as plain class (DelegateClass unsupported in RBS)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
- Add steep CI job to GitHub Actions workflow
- Gate status check on steep job passing
- Document type check command and sig/ convention in CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
@josecolella josecolella requested a review from a team as a code owner March 18, 2026 12:29
Copilot AI review requested due to automatic review settings March 18, 2026 12:29
@codecov
Copy link

codecov bot commented Mar 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.50%. Comparing base (c9472a4) to head (0fac9d9).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #251   +/-   ##
=======================================
  Coverage   99.50%   99.50%           
=======================================
  Files          31       31           
  Lines         808      808           
=======================================
  Hits          804      804           
  Misses          4        4           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the OpenFeature Ruby SDK by integrating static type checking using RBS and Steep. This initiative aims to improve code quality, maintainability, and developer experience by catching type-related errors early and providing better IDE support. The changes involve adding comprehensive type signatures for the entire SDK, configuring the Steep type checker, and incorporating type validation into the continuous integration pipeline.

Highlights

  • RBS Type Signatures Added: Added RBS type signatures for all 32 Ruby source files under lib/, providing static type safety and IDE autocompletion for gem consumers.
  • Steep Integration: Set up Steep infrastructure, including Steepfile, rbs_collection.yaml, and a development dependency, configured with lenient diagnostics for incremental adoption.
  • CI/CD Integration: Introduced a new CI job that gates pull requests on bundle exec steep check passing, ensuring type correctness is maintained.
  • Documentation Update: Documented the sig/ convention in CLAUDE.md, specifying that any changes to lib/ files must include corresponding RBS updates.
  • Key Design Decisions: Implemented Steep in lenient mode to balance type checking rigor with Ruby's dynamic nature, used untyped for genuinely polymorphic flag values, handled Hooks::Hints as a plain class due to RBS limitations, and ensured sig/ files are shipped with the gem.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/main.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Set explicit top-level permissions with minimal `contents: read`
to satisfy security scanning requirements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive set of RBS type signatures for the entire lib directory and integrates Steep for static type checking. This is a significant enhancement to the project's maintainability and code quality. The changes are thorough, covering public and private APIs, constants, and even dynamically generated methods. The configuration for Steep and management of RBS collections appears correct. I have one suggestion to further improve the type safety of event handlers. Overall, this is an excellent and well-executed contribution.

end

interface _ToProc
def call: (untyped) -> untyped

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The _ToProc interface can be made more specific. Event handlers are always called with a Hash[Symbol, untyped] as an argument. Changing the signature to reflect this will improve type safety. Additionally, since the return value of the handler is not used, void is a more idiomatic return type than untyped.

      def call: (Hash[Symbol, untyped]) -> void

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — narrowed to (Hash[Symbol, untyped]) -> void in 0fac9d9.

🤖 Jose's AI agent

Narrow the handler interface signature from `(untyped) -> untyped`
to `(Hash[Symbol, untyped]) -> void` since event handlers always
receive event details hashes and their return values are unused.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
@josecolella josecolella merged commit 506e999 into main Mar 18, 2026
21 of 24 checks passed
@josecolella josecolella deleted the feat/add-rbs-type-signatures branch March 18, 2026 12:34
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Steep-based type checking to the OpenFeature Ruby SDK by introducing Steep configuration, RBS signatures that mirror the lib/ API, and CI automation to enforce type checks.

Changes:

  • Introduce Steep config (Steepfile) and RBS collection configuration (rbs_collection.yaml / lock).
  • Add initial RBS signatures under sig/ for core SDK surface area (API, client, configuration, hooks, providers, telemetry, etc.).
  • Wire Steep into development workflow (Gemfile + lock, Rakefile task, CI job, docs, gitignore).

Reviewed changes

Copilot reviewed 38 out of 40 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
Steepfile Defines Steep target, signature/check paths, diagnostic settings
sig/open_feature/sdk/version.rbs Types OpenFeature::SDK::VERSION
sig/open_feature/sdk/transaction_context_propagator.rbs Types transaction context propagator interface/module
sig/open_feature/sdk/tracking_event_details.rbs Types TrackingEventDetails
sig/open_feature/sdk/thread_local_transaction_context_propagator.rbs Types thread-local propagator implementation
sig/open_feature/sdk/telemetry.rbs Types telemetry constants and event creation API
sig/open_feature/sdk/provider/resolution_details.rbs Types provider resolution details struct + metadata
sig/open_feature/sdk/provider/reason.rbs Types provider reason constants
sig/open_feature/sdk/provider/provider_metadata.rbs Types provider metadata struct
sig/open_feature/sdk/provider/no_op_provider.rbs Types NoOp provider implementation
sig/open_feature/sdk/provider/in_memory_provider.rbs Types in-memory provider implementation
sig/open_feature/sdk/provider/event_emitter.rbs Types provider event emitter mixin
sig/open_feature/sdk/provider/error_code.rbs Types provider error code constants
sig/open_feature/sdk/provider.rbs Types provider interfaces
sig/open_feature/sdk/provider_state.rbs Types provider state constants
sig/open_feature/sdk/provider_state_registry.rbs Types provider state registry
sig/open_feature/sdk/provider_initialization_error.rbs Types initialization error class
sig/open_feature/sdk/provider_event.rbs Types provider event constants
sig/open_feature/sdk/hooks/logging_hook.rbs Types logging hook
sig/open_feature/sdk/hooks/hook.rbs Types hook interface/module
sig/open_feature/sdk/hooks/hook_executor.rbs Types hook executor
sig/open_feature/sdk/hooks/hook_context.rbs Types hook context
sig/open_feature/sdk/hooks/hints.rbs Types hook hints wrapper
sig/open_feature/sdk/event_dispatcher.rbs Types event dispatcher + handler callable interface
sig/open_feature/sdk/evaluation_details.rbs Types evaluation details struct/delegators
sig/open_feature/sdk/evaluation_context.rbs Types evaluation context
sig/open_feature/sdk/evaluation_context_builder.rbs Types evaluation context builder
sig/open_feature/sdk/configuration.rbs Types global configuration object
sig/open_feature/sdk/client.rbs Types client public API + private helpers
sig/open_feature/sdk/client_metadata.rbs Types client metadata struct
sig/open_feature/sdk/api.rbs Types API singleton surface
sig/open_feature/sdk.rbs Types module-level delegation surface
rbs_collection.yaml Configures external RBS sources and stdlib gems
rbs_collection.lock.yaml Locks resolved RBS dependencies and versions
Rakefile Adds optional Steep rake task
Gemfile.lock Adds Steep and dependencies to lockfile
Gemfile Adds steep to development/test dependencies
CLAUDE.md Documents steep install/type-check commands and sig/ conventions
.gitignore Ignores .gem_rbs_collection/ cache
.github/workflows/main.yml Adds Steep type-check job and includes it in CI status

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.


def initialize: () -> void

def add_hooks: (*Array[untyped] new_hooks) -> void

def initialize: (provider: untyped, ?domain: String?, ?evaluation_context: EvaluationContext?) -> void

def add_hooks: (*Array[untyped] new_hooks) -> void
def set_provider: (untyped provider, ?domain: String?) -> void
def set_provider_and_wait: (untyped provider, ?domain: String?) -> void
def hooks: () -> Array[untyped]
def add_hooks: (*Array[untyped] new_hooks) -> void

def run_short_circuit: (ordered_hooks: Array[untyped], hook_context: HookContext, hints: Hints, evaluation_details: EvaluationDetails) -> void

def execute: (ordered_hooks: Array[untyped], hook_context: HookContext, hints: Hints) { (HookContext) -> EvaluationDetails } -> EvaluationDetails?

private

def fetch_details: (type: Symbol, flag_key: String, default_value: untyped, ?evaluation_context: EvaluationContext?, ?invocation_hooks: Array[untyped], ?hook_hints: Hooks::Hints?) -> EvaluationDetails?

def configuration: () -> Configuration

def configure: () { (Configuration) -> void } -> void
module OpenFeature
module SDK
# Delegated to API.instance via method_missing
def self.configure: () { (Configuration) -> void } -> void
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants