Skip to content

feat: implement hooks lifecycle (spec section 4)#214

Merged
josecolella merged 6 commits intomainfrom
feat/hooks-lifecycle
Mar 4, 2026
Merged

feat: implement hooks lifecycle (spec section 4)#214
josecolella merged 6 commits intomainfrom
feat/hooks-lifecycle

Conversation

@josecolella
Copy link
Collaborator

@josecolella josecolella commented Mar 4, 2026

Tip

Best reviewed commit-by-commit

Summary

  • Implements the full OpenFeature hooks lifecycle per specification section 4
  • Adds Hook interface module, HookContext class, and HookExecutor engine
  • Integrates hooks into Client flag evaluation with proper ordering (API → Client → Invocation → Provider for before; reverse for after/error/finally)
  • Includes a no-hooks fast path to avoid allocations when hooks aren't registered
  • Replaces pending tests for Requirements 1.1.4 and 1.2.1

Closes #52

Test plan

  • 320 examples, 0 failures (full bundle exec rake)
  • 99.31% code coverage
  • StandardRB lint clean (58 files, no offenses)
  • Hooks unit tests: spec/open_feature/sdk/hooks/
  • Hooks spec compliance tests: spec/specification/hooks_spec.rb
  • No regressions in existing tests: spec/open_feature/sdk/client_spec.rb, spec/specification/flag_evaluation_api_spec.rb

🤖 Jose's AI agent

@josecolella josecolella requested a review from a team as a code owner March 4, 2026 05:55
@josecolella josecolella requested a review from beeme1mr March 4, 2026 05:55
@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 SDK by introducing a robust and spec-compliant hooks lifecycle. It allows developers to inject custom logic at various stages of flag evaluation, from before the evaluation to after, error handling, and final cleanup. This feature provides greater flexibility and control over how feature flags are processed, enabling advanced use cases like context modification, metric collection, and error recovery, all while maintaining performance with an optimized execution path.

Highlights

  • Full Hooks Lifecycle Implementation: Implemented the complete OpenFeature hooks lifecycle as defined in specification section 4, enabling custom logic at various stages of flag evaluation.
  • New Hooks Modules and Engine: Introduced a Hook interface module, a HookContext class for providing evaluation context to hooks, and a HookExecutor engine to manage the execution of hooks.
  • Integrated Hook Ordering: Integrated hooks into the Client flag evaluation process with the specified ordering: API → Client → Invocation → Provider for 'before' hooks, and the reverse for 'after', 'error', and 'finally' hooks.
  • Performance Optimization: Included a 'no-hooks fast path' to avoid unnecessary allocations and overhead when no hooks are registered for a given flag evaluation.
  • Updated Specification Compliance Tests: Replaced pending tests for Requirements 1.1.4 and 1.2.1, and added comprehensive new tests to ensure full compliance with the OpenFeature hooks specification.
Changelog
  • lib/open_feature/sdk/api.rb
    • Added a new require statement for the 'hooks' module.
  • lib/open_feature/sdk/client.rb
    • Added EMPTY_HINTS constant for performance optimization.
    • Modified fetch_ methods to accept hooks and hook_hints parameters.
    • Refactored fetch_details to orchestrate the hook lifecycle using HookExecutor.
    • Introduced a new private method evaluate_flag to encapsulate the core flag resolution logic.
  • lib/open_feature/sdk/hooks.rb
    • Added a new module to consolidate and require all hook-related components.
  • lib/open_feature/sdk/hooks/hints.rb
    • Added a require statement for the 'delegate' library.
  • lib/open_feature/sdk/hooks/hook.rb
    • Added a new module Hook defining the before, after, error, and finally lifecycle methods with no-op default implementations.
  • lib/open_feature/sdk/hooks/hook_context.rb
    • Added a new class HookContext to encapsulate immutable flag evaluation details and a mutable evaluation context for hooks.
  • lib/open_feature/sdk/hooks/hook_executor.rb
    • Added a new class HookExecutor responsible for orchestrating the execution of hooks in the correct order and handling errors during the lifecycle.
  • spec/open_feature/sdk/hooks/hook_context_spec.rb
    • Added new unit tests for the HookContext class, verifying immutability and mutability of its properties.
  • spec/open_feature/sdk/hooks/hook_executor_spec.rb
    • Added new unit tests for the HookExecutor class, covering hook execution order, context merging, and error handling scenarios.
  • spec/open_feature/sdk/hooks/hook_spec.rb
    • Added new unit tests for the Hook module, verifying default implementations and the ability to override stages.
  • spec/specification/flag_evaluation_api_spec.rb
    • Updated and implemented previously pending tests for API and Client hook registration (Requirements 1.1.4 and 1.2.1).
  • spec/specification/hooks_spec.rb
    • Added a new comprehensive specification compliance test file for the OpenFeature hooks, covering all requirements from section 4.
  • spec/support/recording_hook_helper.rb
    • Added a new helper module RecordingHookHelper to facilitate testing hook execution order by logging calls.
Activity
  • Implemented the full OpenFeature hooks lifecycle per specification section 4.
  • Added Hook interface module, HookContext class, and HookExecutor engine.
  • Integrated hooks into Client flag evaluation with proper ordering (API → Client → Invocation → Provider for before; reverse for after/error/finally).
  • Included a no-hooks fast path to avoid allocations when hooks aren't registered.
  • Replaced pending tests for Requirements 1.1.4 and 1.2.1.
  • Achieved 320 examples, 0 failures in full test suite.
  • Maintained 99.31% code coverage.
  • Ensured StandardRB lint clean with no offenses.
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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

@josecolella josecolella mentioned this pull request Mar 4, 2026
@codecov
Copy link

codecov bot commented Mar 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.64%. Comparing base (e347439) to head (6e30988).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #214      +/-   ##
==========================================
+ Coverage   99.59%   99.64%   +0.05%     
==========================================
  Files          22       26       +4     
  Lines         491      571      +80     
==========================================
+ Hits          489      569      +80     
  Misses          2        2              

☔ 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.

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 implements the full OpenFeature hooks lifecycle, which is a major feature. The implementation is well-structured, following the specification closely. New components like HookExecutor, HookContext, and the Hook module are introduced, and they are integrated into the Client's flag evaluation process. The inclusion of a fast path for when no hooks are present is a good performance consideration. The code is accompanied by a comprehensive suite of unit and specification compliance tests, which is excellent. I have one suggestion regarding the immutability of default_value in the HookContext to fully align with the specification. Overall, this is a high-quality contribution.

josecolella and others added 4 commits March 3, 2026 21:58
Introduce the core hook abstractions per OpenFeature spec section 4:

- Hook module (hooks/hook.rb): provides default no-op implementations
  for before, after, error, and finally stages. Hooks include this
  module and override the stages they care about (spec 4.3.1).

- HookContext class (hooks/hook_context.rb): immutable flag_key and
  flag_value_type with mutable evaluation_context, plus optional
  client_metadata and provider_metadata (spec 4.1.1-4.1.5).

- Barrel require file (hooks.rb): loads all hooks components.

- Move `require "delegate"` into hints.rb where it is actually needed.

Closes #52

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
Implements the hook execution engine per OpenFeature spec 4.4:

- Before hooks run in order: API → Client → Invocation → Provider
- After/Error/Finally hooks run in reverse order
- Before hooks may return EvaluationContext for merging (spec 4.3.4)
- Error in before/after → runs error hooks, returns default (spec 4.4.5-7)
- Error/finally hook failures are logged, remaining hooks still run (spec 4.4.3-4)
- Uses reverse_each to avoid intermediate array allocations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
Wire hooks into the evaluation flow:

- Add `require_relative "hooks"` to api.rb
- Update Client method signatures to accept hooks: and hook_hints:
- Assemble hooks in spec order: API → Client → Invocation → Provider
- Wrap provider calls with HookExecutor when hooks are present
- Fast path: skip hook ceremony entirely when no hooks are registered
- Extract evaluate_flag method shared between fast and hooks paths
- Use frozen EMPTY_HINTS constant for the common no-hints case
- Use splat array assembly to avoid intermediate allocations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
Add comprehensive test coverage for the hooks feature:

Unit tests:
- hook_spec.rb: default no-ops, selective override behavior
- hook_context_spec.rb: immutability, optional fields, mutable context
- hook_executor_spec.rb: ordering, error handling, context propagation

Specification compliance (hooks_spec.rb):
- 4.1.x: HookContext fields, immutability, mutability
- 4.3.x: Stage execution timing, context merging, error/finally
- 4.4.x: Hook ordering, error isolation, default value on failure
- 4.5.x: API/Client/invocation hook registration

Also:
- Replace pending tests for Requirements 1.1.4 and 1.2.1
- Extract shared recording_hook helper to spec/support/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
@josecolella josecolella force-pushed the feat/hooks-lifecycle branch from a310240 to 7cf9c76 Compare March 4, 2026 05:59
josecolella and others added 2 commits March 3, 2026 22:00
Address review feedback:

- Freeze default_value in HookContext to enforce immutability per
  spec 4.1.3, preventing hooks from mutating mutable default values
  like Hash or Array.

- Add test for frozen default_value with a mutable object type.

- Add test for hook_hints Hash conversion to cover the Hints
  construction branch flagged by Codecov.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
Add test for passing a pre-built Hooks::Hints instance as hook_hints,
covering line 82 in client.rb where the Hints object is passed through
directly without conversion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
@josecolella josecolella merged commit 41c3b9e into main Mar 4, 2026
17 checks passed
@josecolella josecolella deleted the feat/hooks-lifecycle branch March 4, 2026 14:41
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.

Implement hooks

1 participant