FA-74116 / Feature flag rollout bucketing / Open access
Semantic version clause: Short versions are rejected · case 01
Clients reporting "2.1" never match any version rule.
ROOT CAUSE
The parser requires exactly three numeric components.
VERIFIED REPAIR
Accept one to three components and pad missing ones with zero.
Unsuccessful approach: Accepting short versions without padding makes [2, 1] compare below [2, 1, 0].
Case contract
Versions are MAJOR[.MINOR[.PATCH]][-prerelease][+build]; missing components are 0, build metadata is ignored, anything else is invalid (return None). A release outranks its prereleases; prerelease identifiers compare numerically when all digits, lexically otherwise, numeric below alphanumeric, and a shorter identifier list ranks lower when it is a prefix. op is semVerLessThan, semVerEqual or semVerGreaterThan; unknown ops return None.
Why this case matters
Targeting by app version gates features to clients that can render them.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(version, op, target):
def parse(s):
s = s.split('+', 1)[0]
core, _, pre = s.partition('-')
parts = core.split('.')
if len(parts) != 3 or not all(p.isdigit() for p in parts):
return None
nums = [int(p) for p in parts] + [0] * (3 - len(parts))
ids = pre.split('.') if pre else []
return nums, ids
def key_id(i):
return (0, int(i), '') if i.isdigit() else (1, 0, i)
def cmp(a, b):
if a[0] != b[0]:
return -1 if a[0] < b[0] else 1
x, y = a[1], b[1]
if not x or not y:
return (len(x) == 0) - (len(y) == 0) if (x or y) else 0
for p, q in zip(x, y):
kp, kq = key_id(p), key_id(q)
if kp != kq:
return -1 if kp < kq else 1
return (len(x) > len(y)) - (len(x) < len(y))
a, b = parse(version), parse(target)
if a is None or b is None:
return None
c = cmp(a, b)
return {'semVerLessThan': c < 0, 'semVerEqual': c == 0, 'semVerGreaterThan': c > 0}.get(op)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('numeric prerelease identifiers compare as numbers',
['1.0.0-beta.11', 'semVerGreaterThan', '1.0.0-beta.2'],
True),
('numeric identifier ranks below alphanumeric', ['1.0.0-1', 'semVerLessThan', '1.0.0-alpha'], True),
('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('single component version', ['1', 'semVerLessThan', '1.0.1'], True),
('version pair sample 1', ['1.0.0-beta.11', 'semVerEqual', '1.0.0-alpha'], False),
('version pair sample 2', ['1.0.0-alpha', 'semVerEqual', '1.0.0'], False)],
[('numeric identifier ranks below alphanumeric', ['1.0.0-1', 'semVerLessThan', '1.0.0-alpha'], True),
('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('single component version', ['1', 'semVerLessThan', '1.0.1'], True),
('version pair sample 40', ['2.1', 'semVerEqual', '2.1.0'], True),
('version pair sample 54', ['1.0.0-alpha.beta', 'semVerEqual', '1.0'], False)],
[('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('version pair sample 8', ['2.1', 'semVerLessThan', '2.10.0'], True),
('version pair sample 11', ['1.0.0+build.5', 'semVerLessThan', '1.0.0.0'], None),
('version pair sample 60', ['1.0.0-alpha.10', 'semVerLessThan', '1'], True)],
[('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('minor ten beats minor nine', ['2.10.0', 'semVerGreaterThan', '2.9.0'], True),
('version pair sample 16', ['1.0.0-alpha.10', 'semVerEqual', '1.0.0'], False),
('version pair sample 21', ['2.1.0', 'semVerGreaterThan', '2.1'], False),
('version pair sample 57', ['1', 'semVerEqual', '1.0.0-10'], False)],
[('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('minor ten beats minor nine', ['2.10.0', 'semVerGreaterThan', '2.9.0'], True),
('four components are invalid', ['1.0.0.0', 'semVerEqual', '1.0.0'], None),
('version pair sample 21', ['2.1.0', 'semVerGreaterThan', '2.1'], False),
('version pair sample 22', ['1.0.0-2', 'semVerEqual', '1.0.0+build.5'], False),
('version pair sample 58', ['1', 'semVerGreaterThan', '1.0.0-alpha.beta'], True)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| numeric prerelease identifiers compare as numbers | True | True | Passed |
| numeric identifier ranks below alphanumeric | True | True | Passed |
| release outranks its prerelease | True | True | Passed |
| two-component version pads patch | None | True | Failed |
| build metadata is ignored | True | True | Passed |
| single component version | None | True | Failed |
| version pair sample 1 | False | False | Passed |
| version pair sample 2 | False | False | Passed |
SHA-256 / 89b9a4ee8390aef14f1d46f4e910162b6ad42f61d6c37b7ba8383f55869ce10a
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(version, op, target):
def parse(s):
s = s.split('+', 1)[0]
core, _, pre = s.partition('-')
parts = core.split('.')
if not 1 <= len(parts) <= 3 or not all(p.isdigit() for p in parts):
return None
nums = [int(p) for p in parts]
ids = pre.split('.') if pre else []
return nums, ids
def key_id(i):
return (0, int(i), '') if i.isdigit() else (1, 0, i)
def cmp(a, b):
if a[0] != b[0]:
return -1 if a[0] < b[0] else 1
x, y = a[1], b[1]
if not x or not y:
return (len(x) == 0) - (len(y) == 0) if (x or y) else 0
for p, q in zip(x, y):
kp, kq = key_id(p), key_id(q)
if kp != kq:
return -1 if kp < kq else 1
return (len(x) > len(y)) - (len(x) < len(y))
a, b = parse(version), parse(target)
if a is None or b is None:
return None
c = cmp(a, b)
return {'semVerLessThan': c < 0, 'semVerEqual': c == 0, 'semVerGreaterThan': c > 0}.get(op)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('numeric prerelease identifiers compare as numbers',
['1.0.0-beta.11', 'semVerGreaterThan', '1.0.0-beta.2'],
True),
('numeric identifier ranks below alphanumeric', ['1.0.0-1', 'semVerLessThan', '1.0.0-alpha'], True),
('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('single component version', ['1', 'semVerLessThan', '1.0.1'], True),
('version pair sample 1', ['1.0.0-beta.11', 'semVerEqual', '1.0.0-alpha'], False),
('version pair sample 2', ['1.0.0-alpha', 'semVerEqual', '1.0.0'], False)],
[('numeric identifier ranks below alphanumeric', ['1.0.0-1', 'semVerLessThan', '1.0.0-alpha'], True),
('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('single component version', ['1', 'semVerLessThan', '1.0.1'], True),
('version pair sample 40', ['2.1', 'semVerEqual', '2.1.0'], True),
('version pair sample 54', ['1.0.0-alpha.beta', 'semVerEqual', '1.0'], False)],
[('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('version pair sample 8', ['2.1', 'semVerLessThan', '2.10.0'], True),
('version pair sample 11', ['1.0.0+build.5', 'semVerLessThan', '1.0.0.0'], None),
('version pair sample 60', ['1.0.0-alpha.10', 'semVerLessThan', '1'], True)],
[('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('minor ten beats minor nine', ['2.10.0', 'semVerGreaterThan', '2.9.0'], True),
('version pair sample 16', ['1.0.0-alpha.10', 'semVerEqual', '1.0.0'], False),
('version pair sample 21', ['2.1.0', 'semVerGreaterThan', '2.1'], False),
('version pair sample 57', ['1', 'semVerEqual', '1.0.0-10'], False)],
[('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('minor ten beats minor nine', ['2.10.0', 'semVerGreaterThan', '2.9.0'], True),
('four components are invalid', ['1.0.0.0', 'semVerEqual', '1.0.0'], None),
('version pair sample 21', ['2.1.0', 'semVerGreaterThan', '2.1'], False),
('version pair sample 22', ['1.0.0-2', 'semVerEqual', '1.0.0+build.5'], False),
('version pair sample 58', ['1', 'semVerGreaterThan', '1.0.0-alpha.beta'], True)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| numeric prerelease identifiers compare as numbers | True | True | Passed |
| numeric identifier ranks below alphanumeric | True | True | Passed |
| release outranks its prerelease | True | True | Passed |
| two-component version pads patch | False | True | Failed |
| build metadata is ignored | True | True | Passed |
| single component version | True | True | Passed |
| version pair sample 1 | False | False | Passed |
| version pair sample 2 | False | False | Passed |
SHA-256 / c93ea30e47218e472abcac418be68f5db35e3b38839be06a38562d41746898ba
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(version, op, target):
def parse(s):
s = s.split('+', 1)[0]
core, _, pre = s.partition('-')
parts = core.split('.')
if not 1 <= len(parts) <= 3 or not all(p.isdigit() for p in parts):
return None
nums = [int(p) for p in parts] + [0] * (3 - len(parts))
ids = pre.split('.') if pre else []
return nums, ids
def key_id(i):
return (0, int(i), '') if i.isdigit() else (1, 0, i)
def cmp(a, b):
if a[0] != b[0]:
return -1 if a[0] < b[0] else 1
x, y = a[1], b[1]
if not x or not y:
return (len(x) == 0) - (len(y) == 0) if (x or y) else 0
for p, q in zip(x, y):
kp, kq = key_id(p), key_id(q)
if kp != kq:
return -1 if kp < kq else 1
return (len(x) > len(y)) - (len(x) < len(y))
a, b = parse(version), parse(target)
if a is None or b is None:
return None
c = cmp(a, b)
return {'semVerLessThan': c < 0, 'semVerEqual': c == 0, 'semVerGreaterThan': c > 0}.get(op)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('numeric prerelease identifiers compare as numbers',
['1.0.0-beta.11', 'semVerGreaterThan', '1.0.0-beta.2'],
True),
('numeric identifier ranks below alphanumeric', ['1.0.0-1', 'semVerLessThan', '1.0.0-alpha'], True),
('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('single component version', ['1', 'semVerLessThan', '1.0.1'], True),
('version pair sample 1', ['1.0.0-beta.11', 'semVerEqual', '1.0.0-alpha'], False),
('version pair sample 2', ['1.0.0-alpha', 'semVerEqual', '1.0.0'], False)],
[('numeric identifier ranks below alphanumeric', ['1.0.0-1', 'semVerLessThan', '1.0.0-alpha'], True),
('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('single component version', ['1', 'semVerLessThan', '1.0.1'], True),
('version pair sample 40', ['2.1', 'semVerEqual', '2.1.0'], True),
('version pair sample 54', ['1.0.0-alpha.beta', 'semVerEqual', '1.0'], False)],
[('release outranks its prerelease', ['1.0.0', 'semVerGreaterThan', '1.0.0-rc.1'], True),
('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('version pair sample 8', ['2.1', 'semVerLessThan', '2.10.0'], True),
('version pair sample 11', ['1.0.0+build.5', 'semVerLessThan', '1.0.0.0'], None),
('version pair sample 60', ['1.0.0-alpha.10', 'semVerLessThan', '1'], True)],
[('two-component version pads patch', ['2.1', 'semVerEqual', '2.1.0'], True),
('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('minor ten beats minor nine', ['2.10.0', 'semVerGreaterThan', '2.9.0'], True),
('version pair sample 16', ['1.0.0-alpha.10', 'semVerEqual', '1.0.0'], False),
('version pair sample 21', ['2.1.0', 'semVerGreaterThan', '2.1'], False),
('version pair sample 57', ['1', 'semVerEqual', '1.0.0-10'], False)],
[('build metadata is ignored', ['1.0.0+build.5', 'semVerEqual', '1.0.0'], True),
('build metadata after prerelease is ignored', ['1.0.0-rc.1+b7', 'semVerEqual', '1.0.0-rc.1'], True),
('longer prerelease list ranks higher', ['1.0.0-alpha.1', 'semVerGreaterThan', '1.0.0-alpha'], True),
('minor ten beats minor nine', ['2.10.0', 'semVerGreaterThan', '2.9.0'], True),
('four components are invalid', ['1.0.0.0', 'semVerEqual', '1.0.0'], None),
('version pair sample 21', ['2.1.0', 'semVerGreaterThan', '2.1'], False),
('version pair sample 22', ['1.0.0-2', 'semVerEqual', '1.0.0+build.5'], False),
('version pair sample 58', ['1', 'semVerGreaterThan', '1.0.0-alpha.beta'], True)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| numeric prerelease identifiers compare as numbers | True | True | Passed |
| numeric identifier ranks below alphanumeric | True | True | Passed |
| release outranks its prerelease | True | True | Passed |
| two-component version pads patch | True | True | Passed |
| build metadata is ignored | True | True | Passed |
| single component version | True | True | Passed |
| version pair sample 1 | False | False | Passed |
| version pair sample 2 | False | False | Passed |
SHA-256 / 0ee86eef344dfa26c8afafeb21870d1db616f22580b7ac6a894d21b81edc61d6
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:53.736932+00:00.
Case digest / 38c30b03f0e45a90dd28c3c3e5fd93781c3bf4fcf06897272cabaab79e71879f