FA-60421 / Loyalty points ledgers / Open access
Points redemption at checkout: balance block floor · case 01
A redemption uses an odd number of points that is not a whole block.
ROOT CAUSE
The balance limit is applied without flooring the balance to a block.
VERIFIED REPAIR
Floor the balance to a whole block before comparing.
Unsuccessful approach: Rounding the balance to the nearest block can spend more points than the member has.
Case contract
Input {balance, order_cents, request_pts, max_pct, cpp (cents per point)}. Balance below 500 returns {"error":"below minimum"}. Points are used in blocks of 100: used = min(request floored to a block, balance floored to a block, cap) where cap = floor(order*max_pct/100 / cpp) floored to a block. Cash due = order - used*cpp. Return {points_used, cash_due}.
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):
bal = x['balance']
order = x['order_cents']
if bal < 500:
return {'error': 'below minimum'}
blk = 100
cap_cents = order * x['max_pct'] // 100
cap = cap_cents // x['cpp'] // blk * blk
use = min(x['request_pts'] // blk * blk, bal, cap)
return {'points_used': use, 'cash_due': order - use * x['cpp']}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['normal', {'balance': 2750, 'order_cents': 4001, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2201}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5001, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1291, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 51, 'cpp': 2}, {'points_used': 1200, 'cash_due': 2600}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1285, 'max_pct': 90, 'cpp': 1}, {'points_used': 1200, 'cash_due': 8800}]], [['normal', {'balance': 2750, 'order_cents': 4002, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2202}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5002, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 2}, {'points_used': 500, 'cash_due': 8999}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1292, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 52, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1295, 'max_pct': 90, 'cpp': 1}, {'points_used': 1200, 'cash_due': 8800}]], [['normal', {'balance': 2750, 'order_cents': 4003, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2203}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5003, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1293, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 53, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1305, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]], [['normal', {'balance': 2750, 'order_cents': 4004, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2204}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5004, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 2}, {'points_used': 500, 'cash_due': 8999}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1294, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 54, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1315, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]], [['normal', {'balance': 2750, 'order_cents': 4005, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2205}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5005, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1295, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 55, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1325, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]]]
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 |
|---|---|---|---|
| normal | {'cash_due': 2201, 'points_used': 1800} | {'cash_due': 2201, 'points_used': 1800} | Passed |
| cap binds | {'cash_due': 1800, 'points_used': 1200} | {'cash_due': 1800, 'points_used': 1200} | Passed |
| minimum balance edge | {'cash_due': 8499, 'points_used': 500} | {'cash_due': 8499, 'points_used': 500} | Passed |
| below minimum | {'error': 'below minimum'} | {'error': 'below minimum'} | Passed |
| balance binds | {'cash_due': 48709, 'points_used': 1291} | {'cash_due': 48800, 'points_used': 1200} | Failed |
| two cents per point | {'cash_due': 2600, 'points_used': 1200} | {'cash_due': 2600, 'points_used': 1200} | Passed |
| odd request | {'cash_due': 8800, 'points_used': 1200} | {'cash_due': 8800, 'points_used': 1200} | Passed |
SHA-256 / 116a33fdcd20466b49dbe0f159ce1da711ddee2ed111f6e169413e4effa42c20
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
bal = x['balance']
order = x['order_cents']
if bal < 500:
return {'error': 'below minimum'}
blk = 100
cap_cents = order * x['max_pct'] // 100
cap = cap_cents // x['cpp'] // blk * blk
use = min(x['request_pts'] // blk * blk, (bal + blk // 2) // blk * blk, cap)
return {'points_used': use, 'cash_due': order - use * x['cpp']}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['normal', {'balance': 2750, 'order_cents': 4001, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2201}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5001, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1291, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 51, 'cpp': 2}, {'points_used': 1200, 'cash_due': 2600}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1285, 'max_pct': 90, 'cpp': 1}, {'points_used': 1200, 'cash_due': 8800}]], [['normal', {'balance': 2750, 'order_cents': 4002, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2202}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5002, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 2}, {'points_used': 500, 'cash_due': 8999}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1292, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 52, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1295, 'max_pct': 90, 'cpp': 1}, {'points_used': 1200, 'cash_due': 8800}]], [['normal', {'balance': 2750, 'order_cents': 4003, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2203}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5003, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1293, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 53, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1305, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]], [['normal', {'balance': 2750, 'order_cents': 4004, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2204}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5004, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 2}, {'points_used': 500, 'cash_due': 8999}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1294, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 54, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1315, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]], [['normal', {'balance': 2750, 'order_cents': 4005, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2205}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5005, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1295, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 55, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1325, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]]]
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 |
|---|---|---|---|
| normal | {'cash_due': 2201, 'points_used': 1800} | {'cash_due': 2201, 'points_used': 1800} | Passed |
| cap binds | {'cash_due': 1800, 'points_used': 1200} | {'cash_due': 1800, 'points_used': 1200} | Passed |
| minimum balance edge | {'cash_due': 8499, 'points_used': 500} | {'cash_due': 8499, 'points_used': 500} | Passed |
| below minimum | {'error': 'below minimum'} | {'error': 'below minimum'} | Passed |
| balance binds | {'cash_due': 48700, 'points_used': 1300} | {'cash_due': 48800, 'points_used': 1200} | Failed |
| two cents per point | {'cash_due': 2600, 'points_used': 1200} | {'cash_due': 2600, 'points_used': 1200} | Passed |
| odd request | {'cash_due': 8800, 'points_used': 1200} | {'cash_due': 8800, 'points_used': 1200} | Passed |
SHA-256 / d61cd9ace28182254bac2fcf292befd16bb62510a8d0fb18050532bace22b8b3
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
bal = x['balance']
order = x['order_cents']
if bal < 500:
return {'error': 'below minimum'}
blk = 100
cap_cents = order * x['max_pct'] // 100
cap = cap_cents // x['cpp'] // blk * blk
use = min(x['request_pts'] // blk * blk, bal // blk * blk, cap)
return {'points_used': use, 'cash_due': order - use * x['cpp']}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['normal', {'balance': 2750, 'order_cents': 4001, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2201}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5001, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1291, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 51, 'cpp': 2}, {'points_used': 1200, 'cash_due': 2600}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1285, 'max_pct': 90, 'cpp': 1}, {'points_used': 1200, 'cash_due': 8800}]], [['normal', {'balance': 2750, 'order_cents': 4002, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2202}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5002, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 2}, {'points_used': 500, 'cash_due': 8999}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1292, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 52, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1295, 'max_pct': 90, 'cpp': 1}, {'points_used': 1200, 'cash_due': 8800}]], [['normal', {'balance': 2750, 'order_cents': 4003, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2203}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5003, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1293, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 53, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1305, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]], [['normal', {'balance': 2750, 'order_cents': 4004, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2204}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5004, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 2}, {'points_used': 500, 'cash_due': 8999}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1294, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 54, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1315, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]], [['normal', {'balance': 2750, 'order_cents': 4005, 'request_pts': 1850, 'max_pct': 50, 'cpp': 1}, {'points_used': 1800, 'cash_due': 2205}], ['cap binds', {'balance': 9000, 'order_cents': 3000, 'request_pts': 5005, 'max_pct': 40, 'cpp': 1}, {'points_used': 1200, 'cash_due': 1800}], ['minimum balance edge', {'balance': 500, 'order_cents': 9999, 'request_pts': 500, 'max_pct': 100, 'cpp': 3}, {'points_used': 500, 'cash_due': 8499}], ['below minimum', {'balance': 499, 'order_cents': 9999, 'request_pts': 400, 'max_pct': 100, 'cpp': 1}, {'error': 'below minimum'}], ['balance binds', {'balance': 1295, 'order_cents': 50000, 'request_pts': 3000, 'max_pct': 60, 'cpp': 1}, {'points_used': 1200, 'cash_due': 48800}], ['two cents per point', {'balance': 5000, 'order_cents': 5000, 'request_pts': 4000, 'max_pct': 55, 'cpp': 2}, {'points_used': 1300, 'cash_due': 2400}], ['odd request', {'balance': 3000, 'order_cents': 10000, 'request_pts': 1325, 'max_pct': 90, 'cpp': 1}, {'points_used': 1300, 'cash_due': 8700}]]]
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 |
|---|---|---|---|
| normal | {'cash_due': 2201, 'points_used': 1800} | {'cash_due': 2201, 'points_used': 1800} | Passed |
| cap binds | {'cash_due': 1800, 'points_used': 1200} | {'cash_due': 1800, 'points_used': 1200} | Passed |
| minimum balance edge | {'cash_due': 8499, 'points_used': 500} | {'cash_due': 8499, 'points_used': 500} | Passed |
| below minimum | {'error': 'below minimum'} | {'error': 'below minimum'} | Passed |
| balance binds | {'cash_due': 48800, 'points_used': 1200} | {'cash_due': 48800, 'points_used': 1200} | Passed |
| two cents per point | {'cash_due': 2600, 'points_used': 1200} | {'cash_due': 2600, 'points_used': 1200} | Passed |
| odd request | {'cash_due': 8800, 'points_used': 1200} | {'cash_due': 8800, 'points_used': 1200} | Passed |
SHA-256 / c20a8b7d07355d18393b41cca6b62325d33aa9c5e63ba834cd8c86f6720ad7c4
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:45.394930+00:00.
Case digest / 27c88ef07120798227a79aec1ac39ed8b0b9c0c3cd046d36ec8dfa674dc63a7a