{"abstract":"Accounts with recent activity are expired because the clock runs from their first earn.","category":"Loyalty points ledgers","checks":9,"contract":"Input {activity:[[day,kind,signed pts]], today, window}. Only activity dated <= today counts. Qualifying kinds are earn, redeem and transfer_out; the reference day is the latest qualifying day, or the earliest activity day if none qualifies (today if no activity). The account is inactive when today - reference > window; an inactive account with a positive balance forfeits it all. Return {expired, balance, last_activity}.","evaluation_group":"w2-loyalty-points-ledgers-inactivity-expiry","failed_approach":"Taking the last qualifying entry in list order fails when activity is not sorted by day.","family":"w2-loyalty-points-ledgers-inactivity-expiry-latest-qualifying-day","id":"FA-60566","implementations":{"attempt":{"sha256":"4927822d845e06ce1209b74b5319d406de47ee23dee4eddb63d37e695b7877cd","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    acts = [a for a in x['activity'] if a[0] <= x['today']]\n    qual = [d for d, k, p in acts if k in ('earn', 'redeem', 'transfer_out')]\n    bal = sum(p for d, k, p in acts)\n    if qual:\n        ref = qual[-1]\n    else:\n        ref = min([d for d, k, p in acts] or [x['today']])\n    inactive = x['today'] - ref > x['window']\n    if inactive and bal > 0:\n        return {'expired': bal, 'balance': 0, 'last_activity': ref}\n    return {'expired': 0, 'balance': bal, 'last_activity': ref}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 51]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 351, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 301]], 'today': 700, 'window': 540}, {'expired': 801, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 101]], 'today': 600, 'window': 540}, {'expired': 501, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 251]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 251, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -101]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 799, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 101]], 'today': 600, 'window': 540}, {'expired': 101, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -151]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -51, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 71]], 'today': 950, 'window': 540}, {'expired': 76, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 21]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 52]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 352, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 302]], 'today': 700, 'window': 540}, {'expired': 802, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 102]], 'today': 600, 'window': 540}, {'expired': 502, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 252]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 252, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -102]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 798, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 102]], 'today': 600, 'window': 540}, {'expired': 102, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -152]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -52, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 72]], 'today': 950, 'window': 540}, {'expired': 77, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 22]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 53]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 353, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 303]], 'today': 700, 'window': 540}, {'expired': 803, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 103]], 'today': 600, 'window': 540}, {'expired': 503, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 253]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 253, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -103]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 797, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 103]], 'today': 600, 'window': 540}, {'expired': 103, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -153]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -53, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 73]], 'today': 950, 'window': 540}, {'expired': 78, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 23]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 54]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 354, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 304]], 'today': 700, 'window': 540}, {'expired': 804, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 104]], 'today': 600, 'window': 540}, {'expired': 504, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 254]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 254, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -104]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 796, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 104]], 'today': 600, 'window': 540}, {'expired': 104, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -154]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -54, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 74]], 'today': 950, 'window': 540}, {'expired': 79, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 24]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 55]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 355, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 305]], 'today': 700, 'window': 540}, {'expired': 805, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 105]], 'today': 600, 'window': 540}, {'expired': 505, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 255]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 255, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -105]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 795, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 105]], 'today': 600, 'window': 540}, {'expired': 105, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -155]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -55, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 75]], 'today': 950, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 25]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"55ecd9b6cb062cba957bdd6e894d1a75c4a99c9ff77e0890108fe856a8172929","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    acts = [a for a in x['activity'] if a[0] <= x['today']]\n    qual = [d for d, k, p in acts if k in ('earn', 'redeem', 'transfer_out')]\n    bal = sum(p for d, k, p in acts)\n    if qual:\n        ref = min(qual)\n    else:\n        ref = min([d for d, k, p in acts] or [x['today']])\n    inactive = x['today'] - ref > x['window']\n    if inactive and bal > 0:\n        return {'expired': bal, 'balance': 0, 'last_activity': ref}\n    return {'expired': 0, 'balance': bal, 'last_activity': ref}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 51]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 351, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 301]], 'today': 700, 'window': 540}, {'expired': 801, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 101]], 'today': 600, 'window': 540}, {'expired': 501, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 251]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 251, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -101]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 799, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 101]], 'today': 600, 'window': 540}, {'expired': 101, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -151]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -51, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 71]], 'today': 950, 'window': 540}, {'expired': 76, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 21]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 52]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 352, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 302]], 'today': 700, 'window': 540}, {'expired': 802, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 102]], 'today': 600, 'window': 540}, {'expired': 502, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 252]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 252, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -102]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 798, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 102]], 'today': 600, 'window': 540}, {'expired': 102, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -152]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -52, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 72]], 'today': 950, 'window': 540}, {'expired': 77, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 22]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 53]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 353, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 303]], 'today': 700, 'window': 540}, {'expired': 803, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 103]], 'today': 600, 'window': 540}, {'expired': 503, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 253]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 253, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -103]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 797, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 103]], 'today': 600, 'window': 540}, {'expired': 103, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -153]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -53, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 73]], 'today': 950, 'window': 540}, {'expired': 78, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 23]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 54]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 354, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 304]], 'today': 700, 'window': 540}, {'expired': 804, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 104]], 'today': 600, 'window': 540}, {'expired': 504, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 254]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 254, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -104]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 796, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 104]], 'today': 600, 'window': 540}, {'expired': 104, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -154]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -54, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 74]], 'today': 950, 'window': 540}, {'expired': 79, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 24]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 55]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 355, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 305]], 'today': 700, 'window': 540}, {'expired': 805, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 105]], 'today': 600, 'window': 540}, {'expired': 505, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 255]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 255, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -105]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 795, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 105]], 'today': 600, 'window': 540}, {'expired': 105, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -155]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -55, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 75]], 'today': 950, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 25]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"d752781b4f0be2f659f4081a74fc167913c4f8c0a40b66eeea8ee401b5509f51","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    acts = [a for a in x['activity'] if a[0] <= x['today']]\n    qual = [d for d, k, p in acts if k in ('earn', 'redeem', 'transfer_out')]\n    bal = sum(p for d, k, p in acts)\n    if qual:\n        ref = max(qual)\n    else:\n        ref = min([d for d, k, p in acts] or [x['today']])\n    inactive = x['today'] - ref > x['window']\n    if inactive and bal > 0:\n        return {'expired': bal, 'balance': 0, 'last_activity': ref}\n    return {'expired': 0, 'balance': bal, 'last_activity': ref}\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 51]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 351, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 301]], 'today': 700, 'window': 540}, {'expired': 801, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 101]], 'today': 600, 'window': 540}, {'expired': 501, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 251]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 251, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -101]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 799, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 101]], 'today': 600, 'window': 540}, {'expired': 101, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -151]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -51, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 71]], 'today': 950, 'window': 540}, {'expired': 76, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 21]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 52]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 352, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 302]], 'today': 700, 'window': 540}, {'expired': 802, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 102]], 'today': 600, 'window': 540}, {'expired': 502, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 252]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 252, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -102]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 798, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 102]], 'today': 600, 'window': 540}, {'expired': 102, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -152]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -52, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 72]], 'today': 950, 'window': 540}, {'expired': 77, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 22]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 53]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 353, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 303]], 'today': 700, 'window': 540}, {'expired': 803, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 103]], 'today': 600, 'window': 540}, {'expired': 503, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 253]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 253, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -103]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 797, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 103]], 'today': 600, 'window': 540}, {'expired': 103, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -153]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -53, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 73]], 'today': 950, 'window': 540}, {'expired': 78, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 23]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 54]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 354, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 304]], 'today': 700, 'window': 540}, {'expired': 804, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 104]], 'today': 600, 'window': 540}, {'expired': 504, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 254]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 254, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -104]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 796, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 104]], 'today': 600, 'window': 540}, {'expired': 104, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -154]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -54, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 74]], 'today': 950, 'window': 540}, {'expired': 79, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 24]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]], [['active member', {'activity': [[10, 'earn', 500], [200, 'redeem', -200], [500, 'earn', 55]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': 355, 'last_activity': 500}], ['inactive', {'activity': [[10, 'earn', 500], [100, 'earn', 305]], 'today': 700, 'window': 540}, {'expired': 805, 'balance': 0, 'last_activity': 100}], ['bonus does not count', {'activity': [[0, 'earn', 400], [500, 'bonus_credit', 105]], 'today': 600, 'window': 540}, {'expired': 505, 'balance': 0, 'last_activity': 0}], ['window edge', {'activity': [[60, 'earn', 255]], 'today': 600, 'window': 540}, {'expired': 0, 'balance': 255, 'last_activity': 60}], ['transfer out counts', {'activity': [[0, 'earn', 900], [300, 'transfer_out', -105]], 'today': 800, 'window': 540}, {'expired': 0, 'balance': 795, 'last_activity': 300}], ['never qualified', {'activity': [[5, 'adjust', 105]], 'today': 600, 'window': 540}, {'expired': 105, 'balance': 0, 'last_activity': 5}], ['negative balance', {'activity': [[0, 'earn', 100], [10, 'adjust', -155]], 'today': 900, 'window': 540}, {'expired': 0, 'balance': -55, 'last_activity': 0}], ['unsorted list', {'activity': [[400, 'earn', 10], [20, 'redeem', -5], [30, 'earn', 75]], 'today': 950, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 400}], ['future dated', {'activity': [[10, 'earn', 80], [1000, 'earn', 25]], 'today': 900, 'window': 540}, {'expired': 80, 'balance': 0, 'last_activity': 10}]]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":"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.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"w2-loyalty-points-ledgers-inactivity-expiry-latest-qualifying-day","generated_at":"2026-09-29T14:46:46.756944+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Member point balances, statements and the program liability depend on this ledger rule.","repair":"Use the latest qualifying activity day.","root_cause":"The reference day is the earliest qualifying day instead of the latest.","sha256":"302b517351e63cbb0fc2efd50e7c245e59a61ec30d0985cf695fd96a0252b055","title":"Account inactivity expiry: latest qualifying day · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":42.414,"exit_code":1,"observations":[{"actual":{"balance":351,"expired":0,"last_activity":500},"check":"active member","expected":{"balance":351,"expired":0,"last_activity":500},"passed":true},{"actual":{"balance":0,"expired":801,"last_activity":100},"check":"inactive","expected":{"balance":0,"expired":801,"last_activity":100},"passed":true},{"actual":{"balance":0,"expired":501,"last_activity":0},"check":"bonus does not count","expected":{"balance":0,"expired":501,"last_activity":0},"passed":true},{"actual":{"balance":251,"expired":0,"last_activity":60},"check":"window edge","expected":{"balance":251,"expired":0,"last_activity":60},"passed":true},{"actual":{"balance":799,"expired":0,"last_activity":300},"check":"transfer out counts","expected":{"balance":799,"expired":0,"last_activity":300},"passed":true},{"actual":{"balance":0,"expired":101,"last_activity":5},"check":"never qualified","expected":{"balance":0,"expired":101,"last_activity":5},"passed":true},{"actual":{"balance":-51,"expired":0,"last_activity":0},"check":"negative balance","expected":{"balance":-51,"expired":0,"last_activity":0},"passed":true},{"actual":{"balance":0,"expired":76,"last_activity":30},"check":"unsorted list","expected":{"balance":0,"expired":76,"last_activity":400},"passed":false},{"actual":{"balance":0,"expired":80,"last_activity":10},"check":"future dated","expected":{"balance":0,"expired":80,"last_activity":10},"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"active member\", \"actual\": {\"expired\": 0, \"balance\": 351, \"last_activity\": 500}, \"expected\": {\"expired\": 0, \"balance\": 351, \"last_activity\": 500}, \"passed\": true}, {\"check\": \"inactive\", \"actual\": {\"expired\": 801, \"balance\": 0, \"last_activity\": 100}, \"expected\": {\"expired\": 801, \"balance\": 0, \"last_activity\": 100}, \"passed\": true}, {\"check\": \"bonus does not count\", \"actual\": {\"expired\": 501, \"balance\": 0, \"last_activity\": 0}, \"expected\": {\"expired\": 501, \"balance\": 0, \"last_activity\": 0}, \"passed\": true}, {\"check\": \"window edge\", \"actual\": {\"expired\": 0, \"balance\": 251, \"last_activity\": 60}, \"expected\": {\"expired\": 0, \"balance\": 251, \"last_activity\": 60}, \"passed\": true}, {\"check\": \"transfer out counts\", \"actual\": {\"expired\": 0, \"balance\": 799, \"last_activity\": 300}, \"expected\": {\"expired\": 0, \"balance\": 799, \"last_activity\": 300}, \"passed\": true}, {\"check\": \"never qualified\", \"actual\": {\"expired\": 101, \"balance\": 0, \"last_activity\": 5}, \"expected\": {\"expired\": 101, \"balance\": 0, \"last_activity\": 5}, \"passed\": true}, {\"check\": \"negative balance\", \"actual\": {\"expired\": 0, \"balance\": -51, \"last_activity\": 0}, \"expected\": {\"expired\": 0, \"balance\": -51, \"last_activity\": 0}, \"passed\": true}, {\"check\": \"unsorted list\", \"actual\": {\"expired\": 76, \"balance\": 0, \"last_activity\": 30}, \"expected\": {\"expired\": 76, \"balance\": 0, \"last_activity\": 400}, \"passed\": false}, {\"check\": \"future dated\", \"actual\": {\"expired\": 80, \"balance\": 0, \"last_activity\": 10}, \"expected\": {\"expired\": 80, \"balance\": 0, \"last_activity\": 10}, \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":40.318,"exit_code":1,"observations":[{"actual":{"balance":0,"expired":351,"last_activity":10},"check":"active member","expected":{"balance":351,"expired":0,"last_activity":500},"passed":false},{"actual":{"balance":0,"expired":801,"last_activity":10},"check":"inactive","expected":{"balance":0,"expired":801,"last_activity":100},"passed":false},{"actual":{"balance":0,"expired":501,"last_activity":0},"check":"bonus does not count","expected":{"balance":0,"expired":501,"last_activity":0},"passed":true},{"actual":{"balance":251,"expired":0,"last_activity":60},"check":"window edge","expected":{"balance":251,"expired":0,"last_activity":60},"passed":true},{"actual":{"balance":0,"expired":799,"last_activity":0},"check":"transfer out counts","expected":{"balance":799,"expired":0,"last_activity":300},"passed":false},{"actual":{"balance":0,"expired":101,"last_activity":5},"check":"never qualified","expected":{"balance":0,"expired":101,"last_activity":5},"passed":true},{"actual":{"balance":-51,"expired":0,"last_activity":0},"check":"negative balance","expected":{"balance":-51,"expired":0,"last_activity":0},"passed":true},{"actual":{"balance":0,"expired":76,"last_activity":20},"check":"unsorted list","expected":{"balance":0,"expired":76,"last_activity":400},"passed":false},{"actual":{"balance":0,"expired":80,"last_activity":10},"check":"future dated","expected":{"balance":0,"expired":80,"last_activity":10},"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"active member\", \"actual\": {\"expired\": 351, \"balance\": 0, \"last_activity\": 10}, \"expected\": {\"expired\": 0, \"balance\": 351, \"last_activity\": 500}, \"passed\": false}, {\"check\": \"inactive\", \"actual\": {\"expired\": 801, \"balance\": 0, \"last_activity\": 10}, \"expected\": {\"expired\": 801, \"balance\": 0, \"last_activity\": 100}, \"passed\": false}, {\"check\": \"bonus does not count\", \"actual\": {\"expired\": 501, \"balance\": 0, \"last_activity\": 0}, \"expected\": {\"expired\": 501, \"balance\": 0, \"last_activity\": 0}, \"passed\": true}, {\"check\": \"window edge\", \"actual\": {\"expired\": 0, \"balance\": 251, \"last_activity\": 60}, \"expected\": {\"expired\": 0, \"balance\": 251, \"last_activity\": 60}, \"passed\": true}, {\"check\": \"transfer out counts\", \"actual\": {\"expired\": 799, \"balance\": 0, \"last_activity\": 0}, \"expected\": {\"expired\": 0, \"balance\": 799, \"last_activity\": 300}, \"passed\": false}, {\"check\": \"never qualified\", \"actual\": {\"expired\": 101, \"balance\": 0, \"last_activity\": 5}, \"expected\": {\"expired\": 101, \"balance\": 0, \"last_activity\": 5}, \"passed\": true}, {\"check\": \"negative balance\", \"actual\": {\"expired\": 0, \"balance\": -51, \"last_activity\": 0}, \"expected\": {\"expired\": 0, \"balance\": -51, \"last_activity\": 0}, \"passed\": true}, {\"check\": \"unsorted list\", \"actual\": {\"expired\": 76, \"balance\": 0, \"last_activity\": 20}, \"expected\": {\"expired\": 76, \"balance\": 0, \"last_activity\": 400}, \"passed\": false}, {\"check\": \"future dated\", \"actual\": {\"expired\": 80, \"balance\": 0, \"last_activity\": 10}, \"expected\": {\"expired\": 80, \"balance\": 0, \"last_activity\": 10}, \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":42.955,"exit_code":0,"observations":[{"actual":{"balance":351,"expired":0,"last_activity":500},"check":"active member","expected":{"balance":351,"expired":0,"last_activity":500},"passed":true},{"actual":{"balance":0,"expired":801,"last_activity":100},"check":"inactive","expected":{"balance":0,"expired":801,"last_activity":100},"passed":true},{"actual":{"balance":0,"expired":501,"last_activity":0},"check":"bonus does not count","expected":{"balance":0,"expired":501,"last_activity":0},"passed":true},{"actual":{"balance":251,"expired":0,"last_activity":60},"check":"window edge","expected":{"balance":251,"expired":0,"last_activity":60},"passed":true},{"actual":{"balance":799,"expired":0,"last_activity":300},"check":"transfer out counts","expected":{"balance":799,"expired":0,"last_activity":300},"passed":true},{"actual":{"balance":0,"expired":101,"last_activity":5},"check":"never qualified","expected":{"balance":0,"expired":101,"last_activity":5},"passed":true},{"actual":{"balance":-51,"expired":0,"last_activity":0},"check":"negative balance","expected":{"balance":-51,"expired":0,"last_activity":0},"passed":true},{"actual":{"balance":0,"expired":76,"last_activity":400},"check":"unsorted list","expected":{"balance":0,"expired":76,"last_activity":400},"passed":true},{"actual":{"balance":0,"expired":80,"last_activity":10},"check":"future dated","expected":{"balance":0,"expired":80,"last_activity":10},"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"active member\", \"actual\": {\"expired\": 0, \"balance\": 351, \"last_activity\": 500}, \"expected\": {\"expired\": 0, \"balance\": 351, \"last_activity\": 500}, \"passed\": true}, {\"check\": \"inactive\", \"actual\": {\"expired\": 801, \"balance\": 0, \"last_activity\": 100}, \"expected\": {\"expired\": 801, \"balance\": 0, \"last_activity\": 100}, \"passed\": true}, {\"check\": \"bonus does not count\", \"actual\": {\"expired\": 501, \"balance\": 0, \"last_activity\": 0}, \"expected\": {\"expired\": 501, \"balance\": 0, \"last_activity\": 0}, \"passed\": true}, {\"check\": \"window edge\", \"actual\": {\"expired\": 0, \"balance\": 251, \"last_activity\": 60}, \"expected\": {\"expired\": 0, \"balance\": 251, \"last_activity\": 60}, \"passed\": true}, {\"check\": \"transfer out counts\", \"actual\": {\"expired\": 0, \"balance\": 799, \"last_activity\": 300}, \"expected\": {\"expired\": 0, \"balance\": 799, \"last_activity\": 300}, \"passed\": true}, {\"check\": \"never qualified\", \"actual\": {\"expired\": 101, \"balance\": 0, \"last_activity\": 5}, \"expected\": {\"expired\": 101, \"balance\": 0, \"last_activity\": 5}, \"passed\": true}, {\"check\": \"negative balance\", \"actual\": {\"expired\": 0, \"balance\": -51, \"last_activity\": 0}, \"expected\": {\"expired\": 0, \"balance\": -51, \"last_activity\": 0}, \"passed\": true}, {\"check\": \"unsorted list\", \"actual\": {\"expired\": 76, \"balance\": 0, \"last_activity\": 400}, \"expected\": {\"expired\": 76, \"balance\": 0, \"last_activity\": 400}, \"passed\": true}, {\"check\": \"future dated\", \"actual\": {\"expired\": 80, \"balance\": 0, \"last_activity\": 10}, \"expected\": {\"expired\": 80, \"balance\": 0, \"last_activity\": 10}, \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}