{"abstract":"A \"hold until next period\" never ends, while a permanent hold is dropped at the next program change.","category":"HVAC thermostat control","checks":8,"contract":"Thermostat hold logic; samples are [schedule_period_id, scheduled_setpoint, action] where action is None, \"resume\", [\"temp\", sp] or [\"perm\", sp]. A hold setpoint is clamped to 50.0-90.0 F (500-900 tenths). A temporary hold lasts until the schedule period id changes from the one in which the hold was set; a permanent hold lasts until resume. A new hold of either kind replaces the current hold. Return the effective setpoint per sample.","contract_signature":"samples","evaluation_group":"w2-hvac-thermostat-control-setpoint-hold-override","failed_approach":"Expiring both kinds still drops permanent holds.","family":"w2-hvac-thermostat-control-setpoint-hold-override-temporary-expiry","id":"FA-92481","implementations":{"attempt":{"sha256":"30f4dbfe29a0215e708f328ac72d1e98724d36e788efd2d20a2d874e6fc28b0d","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(samples):\n    hold = None\n    hold_period = None\n    out = []\n    for period, sched, action in samples:\n        if action == 'resume':\n            hold = None\n        elif action is not None:\n            kind, sp = action\n            sp = max(500, min(900, sp))\n            hold = [kind, sp]\n            hold_period = period\n        if hold is not None and hold[0] in ('temp', 'perm') and period != hold_period:\n            hold = None\n        out.append(sched if hold is None else hold[1])\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: scenario 1',\n   [[[0, 700, ['perm', 690]],\n     [0, 700, None],\n     [0, 700, 'resume'],\n     [1, 700, None],\n     [1, 700, None],\n     [2, 600, None],\n     [2, 600, None],\n     [3, 680, ['perm', 856]],\n     [4, 620, ['perm', 777]],\n     [4, 620, None],\n     [4, 620, None],\n     [5, 600, ['temp', 764]],\n     [5, 600, None],\n     [6, 600, None],\n     [7, 600, ['temp', 726]],\n     [7, 600, None]]],\n   [690, 690, 700, 700, 700, 600, 600, 856, 777, 777, 777, 764, 764, 600, 726, 726]],\n  ['regression: scenario 2',\n   [[[1, 620, None],\n     [1, 620, ['perm', 540]],\n     [1, 620, None],\n     [1, 620, None],\n     [1, 620, ['temp', 863]],\n     [2, 600, None],\n     [3, 700, ['perm', 476]],\n     [3, 700, None],\n     [3, 700, None]]],\n   [620, 540, 540, 540, 863, 600, 500, 500, 500]],\n  ['regression: scenario 3',\n   [[[1, 620, ['temp', 657]],\n     [1, 620, ['temp', 940]],\n     [1, 620, None],\n     [1, 620, None],\n     [1, 620, None],\n     [2, 700, 'resume'],\n     [2, 700, ['temp', 641]],\n     [3, 620, None]]],\n   [657, 900, 900, 900, 900, 700, 641, 620]],\n  ['regression: scenario 4',\n   [[[0, 700, ['temp', 891]],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, ['temp', 877]],\n     [0, 700, None],\n     [0, 700, None],\n     [1, 600, None],\n     [1, 600, 'resume'],\n     [2, 620, ['perm', 514]]]],\n   [891, 891, 891, 877, 877, 877, 600, 600, 514]]],\n [['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: scenario 1',\n   [[[1, 700, 'resume'],\n     [1, 700, ['perm', 548]],\n     [1, 700, None],\n     [1, 700, None],\n     [2, 700, None],\n     [3, 620, None],\n     [4, 620, None],\n     [5, 680, None],\n     [5, 680, ['temp', 806]],\n     [5, 680, None],\n     [5, 680, None],\n     [6, 700, None],\n     [6, 700, None],\n     [6, 700, None]]],\n   [700, 548, 548, 548, 548, 548, 548, 548, 806, 806, 806, 700, 700, 700]],\n  ['regression: scenario 2',\n   [[[0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, ['temp', 520]],\n     [0, 680, None],\n     [1, 680, None],\n     [2, 620, None],\n     [3, 680, ['perm', 745]],\n     [3, 680, None],\n     [3, 680, None],\n     [4, 680, None],\n     [5, 700, ['temp', 765]],\n     [5, 700, None]]],\n   [680, 680, 680, 680, 680, 680, 520, 520, 680, 620, 745, 745, 745, 745, 765, 765]],\n  ['regression: scenario 3',\n   [[[0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, ['perm', 700]],\n     [0, 620, ['temp', 750]],\n     [1, 620, None],\n     [2, 620, None],\n     [2, 620, None]]],\n   [620, 620, 620, 620, 620, 700, 750, 620, 620, 620]],\n  ['regression: scenario 4',\n   [[[0, 680, None],\n     [1, 600, ['temp', 779]],\n     [1, 600, ['temp', 782]],\n     [2, 680, None],\n     [3, 700, ['perm', 454]],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, ['temp', 831]],\n     [3, 700, None],\n     [3, 700, None]]],\n   [680, 779, 782, 680, 500, 500, 500, 500, 500, 831, 831, 831]]],\n [['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: scenario 4',\n   [[[1, 700, ['perm', 595]],\n     [2, 600, None],\n     [2, 600, ['perm', 854]],\n     [3, 600, 'resume'],\n     [3, 600, ['perm', 923]],\n     [3, 600, ['perm', 783]],\n     [3, 600, None],\n     [3, 600, ['temp', 512]]]],\n   [595, 595, 854, 600, 900, 783, 783, 512]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['control: scenario 1',\n   [[[0, 620, None],\n     [1, 680, None],\n     [1, 680, None],\n     [1, 680, None],\n     [2, 680, ['temp', 617]],\n     [2, 680, None],\n     [2, 680, ['perm', 816]],\n     [3, 620, ['perm', 930]],\n     [3, 620, None]]],\n   [620, 680, 680, 680, 617, 617, 816, 900, 900]],\n  ['control: scenario 2',\n   [[[0, 620, 'resume'],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, 'resume'],\n     [1, 620, None],\n     [2, 700, None],\n     [2, 700, None],\n     [2, 700, None],\n     [2, 700, None],\n     [3, 620, None],\n     [3, 620, ['perm', 870]],\n     [3, 620, None]]],\n   [620, 620, 620, 620, 620, 620, 700, 700, 700, 700, 620, 870, 870]],\n  ['control: scenario 3',\n   [[[0, 680, 'resume'],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [2, 620, None],\n     [2, 620, 'resume'],\n     [2, 620, None],\n     [2, 620, ['temp', 685]],\n     [2, 620, ['perm', 890]],\n     [2, 620, ['perm', 678]]]],\n   [680, 700, 700, 700, 620, 620, 620, 685, 890, 678]]],\n [['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: scenario 6',\n   [[[0, 700, None],\n     [0, 700, None],\n     [0, 700, ['perm', 473]],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, None],\n     [1, 600, None],\n     [1, 600, None],\n     [2, 600, None],\n     [3, 680, None]]],\n   [700, 700, 500, 500, 500, 500, 500, 500, 500, 500, 500]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['control: scenario 1',\n   [[[1, 700, ['temp', 847]],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, 'resume'],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, ['perm', 465]],\n     [1, 700, None],\n     [2, 600, ['temp', 552]],\n     [2, 600, None],\n     [2, 600, ['temp', 898]]]],\n   [847, 847, 847, 700, 700, 700, 500, 500, 552, 552, 898]],\n  ['regression: scenario 2',\n   [[[0, 680, 'resume'],\n     [0, 680, None],\n     [0, 680, ['perm', 681]],\n     [0, 680, None],\n     [1, 680, None],\n     [1, 680, None],\n     [1, 680, None],\n     [1, 680, ['temp', 832]],\n     [1, 680, ['temp', 511]],\n     [1, 680, ['perm', 779]],\n     [1, 680, ['temp', 724]],\n     [2, 700, 'resume'],\n     [3, 680, None],\n     [3, 680, 'resume']]],\n   [680, 680, 681, 681, 681, 681, 681, 832, 511, 779, 724, 700, 680, 680]],\n  ['regression: scenario 3',\n   [[[0, 680, None],\n     [1, 680, None],\n     [2, 600, 'resume'],\n     [2, 600, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, 'resume'],\n     [4, 600, None],\n     [4, 600, None],\n     [4, 600, None],\n     [5, 620, ['temp', 645]],\n     [6, 700, None]]],\n   [680, 680, 600, 600, 700, 700, 700, 700, 600, 600, 600, 645, 700]]],\n [['regression: scenario 1',\n   [[[0, 700, None],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, ['perm', 578]],\n     [1, 600, 'resume'],\n     [2, 600, ['temp', 629]],\n     [3, 700, None],\n     [3, 700, ['perm', 796]],\n     [3, 700, ['temp', 558]],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, ['temp', 796]],\n     [4, 700, None],\n     [4, 700, None],\n     [4, 700, None],\n     [4, 700, None]]],\n   [700, 700, 700, 578, 600, 629, 700, 796, 558, 558, 558, 796, 700, 700, 700, 700]],\n  ['regression: scenario 7',\n   [[[0, 680, None],\n     [0, 680, None],\n     [1, 680, ['perm', 831]],\n     [1, 680, None],\n     [1, 680, None],\n     [2, 600, None],\n     [2, 600, None],\n     [2, 600, None],\n     [2, 600, None],\n     [2, 600, 'resume']]],\n   [680, 680, 831, 831, 831, 831, 831, 831, 831, 600]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: scenario 2',\n   [[[0, 600, None],\n     [0, 600, None],\n     [0, 600, None],\n     [0, 600, None],\n     [0, 600, None],\n     [1, 680, ['temp', 866]],\n     [1, 680, None],\n     [1, 680, ['temp', 591]],\n     [1, 680, ['temp', 671]],\n     [2, 700, None],\n     [2, 700, None]]],\n   [600, 600, 600, 600, 600, 866, 866, 591, 671, 700, 700]],\n  ['control: scenario 3',\n   [[[0, 680, None],\n     [0, 680, None],\n     [1, 700, ['perm', 502]],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, ['perm', 754]],\n     [1, 700, None],\n     [2, 700, ['temp', 591]],\n     [2, 700, None],\n     [2, 700, None],\n     [2, 700, ['temp', 502]],\n     [3, 680, 'resume'],\n     [3, 680, None]]],\n   [680, 680, 502, 502, 502, 502, 502, 502, 754, 754, 591, 591, 591, 502, 680, 680]]]]\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":"f5537155dc5b6b812c637d7b8c87b2fb2623438edd0cffe293a1119e3badfa45","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(samples):\n    hold = None\n    hold_period = None\n    out = []\n    for period, sched, action in samples:\n        if action == 'resume':\n            hold = None\n        elif action is not None:\n            kind, sp = action\n            sp = max(500, min(900, sp))\n            hold = [kind, sp]\n            hold_period = period\n        if hold is not None and hold[0] == 'perm' and period != hold_period:\n            hold = None\n        out.append(sched if hold is None else hold[1])\n    return out\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: scenario 1',\n   [[[0, 700, ['perm', 690]],\n     [0, 700, None],\n     [0, 700, 'resume'],\n     [1, 700, None],\n     [1, 700, None],\n     [2, 600, None],\n     [2, 600, None],\n     [3, 680, ['perm', 856]],\n     [4, 620, ['perm', 777]],\n     [4, 620, None],\n     [4, 620, None],\n     [5, 600, ['temp', 764]],\n     [5, 600, None],\n     [6, 600, None],\n     [7, 600, ['temp', 726]],\n     [7, 600, None]]],\n   [690, 690, 700, 700, 700, 600, 600, 856, 777, 777, 777, 764, 764, 600, 726, 726]],\n  ['regression: scenario 2',\n   [[[1, 620, None],\n     [1, 620, ['perm', 540]],\n     [1, 620, None],\n     [1, 620, None],\n     [1, 620, ['temp', 863]],\n     [2, 600, None],\n     [3, 700, ['perm', 476]],\n     [3, 700, None],\n     [3, 700, None]]],\n   [620, 540, 540, 540, 863, 600, 500, 500, 500]],\n  ['regression: scenario 3',\n   [[[1, 620, ['temp', 657]],\n     [1, 620, ['temp', 940]],\n     [1, 620, None],\n     [1, 620, None],\n     [1, 620, None],\n     [2, 700, 'resume'],\n     [2, 700, ['temp', 641]],\n     [3, 620, None]]],\n   [657, 900, 900, 900, 900, 700, 641, 620]],\n  ['regression: scenario 4',\n   [[[0, 700, ['temp', 891]],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, ['temp', 877]],\n     [0, 700, None],\n     [0, 700, None],\n     [1, 600, None],\n     [1, 600, 'resume'],\n     [2, 620, ['perm', 514]]]],\n   [891, 891, 891, 877, 877, 877, 600, 600, 514]]],\n [['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: scenario 1',\n   [[[1, 700, 'resume'],\n     [1, 700, ['perm', 548]],\n     [1, 700, None],\n     [1, 700, None],\n     [2, 700, None],\n     [3, 620, None],\n     [4, 620, None],\n     [5, 680, None],\n     [5, 680, ['temp', 806]],\n     [5, 680, None],\n     [5, 680, None],\n     [6, 700, None],\n     [6, 700, None],\n     [6, 700, None]]],\n   [700, 548, 548, 548, 548, 548, 548, 548, 806, 806, 806, 700, 700, 700]],\n  ['regression: scenario 2',\n   [[[0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, None],\n     [0, 680, ['temp', 520]],\n     [0, 680, None],\n     [1, 680, None],\n     [2, 620, None],\n     [3, 680, ['perm', 745]],\n     [3, 680, None],\n     [3, 680, None],\n     [4, 680, None],\n     [5, 700, ['temp', 765]],\n     [5, 700, None]]],\n   [680, 680, 680, 680, 680, 680, 520, 520, 680, 620, 745, 745, 745, 745, 765, 765]],\n  ['regression: scenario 3',\n   [[[0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, ['perm', 700]],\n     [0, 620, ['temp', 750]],\n     [1, 620, None],\n     [2, 620, None],\n     [2, 620, None]]],\n   [620, 620, 620, 620, 620, 700, 750, 620, 620, 620]],\n  ['regression: scenario 4',\n   [[[0, 680, None],\n     [1, 600, ['temp', 779]],\n     [1, 600, ['temp', 782]],\n     [2, 680, None],\n     [3, 700, ['perm', 454]],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, ['temp', 831]],\n     [3, 700, None],\n     [3, 700, None]]],\n   [680, 779, 782, 680, 500, 500, 500, 500, 500, 831, 831, 831]]],\n [['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: scenario 4',\n   [[[1, 700, ['perm', 595]],\n     [2, 600, None],\n     [2, 600, ['perm', 854]],\n     [3, 600, 'resume'],\n     [3, 600, ['perm', 923]],\n     [3, 600, ['perm', 783]],\n     [3, 600, None],\n     [3, 600, ['temp', 512]]]],\n   [595, 595, 854, 600, 900, 783, 783, 512]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['control: scenario 1',\n   [[[0, 620, None],\n     [1, 680, None],\n     [1, 680, None],\n     [1, 680, None],\n     [2, 680, ['temp', 617]],\n     [2, 680, None],\n     [2, 680, ['perm', 816]],\n     [3, 620, ['perm', 930]],\n     [3, 620, None]]],\n   [620, 680, 680, 680, 617, 617, 816, 900, 900]],\n  ['control: scenario 2',\n   [[[0, 620, 'resume'],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, None],\n     [0, 620, 'resume'],\n     [1, 620, None],\n     [2, 700, None],\n     [2, 700, None],\n     [2, 700, None],\n     [2, 700, None],\n     [3, 620, None],\n     [3, 620, ['perm', 870]],\n     [3, 620, None]]],\n   [620, 620, 620, 620, 620, 620, 700, 700, 700, 700, 620, 870, 870]],\n  ['control: scenario 3',\n   [[[0, 680, 'resume'],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [2, 620, None],\n     [2, 620, 'resume'],\n     [2, 620, None],\n     [2, 620, ['temp', 685]],\n     [2, 620, ['perm', 890]],\n     [2, 620, ['perm', 678]]]],\n   [680, 700, 700, 700, 620, 620, 620, 685, 890, 678]]],\n [['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: scenario 6',\n   [[[0, 700, None],\n     [0, 700, None],\n     [0, 700, ['perm', 473]],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, None],\n     [1, 600, None],\n     [1, 600, None],\n     [2, 600, None],\n     [3, 680, None]]],\n   [700, 700, 500, 500, 500, 500, 500, 500, 500, 500, 500]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['control: scenario 1',\n   [[[1, 700, ['temp', 847]],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, 'resume'],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, ['perm', 465]],\n     [1, 700, None],\n     [2, 600, ['temp', 552]],\n     [2, 600, None],\n     [2, 600, ['temp', 898]]]],\n   [847, 847, 847, 700, 700, 700, 500, 500, 552, 552, 898]],\n  ['regression: scenario 2',\n   [[[0, 680, 'resume'],\n     [0, 680, None],\n     [0, 680, ['perm', 681]],\n     [0, 680, None],\n     [1, 680, None],\n     [1, 680, None],\n     [1, 680, None],\n     [1, 680, ['temp', 832]],\n     [1, 680, ['temp', 511]],\n     [1, 680, ['perm', 779]],\n     [1, 680, ['temp', 724]],\n     [2, 700, 'resume'],\n     [3, 680, None],\n     [3, 680, 'resume']]],\n   [680, 680, 681, 681, 681, 681, 681, 832, 511, 779, 724, 700, 680, 680]],\n  ['regression: scenario 3',\n   [[[0, 680, None],\n     [1, 680, None],\n     [2, 600, 'resume'],\n     [2, 600, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, 'resume'],\n     [4, 600, None],\n     [4, 600, None],\n     [4, 600, None],\n     [5, 620, ['temp', 645]],\n     [6, 700, None]]],\n   [680, 680, 600, 600, 700, 700, 700, 700, 600, 600, 600, 645, 700]]],\n [['regression: scenario 1',\n   [[[0, 700, None],\n     [0, 700, None],\n     [0, 700, None],\n     [0, 700, ['perm', 578]],\n     [1, 600, 'resume'],\n     [2, 600, ['temp', 629]],\n     [3, 700, None],\n     [3, 700, ['perm', 796]],\n     [3, 700, ['temp', 558]],\n     [3, 700, None],\n     [3, 700, None],\n     [3, 700, ['temp', 796]],\n     [4, 700, None],\n     [4, 700, None],\n     [4, 700, None],\n     [4, 700, None]]],\n   [700, 700, 700, 578, 600, 629, 700, 796, 558, 558, 558, 796, 700, 700, 700, 700]],\n  ['regression: scenario 7',\n   [[[0, 680, None],\n     [0, 680, None],\n     [1, 680, ['perm', 831]],\n     [1, 680, None],\n     [1, 680, None],\n     [2, 600, None],\n     [2, 600, None],\n     [2, 600, None],\n     [2, 600, None],\n     [2, 600, 'resume']]],\n   [680, 680, 831, 831, 831, 831, 831, 831, 831, 600]],\n  ['regression: hold lifecycle',\n   [[[1, 680, None],\n     [1, 680, ['temp', 720]],\n     [1, 680, None],\n     [2, 620, None],\n     [2, 620, ['perm', 950]],\n     [3, 700, None],\n     [3, 700, ['temp', 400]],\n     [4, 650, None],\n     [4, 650, 'resume']]],\n   [680, 720, 720, 620, 900, 900, 500, 650, 650]],\n  ['regression: second temporary hold',\n   [[[1, 680, ['temp', 700]], [2, 620, None], [3, 650, ['temp', 710]], [3, 650, None], [4, 600, None]]],\n   [700, 620, 710, 710, 600]],\n  ['regression: temporary over permanent',\n   [[[1, 680, ['perm', 720]], [2, 620, ['temp', 690]], [2, 620, None], [3, 640, None]]],\n   [720, 690, 690, 640]],\n  ['regression: clamp low', [[[1, 680, ['perm', 300]], [2, 620, None]]], [500, 500]],\n  ['regression: scenario 2',\n   [[[0, 600, None],\n     [0, 600, None],\n     [0, 600, None],\n     [0, 600, None],\n     [0, 600, None],\n     [1, 680, ['temp', 866]],\n     [1, 680, None],\n     [1, 680, ['temp', 591]],\n     [1, 680, ['temp', 671]],\n     [2, 700, None],\n     [2, 700, None]]],\n   [600, 600, 600, 600, 600, 866, 866, 591, 671, 700, 700]],\n  ['control: scenario 3',\n   [[[0, 680, None],\n     [0, 680, None],\n     [1, 700, ['perm', 502]],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, None],\n     [1, 700, ['perm', 754]],\n     [1, 700, None],\n     [2, 700, ['temp', 591]],\n     [2, 700, None],\n     [2, 700, None],\n     [2, 700, ['temp', 502]],\n     [3, 680, 'resume'],\n     [3, 680, None]]],\n   [680, 680, 502, 502, 502, 502, 502, 502, 754, 754, 591, 591, 591, 502, 680, 680]]]]\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":"A deterministic bounded teaching model of one thermostat or HVAC controller decision evaluated per sample. Temperatures are integer tenths of a degree Fahrenheit unless stated otherwise. The contract is a stipulated toy convention, not a claim of conformance to any manufacturer, ASHRAE guideline or code. 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-hvac-thermostat-control-setpoint-hold-override-temporary-expiry","generated_at":"2026-09-29T14:51:46.222494+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Residential and light-commercial thermostats make these decisions sample by sample; each defect changes equipment calls in a way that shows up as short cycling, comfort complaints or equipment stress.","root_cause":"The expiry check tests for the permanent hold kind instead of the temporary one.","sha256":"1ffb56531c7938d4dc837b15a15f31d395ad578dc13a6e27f1cbade4c949fa67","title":"Permanent hold expires at the next schedule period while temporary hold persists · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verified":true,"visibility":"public","verification":{"attempt":{"elapsed_ms":44.085,"exit_code":1,"observations":[{"actual":[680,720,720,620,900,700,500,650,650],"check":"regression: hold lifecycle","expected":[680,720,720,620,900,900,500,650,650],"passed":false},{"actual":[500,620],"check":"regression: clamp low","expected":[500,500],"passed":false},{"actual":[700,620,710,710,600],"check":"regression: second temporary hold","expected":[700,620,710,710,600],"passed":true},{"actual":[720,690,690,640],"check":"regression: temporary over permanent","expected":[720,690,690,640],"passed":true},{"actual":[690,690,700,700,700,600,600,856,777,777,777,764,764,600,726,726],"check":"regression: scenario 1","expected":[690,690,700,700,700,600,600,856,777,777,777,764,764,600,726,726],"passed":true},{"actual":[620,540,540,540,863,600,500,500,500],"check":"regression: scenario 2","expected":[620,540,540,540,863,600,500,500,500],"passed":true},{"actual":[657,900,900,900,900,700,641,620],"check":"regression: scenario 3","expected":[657,900,900,900,900,700,641,620],"passed":true},{"actual":[891,891,891,877,877,877,600,600,514],"check":"regression: scenario 4","expected":[891,891,891,877,877,877,600,600,514],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: hold lifecycle\", \"actual\": [680, 720, 720, 620, 900, 700, 500, 650, 650], \"expected\": [680, 720, 720, 620, 900, 900, 500, 650, 650], \"passed\": false}, {\"check\": \"regression: clamp low\", \"actual\": [500, 620], \"expected\": [500, 500], \"passed\": false}, {\"check\": \"regression: second temporary hold\", \"actual\": [700, 620, 710, 710, 600], \"expected\": [700, 620, 710, 710, 600], \"passed\": true}, {\"check\": \"regression: temporary over permanent\", \"actual\": [720, 690, 690, 640], \"expected\": [720, 690, 690, 640], \"passed\": true}, {\"check\": \"regression: scenario 1\", \"actual\": [690, 690, 700, 700, 700, 600, 600, 856, 777, 777, 777, 764, 764, 600, 726, 726], \"expected\": [690, 690, 700, 700, 700, 600, 600, 856, 777, 777, 777, 764, 764, 600, 726, 726], \"passed\": true}, {\"check\": \"regression: scenario 2\", \"actual\": [620, 540, 540, 540, 863, 600, 500, 500, 500], \"expected\": [620, 540, 540, 540, 863, 600, 500, 500, 500], \"passed\": true}, {\"check\": \"regression: scenario 3\", \"actual\": [657, 900, 900, 900, 900, 700, 641, 620], \"expected\": [657, 900, 900, 900, 900, 700, 641, 620], \"passed\": true}, {\"check\": \"regression: scenario 4\", \"actual\": [891, 891, 891, 877, 877, 877, 600, 600, 514], \"expected\": [891, 891, 891, 877, 877, 877, 600, 600, 514], \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":43.083,"exit_code":1,"observations":[{"actual":[680,720,720,720,900,700,500,500,650],"check":"regression: hold lifecycle","expected":[680,720,720,620,900,900,500,650,650],"passed":false},{"actual":[500,620],"check":"regression: clamp low","expected":[500,500],"passed":false},{"actual":[700,700,710,710,710],"check":"regression: second temporary hold","expected":[700,620,710,710,600],"passed":false},{"actual":[720,690,690,690],"check":"regression: temporary over permanent","expected":[720,690,690,640],"passed":false},{"actual":[690,690,700,700,700,600,600,856,777,777,777,764,764,764,726,726],"check":"regression: scenario 1","expected":[690,690,700,700,700,600,600,856,777,777,777,764,764,600,726,726],"passed":false},{"actual":[620,540,540,540,863,863,500,500,500],"check":"regression: scenario 2","expected":[620,540,540,540,863,600,500,500,500],"passed":false},{"actual":[657,900,900,900,900,700,641,641],"check":"regression: scenario 3","expected":[657,900,900,900,900,700,641,620],"passed":false},{"actual":[891,891,891,877,877,877,877,600,514],"check":"regression: scenario 4","expected":[891,891,891,877,877,877,600,600,514],"passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"regression: hold lifecycle\", \"actual\": [680, 720, 720, 720, 900, 700, 500, 500, 650], \"expected\": [680, 720, 720, 620, 900, 900, 500, 650, 650], \"passed\": false}, {\"check\": \"regression: clamp low\", \"actual\": [500, 620], \"expected\": [500, 500], \"passed\": false}, {\"check\": \"regression: second temporary hold\", \"actual\": [700, 700, 710, 710, 710], \"expected\": [700, 620, 710, 710, 600], \"passed\": false}, {\"check\": \"regression: temporary over permanent\", \"actual\": [720, 690, 690, 690], \"expected\": [720, 690, 690, 640], \"passed\": false}, {\"check\": \"regression: scenario 1\", \"actual\": [690, 690, 700, 700, 700, 600, 600, 856, 777, 777, 777, 764, 764, 764, 726, 726], \"expected\": [690, 690, 700, 700, 700, 600, 600, 856, 777, 777, 777, 764, 764, 600, 726, 726], \"passed\": false}, {\"check\": \"regression: scenario 2\", \"actual\": [620, 540, 540, 540, 863, 863, 500, 500, 500], \"expected\": [620, 540, 540, 540, 863, 600, 500, 500, 500], \"passed\": false}, {\"check\": \"regression: scenario 3\", \"actual\": [657, 900, 900, 900, 900, 700, 641, 641], \"expected\": [657, 900, 900, 900, 900, 700, 641, 620], \"passed\": false}, {\"check\": \"regression: scenario 4\", \"actual\": [891, 891, 891, 877, 877, 877, 877, 600, 514], \"expected\": [891, 891, 891, 877, 877, 877, 600, 600, 514], \"passed\": false}], \"passed\": false}\n"}},"member_only":{"stages":["fixed"],"fields":["implementations.fixed","verification.fixed","harness","repair"],"note":"The verified repair, its recorded checks, the repair description, and the scoring harness are available to members."}}