FAILURE MAP
← Case archive

FA-73966 / Feature flag rollout bucketing / Open access

Bucketing attribute selection: Integral floats keep a decimal suffix · case 01

An org id sent as 5.0 by one client and 5 by another lands in different buckets.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The integral float is stringified directly, producing "5.0".

VERIFIED REPAIR

Convert integral floats to int before stringifying.

Unsuccessful approach: Stripping trailing zeros from the float text leaves "5." as the identity.

Case contract

The bucketing identity is user[bucket_by] when bucket_by is non-empty and present, else user["key"]. Booleans, None and non-scalar values are unbucketable -> [None, 0]; integral floats use their integer form, other floats are unbucketable; ints and strings are used verbatim via str(). Return [identity, crc32(flag_key + "." + salt + "." + identity) % 100000].

Why this case matters

Bucketing by organisation or device keeps whole accounts in one variant; the identity must be stable.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import zlib
N = 1
observations = []
def solve(flag_key, salt, user, bucket_by):
    if bucket_by and bucket_by in user:
        value = user[bucket_by]
    else:
        value = user.get('key')
    if isinstance(value, bool) or value is None:
        return [None, 0]
    if isinstance(value, float):
        if value != int(value):
            return [None, 0]
    if not isinstance(value, (int, str)):
        return [None, 0]
    ident = str(value)
    return [ident, zlib.crc32((flag_key + '.' + salt + '.' + ident).encode()) % 100000]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('boolean attribute is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': True}, 'org'], [None, 0]),
  ('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 2', ['new-nav', 'b', {'key': 'u1', 'org': '05', 'team': [1]}, 'org'], ['05', 58693]),
  ('identity sample 3', ['new-nav', 'b', {'key': '007', 'org': 'acme'}, 'org'], ['acme', 9580])],
 [('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 6', ['new-nav', 'b', {'key': 'u1', 'org': -3.0, 'team': 2.5}, 'org'], ['-3', 33868]),
  ('identity sample 7', ['new-nav', 'a', {'key': '007', 'team': 'acme'}, 'team'], ['acme', 37026])],
 [('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('identity sample 6', ['new-nav', 'b', {'key': 'u1', 'org': -3.0, 'team': 2.5}, 'org'], ['-3', 33868]),
  ('identity sample 11', ['new-nav', 'a', {'key': 'u1', 'org': 12, 'team': 2.5}, None], ['u1', 4562]),
  ('identity sample 12', ['new-nav', 'b', {'key': '007'}, None], ['007', 68652])],
 [('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('leading-zero key kept verbatim', ['bulk-edit', 'b', {'key': '05'}, None], ['05', 9565]),
  ('identity sample 16', ['new-nav', 'b', {'key': 'u1', 'org': '0012', 'team': ''}, None], ['u1', 96668]),
  ('identity sample 17', ['new-nav', 'a', {'key': 'u1', 'org': 12, 'team': True}, 'org'], ['12', 46025])],
 [('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('leading-zero key kept verbatim', ['bulk-edit', 'b', {'key': '05'}, None], ['05', 9565]),
  ('false attribute is unbucketable', ['bulk-edit', 'b', {'key': 'u1', 'org': False}, 'org'], [None, 0]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 21', ['new-nav', 'a', {'key': 'u42', 'org': 12, 'team': False}, 'team'], [None, 0]),
  ('identity sample 22', ['bulk-edit', 'a', {'key': 'u1', 'org': ''}, None], ['u1', 93162])]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
boolean attribute is unbucketable[None, 0][None, 0]Passed
integral float bucketed as integer[None, 0]['5', 7520]Failed
integral float matches integer identity['5', 7520]['5', 7520]Passed
missing bucket attribute falls back to key['u1', 4562]['u1', 4562]Passed
zero attribute is a real identity['0', 47112]['0', 47112]Passed
identity sample 1[None, 0]['5', 56729]Failed
identity sample 2['05', 58693]['05', 58693]Passed
identity sample 3['acme', 9580]['acme', 9580]Passed

SHA-256 / 70f0e8315f3773dbce28dbc64948bbab307add539de6655b1bfbdf563de3e211

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import zlib
N = 1
observations = []
def solve(flag_key, salt, user, bucket_by):
    if bucket_by and bucket_by in user:
        value = user[bucket_by]
    else:
        value = user.get('key')
    if isinstance(value, bool) or value is None:
        return [None, 0]
    if isinstance(value, float):
        if value != int(value):
            return [None, 0]
        value = str(value).rstrip('0')
    if not isinstance(value, (int, str)):
        return [None, 0]
    ident = str(value)
    return [ident, zlib.crc32((flag_key + '.' + salt + '.' + ident).encode()) % 100000]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('boolean attribute is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': True}, 'org'], [None, 0]),
  ('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 2', ['new-nav', 'b', {'key': 'u1', 'org': '05', 'team': [1]}, 'org'], ['05', 58693]),
  ('identity sample 3', ['new-nav', 'b', {'key': '007', 'org': 'acme'}, 'org'], ['acme', 9580])],
 [('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 6', ['new-nav', 'b', {'key': 'u1', 'org': -3.0, 'team': 2.5}, 'org'], ['-3', 33868]),
  ('identity sample 7', ['new-nav', 'a', {'key': '007', 'team': 'acme'}, 'team'], ['acme', 37026])],
 [('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('identity sample 6', ['new-nav', 'b', {'key': 'u1', 'org': -3.0, 'team': 2.5}, 'org'], ['-3', 33868]),
  ('identity sample 11', ['new-nav', 'a', {'key': 'u1', 'org': 12, 'team': 2.5}, None], ['u1', 4562]),
  ('identity sample 12', ['new-nav', 'b', {'key': '007'}, None], ['007', 68652])],
 [('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('leading-zero key kept verbatim', ['bulk-edit', 'b', {'key': '05'}, None], ['05', 9565]),
  ('identity sample 16', ['new-nav', 'b', {'key': 'u1', 'org': '0012', 'team': ''}, None], ['u1', 96668]),
  ('identity sample 17', ['new-nav', 'a', {'key': 'u1', 'org': 12, 'team': True}, 'org'], ['12', 46025])],
 [('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('leading-zero key kept verbatim', ['bulk-edit', 'b', {'key': '05'}, None], ['05', 9565]),
  ('false attribute is unbucketable', ['bulk-edit', 'b', {'key': 'u1', 'org': False}, 'org'], [None, 0]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 21', ['new-nav', 'a', {'key': 'u42', 'org': 12, 'team': False}, 'team'], [None, 0]),
  ('identity sample 22', ['bulk-edit', 'a', {'key': 'u1', 'org': ''}, None], ['u1', 93162])]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
boolean attribute is unbucketable[None, 0][None, 0]Passed
integral float bucketed as integer['5.', 60130]['5', 7520]Failed
integral float matches integer identity['5', 7520]['5', 7520]Passed
missing bucket attribute falls back to key['u1', 4562]['u1', 4562]Passed
zero attribute is a real identity['0', 47112]['0', 47112]Passed
identity sample 1['5.', 8460]['5', 56729]Failed
identity sample 2['05', 58693]['05', 58693]Passed
identity sample 3['acme', 9580]['acme', 9580]Passed

SHA-256 / 7dff2d6463cff52c93d2acefb5e19307fbb1c6d4ef599edd2fe75daf71808a9f

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import zlib
N = 1
observations = []
def solve(flag_key, salt, user, bucket_by):
    if bucket_by and bucket_by in user:
        value = user[bucket_by]
    else:
        value = user.get('key')
    if isinstance(value, bool) or value is None:
        return [None, 0]
    if isinstance(value, float):
        if value != int(value):
            return [None, 0]
        value = int(value)
    if not isinstance(value, (int, str)):
        return [None, 0]
    ident = str(value)
    return [ident, zlib.crc32((flag_key + '.' + salt + '.' + ident).encode()) % 100000]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('boolean attribute is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': True}, 'org'], [None, 0]),
  ('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 2', ['new-nav', 'b', {'key': 'u1', 'org': '05', 'team': [1]}, 'org'], ['05', 58693]),
  ('identity sample 3', ['new-nav', 'b', {'key': '007', 'org': 'acme'}, 'org'], ['acme', 9580])],
 [('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 6', ['new-nav', 'b', {'key': 'u1', 'org': -3.0, 'team': 2.5}, 'org'], ['-3', 33868]),
  ('identity sample 7', ['new-nav', 'a', {'key': '007', 'team': 'acme'}, 'team'], ['acme', 37026])],
 [('integral float matches integer identity', ['new-nav', 'a', {'key': 'u1', 'org': 5}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('identity sample 6', ['new-nav', 'b', {'key': 'u1', 'org': -3.0, 'team': 2.5}, 'org'], ['-3', 33868]),
  ('identity sample 11', ['new-nav', 'a', {'key': 'u1', 'org': 12, 'team': 2.5}, None], ['u1', 4562]),
  ('identity sample 12', ['new-nav', 'b', {'key': '007'}, None], ['007', 68652])],
 [('integral float bucketed as integer', ['new-nav', 'a', {'key': 'u1', 'org': 5.0}, 'org'], ['5', 7520]),
  ('missing bucket attribute falls back to key', ['new-nav', 'a', {'key': 'u1'}, 'org'], ['u1', 4562]),
  ('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('leading-zero key kept verbatim', ['bulk-edit', 'b', {'key': '05'}, None], ['05', 9565]),
  ('identity sample 16', ['new-nav', 'b', {'key': 'u1', 'org': '0012', 'team': ''}, None], ['u1', 96668]),
  ('identity sample 17', ['new-nav', 'a', {'key': 'u1', 'org': 12, 'team': True}, 'org'], ['12', 46025])],
 [('zero attribute is a real identity', ['bulk-edit', 'b', {'key': 'u2', 'org': 0}, 'org'], ['0', 47112]),
  ('fractional float is unbucketable', ['new-nav', 'a', {'key': 'u1', 'org': 2.5}, 'org'], [None, 0]),
  ('leading-zero string kept verbatim', ['new-nav', 'a', {'key': 'u1', 'org': '007'}, 'org'], ['007', 42108]),
  ('leading-zero key kept verbatim', ['bulk-edit', 'b', {'key': '05'}, None], ['05', 9565]),
  ('false attribute is unbucketable', ['bulk-edit', 'b', {'key': 'u1', 'org': False}, 'org'], [None, 0]),
  ('identity sample 1', ['new-nav', 'b', {'key': '05', 'org': '', 'team': 5.0}, 'team'], ['5', 56729]),
  ('identity sample 21', ['new-nav', 'a', {'key': 'u42', 'org': 12, 'team': False}, 'team'], [None, 0]),
  ('identity sample 22', ['bulk-edit', 'a', {'key': 'u1', 'org': ''}, None], ['u1', 93162])]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
boolean attribute is unbucketable[None, 0][None, 0]Passed
integral float bucketed as integer['5', 7520]['5', 7520]Passed
integral float matches integer identity['5', 7520]['5', 7520]Passed
missing bucket attribute falls back to key['u1', 4562]['u1', 4562]Passed
zero attribute is a real identity['0', 47112]['0', 47112]Passed
identity sample 1['5', 56729]['5', 56729]Passed
identity sample 2['05', 58693]['05', 58693]Passed
identity sample 3['acme', 9580]['acme', 9580]Passed

SHA-256 / 921b3b9442c13a5f994056a895f48cf199f6de6a2e44da5f77d665190531352b

Verification & scope

A deterministic toy flag-evaluation model with a stipulated contract; it does not reproduce any vendor SDK byte for byte. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:52.379832+00:00.

Case digest / dbfb85c43d7a1fb5db5f79eba22c4a73584bdef994442ad0b353c598b44ea482