{"abstract":"A second refund message for the same order is processed again instead of being ignored.","category":"Loyalty points ledgers","checks":8,"contract":"Events: [\"earn\",day,order,pts] starts pending; points post (become available) on day >= earn day + hold. [\"refund\",day,order] cancels a live earn if day < earn day + hold (\"cancelled\"), otherwise claws it back (\"clawed\"; available may go negative); unknown or already refunded orders are \"ignored\". [\"redeem\",day,pts] spends available (posted - spent) points or returns \"insufficient\". [\"query\",day] returns [pending, available]. Return results list.","evaluation_group":"w2-loyalty-points-ledgers-pending-points","failed_approach":"Ignoring only cancelled earns still reprocesses refunds of clawed earns.","family":"w2-loyalty-points-ledgers-pending-points-repeat-refund-guard","id":"FA-60496","implementations":{"attempt":{"sha256":"0fef00603604b2704c8d082805031c5cbc7cd636d9fd14902ab2eb862c613181","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    earns = {}\n    spent = 0\n    out = []\n    posted = lambda day: sum(e[1] for e in earns.values() if e[2] == 'live' and day >= e[0] + x['hold'])\n    pending = lambda day: sum(e[1] for e in earns.values() if e[2] == 'live' and day < e[0] + x['hold'])\n    for ev in x['events']:\n        k, day = ev[0], ev[1]\n        if k == 'earn':\n            earns[ev[2]] = [day, ev[3], 'live']\n            out.append('pending')\n        elif k == 'refund':\n            e = earns.get(ev[2])\n            if e is None or e[2] == 'cancelled':\n                out.append('ignored')\n                continue\n            if day < e[0] + x['hold']:\n                e[2] = 'cancelled'\n            else:\n                e[2] = 'clawed'\n            out.append(e[2])\n        elif k == 'redeem':\n            if ev[2] > posted(day) - spent:\n                out.append('insufficient')\n                continue\n            spent += ev[2]\n            out.append('ok')\n        else:\n            out.append([pending(day), posted(day) - spent])\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 32]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 41], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 41]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 23]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 31], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 151], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 12], ['query', 5], ['query', 12]]}, ['pending', [12, 0], [0, 12]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 26, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 21], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 33]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 42], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 42]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 24]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 32], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 152], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 13], ['query', 5], ['query', 12]]}, ['pending', [13, 0], [0, 13]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 27, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 22], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 34]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 43], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 43]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 25]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 33], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 153], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 14], ['query', 5], ['query', 12]]}, ['pending', [14, 0], [0, 14]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 28, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 23], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 35]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 44], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 44]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 26]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 34], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 154], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 15], ['query', 5], ['query', 12]]}, ['pending', [15, 0], [0, 15]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 29, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 24], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 36]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 45], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 45]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 27]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 35], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 155], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 16], ['query', 5], ['query', 12]]}, ['pending', [16, 0], [0, 16]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 25, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 25], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]]]\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":"d94157aa779d39e14a7363e3a58b93a39b7d92d5cd10040698b2d4625e75e5d5","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    earns = {}\n    spent = 0\n    out = []\n    posted = lambda day: sum(e[1] for e in earns.values() if e[2] == 'live' and day >= e[0] + x['hold'])\n    pending = lambda day: sum(e[1] for e in earns.values() if e[2] == 'live' and day < e[0] + x['hold'])\n    for ev in x['events']:\n        k, day = ev[0], ev[1]\n        if k == 'earn':\n            earns[ev[2]] = [day, ev[3], 'live']\n            out.append('pending')\n        elif k == 'refund':\n            e = earns.get(ev[2])\n            if e is None:\n                out.append('ignored')\n                continue\n            if day < e[0] + x['hold']:\n                e[2] = 'cancelled'\n            else:\n                e[2] = 'clawed'\n            out.append(e[2])\n        elif k == 'redeem':\n            if ev[2] > posted(day) - spent:\n                out.append('insufficient')\n                continue\n            spent += ev[2]\n            out.append('ok')\n        else:\n            out.append([pending(day), posted(day) - spent])\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 32]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 41], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 41]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 23]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 31], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 151], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 12], ['query', 5], ['query', 12]]}, ['pending', [12, 0], [0, 12]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 26, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 21], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 33]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 42], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 42]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 24]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 32], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 152], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 13], ['query', 5], ['query', 12]]}, ['pending', [13, 0], [0, 13]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 27, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 22], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 34]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 43], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 43]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 25]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 33], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 153], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 14], ['query', 5], ['query', 12]]}, ['pending', [14, 0], [0, 14]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 28, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 23], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 35]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 44], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 44]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 26]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 34], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 154], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 15], ['query', 5], ['query', 12]]}, ['pending', [15, 0], [0, 15]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 29, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 24], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 36]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 45], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 45]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 27]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 35], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 155], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 16], ['query', 5], ['query', 12]]}, ['pending', [16, 0], [0, 16]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 25, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 25], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]]]\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":"40721e7739ccbb479a5544049f4700c965d54bc2387aecefa97213353ac8bd19","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(x):\n    earns = {}\n    spent = 0\n    out = []\n    posted = lambda day: sum(e[1] for e in earns.values() if e[2] == 'live' and day >= e[0] + x['hold'])\n    pending = lambda day: sum(e[1] for e in earns.values() if e[2] == 'live' and day < e[0] + x['hold'])\n    for ev in x['events']:\n        k, day = ev[0], ev[1]\n        if k == 'earn':\n            earns[ev[2]] = [day, ev[3], 'live']\n            out.append('pending')\n        elif k == 'refund':\n            e = earns.get(ev[2])\n            if e is None or e[2] != 'live':\n                out.append('ignored')\n                continue\n            if day < e[0] + x['hold']:\n                e[2] = 'cancelled'\n            else:\n                e[2] = 'clawed'\n            out.append(e[2])\n        elif k == 'redeem':\n            if ev[2] > posted(day) - spent:\n                out.append('insufficient')\n                continue\n            spent += ev[2]\n            out.append('ok')\n        else:\n            out.append([pending(day), posted(day) - spent])\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 32]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 41], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 41]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 23]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 31], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 151], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 12], ['query', 5], ['query', 12]]}, ['pending', [12, 0], [0, 12]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 26, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 21], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 33]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 42], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 42]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 24]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 32], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 152], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 13], ['query', 5], ['query', 12]]}, ['pending', [13, 0], [0, 13]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 27, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 22], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 34]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 43], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 43]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 25]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 33], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 153], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 14], ['query', 5], ['query', 12]]}, ['pending', [14, 0], [0, 14]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 28, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 23], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 35]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 44], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 44]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 26]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 34], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 154], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 15], ['query', 5], ['query', 12]]}, ['pending', [15, 0], [0, 15]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 29, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 24], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]], [['posts after hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['query', 29], ['query', 30], ['query', 36]]}, ['pending', [100, 0], [0, 100], [0, 100]]], ['refund in hold', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 2, 'o2', 45], ['refund', 10, 'o1'], ['query', 40]]}, ['pending', 'pending', 'cancelled', [0, 45]]], ['refund after post', {'hold': 14, 'events': [['earn', 0, 'o1', 70], ['redeem', 20, 50], ['refund', 21, 'o1'], ['query', 27]]}, ['pending', 'ok', 'clawed', [0, -50]]], ['double refund', {'hold': 10, 'events': [['earn', 0, 'o1', 35], ['refund', 3, 'o1'], ['refund', 4, 'o1'], ['refund', 5, 'zz']]}, ['pending', 'cancelled', 'ignored', 'ignored']], ['redeem pending', {'hold': 30, 'events': [['earn', 0, 'o1', 100], ['earn', 25, 'o2', 100], ['redeem', 40, 155], ['redeem', 40, 100]]}, ['pending', 'pending', 'insufficient', 'ok']], ['same day earn query', {'hold': 7, 'events': [['earn', 5, 'o1', 16], ['query', 5], ['query', 12]]}, ['pending', [16, 0], [0, 16]]], ['refund near posting', {'hold': 30, 'events': [['earn', 0, 'o1', 60], ['refund', 25, 'o1'], ['query', 60]]}, ['pending', 'cancelled', [0, 0]]], ['double refund after posting', {'hold': 5, 'events': [['earn', 0, 'o9', 25], ['refund', 9, 'o9'], ['refund', 10, 'o9']]}, ['pending', 'clawed', 'ignored']]]]\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-pending-points-repeat-refund-guard","generated_at":"2026-09-29T14:46:46.105351+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":"Ignore refunds for unknown orders and for earns already cancelled or clawed.","root_cause":"The refund guard only checks that the order exists, not that its earn is still live.","sha256":"4d8958f4894bef83eb6bd7e098a0097e2d75832639e0f5d8fbc7fe966f9e8927","title":"Pending points hold period: repeat refund guard · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":43.778,"exit_code":1,"observations":[{"actual":["pending",[100,0],[0,100],[0,100]],"check":"posts after hold","expected":["pending",[100,0],[0,100],[0,100]],"passed":true},{"actual":["pending","pending","cancelled",[0,41]],"check":"refund in hold","expected":["pending","pending","cancelled",[0,41]],"passed":true},{"actual":["pending","ok","clawed",[0,-50]],"check":"refund after post","expected":["pending","ok","clawed",[0,-50]],"passed":true},{"actual":["pending","cancelled","ignored","ignored"],"check":"double refund","expected":["pending","cancelled","ignored","ignored"],"passed":true},{"actual":["pending","pending","insufficient","ok"],"check":"redeem pending","expected":["pending","pending","insufficient","ok"],"passed":true},{"actual":["pending",[12,0],[0,12]],"check":"same day earn query","expected":["pending",[12,0],[0,12]],"passed":true},{"actual":["pending","cancelled",[0,0]],"check":"refund near posting","expected":["pending","cancelled",[0,0]],"passed":true},{"actual":["pending","clawed","clawed"],"check":"double refund after posting","expected":["pending","clawed","ignored"],"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"posts after hold\", \"actual\": [\"pending\", [100, 0], [0, 100], [0, 100]], \"expected\": [\"pending\", [100, 0], [0, 100], [0, 100]], \"passed\": true}, {\"check\": \"refund in hold\", \"actual\": [\"pending\", \"pending\", \"cancelled\", [0, 41]], \"expected\": [\"pending\", \"pending\", \"cancelled\", [0, 41]], \"passed\": true}, {\"check\": \"refund after post\", \"actual\": [\"pending\", \"ok\", \"clawed\", [0, -50]], \"expected\": [\"pending\", \"ok\", \"clawed\", [0, -50]], \"passed\": true}, {\"check\": \"double refund\", \"actual\": [\"pending\", \"cancelled\", \"ignored\", \"ignored\"], \"expected\": [\"pending\", \"cancelled\", \"ignored\", \"ignored\"], \"passed\": true}, {\"check\": \"redeem pending\", \"actual\": [\"pending\", \"pending\", \"insufficient\", \"ok\"], \"expected\": [\"pending\", \"pending\", \"insufficient\", \"ok\"], \"passed\": true}, {\"check\": \"same day earn query\", \"actual\": [\"pending\", [12, 0], [0, 12]], \"expected\": [\"pending\", [12, 0], [0, 12]], \"passed\": true}, {\"check\": \"refund near posting\", \"actual\": [\"pending\", \"cancelled\", [0, 0]], \"expected\": [\"pending\", \"cancelled\", [0, 0]], \"passed\": true}, {\"check\": \"double refund after posting\", \"actual\": [\"pending\", \"clawed\", \"clawed\"], \"expected\": [\"pending\", \"clawed\", \"ignored\"], \"passed\": false}], \"passed\": false}\n"},"broken":{"elapsed_ms":41.289,"exit_code":1,"observations":[{"actual":["pending",[100,0],[0,100],[0,100]],"check":"posts after hold","expected":["pending",[100,0],[0,100],[0,100]],"passed":true},{"actual":["pending","pending","cancelled",[0,41]],"check":"refund in hold","expected":["pending","pending","cancelled",[0,41]],"passed":true},{"actual":["pending","ok","clawed",[0,-50]],"check":"refund after post","expected":["pending","ok","clawed",[0,-50]],"passed":true},{"actual":["pending","cancelled","cancelled","ignored"],"check":"double refund","expected":["pending","cancelled","ignored","ignored"],"passed":false},{"actual":["pending","pending","insufficient","ok"],"check":"redeem pending","expected":["pending","pending","insufficient","ok"],"passed":true},{"actual":["pending",[12,0],[0,12]],"check":"same day earn query","expected":["pending",[12,0],[0,12]],"passed":true},{"actual":["pending","cancelled",[0,0]],"check":"refund near posting","expected":["pending","cancelled",[0,0]],"passed":true},{"actual":["pending","clawed","clawed"],"check":"double refund after posting","expected":["pending","clawed","ignored"],"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"posts after hold\", \"actual\": [\"pending\", [100, 0], [0, 100], [0, 100]], \"expected\": [\"pending\", [100, 0], [0, 100], [0, 100]], \"passed\": true}, {\"check\": \"refund in hold\", \"actual\": [\"pending\", \"pending\", \"cancelled\", [0, 41]], \"expected\": [\"pending\", \"pending\", \"cancelled\", [0, 41]], \"passed\": true}, {\"check\": \"refund after post\", \"actual\": [\"pending\", \"ok\", \"clawed\", [0, -50]], \"expected\": [\"pending\", \"ok\", \"clawed\", [0, -50]], \"passed\": true}, {\"check\": \"double refund\", \"actual\": [\"pending\", \"cancelled\", \"cancelled\", \"ignored\"], \"expected\": [\"pending\", \"cancelled\", \"ignored\", \"ignored\"], \"passed\": false}, {\"check\": \"redeem pending\", \"actual\": [\"pending\", \"pending\", \"insufficient\", \"ok\"], \"expected\": [\"pending\", \"pending\", \"insufficient\", \"ok\"], \"passed\": true}, {\"check\": \"same day earn query\", \"actual\": [\"pending\", [12, 0], [0, 12]], \"expected\": [\"pending\", [12, 0], [0, 12]], \"passed\": true}, {\"check\": \"refund near posting\", \"actual\": [\"pending\", \"cancelled\", [0, 0]], \"expected\": [\"pending\", \"cancelled\", [0, 0]], \"passed\": true}, {\"check\": \"double refund after posting\", \"actual\": [\"pending\", \"clawed\", \"clawed\"], \"expected\": [\"pending\", \"clawed\", \"ignored\"], \"passed\": false}], \"passed\": false}\n"},"fixed":{"elapsed_ms":40.748,"exit_code":0,"observations":[{"actual":["pending",[100,0],[0,100],[0,100]],"check":"posts after hold","expected":["pending",[100,0],[0,100],[0,100]],"passed":true},{"actual":["pending","pending","cancelled",[0,41]],"check":"refund in hold","expected":["pending","pending","cancelled",[0,41]],"passed":true},{"actual":["pending","ok","clawed",[0,-50]],"check":"refund after post","expected":["pending","ok","clawed",[0,-50]],"passed":true},{"actual":["pending","cancelled","ignored","ignored"],"check":"double refund","expected":["pending","cancelled","ignored","ignored"],"passed":true},{"actual":["pending","pending","insufficient","ok"],"check":"redeem pending","expected":["pending","pending","insufficient","ok"],"passed":true},{"actual":["pending",[12,0],[0,12]],"check":"same day earn query","expected":["pending",[12,0],[0,12]],"passed":true},{"actual":["pending","cancelled",[0,0]],"check":"refund near posting","expected":["pending","cancelled",[0,0]],"passed":true},{"actual":["pending","clawed","ignored"],"check":"double refund after posting","expected":["pending","clawed","ignored"],"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"posts after hold\", \"actual\": [\"pending\", [100, 0], [0, 100], [0, 100]], \"expected\": [\"pending\", [100, 0], [0, 100], [0, 100]], \"passed\": true}, {\"check\": \"refund in hold\", \"actual\": [\"pending\", \"pending\", \"cancelled\", [0, 41]], \"expected\": [\"pending\", \"pending\", \"cancelled\", [0, 41]], \"passed\": true}, {\"check\": \"refund after post\", \"actual\": [\"pending\", \"ok\", \"clawed\", [0, -50]], \"expected\": [\"pending\", \"ok\", \"clawed\", [0, -50]], \"passed\": true}, {\"check\": \"double refund\", \"actual\": [\"pending\", \"cancelled\", \"ignored\", \"ignored\"], \"expected\": [\"pending\", \"cancelled\", \"ignored\", \"ignored\"], \"passed\": true}, {\"check\": \"redeem pending\", \"actual\": [\"pending\", \"pending\", \"insufficient\", \"ok\"], \"expected\": [\"pending\", \"pending\", \"insufficient\", \"ok\"], \"passed\": true}, {\"check\": \"same day earn query\", \"actual\": [\"pending\", [12, 0], [0, 12]], \"expected\": [\"pending\", [12, 0], [0, 12]], \"passed\": true}, {\"check\": \"refund near posting\", \"actual\": [\"pending\", \"cancelled\", [0, 0]], \"expected\": [\"pending\", \"cancelled\", [0, 0]], \"passed\": true}, {\"check\": \"double refund after posting\", \"actual\": [\"pending\", \"clawed\", \"ignored\"], \"expected\": [\"pending\", \"clawed\", \"ignored\"], \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}