FAILURE MAP
← Case archive

FA-92341 / HVAC thermostat control / Open access

Auto changeover squeezes the deadband by lowering the heating setpoint · case 01

Raising cooling close to heating silently drops the heating setpoint the user just chose.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The minimum-deadband rule moves the heating setpoint down instead of the cooling setpoint up.

VERIFIED REPAIR

Enforce the deadband by raising the cooling setpoint to heat_sp + 3.0 F.

Unsuccessful approach: Adding the deadband to the existing cooling setpoint overshoots when the gap was already partly open.

Case contract

Auto heat/cool thermostat sampled once per minute (temps in tenths F). If the cooling setpoint is less than 3.0 F above the heating setpoint it is pushed up to heat_sp + 30 (the heating setpoint wins). Heating starts below heat_sp - 1.0 F and ends at temp >= heat_sp; cooling starts above cool_sp + 1.0 F and ends at temp <= cool_sp. After a heating call ends, cooling may not start until 5 minutes later, and vice versa. Return heat, cool or idle per minute.

Why this case matters

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.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        heat_sp = cool_sp - 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 4',
   [680, 710, [747, 771, 775, 769, 770, 766, 790, 790, 790, 789, 781]],
   ['cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: scenario 6',
   [680, 700, [757, 740, 724, 721, 702, 699, 724, 717, 742, 721, 723, 700]],
   ['cool', 'cool', 'cool', 'cool', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'idle']],
  ['regression: scenario 8',
   [680, 690, [726, 722, 736, 726, 744, 763, 778, 789, 774, 760, 781, 786, 790, 783, 770, 770, 750]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 3',
   [680, 690, [720, 710, 717, 702, 708, 715, 732, 753, 772, 753, 757]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 13',
   [680, 700, [732, 729, 742, 749, 733, 737, 714, 714, 716, 713, 725, 733, 737, 726, 729, 723, 745]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 8',
   [680, 690, [656, 635, 650, 663, 673, 651, 644, 644, 623, 630, 643, 655]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']],
  ['regression: scenario 22',
   [680, 690, [707, 687, 693, 697, 709, 698, 723, 702, 723, 731, 749, 743, 742, 754, 733, 715, 708]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: heat then locked cool['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool']['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']Failed
regression: deadband squeeze['cool', 'cool', 'cool', 'cool', 'cool']['idle', 'cool', 'cool', 'idle', 'idle']Failed
control: exact thresholds['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']Passed
control: short heat cycle then hot['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']Passed
regression: scenario 1['idle', 'idle', 'cool', 'cool', 'idle', 'cool', 'idle', 'idle', 'idle', 'idle', 'idle']['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']Failed
regression: scenario 2['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']Passed
control: scenario 3['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']Passed
control: scenario 4['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'idle', 'idle', 'cool', 'cool']['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'idle', 'idle', 'cool', 'cool']Passed

SHA-256 / aef35a8798735b1b1d3c4ce048aac6a27337288f7bc5776480d63f1f8612347b

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = cool_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 4',
   [680, 710, [747, 771, 775, 769, 770, 766, 790, 790, 790, 789, 781]],
   ['cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: scenario 6',
   [680, 700, [757, 740, 724, 721, 702, 699, 724, 717, 742, 721, 723, 700]],
   ['cool', 'cool', 'cool', 'cool', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'idle']],
  ['regression: scenario 8',
   [680, 690, [726, 722, 736, 726, 744, 763, 778, 789, 774, 760, 781, 786, 790, 783, 770, 770, 750]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 3',
   [680, 690, [720, 710, 717, 702, 708, 715, 732, 753, 772, 753, 757]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 13',
   [680, 700, [732, 729, 742, 749, 733, 737, 714, 714, 716, 713, 725, 733, 737, 726, 729, 723, 745]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 8',
   [680, 690, [656, 635, 650, 663, 673, 651, 644, 644, 623, 630, 643, 655]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']],
  ['regression: scenario 22',
   [680, 690, [707, 687, 693, 697, 709, 698, 723, 702, 723, 731, 749, 743, 742, 754, 733, 715, 708]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: heat then locked cool['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']Failed
regression: deadband squeeze['idle', 'idle', 'idle', 'idle', 'idle']['idle', 'cool', 'cool', 'idle', 'idle']Failed
control: exact thresholds['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']Passed
control: short heat cycle then hot['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']Passed
regression: scenario 1['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']Passed
regression: scenario 2['idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']Failed
control: scenario 3['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']Passed
control: scenario 4['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'idle', 'idle', 'cool', 'cool']['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'idle', 'idle', 'cool', 'cool']Passed

SHA-256 / 4dd2b39f0691e471b6f4df1475b25ecb50709b16f6c68ffc9aa3b0767fe05720

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(heat_sp, cool_sp, temps):
    diff = 10
    if cool_sp - heat_sp < 30:
        cool_sp = heat_sp + 30
    state = 'idle'
    last_heat_end = None
    last_cool_end = None
    out = []
    for minute, temp in enumerate(temps):
        if state == 'heat' and temp >= heat_sp:
            state = 'idle'
            last_heat_end = minute
        elif state == 'cool' and temp <= cool_sp:
            state = 'idle'
            last_cool_end = minute
        if state == 'idle':
            if temp < heat_sp - diff and (last_cool_end is None or minute - last_cool_end >= 5):
                state = 'heat'
            elif temp > cool_sp + diff and (last_heat_end is None or minute - last_heat_end >= 5):
                state = 'cool'
        out.append(state)
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: scenario 1',
   [680, 690, [693, 687, 709, 709, 685, 704, 684, 680, 686, 687, 679]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['regression: scenario 2',
   [680, 700, [681, 686, 698, 703, 724, 744, 752, 732, 755, 742, 762, 772]],
   ['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['control: scenario 3',
   [680,
    740,
    [649,
     667,
     643,
     622,
     637,
     657,
     634,
     649,
     674,
     654,
     672,
     675,
     698,
     687,
     666,
     654,
     670,
     645,
     654,
     637,
     651,
     672]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 4',
   [680, 720, [678, 693, 708, 706, 691, 698, 702, 725, 720, 725, 717, 703, 717, 731, 714, 728, 740, 735]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'cool',
    'cool']]],
 [['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680, 700, [687, 695, 710, 721, 712, 724, 736, 738, 737, 740, 748]],
   ['idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['control: scenario 2',
   [680, 710, [674, 681, 688, 699, 718, 698, 716, 709, 691, 672, 678, 701, 724, 699, 683, 698, 691, 701]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['regression: scenario 3',
   [680, 690, [686, 679, 704, 696, 678, 658, 635, 648, 643, 657, 650, 661, 672, 691, 687, 685, 708, 687]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle']],
  ['control: scenario 4',
   [680, 710, [747, 771, 775, 769, 770, 766, 790, 790, 790, 789, 781]],
   ['cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']]],
 [['regression: scenario 6',
   [680, 700, [757, 740, 724, 721, 702, 699, 724, 717, 742, 721, 723, 700]],
   ['cool', 'cool', 'cool', 'cool', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'idle']],
  ['regression: scenario 8',
   [680, 690, [726, 722, 736, 726, 744, 763, 778, 789, 774, 760, 781, 786, 790, 783, 770, 770, 750]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 710, [758, 778, 769, 788, 780, 787, 790, 773, 789, 766, 785, 779, 790, 790, 790, 777, 790]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['control: scenario 2',
   [680, 740, [646, 654, 672, 672, 692, 717, 736, 759, 769, 790, 790]],
   ['heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']]],
 [['regression: scenario 3',
   [680, 690, [720, 710, 717, 702, 708, 715, 732, 753, 772, 753, 757]],
   ['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool']],
  ['regression: scenario 13',
   [680, 700, [732, 729, 742, 749, 733, 737, 714, 714, 716, 713, 725, 733, 737, 726, 729, 723, 745]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['regression: scenario 1',
   [680,
    690,
    [671, 646, 636, 635, 620, 625, 635, 653, 659, 636, 645, 633, 620, 628, 620, 638, 653, 655, 666, 678]],
   ['idle',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat']],
  ['control: scenario 2',
   [680, 740, [655, 638, 663, 641, 622, 636, 628, 621, 621, 620, 635, 632, 644, 651, 656, 675, 687]],
   ['heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'heat',
    'idle']]],
 [['regression: scenario 8',
   [680, 690, [656, 635, 650, 663, 673, 651, 644, 644, 623, 630, 643, 655]],
   ['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']],
  ['regression: scenario 22',
   [680, 690, [707, 687, 693, 697, 709, 698, 723, 702, 723, 731, 749, 743, 742, 754, 733, 715, 708]],
   ['idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'idle',
    'cool',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle']],
  ['regression: heat then locked cool',
   [680, 700, [650, 660, 690, 700, 712, 725, 725, 725, 715, 710]],
   ['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']],
  ['control: exact thresholds',
   [680, 720, [670, 669, 680, 731, 730, 720, 721]],
   ['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']],
  ['control: short heat cycle then hot',
   [680, 760, [660, 685, 790, 790, 790, 790, 790, 790]],
   ['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']],
  ['regression: deadband squeeze',
   [700, 705, [740, 741, 735, 730, 729]],
   ['idle', 'cool', 'cool', 'idle', 'idle']],
  ['control: scenario 1',
   [680, 720, [774, 779, 785, 790, 790, 781, 778, 777, 770, 767, 743, 730, 708, 690]],
   ['cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'idle',
    'idle']],
  ['control: scenario 2',
   [680,
    710,
    [701,
     679,
     702,
     721,
     727,
     748,
     743,
     720,
     741,
     738,
     762,
     769,
     758,
     740,
     744,
     725,
     748,
     728,
     742,
     755,
     769]],
   ['idle',
    'idle',
    'idle',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool',
    'cool']]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression: heat then locked cool['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']['heat', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'idle']Passed
regression: deadband squeeze['idle', 'cool', 'cool', 'idle', 'idle']['idle', 'cool', 'cool', 'idle', 'idle']Passed
control: exact thresholds['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']['idle', 'heat', 'idle', 'idle', 'idle', 'idle', 'idle']Passed
control: short heat cycle then hot['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']['heat', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'cool']Passed
regression: scenario 1['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle']Passed
regression: scenario 2['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']['idle', 'idle', 'idle', 'idle', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool', 'cool']Passed
control: scenario 3['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']['heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'idle', 'idle', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat', 'heat']Passed
control: scenario 4['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'idle', 'idle', 'cool', 'cool']['idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'idle', 'cool', 'idle', 'idle', 'cool', 'cool']Passed

SHA-256 / 08cef424efbeab7cd25a9356b6176c1ee9598dde826950b50fdb5aa4fc6a99e2

Verification & scope

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.

Observations recorded using Python 3.12.14 at 2026-09-29T14:51:44.901549+00:00.

Case digest / 6b56e3660143149dda6a27ee061aaf4b90f6fdbc5b80f59205b8c82b0332bcb8