FA-60431 / Loyalty points ledgers / Open access
Points redemption at checkout: cash due valuation · case 01
Cash due is wrong for programs where a point is worth more than one cent.
ROOT CAUSE
Cash due subtracts points as if each point were one cent.
VERIFIED REPAIR
Cash due = order - points used * cents per point.
Unsuccessful approach: Clamping the one-cent result at zero hides the unit error only for large redemptions.
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 // blk * blk, cap)
return {'points_used': use, 'cash_due': order - use}
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': 9499, 'points_used': 500} | {'cash_due': 8499, 'points_used': 500} | Failed |
| 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': 3800, 'points_used': 1200} | {'cash_due': 2600, 'points_used': 1200} | Failed |
| odd request | {'cash_due': 8800, 'points_used': 1200} | {'cash_due': 8800, 'points_used': 1200} | Passed |
SHA-256 / 865c8ad3dcfda9a0d365c2f77b4e6ac897cd85c48fe7d8a44e7001b51293fa51
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 * blk, cap)
return {'points_used': use, 'cash_due': max(0, 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': 2200, 'points_used': 1800} | {'cash_due': 2201, 'points_used': 1800} | Failed |
| cap binds | {'cash_due': 1799, 'points_used': 1200} | {'cash_due': 1800, 'points_used': 1200} | Failed |
| minimum balance edge | {'cash_due': 9496, 'points_used': 500} | {'cash_due': 8499, 'points_used': 500} | Failed |
| below minimum | {'error': 'below minimum'} | {'error': 'below minimum'} | Passed |
| balance binds | {'cash_due': 48799, 'points_used': 1200} | {'cash_due': 48800, 'points_used': 1200} | Failed |
| two cents per point | {'cash_due': 3798, 'points_used': 1200} | {'cash_due': 2600, 'points_used': 1200} | Failed |
| odd request | {'cash_due': 8799, 'points_used': 1200} | {'cash_due': 8800, 'points_used': 1200} | Failed |
SHA-256 / ad6f8cbc49630cd0c2b3c9c36232eba3f96216b1ec2e2b66a3b10b4f38bdbf3c
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.527171+00:00.
Case digest / b42c8526c45972008723eed05b2aca685f7090536649aa9c838e1ff0468015b2