FA-92546 / HVAC thermostat control / Open access
Mixed-air low limit closes the damper below minimum ventilation · case 01
Cold mixed air drives the outdoor damper below its minimum ventilation position.
ROOT CAUSE
The low-limit ramp floors at 0% instead of min_pos.
THE FAILURE
The low-limit ramp floors at 0% instead of min_pos.
Unsuccessful approach: Flooring 10% below the minimum position still under-ventilates.
Case contract
Rooftop economizer per sample; samples are [outdoor, return_air, cooling_call, mixed_air, occupied] in tenths F. Economizing is enabled when outdoor <= high_limit - 2.0 F and outdoor < return - diff, and disabled when outdoor > high_limit or outdoor >= return. Unoccupied: damper 0%. Occupied, enabled and cooling: if mixed air < mat_low the damper steps down 10% but not below min_pos, otherwise it steps up 20% from at least min_pos, capped at 100%. Otherwise the damper sits at min_pos. Return [enabled, damper%] per sample.
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(settings, samples):
enabled = False
pos = 0
out = []
for outdoor, ret, cooling, mixed, occupied in samples:
if enabled and (outdoor > settings['high_limit'] or outdoor >= ret):
enabled = False
elif not enabled and outdoor <= settings['high_limit'] - 20 and outdoor < ret - settings['diff']:
enabled = True
if not occupied:
pos = 0
elif enabled and cooling:
if mixed < settings['mat_low']:
pos = max(0, pos - 10)
else:
pos = min(100, max(settings['min_pos'], pos) + 20)
else:
pos = settings['min_pos']
out.append([enabled, pos])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 3',
[{'high_limit': 700, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[608, 734, True, 493, True],
[609, 741, True, 531, True],
[587, 758, True, 553, True],
[561, 759, False, 498, False],
[530, 741, False, 509, True],
[544, 760, True, 493, True],
[548, 751, True, 583, True],
[512, 705, False, 540, False],
[490, 711, True, 562, True]]],
[[True, 30],
[True, 50],
[True, 70],
[True, 0],
[True, 30],
[True, 30],
[True, 50],
[True, 0],
[True, 50]]],
['regression: scenario 9',
[{'high_limit': 750, 'diff': 0, 'min_pos': 15, 'mat_low': 500},
[[461, 733, True, 609, True],
[430, 743, True, 573, True],
[400, 720, True, 486, False],
[400, 729, True, 547, False],
[430, 728, True, 541, True],
[458, 758, True, 496, True],
[421, 758, False, 529, True],
[400, 729, True, 546, True],
[400, 757, True, 616, False],
[400, 754, False, 597, True],
[409, 725, True, 493, True],
[443, 753, True, 515, True],
[468, 739, True, 564, True],
[464, 745, True, 595, True]]],
[[True, 35],
[True, 55],
[True, 0],
[True, 0],
[True, 35],
[True, 25],
[True, 15],
[True, 35],
[True, 0],
[True, 15],
[True, 15],
[True, 35],
[True, 55],
[True, 75]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[648, 701, True, 604, True],
[640, 708, True, 484, True],
[671, 716, True, 536, True],
[649, 721, True, 616, False],
[640, 749, True, 619, True],
[651, 751, True, 599, True],
[669, 718, False, 504, True],
[638, 704, False, 542, True]]],
[[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]]]],
[['regression: scenario 10',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[641, 705, True, 484, True],
[633, 750, True, 549, True],
[611, 743, False, 559, True],
[596, 715, True, 522, True],
[625, 713, True, 564, True],
[641, 703, True, 560, True],
[618, 717, True, 585, True],
[632, 711, False, 552, True],
[608, 758, True, 602, True]]],
[[False, 30],
[False, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 30],
[True, 50]]],
['regression: scenario 11',
[{'high_limit': 700, 'diff': 40, 'min_pos': 20, 'mat_low': 530},
[[549, 748, True, 480, True],
[542, 746, True, 610, True],
[576, 751, True, 570, True],
[563, 703, True, 541, True],
[568, 754, True, 547, True],
[599, 711, False, 553, True],
[613, 744, True, 570, True],
[605, 755, True, 487, True]]],
[[True, 20], [True, 40], [True, 60], [True, 80], [True, 100], [True, 20], [True, 40], [True, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 0, 'min_pos': 30, 'mat_low': 500},
[[754, 754, False, 483, False],
[716, 749, True, 499, True],
[699, 701, False, 573, True],
[694, 750, True, 534, False],
[704, 716, False, 517, True],
[708, 701, False, 567, True],
[743, 735, True, 609, True],
[775, 747, False, 554, True],
[743, 731, False, 583, False],
[764, 708, False, 507, True],
[795, 743, False, 528, True],
[770, 706, True, 501, True],
[764, 752, False, 578, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]]],
[['regression: scenario 6',
[{'high_limit': 700, 'diff': 0, 'min_pos': 20, 'mat_low': 530},
[[763, 718, True, 585, True],
[759, 718, True, 579, True],
[756, 741, True, 590, True],
[742, 724, True, 569, True],
[765, 717, True, 612, True],
[763, 727, False, 522, True],
[735, 744, True, 480, True],
[715, 733, True, 519, True],
[685, 712, True, 608, True],
[656, 730, True, 484, True],
[659, 758, False, 561, True],
[625, 726, True, 500, True],
[640, 750, True, 506, True],
[640, 711, False, 549, True]]],
[[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20]]],
['regression: scenario 7',
[{'high_limit': 750, 'diff': 0, 'min_pos': 15, 'mat_low': 530},
[[542, 752, True, 490, True],
[555, 756, True, 575, True],
[585, 708, True, 514, True],
[557, 741, False, 502, False],
[556, 709, True, 525, False],
[539, 745, True, 548, True],
[522, 700, False, 488, False],
[494, 759, False, 510, False]]],
[[True, 15], [True, 35], [True, 25], [True, 0], [True, 0], [True, 35], [True, 0], [True, 0]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[678, 721, True, 495, True],
[646, 726, True, 616, True],
[669, 714, True, 600, True],
[630, 728, True, 542, True],
[626, 751, True, 530, False],
[638, 723, False, 607, True],
[654, 706, True, 557, True],
[625, 704, False, 618, True]]],
[[False, 20], [False, 20], [False, 20], [True, 40], [True, 0], [True, 20], [False, 20], [True, 20]]]],
[['regression: scenario 12',
[{'high_limit': 750, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[515, 738, False, 499, True],
[516, 729, True, 505, True],
[539, 717, True, 564, True],
[501, 735, True, 517, True],
[536, 757, False, 600, True],
[568, 725, True, 509, True],
[567, 755, True, 620, False],
[603, 717, True, 614, True]]],
[[True, 20], [True, 20], [True, 40], [True, 30], [True, 20], [True, 20], [True, 0], [True, 40]]],
['regression: scenario 15',
[{'high_limit': 700, 'diff': 40, 'min_pos': 20, 'mat_low': 500},
[[661, 713, True, 604, False],
[637, 744, True, 489, True],
[660, 709, True, 569, True],
[660, 708, True, 541, True],
[642, 744, True, 609, True],
[625, 711, True, 504, True],
[622, 736, True, 536, True],
[659, 732, False, 531, True],
[680, 703, True, 611, True],
[693, 730, True, 557, True],
[712, 724, True, 590, True],
[672, 719, False, 613, True]]],
[[True, 0],
[True, 20],
[True, 40],
[True, 60],
[True, 80],
[True, 100],
[True, 100],
[True, 20],
[True, 40],
[True, 60],
[False, 20],
[True, 20]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['regression: scenario 1',
[{'high_limit': 750, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[505, 738, False, 612, True],
[535, 706, True, 498, True],
[549, 734, False, 604, True],
[558, 729, True, 521, True],
[540, 724, True, 571, True],
[530, 749, True, 592, True],
[507, 715, True, 533, False],
[468, 703, False, 516, True],
[457, 715, False, 563, True],
[444, 708, False, 513, True]]],
[[True, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 0],
[True, 30],
[True, 30],
[True, 30]]]],
[['regression: scenario 16',
[{'high_limit': 700, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[720, 758, False, 495, False],
[728, 726, False, 492, True],
[730, 722, True, 522, True],
[691, 711, False, 558, True],
[668, 702, True, 489, True],
[681, 730, True, 486, True],
[642, 726, True, 605, True],
[626, 742, True, 521, True],
[602, 731, False, 496, True],
[568, 720, True, 556, False],
[600, 750, True, 505, True],
[575, 740, False, 534, True],
[567, 714, True, 560, True],
[573, 749, True, 508, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 40],
[True, 30],
[True, 0],
[True, 30],
[True, 30],
[True, 50],
[True, 40]]],
['regression: scenario 18',
[{'high_limit': 650, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[593, 759, True, 602, True],
[606, 742, True, 509, True],
[581, 708, False, 586, True],
[617, 733, False, 513, True],
[643, 718, True, 503, True],
[662, 746, False, 598, True],
[684, 723, False, 619, False],
[714, 711, False, 612, False],
[704, 749, False, 523, True]]],
[[True, 35],
[True, 25],
[True, 15],
[True, 15],
[True, 15],
[False, 15],
[False, 0],
[False, 0],
[False, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[684, 714, False, 579, True],
[715, 719, False, 481, True],
[737, 759, True, 615, False],
[715, 756, True, 609, False],
[743, 742, True, 491, True],
[776, 713, False, 491, True],
[798, 748, False, 566, True],
[800, 751, True, 547, True],
[800, 736, True, 486, True],
[760, 727, False, 570, True]]],
[[True, 20],
[True, 20],
[True, 0],
[True, 0],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20]]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: scenario 3 | [[True, 0], [True, 50], [True, 70], [True, 0], [True, 30], [True, 20], [True, 50], [True, 0], [True, 50]] | [[True, 30], [True, 50], [True, 70], [True, 0], [True, 30], [True, 30], [True, 50], [True, 0], [True, 50]] | Failed |
| regression: scenario 9 | [[True, 35], [True, 55], [True, 0], [True, 0], [True, 35], [True, 25], [True, 15], [True, 35], [True, 0], [True, 15], [True, 5], [True, 35], [True, 55], [True, 75]] | [[True, 35], [True, 55], [True, 0], [True, 0], [True, 35], [True, 25], [True, 15], [True, 35], [True, 0], [True, 15], [True, 15], [True, 35], [True, 55], [True, 75]] | Failed |
| control: economizer cycle | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | Passed |
| control: enable band edges | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | Passed |
| control: cold mixed air | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | Passed |
| control: night cooling call | [[True, 0], [True, 40]] | [[True, 0], [True, 40]] | Passed |
| control: near return disable | [[True, 35], [True, 55], [True, 75], [False, 15]] | [[True, 35], [True, 55], [True, 75], [False, 15]] | Passed |
| control: scenario 1 | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | Passed |
SHA-256 / 8a3c2f8e393b34d5f0f54ed013264bafd373689a3715a1121d0512d0154910a8
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(settings, samples):
enabled = False
pos = 0
out = []
for outdoor, ret, cooling, mixed, occupied in samples:
if enabled and (outdoor > settings['high_limit'] or outdoor >= ret):
enabled = False
elif not enabled and outdoor <= settings['high_limit'] - 20 and outdoor < ret - settings['diff']:
enabled = True
if not occupied:
pos = 0
elif enabled and cooling:
if mixed < settings['mat_low']:
pos = max(settings['min_pos'] - 10, pos - 10)
else:
pos = min(100, max(settings['min_pos'], pos) + 20)
else:
pos = settings['min_pos']
out.append([enabled, pos])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 3',
[{'high_limit': 700, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[608, 734, True, 493, True],
[609, 741, True, 531, True],
[587, 758, True, 553, True],
[561, 759, False, 498, False],
[530, 741, False, 509, True],
[544, 760, True, 493, True],
[548, 751, True, 583, True],
[512, 705, False, 540, False],
[490, 711, True, 562, True]]],
[[True, 30],
[True, 50],
[True, 70],
[True, 0],
[True, 30],
[True, 30],
[True, 50],
[True, 0],
[True, 50]]],
['regression: scenario 9',
[{'high_limit': 750, 'diff': 0, 'min_pos': 15, 'mat_low': 500},
[[461, 733, True, 609, True],
[430, 743, True, 573, True],
[400, 720, True, 486, False],
[400, 729, True, 547, False],
[430, 728, True, 541, True],
[458, 758, True, 496, True],
[421, 758, False, 529, True],
[400, 729, True, 546, True],
[400, 757, True, 616, False],
[400, 754, False, 597, True],
[409, 725, True, 493, True],
[443, 753, True, 515, True],
[468, 739, True, 564, True],
[464, 745, True, 595, True]]],
[[True, 35],
[True, 55],
[True, 0],
[True, 0],
[True, 35],
[True, 25],
[True, 15],
[True, 35],
[True, 0],
[True, 15],
[True, 15],
[True, 35],
[True, 55],
[True, 75]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[648, 701, True, 604, True],
[640, 708, True, 484, True],
[671, 716, True, 536, True],
[649, 721, True, 616, False],
[640, 749, True, 619, True],
[651, 751, True, 599, True],
[669, 718, False, 504, True],
[638, 704, False, 542, True]]],
[[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]]]],
[['regression: scenario 10',
[{'high_limit': 650, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[641, 705, True, 484, True],
[633, 750, True, 549, True],
[611, 743, False, 559, True],
[596, 715, True, 522, True],
[625, 713, True, 564, True],
[641, 703, True, 560, True],
[618, 717, True, 585, True],
[632, 711, False, 552, True],
[608, 758, True, 602, True]]],
[[False, 30],
[False, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 30],
[True, 50]]],
['regression: scenario 11',
[{'high_limit': 700, 'diff': 40, 'min_pos': 20, 'mat_low': 530},
[[549, 748, True, 480, True],
[542, 746, True, 610, True],
[576, 751, True, 570, True],
[563, 703, True, 541, True],
[568, 754, True, 547, True],
[599, 711, False, 553, True],
[613, 744, True, 570, True],
[605, 755, True, 487, True]]],
[[True, 20], [True, 40], [True, 60], [True, 80], [True, 100], [True, 20], [True, 40], [True, 30]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 0, 'min_pos': 30, 'mat_low': 500},
[[754, 754, False, 483, False],
[716, 749, True, 499, True],
[699, 701, False, 573, True],
[694, 750, True, 534, False],
[704, 716, False, 517, True],
[708, 701, False, 567, True],
[743, 735, True, 609, True],
[775, 747, False, 554, True],
[743, 731, False, 583, False],
[764, 708, False, 507, True],
[795, 743, False, 528, True],
[770, 706, True, 501, True],
[764, 752, False, 578, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30]]]],
[['regression: scenario 6',
[{'high_limit': 700, 'diff': 0, 'min_pos': 20, 'mat_low': 530},
[[763, 718, True, 585, True],
[759, 718, True, 579, True],
[756, 741, True, 590, True],
[742, 724, True, 569, True],
[765, 717, True, 612, True],
[763, 727, False, 522, True],
[735, 744, True, 480, True],
[715, 733, True, 519, True],
[685, 712, True, 608, True],
[656, 730, True, 484, True],
[659, 758, False, 561, True],
[625, 726, True, 500, True],
[640, 750, True, 506, True],
[640, 711, False, 549, True]]],
[[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20],
[True, 20]]],
['regression: scenario 7',
[{'high_limit': 750, 'diff': 0, 'min_pos': 15, 'mat_low': 530},
[[542, 752, True, 490, True],
[555, 756, True, 575, True],
[585, 708, True, 514, True],
[557, 741, False, 502, False],
[556, 709, True, 525, False],
[539, 745, True, 548, True],
[522, 700, False, 488, False],
[494, 759, False, 510, False]]],
[[True, 15], [True, 35], [True, 25], [True, 0], [True, 0], [True, 35], [True, 0], [True, 0]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 650, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[678, 721, True, 495, True],
[646, 726, True, 616, True],
[669, 714, True, 600, True],
[630, 728, True, 542, True],
[626, 751, True, 530, False],
[638, 723, False, 607, True],
[654, 706, True, 557, True],
[625, 704, False, 618, True]]],
[[False, 20], [False, 20], [False, 20], [True, 40], [True, 0], [True, 20], [False, 20], [True, 20]]]],
[['regression: scenario 12',
[{'high_limit': 750, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[515, 738, False, 499, True],
[516, 729, True, 505, True],
[539, 717, True, 564, True],
[501, 735, True, 517, True],
[536, 757, False, 600, True],
[568, 725, True, 509, True],
[567, 755, True, 620, False],
[603, 717, True, 614, True]]],
[[True, 20], [True, 20], [True, 40], [True, 30], [True, 20], [True, 20], [True, 0], [True, 40]]],
['regression: scenario 15',
[{'high_limit': 700, 'diff': 40, 'min_pos': 20, 'mat_low': 500},
[[661, 713, True, 604, False],
[637, 744, True, 489, True],
[660, 709, True, 569, True],
[660, 708, True, 541, True],
[642, 744, True, 609, True],
[625, 711, True, 504, True],
[622, 736, True, 536, True],
[659, 732, False, 531, True],
[680, 703, True, 611, True],
[693, 730, True, 557, True],
[712, 724, True, 590, True],
[672, 719, False, 613, True]]],
[[True, 0],
[True, 20],
[True, 40],
[True, 60],
[True, 80],
[True, 100],
[True, 100],
[True, 20],
[True, 40],
[True, 60],
[False, 20],
[True, 20]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['regression: scenario 1',
[{'high_limit': 750, 'diff': 20, 'min_pos': 30, 'mat_low': 500},
[[505, 738, False, 612, True],
[535, 706, True, 498, True],
[549, 734, False, 604, True],
[558, 729, True, 521, True],
[540, 724, True, 571, True],
[530, 749, True, 592, True],
[507, 715, True, 533, False],
[468, 703, False, 516, True],
[457, 715, False, 563, True],
[444, 708, False, 513, True]]],
[[True, 30],
[True, 30],
[True, 30],
[True, 50],
[True, 70],
[True, 90],
[True, 0],
[True, 30],
[True, 30],
[True, 30]]]],
[['regression: scenario 16',
[{'high_limit': 700, 'diff': 40, 'min_pos': 30, 'mat_low': 530},
[[720, 758, False, 495, False],
[728, 726, False, 492, True],
[730, 722, True, 522, True],
[691, 711, False, 558, True],
[668, 702, True, 489, True],
[681, 730, True, 486, True],
[642, 726, True, 605, True],
[626, 742, True, 521, True],
[602, 731, False, 496, True],
[568, 720, True, 556, False],
[600, 750, True, 505, True],
[575, 740, False, 534, True],
[567, 714, True, 560, True],
[573, 749, True, 508, True]]],
[[False, 0],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[False, 30],
[True, 50],
[True, 40],
[True, 30],
[True, 0],
[True, 30],
[True, 30],
[True, 50],
[True, 40]]],
['regression: scenario 18',
[{'high_limit': 650, 'diff': 40, 'min_pos': 15, 'mat_low': 530},
[[593, 759, True, 602, True],
[606, 742, True, 509, True],
[581, 708, False, 586, True],
[617, 733, False, 513, True],
[643, 718, True, 503, True],
[662, 746, False, 598, True],
[684, 723, False, 619, False],
[714, 711, False, 612, False],
[704, 749, False, 523, True]]],
[[True, 35],
[True, 25],
[True, 15],
[True, 15],
[True, 15],
[False, 15],
[False, 0],
[False, 0],
[False, 15]]],
['control: economizer cycle',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[600, 740, True, 560, True],
[600, 740, True, 560, True],
[600, 740, True, 520, True],
[690, 740, True, 560, True],
[705, 740, True, 560, True],
[600, 740, False, 560, True],
[600, 740, True, 560, False]]],
[[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]]],
['control: enable band edges',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[681, 740, True, 560, True],
[680, 700, True, 560, True],
[680, 701, True, 560, True],
[700, 750, True, 560, True],
[710, 750, True, 560, True]]],
[[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]]],
['control: cold mixed air',
[{'high_limit': 700, 'diff': 0, 'min_pos': 30, 'mat_low': 530},
[[450, 720, True, 600, True],
[450, 720, True, 600, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True],
[450, 720, True, 500, True]]],
[[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]]],
['control: night cooling call',
[{'high_limit': 700, 'diff': 20, 'min_pos': 20, 'mat_low': 530},
[[550, 740, True, 560, False], [550, 740, True, 560, True]]],
[[True, 0], [True, 40]]],
['control: near return disable',
[{'high_limit': 750, 'diff': 20, 'min_pos': 15, 'mat_low': 500},
[[700, 730, True, 560, True],
[715, 730, True, 560, True],
[725, 730, True, 560, True],
[730, 730, True, 560, True]]],
[[True, 35], [True, 55], [True, 75], [False, 15]]],
['control: scenario 1',
[{'high_limit': 750, 'diff': 0, 'min_pos': 20, 'mat_low': 500},
[[684, 714, False, 579, True],
[715, 719, False, 481, True],
[737, 759, True, 615, False],
[715, 756, True, 609, False],
[743, 742, True, 491, True],
[776, 713, False, 491, True],
[798, 748, False, 566, True],
[800, 751, True, 547, True],
[800, 736, True, 486, True],
[760, 727, False, 570, True]]],
[[True, 20],
[True, 20],
[True, 0],
[True, 0],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20],
[False, 20]]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: scenario 3 | [[True, 20], [True, 50], [True, 70], [True, 0], [True, 30], [True, 20], [True, 50], [True, 0], [True, 50]] | [[True, 30], [True, 50], [True, 70], [True, 0], [True, 30], [True, 30], [True, 50], [True, 0], [True, 50]] | Failed |
| regression: scenario 9 | [[True, 35], [True, 55], [True, 0], [True, 0], [True, 35], [True, 25], [True, 15], [True, 35], [True, 0], [True, 15], [True, 5], [True, 35], [True, 55], [True, 75]] | [[True, 35], [True, 55], [True, 0], [True, 0], [True, 35], [True, 25], [True, 15], [True, 35], [True, 0], [True, 15], [True, 15], [True, 35], [True, 55], [True, 75]] | Failed |
| control: economizer cycle | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | [[True, 40], [True, 60], [True, 50], [True, 70], [False, 20], [True, 20], [True, 0]] | Passed |
| control: enable band edges | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | [[False, 20], [False, 20], [True, 40], [True, 60], [False, 20]] | Passed |
| control: cold mixed air | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | [[True, 50], [True, 70], [True, 60], [True, 50], [True, 40], [True, 30]] | Passed |
| control: night cooling call | [[True, 0], [True, 40]] | [[True, 0], [True, 40]] | Passed |
| control: near return disable | [[True, 35], [True, 55], [True, 75], [False, 15]] | [[True, 35], [True, 55], [True, 75], [False, 15]] | Passed |
| control: scenario 1 | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | [[True, 40], [True, 30], [True, 50], [True, 0], [True, 40], [True, 60], [True, 20], [True, 20]] | Passed |
SHA-256 / 7f35ea0867829d60d66e7dd2bf867305a4e03fa6a11acb995f797c5a3e23b3a4
HELD IN THE MEMBER ARCHIVE
The verified repair and its recorded checks are member-only.
This mechanism has 8 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.
Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.
Member access is invitation-based. Sign in with your invited account to inspect the repair.
Sign in to the archive ↗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:46.674765+00:00.
Case digest / 60236d874ce14d05dd4117fd1e12aaef42c45a3f416c96240e2ca49b84630062