{"abstract":"Unplugging a suspended vehicle is not flagged as an abrupt termination.","category":"EV charging session scheduling","checks":7,"contract":"Chronological [t, event] pairs drive states Available, Preparing, Charging, SuspendedEV, Finishing. plug: Available->Preparing. auth: Preparing->Charging. ev_pause/ev_resume toggle Charging/SuspendedEV. stop: Charging or SuspendedEV->Finishing. unplug: any non-Available state ->Available, counted abrupt from Charging or SuspendedEV. Before handling each event, a Preparing connector whose plug is more than timeout seconds old returns to Available (one timeout). Invalid events are ignored. Return [state, seconds spent in Charging, timeouts, abrupt].","evaluation_group":"w2-ev_charging_session_scheduling-connector-session-states","failed_approach":"Counting everything except Preparing also flags orderly unplugs from Finishing.","family":"w2-ev_charging_session_scheduling-connector-session-states-abrupt-unplug-classification","id":"FA-93206","implementations":{"attempt":{"sha256":"ba6cef1f8301b3fbc148b6536e352a5d0d18450a3b74bfe30187c026662e0967","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(events, timeout):\n    state = 'Available'\n    plug_t = None\n    since = None\n    charged = 0\n    timeouts = 0\n    abrupt = 0\n    for t, ev in events:\n        if state == 'Preparing' and t - plug_t > timeout:\n            state = 'Available'\n            timeouts += 1\n        if ev == 'plug' and state == 'Available':\n            state = 'Preparing'\n            plug_t = t\n        elif ev == 'auth' and state == 'Preparing':\n            state = 'Charging'\n            since = t\n        elif ev == 'ev_pause' and state == 'Charging':\n            charged += t - since\n            state = 'SuspendedEV'\n        elif ev == 'ev_resume' and state == 'SuspendedEV':\n            state = 'Charging'\n            since = t\n        elif ev == 'stop' and state in ('Charging', 'SuspendedEV'):\n            if state == 'Charging':\n                charged += t - since\n            state = 'Finishing'\n        elif ev == 'unplug' and state != 'Available':\n            if state == 'Charging':\n                charged += t - since\n            if state != 'Preparing':\n                abrupt += 1\n            state = 'Available'\n    return [state, charged, timeouts, abrupt]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[5, 'plug'], [10, 'auth'], [71, 'stop'], [76, 'unplug'], [137, 'ev_resume'], [142, 'plug'],\n     [142, 'plug'], [202, 'plug'], [322, 'ev_resume'], [352, 'auth']],\n    30],\n   ['Available', 61, 2, 0]],\n  ['control 1',\n   [[[5, 'plug'], [5, 'plug'], [15, 'plug'], [315, 'auth'], [376, 'ev_resume'], [496, 'auth'],\n     [796, 'auth'], [1096, 'unplug']],\n    60],\n   ['Available', 0, 1, 0]],\n  ['control 2', [[[30, 'unplug'], [91, 'auth'], [96, 'unplug'], [106, 'ev_resume']], 90],\n   ['Available', 0, 0, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[5, 'plug'], [10, 'auth'], [71, 'stop'], [76, 'unplug'], [137, 'ev_resume'], [142, 'plug'],\n     [142, 'plug'], [202, 'plug'], [322, 'ev_resume'], [352, 'auth']],\n    30],\n   ['Available', 61, 2, 0]],\n  ['control 1', [[[120, 'auth'], [240, 'ev_resume'], [540, 'unplug']], 60], ['Available', 0, 0, 0]],\n  ['control 2', [[[60, 'unplug'], [180, 'stop'], [240, 'stop'], [270, 'unplug']], 60],\n   ['Available', 0, 0, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[60, 'plug'], [121, 'auth'], [241, 'auth'], [251, 'stop'], [371, 'unplug'], [376, 'ev_pause'],\n     [406, 'unplug']],\n    90],\n   ['Available', 130, 0, 0]],\n  ['control 1', [[[0, 'unplug'], [10, 'ev_pause'], [10, 'plug']], 60], ['Preparing', 0, 0, 0]],\n  ['control 2', [[[300, 'plug'], [360, 'ev_pause'], [421, 'ev_pause']], 30],\n   ['Available', 0, 1, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[30, 'plug'], [60, 'auth'], [120, 'ev_resume'], [180, 'ev_resume'], [185, 'plug'],\n     [190, 'plug'], [250, 'ev_pause'], [370, 'unplug']],\n    30],\n   ['Available', 190, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[61, 'plug'], [91, 'auth'], [101, 'stop'], [401, 'ev_resume'], [701, 'plug'], [701, 'plug'],\n     [706, 'unplug'], [706, 'ev_pause'], [736, 'auth']],\n    90],\n   ['Available', 10, 0, 0]],\n  ['control 1',\n   [[[61, 'auth'], [91, 'unplug'], [211, 'unplug'], [221, 'ev_resume'], [281, 'plug'],\n     [311, 'ev_pause'], [321, 'unplug'], [326, 'plug']],\n    30],\n   ['Preparing', 0, 1, 0]],\n  ['control 2',\n   [[[300, 'plug'], [600, 'ev_resume'], [900, 'ev_resume'], [961, 'unplug'], [966, 'auth'],\n     [971, 'unplug'], [1271, 'auth'], [1276, 'ev_pause']],\n    60],\n   ['Available', 0, 1, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[10, 'plug'], [130, 'auth'], [250, 'plug'], [250, 'auth'], [250, 'ev_pause'], [550, 'unplug'],\n     [555, 'auth'], [565, 'auth']],\n    60],\n   ['Available', 0, 1, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[30, 'plug'], [150, 'stop'], [211, 'plug'], [272, 'auth'], [392, 'stop'], [453, 'plug'],\n     [753, 'unplug']],\n    90],\n   ['Available', 120, 1, 0]],\n  ['control 1', [[[61, 'unplug'], [361, 'auth'], [421, 'unplug'], [426, 'ev_pause']], 60],\n   ['Available', 0, 0, 0]],\n  ['control 2',\n   [[[5, 'ev_resume'], [305, 'unplug'], [605, 'plug'], [665, 'ev_resume'], [670, 'ev_resume']], 60],\n   ['Available', 0, 1, 0]]]]\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":"50a3eda0cad78e69afaab16551ceff26bfe06b3ed5952e249b027615ff8ed866","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(events, timeout):\n    state = 'Available'\n    plug_t = None\n    since = None\n    charged = 0\n    timeouts = 0\n    abrupt = 0\n    for t, ev in events:\n        if state == 'Preparing' and t - plug_t > timeout:\n            state = 'Available'\n            timeouts += 1\n        if ev == 'plug' and state == 'Available':\n            state = 'Preparing'\n            plug_t = t\n        elif ev == 'auth' and state == 'Preparing':\n            state = 'Charging'\n            since = t\n        elif ev == 'ev_pause' and state == 'Charging':\n            charged += t - since\n            state = 'SuspendedEV'\n        elif ev == 'ev_resume' and state == 'SuspendedEV':\n            state = 'Charging'\n            since = t\n        elif ev == 'stop' and state in ('Charging', 'SuspendedEV'):\n            if state == 'Charging':\n                charged += t - since\n            state = 'Finishing'\n        elif ev == 'unplug' and state != 'Available':\n            if state == 'Charging':\n                charged += t - since\n            if state == 'Charging':\n                abrupt += 1\n            state = 'Available'\n    return [state, charged, timeouts, abrupt]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[5, 'plug'], [10, 'auth'], [71, 'stop'], [76, 'unplug'], [137, 'ev_resume'], [142, 'plug'],\n     [142, 'plug'], [202, 'plug'], [322, 'ev_resume'], [352, 'auth']],\n    30],\n   ['Available', 61, 2, 0]],\n  ['control 1',\n   [[[5, 'plug'], [5, 'plug'], [15, 'plug'], [315, 'auth'], [376, 'ev_resume'], [496, 'auth'],\n     [796, 'auth'], [1096, 'unplug']],\n    60],\n   ['Available', 0, 1, 0]],\n  ['control 2', [[[30, 'unplug'], [91, 'auth'], [96, 'unplug'], [106, 'ev_resume']], 90],\n   ['Available', 0, 0, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[5, 'plug'], [10, 'auth'], [71, 'stop'], [76, 'unplug'], [137, 'ev_resume'], [142, 'plug'],\n     [142, 'plug'], [202, 'plug'], [322, 'ev_resume'], [352, 'auth']],\n    30],\n   ['Available', 61, 2, 0]],\n  ['control 1', [[[120, 'auth'], [240, 'ev_resume'], [540, 'unplug']], 60], ['Available', 0, 0, 0]],\n  ['control 2', [[[60, 'unplug'], [180, 'stop'], [240, 'stop'], [270, 'unplug']], 60],\n   ['Available', 0, 0, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[60, 'plug'], [121, 'auth'], [241, 'auth'], [251, 'stop'], [371, 'unplug'], [376, 'ev_pause'],\n     [406, 'unplug']],\n    90],\n   ['Available', 130, 0, 0]],\n  ['control 1', [[[0, 'unplug'], [10, 'ev_pause'], [10, 'plug']], 60], ['Preparing', 0, 0, 0]],\n  ['control 2', [[[300, 'plug'], [360, 'ev_pause'], [421, 'ev_pause']], 30],\n   ['Available', 0, 1, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[30, 'plug'], [60, 'auth'], [120, 'ev_resume'], [180, 'ev_resume'], [185, 'plug'],\n     [190, 'plug'], [250, 'ev_pause'], [370, 'unplug']],\n    30],\n   ['Available', 190, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[61, 'plug'], [91, 'auth'], [101, 'stop'], [401, 'ev_resume'], [701, 'plug'], [701, 'plug'],\n     [706, 'unplug'], [706, 'ev_pause'], [736, 'auth']],\n    90],\n   ['Available', 10, 0, 0]],\n  ['control 1',\n   [[[61, 'auth'], [91, 'unplug'], [211, 'unplug'], [221, 'ev_resume'], [281, 'plug'],\n     [311, 'ev_pause'], [321, 'unplug'], [326, 'plug']],\n    30],\n   ['Preparing', 0, 1, 0]],\n  ['control 2',\n   [[[300, 'plug'], [600, 'ev_resume'], [900, 'ev_resume'], [961, 'unplug'], [966, 'auth'],\n     [971, 'unplug'], [1271, 'auth'], [1276, 'ev_pause']],\n    60],\n   ['Available', 0, 1, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[10, 'plug'], [130, 'auth'], [250, 'plug'], [250, 'auth'], [250, 'ev_pause'], [550, 'unplug'],\n     [555, 'auth'], [565, 'auth']],\n    60],\n   ['Available', 0, 1, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[30, 'plug'], [150, 'stop'], [211, 'plug'], [272, 'auth'], [392, 'stop'], [453, 'plug'],\n     [753, 'unplug']],\n    90],\n   ['Available', 120, 1, 0]],\n  ['control 1', [[[61, 'unplug'], [361, 'auth'], [421, 'unplug'], [426, 'ev_pause']], 60],\n   ['Available', 0, 0, 0]],\n  ['control 2',\n   [[[5, 'ev_resume'], [305, 'unplug'], [605, 'plug'], [665, 'ev_resume'], [670, 'ev_resume']], 60],\n   ['Available', 0, 1, 0]]]]\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":"22ae23738dbb06c2e846ce19706509b785eeefeab78cafdc1b1c78e4ed3f0a93","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(events, timeout):\n    state = 'Available'\n    plug_t = None\n    since = None\n    charged = 0\n    timeouts = 0\n    abrupt = 0\n    for t, ev in events:\n        if state == 'Preparing' and t - plug_t > timeout:\n            state = 'Available'\n            timeouts += 1\n        if ev == 'plug' and state == 'Available':\n            state = 'Preparing'\n            plug_t = t\n        elif ev == 'auth' and state == 'Preparing':\n            state = 'Charging'\n            since = t\n        elif ev == 'ev_pause' and state == 'Charging':\n            charged += t - since\n            state = 'SuspendedEV'\n        elif ev == 'ev_resume' and state == 'SuspendedEV':\n            state = 'Charging'\n            since = t\n        elif ev == 'stop' and state in ('Charging', 'SuspendedEV'):\n            if state == 'Charging':\n                charged += t - since\n            state = 'Finishing'\n        elif ev == 'unplug' and state != 'Available':\n            if state == 'Charging':\n                charged += t - since\n            if state in ('Charging', 'SuspendedEV'):\n                abrupt += 1\n            state = 'Available'\n    return [state, charged, timeouts, abrupt]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[5, 'plug'], [10, 'auth'], [71, 'stop'], [76, 'unplug'], [137, 'ev_resume'], [142, 'plug'],\n     [142, 'plug'], [202, 'plug'], [322, 'ev_resume'], [352, 'auth']],\n    30],\n   ['Available', 61, 2, 0]],\n  ['control 1',\n   [[[5, 'plug'], [5, 'plug'], [15, 'plug'], [315, 'auth'], [376, 'ev_resume'], [496, 'auth'],\n     [796, 'auth'], [1096, 'unplug']],\n    60],\n   ['Available', 0, 1, 0]],\n  ['control 2', [[[30, 'unplug'], [91, 'auth'], [96, 'unplug'], [106, 'ev_resume']], 90],\n   ['Available', 0, 0, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[5, 'plug'], [10, 'auth'], [71, 'stop'], [76, 'unplug'], [137, 'ev_resume'], [142, 'plug'],\n     [142, 'plug'], [202, 'plug'], [322, 'ev_resume'], [352, 'auth']],\n    30],\n   ['Available', 61, 2, 0]],\n  ['control 1', [[[120, 'auth'], [240, 'ev_resume'], [540, 'unplug']], 60], ['Available', 0, 0, 0]],\n  ['control 2', [[[60, 'unplug'], [180, 'stop'], [240, 'stop'], [270, 'unplug']], 60],\n   ['Available', 0, 0, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[60, 'ev_pause'], [360, 'ev_pause'], [660, 'ev_pause'], [720, 'auth'], [781, 'plug'],\n     [791, 'auth'], [1091, 'ev_pause'], [1391, 'auth'], [1691, 'unplug']],\n    90],\n   ['Available', 300, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[60, 'plug'], [121, 'auth'], [241, 'auth'], [251, 'stop'], [371, 'unplug'], [376, 'ev_pause'],\n     [406, 'unplug']],\n    90],\n   ['Available', 130, 0, 0]],\n  ['control 1', [[[0, 'unplug'], [10, 'ev_pause'], [10, 'plug']], 60], ['Preparing', 0, 0, 0]],\n  ['control 2', [[[300, 'plug'], [360, 'ev_pause'], [421, 'ev_pause']], 30],\n   ['Available', 0, 1, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[30, 'plug'], [60, 'auth'], [120, 'ev_resume'], [180, 'ev_resume'], [185, 'plug'],\n     [190, 'plug'], [250, 'ev_pause'], [370, 'unplug']],\n    30],\n   ['Available', 190, 0, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[61, 'plug'], [91, 'auth'], [101, 'stop'], [401, 'ev_resume'], [701, 'plug'], [701, 'plug'],\n     [706, 'unplug'], [706, 'ev_pause'], [736, 'auth']],\n    90],\n   ['Available', 10, 0, 0]],\n  ['control 1',\n   [[[61, 'auth'], [91, 'unplug'], [211, 'unplug'], [221, 'ev_resume'], [281, 'plug'],\n     [311, 'ev_pause'], [321, 'unplug'], [326, 'plug']],\n    30],\n   ['Preparing', 0, 1, 0]],\n  ['control 2',\n   [[[300, 'plug'], [600, 'ev_resume'], [900, 'ev_resume'], [961, 'unplug'], [966, 'auth'],\n     [971, 'unplug'], [1271, 'auth'], [1276, 'ev_pause']],\n    60],\n   ['Available', 0, 1, 0]]],\n [['boundary: authorised exactly at timeout', [[[0, 'plug'], [60, 'auth'], [100, 'stop']], 60],\n   ['Finishing', 40, 0, 0]],\n  ['normal: pause and resume',\n   [[[0, 'plug'], [5, 'auth'], [65, 'ev_pause'], [125, 'ev_resume'], [185, 'stop'],\n     [190, 'unplug']],\n    60],\n   ['Available', 120, 0, 0]],\n  ['boundary: unplug while charging', [[[0, 'plug'], [10, 'auth'], [70, 'unplug']], 60],\n   ['Available', 60, 0, 1]],\n  ['regression: abrupt unplug classification',\n   [[[10, 'plug'], [130, 'auth'], [250, 'plug'], [250, 'auth'], [250, 'ev_pause'], [550, 'unplug'],\n     [555, 'auth'], [565, 'auth']],\n    60],\n   ['Available', 0, 1, 1]],\n  ['regression: abrupt unplug classification (partial repair)',\n   [[[30, 'plug'], [150, 'stop'], [211, 'plug'], [272, 'auth'], [392, 'stop'], [453, 'plug'],\n     [753, 'unplug']],\n    90],\n   ['Available', 120, 1, 0]],\n  ['control 1', [[[61, 'unplug'], [361, 'auth'], [421, 'unplug'], [426, 'ev_pause']], 60],\n   ['Available', 0, 0, 0]],\n  ['control 2',\n   [[[5, 'ev_resume'], [305, 'unplug'], [605, 'plug'], [665, 'ev_resume'], [670, 'ev_resume']], 60],\n   ['Available', 0, 1, 0]]]]\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-connector-session-states-abrupt-unplug-classification","generated_at":"2026-09-29T14:51:52.987400+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":"Count unplugs from Charging or SuspendedEV as abrupt.","root_cause":"Only the Charging state counts toward abrupt unplugs.","sha256":"753284df03c6a4e7569d2a89827a2ac9c7f4874392efb50de68004d27231036d","title":"Connector session state machine: abrupt unplug classification · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":42.062,"exit_code":1,"observations":[{"actual":["Finishing",40,0,0],"check":"boundary: authorised exactly at timeout","expected":["Finishing",40,0,0],"passed":true},{"actual":["Available",120,0,1],"check":"normal: pause and resume","expected":["Available",120,0,0],"passed":false},{"actual":["Available",60,0,1],"check":"boundary: unplug while charging","expected":["Available",60,0,1],"passed":true},{"actual":["Available",300,0,1],"check":"regression: abrupt unplug classification","expected":["Available",300,0,1],"passed":true},{"actual":["Available",61,2,1],"check":"regression: abrupt unplug classification (partial repair)","expected":["Available",61,2,0],"passed":false},{"actual":["Available",0,1,0],"check":"control 1","expected":["Available",0,1,0],"passed":true},{"actual":["Available",0,0,0],"check":"control 2","expected":["Available",0,0,0],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"boundary: authorised exactly at timeout\", \"actual\": [\"Finishing\", 40, 0, 0], \"expected\": [\"Finishing\", 40, 0, 0], \"passed\": true}, {\"check\": \"normal: pause and resume\", \"actual\": [\"Available\", 120, 0, 1], \"expected\": [\"Available\", 120, 0, 0], \"passed\": false}, {\"check\": \"boundary: unplug while charging\", \"actual\": [\"Available\", 60, 0, 1], \"expected\": [\"Available\", 60, 0, 1], \"passed\": true}, {\"check\": \"regression: abrupt unplug classification\", \"actual\": [\"Available\", 300, 0, 1], \"expected\": [\"Available\", 300, 0, 1], \"passed\": true}, {\"check\": \"regression: abrupt unplug classification (partial repair)\", \"actual\": [\"Available\", 61, 2, 1], \"expected\": [\"Available\", 61, 2, 0], \"passed\": false}, {\"check\": \"control 1\", \"actual\": [\"Available\", 0, 1, 0], \"expected\": [\"Available\", 0, 1, 0], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [\"Available\", 0, 0, 0], \"expected\": [\"Available\", 0, 0, 0], \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":41.837,"exit_code":1,"observations":[{"actual":["Finishing",40,0,0],"check":"boundary: authorised exactly at timeout","expected":["Finishing",40,0,0],"passed":true},{"actual":["Available",120,0,0],"check":"normal: pause and resume","expected":["Available",120,0,0],"passed":true},{"actual":["Available",60,0,1],"check":"boundary: unplug while charging","expected":["Available",60,0,1],"passed":true},{"actual":["Available",300,0,0],"check":"regression: abrupt unplug classification","expected":["Available",300,0,1],"passed":false},{"actual":["Available",61,2,0],"check":"regression: abrupt unplug classification (partial repair)","expected":["Available",61,2,0],"passed":true},{"actual":["Available",0,1,0],"check":"control 1","expected":["Available",0,1,0],"passed":true},{"actual":["Available",0,0,0],"check":"control 2","expected":["Available",0,0,0],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"boundary: authorised exactly at timeout\", \"actual\": [\"Finishing\", 40, 0, 0], \"expected\": [\"Finishing\", 40, 0, 0], \"passed\": true}, {\"check\": \"normal: pause and resume\", \"actual\": [\"Available\", 120, 0, 0], \"expected\": [\"Available\", 120, 0, 0], \"passed\": true}, {\"check\": \"boundary: unplug while charging\", \"actual\": [\"Available\", 60, 0, 1], \"expected\": [\"Available\", 60, 0, 1], \"passed\": true}, {\"check\": \"regression: abrupt unplug classification\", \"actual\": [\"Available\", 300, 0, 0], \"expected\": [\"Available\", 300, 0, 1], \"passed\": false}, {\"check\": \"regression: abrupt unplug classification (partial repair)\", \"actual\": [\"Available\", 61, 2, 0], \"expected\": [\"Available\", 61, 2, 0], \"passed\": true}, {\"check\": \"control 1\", \"actual\": [\"Available\", 0, 1, 0], \"expected\": [\"Available\", 0, 1, 0], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [\"Available\", 0, 0, 0], \"expected\": [\"Available\", 0, 0, 0], \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":43.765,"exit_code":0,"observations":[{"actual":["Finishing",40,0,0],"check":"boundary: authorised exactly at timeout","expected":["Finishing",40,0,0],"passed":true},{"actual":["Available",120,0,0],"check":"normal: pause and resume","expected":["Available",120,0,0],"passed":true},{"actual":["Available",60,0,1],"check":"boundary: unplug while charging","expected":["Available",60,0,1],"passed":true},{"actual":["Available",300,0,1],"check":"regression: abrupt unplug classification","expected":["Available",300,0,1],"passed":true},{"actual":["Available",61,2,0],"check":"regression: abrupt unplug classification (partial repair)","expected":["Available",61,2,0],"passed":true},{"actual":["Available",0,1,0],"check":"control 1","expected":["Available",0,1,0],"passed":true},{"actual":["Available",0,0,0],"check":"control 2","expected":["Available",0,0,0],"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"boundary: authorised exactly at timeout\", \"actual\": [\"Finishing\", 40, 0, 0], \"expected\": [\"Finishing\", 40, 0, 0], \"passed\": true}, {\"check\": \"normal: pause and resume\", \"actual\": [\"Available\", 120, 0, 0], \"expected\": [\"Available\", 120, 0, 0], \"passed\": true}, {\"check\": \"boundary: unplug while charging\", \"actual\": [\"Available\", 60, 0, 1], \"expected\": [\"Available\", 60, 0, 1], \"passed\": true}, {\"check\": \"regression: abrupt unplug classification\", \"actual\": [\"Available\", 300, 0, 1], \"expected\": [\"Available\", 300, 0, 1], \"passed\": true}, {\"check\": \"regression: abrupt unplug classification (partial repair)\", \"actual\": [\"Available\", 61, 2, 0], \"expected\": [\"Available\", 61, 2, 0], \"passed\": true}, {\"check\": \"control 1\", \"actual\": [\"Available\", 0, 1, 0], \"expected\": [\"Available\", 0, 1, 0], \"passed\": true}, {\"check\": \"control 2\", \"actual\": [\"Available\", 0, 0, 0], \"expected\": [\"Available\", 0, 0, 0], \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}