FAILURE MAP
← Case archive

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.

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

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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