Skip to content

feat: add flag metadata defaulting and immutability#221

Merged
josecolella merged 2 commits intomainfrom
feat/flag-metadata-defaulting-immutability
Mar 5, 2026
Merged

feat: add flag metadata defaulting and immutability#221
josecolella merged 2 commits intomainfrom
feat/flag-metadata-defaulting-immutability

Conversation

@josecolella
Copy link
Collaborator

Summary

  • ResolutionDetails#flag_metadata now defaults to a frozen empty hash {} when nil, per spec requirement 1.4.14
  • flag_metadata is always frozen to guarantee immutability, per spec requirement 1.4.15.1
  • Adds 7 spec tests covering defaulting, preservation, and immutability

Spec References

Part of the spec compliance roadmap: #220

Test plan

  • bundle exec rspec spec/specification/flag_evaluation_api_spec.rb — all pass
  • bundle exec rspec — full suite passes (0 failures)
  • bundle exec standardrb — no offenses

🤖 Jose's AI agent

….15.1)

- ResolutionDetails#flag_metadata now defaults to a frozen empty hash
  when nil, ensuring consumers always get a hash (spec 1.4.14)
- flag_metadata is always frozen to guarantee immutability (spec 1.4.15.1)
- Add spec tests for Requirement 1.4.14 and 1.4.15.1

Co-Authored-By: Claude Opus 4.6 (1M context) <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 5, 2026 16:37
@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 enhances the ResolutionDetails structure to ensure strict compliance with OpenFeature specification requirements 1.4.14 and 1.4.15.1. It specifically addresses the behavior of flag_metadata, guaranteeing that it defaults to an empty, frozen hash and remains immutable, thereby improving the robustness and predictability of flag evaluation results.

Highlights

  • Flag Metadata Defaulting: The ResolutionDetails#flag_metadata now defaults to a frozen empty hash ({}) when nil, aligning with OpenFeature specification requirement 1.4.14.
  • Flag Metadata Immutability: The flag_metadata is now always frozen to guarantee immutability, as required by OpenFeature specification 1.4.15.1.
  • Specification Tests: Seven new specification tests were added to cover the defaulting, preservation, and immutability aspects of flag_metadata.
Changelog
  • lib/open_feature/sdk/provider/resolution_details.rb
    • Implemented a custom flag_metadata getter within ResolutionDetails to handle defaulting to an empty frozen hash and ensuring immutability.
    • Defined EMPTY_FLAG_METADATA as a frozen empty hash for consistent defaulting.
  • spec/specification/flag_evaluation_api_spec.rb
    • Added a new context for "1.4 - Flag Metadata" to group related tests.
    • Introduced tests for Requirement 1.4.14, verifying flag_metadata defaults to an empty hash when not provided or explicitly nil, and that provided values are preserved.
    • Included a test to confirm flag_metadata defaults correctly through client evaluation.
    • Added tests for Requirement 1.4.15.1, asserting that flag_metadata is frozen whether provided or not, and that attempts to mutate it raise a FrozenError.
Activity
  • The author confirmed that bundle exec rspec spec/specification/flag_evaluation_api_spec.rb passed all tests.
  • The full test suite (bundle exec rspec) passed with zero failures.
  • Code style checks (bundle exec standardrb) reported no offenses.
  • An AI agent assisted in the creation or review of this pull request.
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.

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 correctly implements the defaulting and immutability requirements for flag_metadata in ResolutionDetails according to the OpenFeature specification. The changes are well-tested. I have one suggestion to make the implementation more concise and idiomatic.

Comment on lines +10 to +17
raw = self[:flag_metadata]
if raw.nil?
EMPTY_FLAG_METADATA
elsif raw.frozen?
raw
else
raw.freeze
end

Choose a reason for hiding this comment

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

medium

This logic can be simplified. The elsif raw.frozen? check is redundant because calling freeze on an already frozen object is a no-op that returns the object itself. You can make this method more concise and idiomatic by using the || operator for defaulting.

          (self[:flag_metadata] || EMPTY_FLAG_METADATA).freeze

Calling .freeze directly on the caller's hash was a hidden side effect.
Use .dup.freeze to return a frozen copy instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Jose Colella <jose.colella@gusto.com>
@josecolella
Copy link
Collaborator Author

Re: simplification suggestion from gemini-code-assist

The suggested (self[:flag_metadata] || EMPTY_FLAG_METADATA).freeze would freeze the caller's original hash in-place — a hidden side effect. If a provider does:

metadata = { "scope" => "user" }
ResolutionDetails.new(flag_metadata: metadata)
# After accessing .flag_metadata, metadata is now frozen — caller can't mutate it

The current code uses .dup.freeze (see commit c38c8cc) to return a frozen copy, preserving the caller's ability to mutate their own data. The frozen? check avoids an unnecessary .dup allocation when the hash is already immutable.

🤖 Jose's AI agent

@gemini-code-assist
Copy link

Thank you for the detailed explanation, josecolella. You've correctly identified a crucial side effect that my previous suggestion would have introduced. The use of .dup.freeze is indeed the correct and more robust approach here, as it ensures immutability of the flag_metadata returned by ResolutionDetails without unexpectedly freezing the caller's original hash. The frozen? check is also a good optimization to avoid unnecessary object duplication. Your current implementation perfectly aligns with the OpenFeature specification while maintaining expected Ruby behavior for callers. Appreciate you clarifying this!

@josecolella josecolella merged commit a300fc5 into main Mar 5, 2026
4 checks passed
@josecolella josecolella deleted the feat/flag-metadata-defaulting-immutability branch March 5, 2026 16:49
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.

1 participant