FA-92646 / HVAC thermostat control / Open access
Control temperature is lost when every sensor is invalid · case 01
A momentary wireless dropout makes the thermostat report no temperature.
ROOT CAUSE
The previous value is cleared when no reading is valid.
THE FAILURE
The previous value is cleared when no reading is valid.
Unsuccessful approach: Substituting a fixed default before the first valid reading invents a temperature.
Case contract
Thermostat with remote room sensors; each sample is a list of [room, reading tenths F or None, occupied]. Readings outside 32.0-100.0 F or None are invalid. The control temperature is the mean of valid readings from occupied rooms, or of all valid readings if no occupied room has one, rounded half up to a tenth; if nothing is valid the previous control temperature is held (None initially). Return the control temperature 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(samples):
last = None
out = []
for readings in samples:
valid = [(t, occ) for room, t, occ in readings if t is not None and 320 <= t <= 1000]
pool = [t for t, occ in valid if occ] or [t for t, occ in valid]
if pool:
last = (2 * sum(pool) + len(pool)) // (2 * len(pool))
else:
last = None
out.append(last)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 611, False], ['bed', 799, True]],
[['living', 697, True]],
[['living', 697, False], ['bed', 609, True]],
[['living', 751, False], ['bed', 736, True], ['den', 616, True]],
[['living', 1100, False], ['bed', 686, True]],
[['living', 798, False], ['bed', 743, True], ['den', 646, False], ['office', 720, False]],
[['living', 668, False], ['bed', None, True], ['den', None, True], ['office', 795, False]]]],
[799, 697, 609, 676, 686, 743, 732]],
['regression: scenario 2',
[[[['living', 602, False], ['bed', 665, False], ['den', 790, False], ['office', 677, False]],
[['living', 638, False]],
[['living', 602, False], ['bed', 750, True]],
[['living', None, True], ['bed', 250, False], ['den', 250, True]],
[['living', 744, True], ['bed', 711, True], ['den', 676, False], ['office', 640, True]],
[['living', 771, False], ['bed', 646, True]],
[['living', 608, False]],
[['living', 663, True], ['bed', 793, True], ['den', 250, True], ['office', 743, False]]]],
[684, 638, 750, 750, 698, 646, 608, 728]],
['regression: scenario 3',
[[[['living', 677, True]],
[['living', 624, True], ['bed', 620, True]],
[['living', 605, True], ['bed', 250, False]],
[['living', 764, False]],
[['living', 612, False], ['bed', None, True], ['den', 664, True]],
[['living', None, True], ['bed', 250, False], ['den', 250, True]],
[['living', 745, True], ['bed', None, False]],
[['living', 665, True]]]],
[677, 622, 605, 764, 664, 664, 745, 665]],
['control: scenario 4',
[[[['living', 600, True]],
[['living', 742, True], ['bed', 607, False], ['den', 638, True], ['office', 791, True]],
[['living', 710, False], ['bed', 602, True], ['den', 716, False]],
[['living', 607, False], ['bed', 735, True], ['den', 739, False], ['office', 723, True]],
[['living', 621, True], ['bed', 625, False], ['den', 688, True], ['office', 627, False]],
[['living', 677, False], ['bed', 775, True]],
[['living', 1100, False], ['bed', None, False], ['den', 687, False]]]],
[600, 724, 602, 729, 655, 775, 687]]],
[['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['regression: scenario 48',
[[[['living', 1100, False]],
[['living', 686, False], ['bed', 704, True]],
[['living', 632, False]],
[['living', 620, False], ['bed', 693, False]],
[['living', 1100, True], ['bed', 656, False], ['den', None, False], ['office', 1100, False]]]],
[None, 704, 632, 657, 656]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 631, False], ['bed', None, True]],
[['living', 674, True], ['bed', 788, True]],
[['living', 629, False]],
[['living', 617, False], ['bed', 636, True], ['den', 622, True]],
[['living', 602, True], ['bed', 659, True], ['den', 604, False]],
[['living', 641, True], ['bed', 600, True]],
[['living', 781, True]],
[['living', 615, True], ['bed', 788, True], ['den', 658, True]]]],
[631, 731, 629, 629, 631, 621, 781, 687]],
['control: scenario 2',
[[[['living', 675, True], ['bed', 724, False], ['den', 793, True]],
[['living', 711, False], ['bed', 720, False]],
[['living', 722, False], ['bed', 1100, False], ['den', 606, False], ['office', 625, True]],
[['living', 657, False], ['bed', 619, True], ['den', 800, True], ['office', 629, True]],
[['living', 765, True], ['bed', 726, True]],
[['living', 785, False], ['bed', 626, False]]]],
[734, 716, 625, 683, 746, 706]],
['regression: scenario 3',
[[[['living', 795, False]],
[['living', 769, False], ['bed', 675, False]],
[['living', 640, False]],
[['living', None, False]],
[['living', None, False], ['bed', 790, True], ['den', 684, False], ['office', 789, True]],
[['living', 722, True], ['bed', 750, True], ['den', 250, True]],
[['living', 663, True], ['bed', 670, True]],
[['living', 628, False], ['bed', 687, False], ['den', 635, False]]]],
[795, 722, 640, 640, 790, 736, 667, 650]]],
[['regression: scenario 4',
[[[['living', 647, True], ['bed', 693, False]],
[['living', None, False], ['bed', 1100, False], ['den', None, True], ['office', None, False]],
[['living', 791, False], ['bed', 250, False], ['den', 778, True]],
[['living', 650, False], ['bed', None, True]],
[['living', 1100, True], ['bed', 680, False], ['den', 250, False]],
[['living', 700, True], ['bed', 1100, True], ['den', 712, False]],
[['living', 662, True], ['bed', 670, True]]]],
[647, 647, 778, 650, 680, 700, 666]],
['regression: scenario 14',
[[[['living', None, True]],
[['living', 603, True], ['bed', 671, False], ['den', 792, False], ['office', 751, False]],
[['living', 693, False]],
[['living', 613, True], ['bed', None, True], ['den', None, False], ['office', None, True]],
[['living', 719, False]],
[['living', 1100, False], ['bed', 610, False], ['den', 674, False], ['office', 667, True]],
[['living', 667, True]],
[['living', 605, True], ['bed', 623, True], ['den', 783, False], ['office', 712, True]]]],
[None, 603, 693, 613, 719, 667, 667, 647]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 627, False], ['bed', 693, True], ['den', 769, True], ['office', 704, True]],
[['living', 738, True], ['bed', 638, True]],
[['living', 760, True], ['bed', 696, True], ['den', 683, False]],
[['living', 771, False], ['bed', 1100, False]],
[['living', 686, True]],
[['living', 734, True]],
[['living', None, True], ['bed', 649, False], ['den', 767, False]],
[['living', 722, True]],
[['living', 686, True], ['bed', 715, False], ['den', 617, False], ['office', 250, False]],
[['living', 790, True]]]],
[722, 688, 728, 771, 686, 734, 708, 722, 686, 790]],
['control: scenario 2',
[[[['living', 663, False], ['bed', 781, True], ['den', 775, False], ['office', 722, True]],
[['living', 655, False]],
[['living', 777, False], ['bed', 790, True], ['den', 637, False], ['office', 682, False]],
[['living', 250, False], ['bed', 701, True], ['den', 745, True], ['office', 626, False]],
[['living', 619, False]],
[['living', 619, True], ['bed', 710, True]],
[['living', 732, False]],
[['living', 772, False]],
[['living', None, True], ['bed', 664, True], ['den', 728, True], ['office', 783, False]],
[['living', 1100, True], ['bed', 734, True], ['den', 657, False], ['office', 601, False]]]],
[752, 655, 790, 723, 619, 665, 732, 772, 696, 734]]],
[['regression: scenario 4',
[[[['living', 614, False]],
[['living', 616, True], ['bed', None, True], ['den', 783, True]],
[['living', 250, True], ['bed', 250, True]],
[['living', None, True]],
[['living', 663, True]],
[['living', 656, True], ['bed', 250, True], ['den', 679, True]],
[['living', 799, True], ['bed', 707, True], ['den', 751, True]],
[['living', None, True]]]],
[614, 700, 700, 700, 663, 668, 752, 752]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 782, False], ['bed', 708, True], ['den', 678, True]],
[['living', 772, False], ['bed', 795, True]],
[['living', 744, True], ['bed', 616, True], ['den', 694, True]],
[['living', 790, False], ['bed', 733, True]],
[['living', 722, True], ['bed', 701, False]],
[['living', 699, False], ['bed', 636, False]]]],
[693, 795, 685, 733, 722, 668]],
['regression: scenario 2',
[[[['living', 675, False], ['bed', 659, True], ['den', 738, False], ['office', 1100, False]],
[['living', 250, True], ['bed', 649, True]],
[['living', 604, True]],
[['living', 629, True], ['bed', None, True], ['den', 610, True]],
[['living', 633, False], ['bed', None, True], ['den', 778, True], ['office', 795, False]],
[['living', None, False]]]],
[659, 649, 604, 620, 778, 778]],
['control: scenario 3',
[[[['living', 687, True], ['bed', 761, True]],
[['living', 719, False], ['bed', None, False], ['den', 774, True]],
[['living', 723, False]],
[['living', 649, False]],
[['living', 649, False], ['bed', 705, True]],
[['living', 699, True]]]],
[724, 774, 723, 649, 705, 699]]],
[['regression: scenario 4',
[[[['living', 1100, False], ['bed', 686, True], ['den', 660, False]],
[['living', 716, True], ['bed', 689, True], ['den', 723, False]],
[['living', 729, True]],
[['living', 660, False], ['bed', 657, True]],
[['living', 765, True]],
[['living', 250, False]],
[['living', None, True], ['bed', 707, True], ['den', 646, True]],
[['living', 766, False], ['bed', 703, False]],
[['living', 617, True], ['bed', 608, True], ['den', 250, True]],
[['living', 603, False], ['bed', 746, True], ['den', None, True], ['office', 695, False]]]],
[686, 703, 729, 657, 765, 765, 677, 735, 613, 746]],
['regression: scenario 40',
[[[['living', None, True]],
[['living', 665, False], ['bed', 608, False], ['den', 792, True], ['office', 620, False]],
[['living', 647, False]],
[['living', 755, True], ['bed', 690, False], ['den', 709, True], ['office', None, True]],
[['living', 691, True], ['bed', 698, True], ['den', 700, False], ['office', 250, False]],
[['living', 750, True], ['bed', 722, True], ['den', 783, True], ['office', 665, False]],
[['living', 644, False], ['bed', 639, False], ['den', 710, False]],
[['living', 629, False]],
[['living', 644, True], ['bed', 612, False], ['den', 762, False], ['office', 767, False]]]],
[None, 792, 647, 732, 695, 752, 664, 629, 644]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['regression: scenario 1',
[[[['living', 1100, True]],
[['living', 634, False], ['bed', 740, False], ['den', None, True]],
[['living', 1100, True]],
[['living', 618, False], ['bed', 787, False], ['den', 754, False]],
[['living', 673, False], ['bed', 795, True], ['den', 612, False], ['office', 721, True]]]],
[None, 687, 687, 720, 758]],
['regression: scenario 2',
[[[['living', 733, True], ['bed', 629, True], ['den', 631, True], ['office', 649, True]],
[['living', 766, True]],
[['living', 727, False], ['bed', 610, False]],
[['living', None, True]],
[['living', 627, False]],
[['living', 689, True], ['bed', 635, True], ['den', 250, False]],
[['living', 613, False], ['bed', 717, False]],
[['living', 1100, True]]]],
[661, 766, 669, 669, 627, 662, 665, 665]]]]
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: occupancy and faults | [700, 686, None, 702] | [700, 686, 686, 702] | Failed |
| regression: first sample all invalid | [None, 700, None] | [None, 700, 700] | Failed |
| control: shorted sensor | [700, 690] | [700, 690] | Passed |
| control: single occupied room | [700, 706] | [700, 706] | Passed |
| control: scenario 1 | [799, 697, 609, 676, 686, 743, 732] | [799, 697, 609, 676, 686, 743, 732] | Passed |
| regression: scenario 2 | [684, 638, 750, None, 698, 646, 608, 728] | [684, 638, 750, 750, 698, 646, 608, 728] | Failed |
| regression: scenario 3 | [677, 622, 605, 764, 664, None, 745, 665] | [677, 622, 605, 764, 664, 664, 745, 665] | Failed |
| control: scenario 4 | [600, 724, 602, 729, 655, 775, 687] | [600, 724, 602, 729, 655, 775, 687] | Passed |
SHA-256 / 0c154bf3b9938c32109cd267fb3d92c69ad18699e7a7dd1564f12aed6e7af0ed
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(samples):
last = None
out = []
for readings in samples:
valid = [(t, occ) for room, t, occ in readings if t is not None and 320 <= t <= 1000]
pool = [t for t, occ in valid if occ] or [t for t, occ in valid]
if pool:
last = (2 * sum(pool) + len(pool)) // (2 * len(pool))
elif last is None:
last = 700
out.append(last)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 611, False], ['bed', 799, True]],
[['living', 697, True]],
[['living', 697, False], ['bed', 609, True]],
[['living', 751, False], ['bed', 736, True], ['den', 616, True]],
[['living', 1100, False], ['bed', 686, True]],
[['living', 798, False], ['bed', 743, True], ['den', 646, False], ['office', 720, False]],
[['living', 668, False], ['bed', None, True], ['den', None, True], ['office', 795, False]]]],
[799, 697, 609, 676, 686, 743, 732]],
['regression: scenario 2',
[[[['living', 602, False], ['bed', 665, False], ['den', 790, False], ['office', 677, False]],
[['living', 638, False]],
[['living', 602, False], ['bed', 750, True]],
[['living', None, True], ['bed', 250, False], ['den', 250, True]],
[['living', 744, True], ['bed', 711, True], ['den', 676, False], ['office', 640, True]],
[['living', 771, False], ['bed', 646, True]],
[['living', 608, False]],
[['living', 663, True], ['bed', 793, True], ['den', 250, True], ['office', 743, False]]]],
[684, 638, 750, 750, 698, 646, 608, 728]],
['regression: scenario 3',
[[[['living', 677, True]],
[['living', 624, True], ['bed', 620, True]],
[['living', 605, True], ['bed', 250, False]],
[['living', 764, False]],
[['living', 612, False], ['bed', None, True], ['den', 664, True]],
[['living', None, True], ['bed', 250, False], ['den', 250, True]],
[['living', 745, True], ['bed', None, False]],
[['living', 665, True]]]],
[677, 622, 605, 764, 664, 664, 745, 665]],
['control: scenario 4',
[[[['living', 600, True]],
[['living', 742, True], ['bed', 607, False], ['den', 638, True], ['office', 791, True]],
[['living', 710, False], ['bed', 602, True], ['den', 716, False]],
[['living', 607, False], ['bed', 735, True], ['den', 739, False], ['office', 723, True]],
[['living', 621, True], ['bed', 625, False], ['den', 688, True], ['office', 627, False]],
[['living', 677, False], ['bed', 775, True]],
[['living', 1100, False], ['bed', None, False], ['den', 687, False]]]],
[600, 724, 602, 729, 655, 775, 687]]],
[['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['regression: scenario 48',
[[[['living', 1100, False]],
[['living', 686, False], ['bed', 704, True]],
[['living', 632, False]],
[['living', 620, False], ['bed', 693, False]],
[['living', 1100, True], ['bed', 656, False], ['den', None, False], ['office', 1100, False]]]],
[None, 704, 632, 657, 656]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 631, False], ['bed', None, True]],
[['living', 674, True], ['bed', 788, True]],
[['living', 629, False]],
[['living', 617, False], ['bed', 636, True], ['den', 622, True]],
[['living', 602, True], ['bed', 659, True], ['den', 604, False]],
[['living', 641, True], ['bed', 600, True]],
[['living', 781, True]],
[['living', 615, True], ['bed', 788, True], ['den', 658, True]]]],
[631, 731, 629, 629, 631, 621, 781, 687]],
['control: scenario 2',
[[[['living', 675, True], ['bed', 724, False], ['den', 793, True]],
[['living', 711, False], ['bed', 720, False]],
[['living', 722, False], ['bed', 1100, False], ['den', 606, False], ['office', 625, True]],
[['living', 657, False], ['bed', 619, True], ['den', 800, True], ['office', 629, True]],
[['living', 765, True], ['bed', 726, True]],
[['living', 785, False], ['bed', 626, False]]]],
[734, 716, 625, 683, 746, 706]],
['regression: scenario 3',
[[[['living', 795, False]],
[['living', 769, False], ['bed', 675, False]],
[['living', 640, False]],
[['living', None, False]],
[['living', None, False], ['bed', 790, True], ['den', 684, False], ['office', 789, True]],
[['living', 722, True], ['bed', 750, True], ['den', 250, True]],
[['living', 663, True], ['bed', 670, True]],
[['living', 628, False], ['bed', 687, False], ['den', 635, False]]]],
[795, 722, 640, 640, 790, 736, 667, 650]]],
[['regression: scenario 4',
[[[['living', 647, True], ['bed', 693, False]],
[['living', None, False], ['bed', 1100, False], ['den', None, True], ['office', None, False]],
[['living', 791, False], ['bed', 250, False], ['den', 778, True]],
[['living', 650, False], ['bed', None, True]],
[['living', 1100, True], ['bed', 680, False], ['den', 250, False]],
[['living', 700, True], ['bed', 1100, True], ['den', 712, False]],
[['living', 662, True], ['bed', 670, True]]]],
[647, 647, 778, 650, 680, 700, 666]],
['regression: scenario 14',
[[[['living', None, True]],
[['living', 603, True], ['bed', 671, False], ['den', 792, False], ['office', 751, False]],
[['living', 693, False]],
[['living', 613, True], ['bed', None, True], ['den', None, False], ['office', None, True]],
[['living', 719, False]],
[['living', 1100, False], ['bed', 610, False], ['den', 674, False], ['office', 667, True]],
[['living', 667, True]],
[['living', 605, True], ['bed', 623, True], ['den', 783, False], ['office', 712, True]]]],
[None, 603, 693, 613, 719, 667, 667, 647]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 627, False], ['bed', 693, True], ['den', 769, True], ['office', 704, True]],
[['living', 738, True], ['bed', 638, True]],
[['living', 760, True], ['bed', 696, True], ['den', 683, False]],
[['living', 771, False], ['bed', 1100, False]],
[['living', 686, True]],
[['living', 734, True]],
[['living', None, True], ['bed', 649, False], ['den', 767, False]],
[['living', 722, True]],
[['living', 686, True], ['bed', 715, False], ['den', 617, False], ['office', 250, False]],
[['living', 790, True]]]],
[722, 688, 728, 771, 686, 734, 708, 722, 686, 790]],
['control: scenario 2',
[[[['living', 663, False], ['bed', 781, True], ['den', 775, False], ['office', 722, True]],
[['living', 655, False]],
[['living', 777, False], ['bed', 790, True], ['den', 637, False], ['office', 682, False]],
[['living', 250, False], ['bed', 701, True], ['den', 745, True], ['office', 626, False]],
[['living', 619, False]],
[['living', 619, True], ['bed', 710, True]],
[['living', 732, False]],
[['living', 772, False]],
[['living', None, True], ['bed', 664, True], ['den', 728, True], ['office', 783, False]],
[['living', 1100, True], ['bed', 734, True], ['den', 657, False], ['office', 601, False]]]],
[752, 655, 790, 723, 619, 665, 732, 772, 696, 734]]],
[['regression: scenario 4',
[[[['living', 614, False]],
[['living', 616, True], ['bed', None, True], ['den', 783, True]],
[['living', 250, True], ['bed', 250, True]],
[['living', None, True]],
[['living', 663, True]],
[['living', 656, True], ['bed', 250, True], ['den', 679, True]],
[['living', 799, True], ['bed', 707, True], ['den', 751, True]],
[['living', None, True]]]],
[614, 700, 700, 700, 663, 668, 752, 752]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['control: scenario 1',
[[[['living', 782, False], ['bed', 708, True], ['den', 678, True]],
[['living', 772, False], ['bed', 795, True]],
[['living', 744, True], ['bed', 616, True], ['den', 694, True]],
[['living', 790, False], ['bed', 733, True]],
[['living', 722, True], ['bed', 701, False]],
[['living', 699, False], ['bed', 636, False]]]],
[693, 795, 685, 733, 722, 668]],
['regression: scenario 2',
[[[['living', 675, False], ['bed', 659, True], ['den', 738, False], ['office', 1100, False]],
[['living', 250, True], ['bed', 649, True]],
[['living', 604, True]],
[['living', 629, True], ['bed', None, True], ['den', 610, True]],
[['living', 633, False], ['bed', None, True], ['den', 778, True], ['office', 795, False]],
[['living', None, False]]]],
[659, 649, 604, 620, 778, 778]],
['control: scenario 3',
[[[['living', 687, True], ['bed', 761, True]],
[['living', 719, False], ['bed', None, False], ['den', 774, True]],
[['living', 723, False]],
[['living', 649, False]],
[['living', 649, False], ['bed', 705, True]],
[['living', 699, True]]]],
[724, 774, 723, 649, 705, 699]]],
[['regression: scenario 4',
[[[['living', 1100, False], ['bed', 686, True], ['den', 660, False]],
[['living', 716, True], ['bed', 689, True], ['den', 723, False]],
[['living', 729, True]],
[['living', 660, False], ['bed', 657, True]],
[['living', 765, True]],
[['living', 250, False]],
[['living', None, True], ['bed', 707, True], ['den', 646, True]],
[['living', 766, False], ['bed', 703, False]],
[['living', 617, True], ['bed', 608, True], ['den', 250, True]],
[['living', 603, False], ['bed', 746, True], ['den', None, True], ['office', 695, False]]]],
[686, 703, 729, 657, 765, 765, 677, 735, 613, 746]],
['regression: scenario 40',
[[[['living', None, True]],
[['living', 665, False], ['bed', 608, False], ['den', 792, True], ['office', 620, False]],
[['living', 647, False]],
[['living', 755, True], ['bed', 690, False], ['den', 709, True], ['office', None, True]],
[['living', 691, True], ['bed', 698, True], ['den', 700, False], ['office', 250, False]],
[['living', 750, True], ['bed', 722, True], ['den', 783, True], ['office', 665, False]],
[['living', 644, False], ['bed', 639, False], ['den', 710, False]],
[['living', 629, False]],
[['living', 644, True], ['bed', 612, False], ['den', 762, False], ['office', 767, False]]]],
[None, 792, 647, 732, 695, 752, 664, 629, 644]],
['regression: occupancy and faults',
[[[['liv', 700, True], ['bed', 680, False]],
[['liv', None, True], ['bed', 681, False], ['den', 690, False]],
[['liv', 1200, True]],
[['liv', 701, True], ['bed', 702, True]]]],
[700, 686, 686, 702]],
['control: shorted sensor',
[[[['a', 700, True], ['b', 5, True]], [['a', 1300, False], ['b', 690, False]]]],
[700, 690]],
['regression: first sample all invalid',
[[[['a', None, True]], [['a', 700, True]], [['a', None, False]]]],
[None, 700, 700]],
['control: single occupied room',
[[[['a', 700, True], ['b', 650, False], ['c', 660, False]], [['a', 707, True], ['b', 704, True]]]],
[700, 706]],
['regression: scenario 1',
[[[['living', 1100, True]],
[['living', 634, False], ['bed', 740, False], ['den', None, True]],
[['living', 1100, True]],
[['living', 618, False], ['bed', 787, False], ['den', 754, False]],
[['living', 673, False], ['bed', 795, True], ['den', 612, False], ['office', 721, True]]]],
[None, 687, 687, 720, 758]],
['regression: scenario 2',
[[[['living', 733, True], ['bed', 629, True], ['den', 631, True], ['office', 649, True]],
[['living', 766, True]],
[['living', 727, False], ['bed', 610, False]],
[['living', None, True]],
[['living', 627, False]],
[['living', 689, True], ['bed', 635, True], ['den', 250, False]],
[['living', 613, False], ['bed', 717, False]],
[['living', 1100, True]]]],
[661, 766, 669, 669, 627, 662, 665, 665]]]]
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: occupancy and faults | [700, 686, 686, 702] | [700, 686, 686, 702] | Passed |
| regression: first sample all invalid | [700, 700, 700] | [None, 700, 700] | Failed |
| control: shorted sensor | [700, 690] | [700, 690] | Passed |
| control: single occupied room | [700, 706] | [700, 706] | Passed |
| control: scenario 1 | [799, 697, 609, 676, 686, 743, 732] | [799, 697, 609, 676, 686, 743, 732] | Passed |
| regression: scenario 2 | [684, 638, 750, 750, 698, 646, 608, 728] | [684, 638, 750, 750, 698, 646, 608, 728] | Passed |
| regression: scenario 3 | [677, 622, 605, 764, 664, 664, 745, 665] | [677, 622, 605, 764, 664, 664, 745, 665] | Passed |
| control: scenario 4 | [600, 724, 602, 729, 655, 775, 687] | [600, 724, 602, 729, 655, 775, 687] | Passed |
SHA-256 / 13d1dc4486c97a5e8611515cf45362430248a493c7b3f6e292e445b8f3a93c9a
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:47.716020+00:00.
Case digest / 2145210d55a3c2a714fe35b1a9d0ebef2e365d28794b06450e551fe1a45bb43e