FAILURE MAP
← Case archive

FA-60801 / Loyalty points ledgers / Open access

Purchased points with volume bonus: bonus counted toward limit · case 01

Members hit the annual purchase limit early because bonus points are counted as purchased.

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

ROOT CAUSE

The year-to-date purchased total adds bonus points.

VERIFIED REPAIR

Count only purchased base points toward the limit.

Unsuccessful approach: Checking the limit with bonus included while recording only base still rejects valid purchases.

Case contract

Input {requests:[pts], price_per_1000, tiers:[[min_base, bonus_bp]] ascending, annual_limit, bought_ytd}. Each request is floored to whole thousands ("too small" when that is 0). A request whose base would push purchased base points above annual_limit is rejected whole ("limit"); bonus points never count toward the limit. Bonus = floor(base*bp/10000) with bp from the highest tier whose min_base <= this request base. Price = base/1000*price_per_1000. Return {results:[[base,bonus,price]|reason], bought_ytd}.

Why this case matters

Member point balances, statements and the program liability depend on this ledger rule.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    bought = x['bought_ytd']
    out = []
    for req in x['requests']:
        base = req // 1000 * 1000
        if base == 0:
            out.append('too small')
            continue
        if bought + base > x['annual_limit']:
            out.append('limit')
            continue
        bp = 0
        for mn, b in x['tiers']:
            if base >= mn:
                bp = b
        bonus = base * bp // 10000
        price = base // 1000 * x['price_per_1000']
        bought += base + bonus
        out.append([base, bonus, price])
    return {'results': out, 'bought_ytd': bought}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['small buy', {'requests': [3501], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 20000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 25000}], ['big buy', {'requests': [21000], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1001], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 11000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20001], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4001}, {'results': ['limit'], 'bought_ytd': 4001}]], [['small buy', {'requests': [3502], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 40000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 45000}], ['big buy', {'requests': [21001], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1002], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 12000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20002], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4002}, {'results': ['limit'], 'bought_ytd': 4002}]], [['small buy', {'requests': [3503], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 60000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 65000}], ['big buy', {'requests': [21002], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1003], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 13000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20003], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4003}, {'results': ['limit'], 'bought_ytd': 4003}]], [['small buy', {'requests': [3504], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 80000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 85000}], ['big buy', {'requests': [21003], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1004], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 14000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20004], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4004}, {'results': ['limit'], 'bought_ytd': 4004}]], [['small buy', {'requests': [3505], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 100000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 105000}], ['big buy', {'requests': [21004], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1005], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 15000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20005], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4005}, {'results': ['limit'], 'bought_ytd': 4005}]]]
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
small buy{'bought_ytd': 3000, 'results': [[3000, 0, 3750]]}{'bought_ytd': 3000, 'results': [[3000, 0, 3750]]}Passed
tier edge{'bought_ytd': 25500, 'results': [[5000, 500, 6250]]}{'bought_ytd': 25000, 'results': [[5000, 500, 6250]]}Failed
big buy{'bought_ytd': 26250, 'results': [[21000, 5250, 23100]]}{'bought_ytd': 21000, 'results': [[21000, 5250, 23100]]}Failed
too small{'bought_ytd': 1000, 'results': ['too small', [1000, 0, 1000]]}{'bought_ytd': 1000, 'results': ['too small', [1000, 0, 1000]]}Passed
limit hit{'bought_ytd': 54600, 'results': [[30000, 7500, 30000], 'limit', [11000, 1100, 11000]]}{'bought_ytd': 65000, 'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit']}Failed
bonus not limited{'bought_ytd': 25000, 'results': [[20000, 5000, 18000], 'limit']}{'bought_ytd': 40000, 'results': [[20000, 5000, 18000], [20000, 5000, 18000]]}Failed
near limit{'bought_ytd': 4001, 'results': ['limit']}{'bought_ytd': 4001, 'results': ['limit']}Passed

SHA-256 / 798f517b899786045255c0ed6f6846a288d539f9012c0820ccabc87939ba7cfe

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    bought = x['bought_ytd']
    out = []
    for req in x['requests']:
        base = req // 1000 * 1000
        if base == 0:
            out.append('too small')
            continue
        if bought + base + base * 2500 // 10000 > x['annual_limit']:
            out.append('limit')
            continue
        bp = 0
        for mn, b in x['tiers']:
            if base >= mn:
                bp = b
        bonus = base * bp // 10000
        price = base // 1000 * x['price_per_1000']
        bought += base
        out.append([base, bonus, price])
    return {'results': out, 'bought_ytd': bought}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['small buy', {'requests': [3501], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 20000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 25000}], ['big buy', {'requests': [21000], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1001], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 11000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20001], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4001}, {'results': ['limit'], 'bought_ytd': 4001}]], [['small buy', {'requests': [3502], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 40000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 45000}], ['big buy', {'requests': [21001], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1002], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 12000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20002], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4002}, {'results': ['limit'], 'bought_ytd': 4002}]], [['small buy', {'requests': [3503], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 60000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 65000}], ['big buy', {'requests': [21002], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1003], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 13000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20003], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4003}, {'results': ['limit'], 'bought_ytd': 4003}]], [['small buy', {'requests': [3504], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 80000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 85000}], ['big buy', {'requests': [21003], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1004], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 14000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20004], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4004}, {'results': ['limit'], 'bought_ytd': 4004}]], [['small buy', {'requests': [3505], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 100000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 105000}], ['big buy', {'requests': [21004], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1005], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 15000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20005], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4005}, {'results': ['limit'], 'bought_ytd': 4005}]]]
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
small buy{'bought_ytd': 3000, 'results': [[3000, 0, 3750]]}{'bought_ytd': 3000, 'results': [[3000, 0, 3750]]}Passed
tier edge{'bought_ytd': 25000, 'results': [[5000, 500, 6250]]}{'bought_ytd': 25000, 'results': [[5000, 500, 6250]]}Passed
big buy{'bought_ytd': 21000, 'results': [[21000, 5250, 23100]]}{'bought_ytd': 21000, 'results': [[21000, 5250, 23100]]}Passed
too small{'bought_ytd': 1000, 'results': ['too small', [1000, 0, 1000]]}{'bought_ytd': 1000, 'results': ['too small', [1000, 0, 1000]]}Passed
limit hit{'bought_ytd': 46000, 'results': [[30000, 7500, 30000], 'limit', [11000, 1100, 11000]]}{'bought_ytd': 65000, 'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit']}Failed
bonus not limited{'bought_ytd': 20000, 'results': [[20000, 5000, 18000], 'limit']}{'bought_ytd': 40000, 'results': [[20000, 5000, 18000], [20000, 5000, 18000]]}Failed
near limit{'bought_ytd': 4001, 'results': ['limit']}{'bought_ytd': 4001, 'results': ['limit']}Passed

SHA-256 / 39e3ffdc8eb8d3cb35476059eb1a0d2452b57824417c8daa841050c5d7847c6d

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    bought = x['bought_ytd']
    out = []
    for req in x['requests']:
        base = req // 1000 * 1000
        if base == 0:
            out.append('too small')
            continue
        if bought + base > x['annual_limit']:
            out.append('limit')
            continue
        bp = 0
        for mn, b in x['tiers']:
            if base >= mn:
                bp = b
        bonus = base * bp // 10000
        price = base // 1000 * x['price_per_1000']
        bought += base
        out.append([base, bonus, price])
    return {'results': out, 'bought_ytd': bought}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['small buy', {'requests': [3501], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 20000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 25000}], ['big buy', {'requests': [21000], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1001], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 11000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20001], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4001}, {'results': ['limit'], 'bought_ytd': 4001}]], [['small buy', {'requests': [3502], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 40000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 45000}], ['big buy', {'requests': [21001], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1002], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 12000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20002], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4002}, {'results': ['limit'], 'bought_ytd': 4002}]], [['small buy', {'requests': [3503], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 60000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 65000}], ['big buy', {'requests': [21002], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1003], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 13000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20003], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4003}, {'results': ['limit'], 'bought_ytd': 4003}]], [['small buy', {'requests': [3504], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 80000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 85000}], ['big buy', {'requests': [21003], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1004], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 14000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20004], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4004}, {'results': ['limit'], 'bought_ytd': 4004}]], [['small buy', {'requests': [3505], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[3000, 0, 3750]], 'bought_ytd': 3000}], ['tier edge', {'requests': [5000], 'price_per_1000': 1250, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 1000000, 'bought_ytd': 100000}, {'results': [[5000, 500, 6250]], 'bought_ytd': 105000}], ['big buy', {'requests': [21004], 'price_per_1000': 1100, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': [[21000, 5250, 23100]], 'bought_ytd': 21000}], ['too small', {'requests': [999, 1005], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 100000, 'bought_ytd': 0}, {'results': ['too small', [1000, 0, 1000]], 'bought_ytd': 1000}], ['limit hit', {'requests': [30000, 30000, 15000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 70000, 'bought_ytd': 5000}, {'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit'], 'bought_ytd': 65000}], ['bonus not limited', {'requests': [20000, 20005], 'price_per_1000': 900, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 40000, 'bought_ytd': 0}, {'results': [[20000, 5000, 18000], [20000, 5000, 18000]], 'bought_ytd': 40000}], ['near limit', {'requests': [7000], 'price_per_1000': 1000, 'tiers': [[0, 0], [5000, 1000], [20000, 2500]], 'annual_limit': 10000, 'bought_ytd': 4005}, {'results': ['limit'], 'bought_ytd': 4005}]]]
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
small buy{'bought_ytd': 3000, 'results': [[3000, 0, 3750]]}{'bought_ytd': 3000, 'results': [[3000, 0, 3750]]}Passed
tier edge{'bought_ytd': 25000, 'results': [[5000, 500, 6250]]}{'bought_ytd': 25000, 'results': [[5000, 500, 6250]]}Passed
big buy{'bought_ytd': 21000, 'results': [[21000, 5250, 23100]]}{'bought_ytd': 21000, 'results': [[21000, 5250, 23100]]}Passed
too small{'bought_ytd': 1000, 'results': ['too small', [1000, 0, 1000]]}{'bought_ytd': 1000, 'results': ['too small', [1000, 0, 1000]]}Passed
limit hit{'bought_ytd': 65000, 'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit']}{'bought_ytd': 65000, 'results': [[30000, 7500, 30000], [30000, 7500, 30000], 'limit']}Passed
bonus not limited{'bought_ytd': 40000, 'results': [[20000, 5000, 18000], [20000, 5000, 18000]]}{'bought_ytd': 40000, 'results': [[20000, 5000, 18000], [20000, 5000, 18000]]}Passed
near limit{'bought_ytd': 4001, 'results': ['limit']}{'bought_ytd': 4001, 'results': ['limit']}Passed

SHA-256 / 81a17f22fd26ace258d124ec03de8e821856178616e6689b9802a183e81c27c5

Verification & scope

Stipulated bounded loyalty-program model with explicit toy rules; not any real program terms. 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:46:48.989462+00:00.

Case digest / f3ef3885524f34c404558aa716b7139d715f2a5f8194fb703b2670fd98e7ce31