FA-92501 / HVAC thermostat control / Open access
Celsius arrow press steps from a truncated half degree · case 01
The first press after switching to C sometimes leaves the displayed value unchanged or jumps the wrong way.
ROOT CAUSE
The starting half-degree for a C step truncates the stored value instead of rounding half up.
VERIFIED REPAIR
Round the stored value half up to 0.5 C before stepping.
Unsuccessful approach: Rounding to the nearest whole degree C before doubling loses the half-degree position.
Case contract
Thermostat setpoint keypad. The setpoint is stored in tenths F. In C mode the display shows the stored value converted to C and rounded half up to 0.5 C; a press moves the displayed value by 0.5 C and stores exactly 320 + half_degrees*9 tenths F. In F mode the display rounds half up to whole F and a press moves the whole-degree value by 1 F. After each press the stored value is clamped to 45.0-90.0 F. Return [display, stored_tenths_F] per press.
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(unit, presses, start_f10):
f10 = start_f10
out = []
for p in presses:
step = 1 if p == 'up' else -1
if unit == 'C':
c_half = (f10 - 320) // 9
f10 = 320 + (c_half + step) * 9
else:
f_whole = (f10 + 5) // 10
f10 = (f_whole + step) * 10
f10 = max(450, min(900, f10))
if unit == 'C':
c_half = (2 * (f10 - 320) + 9) // 18
out.append(['%d.%d' % (c_half // 2, 5 * (c_half % 2)), f10])
else:
out.append([str((f10 + 5) // 10), f10])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 19',
['C', ['up', 'up', 'up', 'up', 'up', 'up'], 659],
[['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['control: scenario 1',
['F', ['down', 'down', 'up', 'up', 'up', 'down', 'down', 'up'], 716],
[['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]]]],
[['regression: scenario 46',
['C', ['up', 'down', 'up', 'up', 'up', 'up', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.5', 455], ['8.0', 464], ['8.5', 473], ['9.0', 482], ['9.5', 491]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['control: scenario 1',
['F', ['down', 'down', 'up', 'up', 'down', 'down', 'down', 'up'], 652],
[['64', 640], ['63', 630], ['64', 640], ['65', 650], ['64', 640], ['63', 630], ['62', 620], ['63', 630]]]],
[['regression: scenario 56',
['C', ['up', 'down', 'up', 'down', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 1',
['C', ['up', 'up', 'down', 'up', 'down'], 707],
[['22.0', 716], ['22.5', 725], ['22.0', 716], ['22.5', 725], ['22.0', 716]]]],
[['regression: scenario 35',
['C', ['up', 'down', 'up', 'down', 'down', 'up', 'up', 'down'], 776],
[['26.0', 788],
['25.5', 779],
['26.0', 788],
['25.5', 779],
['25.0', 770],
['25.5', 779],
['26.0', 788],
['25.5', 779]]],
['regression: scenario 5',
['C', ['down', 'down', 'up', 'up', 'up'], 445],
[['7.0', 450], ['7.0', 450], ['7.5', 455], ['8.0', 464], ['8.5', 473]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 32',
['C', ['down', 'down', 'up', 'down', 'down'], 652],
[['18.0', 644], ['17.5', 635], ['18.0', 644], ['17.5', 635], ['17.0', 626]]],
['regression: scenario 6',
['C', ['down', 'down', 'down', 'up', 'down', 'down'], 725],
[['22.0', 716], ['21.5', 707], ['21.0', 698], ['21.5', 707], ['21.0', 698], ['20.5', 689]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]]]
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 19 | [['19.0', 662], ['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707]] | [['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]] | Failed |
| regression: celsius steps | [['22.5', 725], ['23.0', 734], ['22.5', 725]] | [['22.5', 725], ['23.0', 734], ['22.5', 725]] | Passed |
| control: fahrenheit half value | [['74', 740], ['73', 730], ['72', 720]] | [['74', 740], ['73', 730], ['72', 720]] | Passed |
| control: upper clamp | [['90', 900]] | [['90', 900]] | Passed |
| regression: celsius off-grid start | [['21.5', 707], ['22.0', 716]] | [['21.5', 707], ['22.0', 716]] | Passed |
| control: celsius lower clamp | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | Passed |
| regression: celsius half-step display | [['22.0', 716]] | [['22.0', 716]] | Passed |
| control: scenario 1 | [['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]] | [['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]] | Passed |
SHA-256 / 2fcd500e73cb6783aaa593f16d0a274532fb547a161b587090b9b842d858c5ba
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(unit, presses, start_f10):
f10 = start_f10
out = []
for p in presses:
step = 1 if p == 'up' else -1
if unit == 'C':
c_half = 2 * ((f10 - 320 + 9) // 18)
f10 = 320 + (c_half + step) * 9
else:
f_whole = (f10 + 5) // 10
f10 = (f_whole + step) * 10
f10 = max(450, min(900, f10))
if unit == 'C':
c_half = (2 * (f10 - 320) + 9) // 18
out.append(['%d.%d' % (c_half // 2, 5 * (c_half % 2)), f10])
else:
out.append([str((f10 + 5) // 10), f10])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 19',
['C', ['up', 'up', 'up', 'up', 'up', 'up'], 659],
[['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['control: scenario 1',
['F', ['down', 'down', 'up', 'up', 'up', 'down', 'down', 'up'], 716],
[['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]]]],
[['regression: scenario 46',
['C', ['up', 'down', 'up', 'up', 'up', 'up', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.5', 455], ['8.0', 464], ['8.5', 473], ['9.0', 482], ['9.5', 491]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['control: scenario 1',
['F', ['down', 'down', 'up', 'up', 'down', 'down', 'down', 'up'], 652],
[['64', 640], ['63', 630], ['64', 640], ['65', 650], ['64', 640], ['63', 630], ['62', 620], ['63', 630]]]],
[['regression: scenario 56',
['C', ['up', 'down', 'up', 'down', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 1',
['C', ['up', 'up', 'down', 'up', 'down'], 707],
[['22.0', 716], ['22.5', 725], ['22.0', 716], ['22.5', 725], ['22.0', 716]]]],
[['regression: scenario 35',
['C', ['up', 'down', 'up', 'down', 'down', 'up', 'up', 'down'], 776],
[['26.0', 788],
['25.5', 779],
['26.0', 788],
['25.5', 779],
['25.0', 770],
['25.5', 779],
['26.0', 788],
['25.5', 779]]],
['regression: scenario 5',
['C', ['down', 'down', 'up', 'up', 'up'], 445],
[['7.0', 450], ['7.0', 450], ['7.5', 455], ['8.0', 464], ['8.5', 473]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 32',
['C', ['down', 'down', 'up', 'down', 'down'], 652],
[['18.0', 644], ['17.5', 635], ['18.0', 644], ['17.5', 635], ['17.0', 626]]],
['regression: scenario 6',
['C', ['down', 'down', 'down', 'up', 'down', 'down'], 725],
[['22.0', 716], ['21.5', 707], ['21.0', 698], ['21.5', 707], ['21.0', 698], ['20.5', 689]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]]]
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 19 | [['19.5', 671], ['20.5', 689], ['21.5', 707], ['22.5', 725], ['23.5', 743], ['24.5', 761]] | [['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]] | Failed |
| regression: celsius steps | [['22.5', 725], ['23.5', 743], ['23.5', 743]] | [['22.5', 725], ['23.0', 734], ['22.5', 725]] | Failed |
| control: fahrenheit half value | [['74', 740], ['73', 730], ['72', 720]] | [['74', 740], ['73', 730], ['72', 720]] | Passed |
| control: upper clamp | [['90', 900]] | [['90', 900]] | Passed |
| regression: celsius off-grid start | [['21.5', 707], ['22.5', 725]] | [['21.5', 707], ['22.0', 716]] | Failed |
| control: celsius lower clamp | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | Passed |
| regression: celsius half-step display | [['22.5', 725]] | [['22.0', 716]] | Failed |
| control: scenario 1 | [['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]] | [['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]] | Passed |
SHA-256 / 2487354ca993cce26b301b3189334c8ddbc5929681efeaeebb3b7eb856094794
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(unit, presses, start_f10):
f10 = start_f10
out = []
for p in presses:
step = 1 if p == 'up' else -1
if unit == 'C':
c_half = (2 * (f10 - 320) + 9) // 18
f10 = 320 + (c_half + step) * 9
else:
f_whole = (f10 + 5) // 10
f10 = (f_whole + step) * 10
f10 = max(450, min(900, f10))
if unit == 'C':
c_half = (2 * (f10 - 320) + 9) // 18
out.append(['%d.%d' % (c_half // 2, 5 * (c_half % 2)), f10])
else:
out.append([str((f10 + 5) // 10), f10])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: scenario 19',
['C', ['up', 'up', 'up', 'up', 'up', 'up'], 659],
[['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['control: scenario 1',
['F', ['down', 'down', 'up', 'up', 'up', 'down', 'down', 'up'], 716],
[['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]]]],
[['regression: scenario 46',
['C', ['up', 'down', 'up', 'up', 'up', 'up', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.5', 455], ['8.0', 464], ['8.5', 473], ['9.0', 482], ['9.5', 491]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['control: scenario 1',
['F', ['down', 'down', 'up', 'up', 'down', 'down', 'down', 'up'], 652],
[['64', 640], ['63', 630], ['64', 640], ['65', 650], ['64', 640], ['63', 630], ['62', 620], ['63', 630]]]],
[['regression: scenario 56',
['C', ['up', 'down', 'up', 'down', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 1',
['C', ['up', 'up', 'down', 'up', 'down'], 707],
[['22.0', 716], ['22.5', 725], ['22.0', 716], ['22.5', 725], ['22.0', 716]]]],
[['regression: scenario 35',
['C', ['up', 'down', 'up', 'down', 'down', 'up', 'up', 'down'], 776],
[['26.0', 788],
['25.5', 779],
['26.0', 788],
['25.5', 779],
['25.0', 770],
['25.5', 779],
['26.0', 788],
['25.5', 779]]],
['regression: scenario 5',
['C', ['down', 'down', 'up', 'up', 'up'], 445],
[['7.0', 450], ['7.0', 450], ['7.5', 455], ['8.0', 464], ['8.5', 473]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 32',
['C', ['down', 'down', 'up', 'down', 'down'], 652],
[['18.0', 644], ['17.5', 635], ['18.0', 644], ['17.5', 635], ['17.0', 626]]],
['regression: scenario 6',
['C', ['down', 'down', 'down', 'up', 'down', 'down'], 725],
[['22.0', 716], ['21.5', 707], ['21.0', 698], ['21.5', 707], ['21.0', 698], ['20.5', 689]]],
['regression: celsius steps',
['C', ['up', 'up', 'down'], 720],
[['22.5', 725], ['23.0', 734], ['22.5', 725]]],
['control: fahrenheit half value',
['F', ['up', 'down', 'down'], 725],
[['74', 740], ['73', 730], ['72', 720]]],
['control: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]]]
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 19 | [['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]] | [['19.5', 671], ['20.0', 680], ['20.5', 689], ['21.0', 698], ['21.5', 707], ['22.0', 716]] | Passed |
| regression: celsius steps | [['22.5', 725], ['23.0', 734], ['22.5', 725]] | [['22.5', 725], ['23.0', 734], ['22.5', 725]] | Passed |
| control: fahrenheit half value | [['74', 740], ['73', 730], ['72', 720]] | [['74', 740], ['73', 730], ['72', 720]] | Passed |
| control: upper clamp | [['90', 900]] | [['90', 900]] | Passed |
| regression: celsius off-grid start | [['21.5', 707], ['22.0', 716]] | [['21.5', 707], ['22.0', 716]] | Passed |
| control: celsius lower clamp | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | Passed |
| regression: celsius half-step display | [['22.0', 716]] | [['22.0', 716]] | Passed |
| control: scenario 1 | [['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]] | [['71', 710], ['70', 700], ['71', 710], ['72', 720], ['73', 730], ['72', 720], ['71', 710], ['72', 720]] | Passed |
SHA-256 / 39cbf77bf1a56e152aafe53f74935b37ba7ac6279fb5fc998d83f3f82e6ae5cd
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.315757+00:00.
Case digest / 66a3f755805e4edb38bc84961757572a49e966f81c4cf5eb4be1cb3cf7f76146