open-feature / open-feature/flagd

feat: implement numeric coercion contract (ADR)

Open
#1,995 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

v1.0-prereq
Dominant language
Go
Stars
997
Forks
136
Avg merge
4d 8h
Merged PRs (30d)
11

Description

Tracking issue for implementing the numeric coercion ADR across flagd, flagd-schemas, flagd-testbed, and all flagd providers.

Contract summary

Per docs/architecture-decisions/numeric-coercion.md (merged in #1979):

  • Numeric variants are returned through a numeric accessor only when the conversion is lossless; otherwise TYPE_MISMATCH.
  • Numeric flag values are constrained to the IEEE-754 safe-integer range, [-(2^53 - 1), 2^53 - 1], in the flag-definition schema. This is an ordinary schema validation, surfaced like any other schema validation (today flagd logs schema violations as warnings; that behavior is unchanged). Hard-failing a load on schema violations is intentionally out of scope for this work and is tracked separately as its own feature.
  • The lossless-coercion contract applies identically across gRPC, OFREP, and in-process evaluation.
  • Int bounds are relative to the accessor in use. Accessor widths differ by language; the shared testbed treats Long (int64) as the canonical wide numeric accessor and only encodes language-agnostic assertions, with int32-specific cases behind @int32-bounded.
Per-language accessor widths
Language Integer accessor Long accessor Notes
Go (flagd-core) int64 (ResolveIntValue) same (int64 is canonical) no separate Long; Integer is the canonical Long, so Go excludes @int32-bounded
Java int32 (getInteger) int64 (getLong, java-sdk#1985) Long safe-delegates to int; canonical wide accessor
.NET int32 int64 (Int64)
Python arbitrary-precision int n/a effectively wide
JS / Web double double no int64; exact up to 2^53 - 1 (the cap)

Float accessors cap at the safe-integer range.

No wire format changes. AnyFlag.double_value in ResolveAll remains as-is; the ADR intentionally treats the parsed int/float JSON type as non-authoritative, which keeps bulk eval faithful under the same rule.

Breaking change scope

  • Callers relying on silent truncation (Go, Java in-process): 3.14 via getIntegerDetails returns TYPE_MISMATCH instead of 3.
  • Some SDKs flip the other way: 10.0 via getIntegerDetails returns 10 (previously TYPE_MISMATCH in Python and .NET).
  • Config authors: flag definitions with numeric values outside [-(2^53 - 1), 2^53 - 1] fail schema validation (a load-time warning today, consistent with every other schema validation). They are not hard-rejected at load by this work.

flagd is pre-1.0, so the coercion behavior change ships as a feat!: minor bump with breaking changes called out in release notes.

Testbed rollout

There is no existing numeric-coercion suite to preserve. This work adds a new, opt-in @numeric-coercion tagged suite (following the opt-in @fractional-v1/@fractional-v2 precedent) so providers adopt it as they migrate. Coverage is added to both the provider gherkin/ suite and the self-contained evaluator/ suite. Width-specific expectations sit behind @int32-bounded; wide and boundary values use the canonical Long accessor.

Rollout order

  1. flagd-schemas: add the safe-integer range to numeric variant values (schema validation)
  2. flagd-testbed: add ADR-conformant scenarios under @numeric-coercion (both suites), release new tag
  3. flagd + flagd-core (Go): implement the lossless-coercion contract, bump testbed submodule
  4. Providers (in parallel once the testbed tag lands): bump testbed submodule, implement the lossless-coercion contract in each in-process resolver, apply the same lossless check client-side for AnyFlag.double_value
  5. Coordinated provider releases

Child issues

  • flagd-schemas: open-feature/flagd-schemas#219
  • flagd-testbed: open-feature/flagd-testbed#379
  • flagd (core + Go flagd-core): open-feature/flagd#1996
  • go-sdk-contrib: open-feature/go-sdk-contrib#923
  • java-sdk-contrib: open-feature/java-sdk-contrib#1822
  • js-sdk-contrib: open-feature/js-sdk-contrib#1578
  • python-sdk-contrib: open-feature/python-sdk-contrib#402
  • dotnet-sdk-contrib: open-feature/dotnet-sdk-contrib#709
  • php-sdk-contrib: open-feature/php-sdk-contrib#169
  • rust-sdk-contrib: open-feature/rust-sdk-contrib#151

Related

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with docs/architecture-decisions/numeric-coercion.md and the rollout order in this issue, then review the linked child issues for flagd-schemas, flagd-testbed, flagd, and each provider. Done means the child implementations and opt-in test suites are complete, the testbed tag and submodule updates land, and coordinated provider releases ship the contract.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, go, java, javascript, php, python, rust
Domain
api, backend, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.