{"abstract":"Every idle session is billed one extra minute.","category":"EV charging session scheduling","checks":7,"contract":"Times are minutes 0..1440 of one day. done is when charging finished (None: never finished, no idle fee). Idle time starts at done+grace and ends at unplug (exclusive). Only minutes in [360, 1320) are billable (night waiver). Fee = ceil(billable/block)*rate cents, then capped at cap when cap is not None (cap 0 waives the fee). Return [billable minutes, fee].","evaluation_group":"w2-ev_charging_session_scheduling-idle-occupancy-fee","failed_approach":"Shifting both ends drops the first idle minute and still bills the unplug minute.","family":"w2-ev_charging_session_scheduling-idle-occupancy-fee-unplug-minute-exclusivity","id":"FA-92896","implementations":{"attempt":{"sha256":"81c56c7d78fd954e9b69b3be4f501e74c34a20f4a3f0ee7044467d983ceef0da","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(plug, done, unplug, grace, rate, cap, block):\n    if done is None or unplug <= done:\n        return [0, 0]\n    start = done + grace\n    if unplug <= start:\n        return [0, 0]\n    billable = 0\n    for m in range(start + 1, unplug + 1):\n        if 360 <= m < 1320:\n            billable += 1\n    blocks = -(-billable // block)\n    fee = blocks * rate\n    if cap is not None:\n        fee = min(fee, cap)\n    return [billable, fee]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [267, 472, 501, 15, 5, None, 10], [14, 10]],\n  ['regression: unplug minute exclusivity (partial repair)', [745, 831, 1323, 0, 40, 1000, 10],\n   [489, 1000]],\n  ['control 1', [708, 712, 1012, 15, 25, 1000, 5], [285, 1000]],\n  ['control 2', [424, 721, 1119, 10, 5, None, 15], [388, 130]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [11, 183, 646, 10, 40, 1000, 15], [286, 800]],\n  ['regression: unplug minute exclusivity (partial repair)', [935, 1103, 1359, 15, 25, 0, 15],\n   [202, 0]],\n  ['control 1', [1149, 1440, 1440, 0, 25, None, 1], [0, 0]],\n  ['control 2', [421, 583, 815, 15, 5, 300, 15], [217, 75]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [768, 1112, 1161, 10, 25, 300, 1], [39, 300]],\n  ['regression: unplug minute exclusivity (partial repair)', [1142, 1227, 1440, 10, 25, 0, 1],\n   [83, 0]],\n  ['control 1', [1122, 1422, 1434, 10, 25, 0, 10], [0, 0]],\n  ['control 2', [325, 596, 781, 30, 25, None, 15], [155, 275]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [37, 320, 601, 10, 5, None, 10], [241, 125]],\n  ['regression: unplug minute exclusivity (partial repair)', [278, 314, 487, 30, 5, 0, 15],\n   [127, 0]],\n  ['control 1', [887, 906, 899, 15, 5, 1000, 10], [0, 0]],\n  ['control 2', [684, 849, 1335, 30, 25, 1000, 10], [441, 1000]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [703, 737, 1228, 0, 25, 1000, 15], [491, 825]],\n  ['regression: unplug minute exclusivity (partial repair)', [1177, 1189, 1440, 15, 5, 1000, 5],\n   [116, 120]],\n  ['control 1', [942, 1011, 983, 15, 25, 300, 10], [0, 0]],\n  ['control 2', [306, 373, 739, 10, 25, None, 10], [356, 900]]]]\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":"892cb3057a15b5b5d3533406c8bc376b4a5f7a1345d7634c4e6fd644eafec1e4","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(plug, done, unplug, grace, rate, cap, block):\n    if done is None or unplug <= done:\n        return [0, 0]\n    start = done + grace\n    if unplug <= start:\n        return [0, 0]\n    billable = 0\n    for m in range(start, unplug + 1):\n        if 360 <= m < 1320:\n            billable += 1\n    blocks = -(-billable // block)\n    fee = blocks * rate\n    if cap is not None:\n        fee = min(fee, cap)\n    return [billable, fee]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [267, 472, 501, 15, 5, None, 10], [14, 10]],\n  ['regression: unplug minute exclusivity (partial repair)', [745, 831, 1323, 0, 40, 1000, 10],\n   [489, 1000]],\n  ['control 1', [708, 712, 1012, 15, 25, 1000, 5], [285, 1000]],\n  ['control 2', [424, 721, 1119, 10, 5, None, 15], [388, 130]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [11, 183, 646, 10, 40, 1000, 15], [286, 800]],\n  ['regression: unplug minute exclusivity (partial repair)', [935, 1103, 1359, 15, 25, 0, 15],\n   [202, 0]],\n  ['control 1', [1149, 1440, 1440, 0, 25, None, 1], [0, 0]],\n  ['control 2', [421, 583, 815, 15, 5, 300, 15], [217, 75]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [768, 1112, 1161, 10, 25, 300, 1], [39, 300]],\n  ['regression: unplug minute exclusivity (partial repair)', [1142, 1227, 1440, 10, 25, 0, 1],\n   [83, 0]],\n  ['control 1', [1122, 1422, 1434, 10, 25, 0, 10], [0, 0]],\n  ['control 2', [325, 596, 781, 30, 25, None, 15], [155, 275]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [37, 320, 601, 10, 5, None, 10], [241, 125]],\n  ['regression: unplug minute exclusivity (partial repair)', [278, 314, 487, 30, 5, 0, 15],\n   [127, 0]],\n  ['control 1', [887, 906, 899, 15, 5, 1000, 10], [0, 0]],\n  ['control 2', [684, 849, 1335, 30, 25, 1000, 10], [441, 1000]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [703, 737, 1228, 0, 25, 1000, 15], [491, 825]],\n  ['regression: unplug minute exclusivity (partial repair)', [1177, 1189, 1440, 15, 5, 1000, 5],\n   [116, 120]],\n  ['control 1', [942, 1011, 983, 15, 25, 300, 10], [0, 0]],\n  ['control 2', [306, 373, 739, 10, 25, None, 10], [356, 900]]]]\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":"f0f80a2214b848624d7dce5e34f227a095b446f1b76ece837c90fd8eef854bd0","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(plug, done, unplug, grace, rate, cap, block):\n    if done is None or unplug <= done:\n        return [0, 0]\n    start = done + grace\n    if unplug <= start:\n        return [0, 0]\n    billable = 0\n    for m in range(start, unplug):\n        if 360 <= m < 1320:\n            billable += 1\n    blocks = -(-billable // block)\n    fee = blocks * rate\n    if cap is not None:\n        fee = min(fee, cap)\n    return [billable, fee]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [267, 472, 501, 15, 5, None, 10], [14, 10]],\n  ['regression: unplug minute exclusivity (partial repair)', [745, 831, 1323, 0, 40, 1000, 10],\n   [489, 1000]],\n  ['control 1', [708, 712, 1012, 15, 25, 1000, 5], [285, 1000]],\n  ['control 2', [424, 721, 1119, 10, 5, None, 15], [388, 130]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [11, 183, 646, 10, 40, 1000, 15], [286, 800]],\n  ['regression: unplug minute exclusivity (partial repair)', [935, 1103, 1359, 15, 25, 0, 15],\n   [202, 0]],\n  ['control 1', [1149, 1440, 1440, 0, 25, None, 1], [0, 0]],\n  ['control 2', [421, 583, 815, 15, 5, 300, 15], [217, 75]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [768, 1112, 1161, 10, 25, 300, 1], [39, 300]],\n  ['regression: unplug minute exclusivity (partial repair)', [1142, 1227, 1440, 10, 25, 0, 1],\n   [83, 0]],\n  ['control 1', [1122, 1422, 1434, 10, 25, 0, 10], [0, 0]],\n  ['control 2', [325, 596, 781, 30, 25, None, 15], [155, 275]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [37, 320, 601, 10, 5, None, 10], [241, 125]],\n  ['regression: unplug minute exclusivity (partial repair)', [278, 314, 487, 30, 5, 0, 15],\n   [127, 0]],\n  ['control 1', [887, 906, 899, 15, 5, 1000, 10], [0, 0]],\n  ['control 2', [684, 849, 1335, 30, 25, 1000, 10], [441, 1000]]],\n [['boundary: unplugged inside grace', [600, 700, 710, 15, 25, None, 5], [0, 0]],\n  ['boundary: never finished', [600, None, 900, 0, 25, None, 5], [0, 0]],\n  ['boundary: idle crosses night waiver', [1200, 1300, 1400, 0, 10, None, 1], [20, 200]],\n  ['regression: unplug minute exclusivity', [703, 737, 1228, 0, 25, 1000, 15], [491, 825]],\n  ['regression: unplug minute exclusivity (partial repair)', [1177, 1189, 1440, 15, 5, 1000, 5],\n   [116, 120]],\n  ['control 1', [942, 1011, 983, 15, 25, 300, 10], [0, 0]],\n  ['control 2', [306, 373, 739, 10, 25, None, 10], [356, 900]]]]\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":"Deterministic stipulated toy contract for teaching; no claim of conformance with any standard, vendor protocol or production controller. 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-ev_charging_session_scheduling-idle-occupancy-fee-unplug-minute-exclusivity","generated_at":"2026-09-29T14:51:50.151696+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Depot, workplace and public EV chargers schedule sessions against prices, circuit limits and departure deadlines; a wrong decision silently strands a driver or overloads a feeder.","repair":"Iterate idle minutes over [start, unplug).","root_cause":"The unplug minute is included in the idle interval.","sha256":"8e5c55bf82499fedf0383dcfdc2afda5a8176d39ba08dd07f5ddd4b3ee0acde5","title":"Post-charge idle occupancy fee: unplug minute exclusivity · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":40.157,"exit_code":1,"observations":[{"actual":[0,0],"check":"boundary: unplugged inside grace","expected":[0,0],"passed":true},{"actual":[0,0],"check":"boundary: never finished","expected":[0,0],"passed":true},{"actual":[19,190],"check":"boundary: idle crosses night waiver","expected":[20,200],"passed":false},{"actual":[14,10],"check":"regression: unplug minute exclusivity","expected":[14,10],"passed":true},{"actual":[488,1000],"check":"regression: unplug minute exclusivity (partial repair)","expected":[489,1000],"passed":false},{"actual":[285,1000],"check":"control 1","expected":[285,1000],"passed":true},{"actual":[388,130],"check":"control 2","expected":[388,130],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"boundary: unplugged inside grace\", \"actual\": [0, 0], \"expected\": [0, 0], \"passed\": true}, {\"check\": \"boundary: never finished\", \"actual\": [0, 0], \"expected\": [0, 0], \"passed\": true}, {\"check\": \"boundary: idle crosses night waiver\", \"actual\": [19, 190], \"expected\": [20, 200], \"passed\": false}, {\"check\": \"regression: unplug minute exclusivity\", \"actual\": [14, 10], \"expected\": [14, 10], \"passed\": true}, {\"check\": \"regression: unplug minute exclusivity (partial repair)\", \"actual\": [488, 1000], \"expected\": [489, 1000], \"passed\": false}, {\"check\": \"control 1\", \"actual\": [285, 1000], \"expected\": [285, 1000], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [388, 130], \"expected\": [388, 130], \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":41.169,"exit_code":1,"observations":[{"actual":[0,0],"check":"boundary: unplugged inside grace","expected":[0,0],"passed":true},{"actual":[0,0],"check":"boundary: never finished","expected":[0,0],"passed":true},{"actual":[20,200],"check":"boundary: idle crosses night waiver","expected":[20,200],"passed":true},{"actual":[15,10],"check":"regression: unplug minute exclusivity","expected":[14,10],"passed":false},{"actual":[489,1000],"check":"regression: unplug minute exclusivity (partial repair)","expected":[489,1000],"passed":true},{"actual":[286,1000],"check":"control 1","expected":[285,1000],"passed":false},{"actual":[389,130],"check":"control 2","expected":[388,130],"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"boundary: unplugged inside grace\", \"actual\": [0, 0], \"expected\": [0, 0], \"passed\": true}, {\"check\": \"boundary: never finished\", \"actual\": [0, 0], \"expected\": [0, 0], \"passed\": true}, {\"check\": \"boundary: idle crosses night waiver\", \"actual\": [20, 200], \"expected\": [20, 200], \"passed\": true}, {\"check\": \"regression: unplug minute exclusivity\", \"actual\": [15, 10], \"expected\": [14, 10], \"passed\": false}, {\"check\": \"regression: unplug minute exclusivity (partial repair)\", \"actual\": [489, 1000], \"expected\": [489, 1000], \"passed\": true}, {\"check\": \"control 1\", \"actual\": [286, 1000], \"expected\": [285, 1000], \"passed\": false}, {\"check\": \"control 2\", \"actual\": [389, 130], \"expected\": [388, 130], \"passed\": false}], \"passed\": false}\n"},"fixed":{"elapsed_ms":40.308,"exit_code":0,"observations":[{"actual":[0,0],"check":"boundary: unplugged inside grace","expected":[0,0],"passed":true},{"actual":[0,0],"check":"boundary: never finished","expected":[0,0],"passed":true},{"actual":[20,200],"check":"boundary: idle crosses night waiver","expected":[20,200],"passed":true},{"actual":[14,10],"check":"regression: unplug minute exclusivity","expected":[14,10],"passed":true},{"actual":[489,1000],"check":"regression: unplug minute exclusivity (partial repair)","expected":[489,1000],"passed":true},{"actual":[285,1000],"check":"control 1","expected":[285,1000],"passed":true},{"actual":[388,130],"check":"control 2","expected":[388,130],"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"boundary: unplugged inside grace\", \"actual\": [0, 0], \"expected\": [0, 0], \"passed\": true}, {\"check\": \"boundary: never finished\", \"actual\": [0, 0], \"expected\": [0, 0], \"passed\": true}, {\"check\": \"boundary: idle crosses night waiver\", \"actual\": [20, 200], \"expected\": [20, 200], \"passed\": true}, {\"check\": \"regression: unplug minute exclusivity\", \"actual\": [14, 10], \"expected\": [14, 10], \"passed\": true}, {\"check\": \"regression: unplug minute exclusivity (partial repair)\", \"actual\": [489, 1000], \"expected\": [489, 1000], \"passed\": true}, {\"check\": \"control 1\", \"actual\": [285, 1000], \"expected\": [285, 1000], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [388, 130], \"expected\": [388, 130], \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}