FAILURE MAP
← Case archive

FA-60611 / Loyalty points ledgers / Open access

Breakage-based revenue recognition: millicent conversion · case 01

The deferred balance is ten times too large.

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

ROOT CAUSE

Millicents per point are converted to cents by dividing by 100.

VERIFIED REPAIR

Divide by 1000 to convert millicents to cents.

Unsuccessful approach: Dividing by 10000 makes the balance ten times too small.

Case contract

Input {issued, redeemed, value_mc (millicents per point), breakage_bp}. Deferred revenue = floor(issued*value_mc/1000) cents. Expected redemptions = floor(issued*(10000-breakage_bp)/10000); if zero return {"error"}. Recognized = floor(deferred*min(redeemed, expected)/expected); liability = deferred - recognized. Return {deferred, expected, recognized, liability}.

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):
    deferred = x['issued'] * x['value_mc'] // 100
    expected = x['issued'] * (10000 - x['breakage_bp']) // 10000
    if expected <= 0:
        return {'error': 'no expected redemptions'}
    used = min(x['redeemed'], expected)
    recognized = deferred * used // expected
    return {'deferred': deferred, 'expected': expected, 'recognized': recognized, 'liability': deferred - recognized}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['mid program', {'issued': 100000, 'redeemed': 30001, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37501, 'liability': 62499}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1001}, {'deferred': 40000, 'expected': 44995, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1112, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1112, 'liability': 1387}], ['no breakage', {'issued': 700, 'redeemed': 351, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 351, 'liability': 349}], ['all breakage', {'issued': 700, 'redeemed': 1, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 4, 'breakage_bp': 3000}, {'deferred': 399, 'expected': 69999, 'recognized': 70, 'liability': 329}]], [['mid program', {'issued': 100000, 'redeemed': 30002, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37502, 'liability': 62498}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1002}, {'deferred': 40000, 'expected': 44990, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1113, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1113, 'liability': 1386}], ['no breakage', {'issued': 700, 'redeemed': 352, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 352, 'liability': 348}], ['all breakage', {'issued': 700, 'redeemed': 2, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 5, 'breakage_bp': 3000}, {'deferred': 499, 'expected': 69999, 'recognized': 88, 'liability': 411}]], [['mid program', {'issued': 100000, 'redeemed': 30003, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37503, 'liability': 62497}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1003}, {'deferred': 40000, 'expected': 44985, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1114, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1114, 'liability': 1385}], ['no breakage', {'issued': 700, 'redeemed': 353, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 353, 'liability': 347}], ['all breakage', {'issued': 700, 'redeemed': 3, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 6, 'breakage_bp': 3000}, {'deferred': 599, 'expected': 69999, 'recognized': 105, 'liability': 494}]], [['mid program', {'issued': 100000, 'redeemed': 30004, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37505, 'liability': 62495}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1004}, {'deferred': 40000, 'expected': 44980, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1115, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1115, 'liability': 1384}], ['no breakage', {'issued': 700, 'redeemed': 354, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 354, 'liability': 346}], ['all breakage', {'issued': 700, 'redeemed': 4, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 7, 'breakage_bp': 3000}, {'deferred': 699, 'expected': 69999, 'recognized': 123, 'liability': 576}]], [['mid program', {'issued': 100000, 'redeemed': 30005, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37506, 'liability': 62494}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1005}, {'deferred': 40000, 'expected': 44975, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1116, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1116, 'liability': 1383}], ['no breakage', {'issued': 700, 'redeemed': 355, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 355, 'liability': 345}], ['all breakage', {'issued': 700, 'redeemed': 5, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 8, 'breakage_bp': 3000}, {'deferred': 799, 'expected': 69999, 'recognized': 140, 'liability': 659}]]]
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
mid program{'deferred': 1000000, 'expected': 80000, 'liability': 624988, 'recognized': 375012}{'deferred': 100000, 'expected': 80000, 'liability': 62499, 'recognized': 37501}Failed
fully redeemed{'deferred': 400000, 'expected': 44995, 'liability': 0, 'recognized': 400000}{'deferred': 40000, 'expected': 44995, 'liability': 0, 'recognized': 40000}Failed
over redeemed{'deferred': 12000, 'expected': 850, 'liability': 0, 'recognized': 12000}{'deferred': 1200, 'expected': 850, 'liability': 0, 'recognized': 1200}Failed
fractional value{'deferred': 24997, 'expected': 2499, 'liability': 13874, 'recognized': 11123}{'deferred': 2499, 'expected': 2499, 'liability': 1387, 'recognized': 1112}Failed
no breakage{'deferred': 7000, 'expected': 700, 'liability': 3490, 'recognized': 3510}{'deferred': 700, 'expected': 700, 'liability': 349, 'recognized': 351}Failed
all breakage{'error': 'no expected redemptions'}{'error': 'no expected redemptions'}Passed
tiny value{'deferred': 3999, 'expected': 69999, 'liability': 3294, 'recognized': 705}{'deferred': 399, 'expected': 69999, 'liability': 329, 'recognized': 70}Failed

SHA-256 / a64b30071d85e04f5979de79bbff83f8f4f8852ab9e8a588d966f4f667a44692

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    deferred = x['issued'] * x['value_mc'] // 10000
    expected = x['issued'] * (10000 - x['breakage_bp']) // 10000
    if expected <= 0:
        return {'error': 'no expected redemptions'}
    used = min(x['redeemed'], expected)
    recognized = deferred * used // expected
    return {'deferred': deferred, 'expected': expected, 'recognized': recognized, 'liability': deferred - recognized}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['mid program', {'issued': 100000, 'redeemed': 30001, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37501, 'liability': 62499}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1001}, {'deferred': 40000, 'expected': 44995, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1112, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1112, 'liability': 1387}], ['no breakage', {'issued': 700, 'redeemed': 351, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 351, 'liability': 349}], ['all breakage', {'issued': 700, 'redeemed': 1, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 4, 'breakage_bp': 3000}, {'deferred': 399, 'expected': 69999, 'recognized': 70, 'liability': 329}]], [['mid program', {'issued': 100000, 'redeemed': 30002, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37502, 'liability': 62498}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1002}, {'deferred': 40000, 'expected': 44990, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1113, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1113, 'liability': 1386}], ['no breakage', {'issued': 700, 'redeemed': 352, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 352, 'liability': 348}], ['all breakage', {'issued': 700, 'redeemed': 2, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 5, 'breakage_bp': 3000}, {'deferred': 499, 'expected': 69999, 'recognized': 88, 'liability': 411}]], [['mid program', {'issued': 100000, 'redeemed': 30003, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37503, 'liability': 62497}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1003}, {'deferred': 40000, 'expected': 44985, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1114, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1114, 'liability': 1385}], ['no breakage', {'issued': 700, 'redeemed': 353, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 353, 'liability': 347}], ['all breakage', {'issued': 700, 'redeemed': 3, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 6, 'breakage_bp': 3000}, {'deferred': 599, 'expected': 69999, 'recognized': 105, 'liability': 494}]], [['mid program', {'issued': 100000, 'redeemed': 30004, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37505, 'liability': 62495}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1004}, {'deferred': 40000, 'expected': 44980, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1115, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1115, 'liability': 1384}], ['no breakage', {'issued': 700, 'redeemed': 354, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 354, 'liability': 346}], ['all breakage', {'issued': 700, 'redeemed': 4, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 7, 'breakage_bp': 3000}, {'deferred': 699, 'expected': 69999, 'recognized': 123, 'liability': 576}]], [['mid program', {'issued': 100000, 'redeemed': 30005, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37506, 'liability': 62494}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1005}, {'deferred': 40000, 'expected': 44975, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1116, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1116, 'liability': 1383}], ['no breakage', {'issued': 700, 'redeemed': 355, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 355, 'liability': 345}], ['all breakage', {'issued': 700, 'redeemed': 5, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 8, 'breakage_bp': 3000}, {'deferred': 799, 'expected': 69999, 'recognized': 140, 'liability': 659}]]]
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
mid program{'deferred': 10000, 'expected': 80000, 'liability': 6250, 'recognized': 3750}{'deferred': 100000, 'expected': 80000, 'liability': 62499, 'recognized': 37501}Failed
fully redeemed{'deferred': 4000, 'expected': 44995, 'liability': 0, 'recognized': 4000}{'deferred': 40000, 'expected': 44995, 'liability': 0, 'recognized': 40000}Failed
over redeemed{'deferred': 120, 'expected': 850, 'liability': 0, 'recognized': 120}{'deferred': 1200, 'expected': 850, 'liability': 0, 'recognized': 1200}Failed
fractional value{'deferred': 249, 'expected': 2499, 'liability': 139, 'recognized': 110}{'deferred': 2499, 'expected': 2499, 'liability': 1387, 'recognized': 1112}Failed
no breakage{'deferred': 70, 'expected': 700, 'liability': 35, 'recognized': 35}{'deferred': 700, 'expected': 700, 'liability': 349, 'recognized': 351}Failed
all breakage{'error': 'no expected redemptions'}{'error': 'no expected redemptions'}Passed
tiny value{'deferred': 39, 'expected': 69999, 'liability': 33, 'recognized': 6}{'deferred': 399, 'expected': 69999, 'liability': 329, 'recognized': 70}Failed

SHA-256 / 4aa1874b552ada9108ca08b2d1964c4e67e595de7f1dd741e71f2df657771dad

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    deferred = x['issued'] * x['value_mc'] // 1000
    expected = x['issued'] * (10000 - x['breakage_bp']) // 10000
    if expected <= 0:
        return {'error': 'no expected redemptions'}
    used = min(x['redeemed'], expected)
    recognized = deferred * used // expected
    return {'deferred': deferred, 'expected': expected, 'recognized': recognized, 'liability': deferred - recognized}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['mid program', {'issued': 100000, 'redeemed': 30001, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37501, 'liability': 62499}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1001}, {'deferred': 40000, 'expected': 44995, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1112, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1112, 'liability': 1387}], ['no breakage', {'issued': 700, 'redeemed': 351, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 351, 'liability': 349}], ['all breakage', {'issued': 700, 'redeemed': 1, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 4, 'breakage_bp': 3000}, {'deferred': 399, 'expected': 69999, 'recognized': 70, 'liability': 329}]], [['mid program', {'issued': 100000, 'redeemed': 30002, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37502, 'liability': 62498}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1002}, {'deferred': 40000, 'expected': 44990, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1113, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1113, 'liability': 1386}], ['no breakage', {'issued': 700, 'redeemed': 352, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 352, 'liability': 348}], ['all breakage', {'issued': 700, 'redeemed': 2, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 5, 'breakage_bp': 3000}, {'deferred': 499, 'expected': 69999, 'recognized': 88, 'liability': 411}]], [['mid program', {'issued': 100000, 'redeemed': 30003, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37503, 'liability': 62497}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1003}, {'deferred': 40000, 'expected': 44985, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1114, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1114, 'liability': 1385}], ['no breakage', {'issued': 700, 'redeemed': 353, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 353, 'liability': 347}], ['all breakage', {'issued': 700, 'redeemed': 3, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 6, 'breakage_bp': 3000}, {'deferred': 599, 'expected': 69999, 'recognized': 105, 'liability': 494}]], [['mid program', {'issued': 100000, 'redeemed': 30004, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37505, 'liability': 62495}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1004}, {'deferred': 40000, 'expected': 44980, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1115, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1115, 'liability': 1384}], ['no breakage', {'issued': 700, 'redeemed': 354, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 354, 'liability': 346}], ['all breakage', {'issued': 700, 'redeemed': 4, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 7, 'breakage_bp': 3000}, {'deferred': 699, 'expected': 69999, 'recognized': 123, 'liability': 576}]], [['mid program', {'issued': 100000, 'redeemed': 30005, 'value_mc': 1000, 'breakage_bp': 2000}, {'deferred': 100000, 'expected': 80000, 'recognized': 37506, 'liability': 62494}], ['fully redeemed', {'issued': 50000, 'redeemed': 45000, 'value_mc': 800, 'breakage_bp': 1005}, {'deferred': 40000, 'expected': 44975, 'recognized': 40000, 'liability': 0}], ['over redeemed', {'issued': 1000, 'redeemed': 950, 'value_mc': 1200, 'breakage_bp': 1500}, {'deferred': 1200, 'expected': 850, 'recognized': 1200, 'liability': 0}], ['fractional value', {'issued': 3333, 'redeemed': 1116, 'value_mc': 750, 'breakage_bp': 2500}, {'deferred': 2499, 'expected': 2499, 'recognized': 1116, 'liability': 1383}], ['no breakage', {'issued': 700, 'redeemed': 355, 'value_mc': 1000, 'breakage_bp': 0}, {'deferred': 700, 'expected': 700, 'recognized': 355, 'liability': 345}], ['all breakage', {'issued': 700, 'redeemed': 5, 'value_mc': 1000, 'breakage_bp': 10000}, {'error': 'no expected redemptions'}], ['tiny value', {'issued': 99999, 'redeemed': 12345, 'value_mc': 8, 'breakage_bp': 3000}, {'deferred': 799, 'expected': 69999, 'recognized': 140, 'liability': 659}]]]
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
mid program{'deferred': 100000, 'expected': 80000, 'liability': 62499, 'recognized': 37501}{'deferred': 100000, 'expected': 80000, 'liability': 62499, 'recognized': 37501}Passed
fully redeemed{'deferred': 40000, 'expected': 44995, 'liability': 0, 'recognized': 40000}{'deferred': 40000, 'expected': 44995, 'liability': 0, 'recognized': 40000}Passed
over redeemed{'deferred': 1200, 'expected': 850, 'liability': 0, 'recognized': 1200}{'deferred': 1200, 'expected': 850, 'liability': 0, 'recognized': 1200}Passed
fractional value{'deferred': 2499, 'expected': 2499, 'liability': 1387, 'recognized': 1112}{'deferred': 2499, 'expected': 2499, 'liability': 1387, 'recognized': 1112}Passed
no breakage{'deferred': 700, 'expected': 700, 'liability': 349, 'recognized': 351}{'deferred': 700, 'expected': 700, 'liability': 349, 'recognized': 351}Passed
all breakage{'error': 'no expected redemptions'}{'error': 'no expected redemptions'}Passed
tiny value{'deferred': 399, 'expected': 69999, 'liability': 329, 'recognized': 70}{'deferred': 399, 'expected': 69999, 'liability': 329, 'recognized': 70}Passed

SHA-256 / 706960ac1820d2a9bad824de1f32ae415e5461d59821348a885a754271cc68cb

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:47.302969+00:00.

Case digest / 5fd81d98a7b8013d38c2e5901998b1134c63ea258a8f6020483b42f21b96d29c