FA-92516 / HVAC thermostat control / Open access
Setpoint limits are only enforced in one display unit · case 01
Switching to Celsius lets the user walk the setpoint outside 45-90 F.
ROOT CAUSE
The clamp is missing after the press.
VERIFIED REPAIR
Clamp the stored value after every press, in both units.
Unsuccessful approach: Clamping inside the Fahrenheit branch only still lets Celsius presses escape the limits.
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 = (2 * (f10 - 320) + 9) // 18
f10 = 320 + (c_half + step) * 9
else:
f_whole = (f10 + 5) // 10
f10 = (f_whole + step) * 10
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: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: 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: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: 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]]],
['control: scenario 2',
['C', ['down', 'down', 'down', 'up', 'down', 'down', 'up'], 716],
[['21.5', 707],
['21.0', 698],
['20.5', 689],
['21.0', 698],
['20.5', 689],
['20.0', 680],
['20.5', 689]]]],
[['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 22',
['C', ['down', 'down', 'up', 'down', 'down'], 445],
[['7.0', 450], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.0', 450]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: 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 4',
['C', ['down', 'up', 'down', 'down', 'down', 'down', 'down', 'up'], 445],
[['7.0', 450],
['7.5', 455],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.5', 455]]],
['regression: scenario 25',
['C', ['down', 'down', 'down', 'up', 'down', 'up', 'up', 'up', 'down'], 905],
[['32.0', 896],
['31.5', 887],
['31.0', 878],
['31.5', 887],
['31.0', 878],
['31.5', 887],
['32.0', 896],
['32.0', 900],
['31.5', 887]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 6',
['C', ['up', 'up', 'down', 'down', 'down', 'down', 'down', 'down'], 905],
[['32.0', 900],
['32.0', 900],
['31.5', 887],
['31.0', 878],
['30.5', 869],
['30.0', 860],
['29.5', 851],
['29.0', 842]]],
['regression: scenario 18',
['C', ['up', 'up', 'down', 'up', 'up', 'down', 'down', 'up'], 905],
[['32.0', 900],
['32.0', 900],
['31.5', 887],
['32.0', 896],
['32.0', 900],
['31.5', 887],
['31.0', 878],
['31.5', 887]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 9',
['C', ['up', 'down', 'down', 'up', 'down', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 28',
['C', ['up', 'down', 'down', 'up', 'down'], 905],
[['32.0', 900], ['31.5', 887], ['31.0', 878], ['31.5', 887], ['31.0', 878]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: 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: upper clamp | [['91', 910]] | [['90', 900]] | Failed |
| regression: celsius lower clamp | [['7.0', 446], ['6.5', 437], ['7.0', 446]] | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | Failed |
| control: 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: celsius off-grid start | [['21.5', 707], ['22.0', 716]] | [['21.5', 707], ['22.0', 716]] | Passed |
| control: 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 |
| control: scenario 2 | [['21.5', 707], ['21.0', 698], ['20.5', 689], ['21.0', 698], ['20.5', 689], ['20.0', 680], ['20.5', 689]] | [['21.5', 707], ['21.0', 698], ['20.5', 689], ['21.0', 698], ['20.5', 689], ['20.0', 680], ['20.5', 689]] | Passed |
SHA-256 / d85cf2da8050d315fd59236cc14854ab200ad150ac319744e7c2aada459097ab
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 = max(450, min(900, (f_whole + step) * 10))
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: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: 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: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: 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]]],
['control: scenario 2',
['C', ['down', 'down', 'down', 'up', 'down', 'down', 'up'], 716],
[['21.5', 707],
['21.0', 698],
['20.5', 689],
['21.0', 698],
['20.5', 689],
['20.0', 680],
['20.5', 689]]]],
[['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 22',
['C', ['down', 'down', 'up', 'down', 'down'], 445],
[['7.0', 450], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.0', 450]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: 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 4',
['C', ['down', 'up', 'down', 'down', 'down', 'down', 'down', 'up'], 445],
[['7.0', 450],
['7.5', 455],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.5', 455]]],
['regression: scenario 25',
['C', ['down', 'down', 'down', 'up', 'down', 'up', 'up', 'up', 'down'], 905],
[['32.0', 896],
['31.5', 887],
['31.0', 878],
['31.5', 887],
['31.0', 878],
['31.5', 887],
['32.0', 896],
['32.0', 900],
['31.5', 887]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 6',
['C', ['up', 'up', 'down', 'down', 'down', 'down', 'down', 'down'], 905],
[['32.0', 900],
['32.0', 900],
['31.5', 887],
['31.0', 878],
['30.5', 869],
['30.0', 860],
['29.5', 851],
['29.0', 842]]],
['regression: scenario 18',
['C', ['up', 'up', 'down', 'up', 'up', 'down', 'down', 'up'], 905],
[['32.0', 900],
['32.0', 900],
['31.5', 887],
['32.0', 896],
['32.0', 900],
['31.5', 887],
['31.0', 878],
['31.5', 887]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 9',
['C', ['up', 'down', 'down', 'up', 'down', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 28',
['C', ['up', 'down', 'down', 'up', 'down'], 905],
[['32.0', 900], ['31.5', 887], ['31.0', 878], ['31.5', 887], ['31.0', 878]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: 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: upper clamp | [['90', 900]] | [['90', 900]] | Passed |
| regression: celsius lower clamp | [['7.0', 446], ['6.5', 437], ['7.0', 446]] | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | Failed |
| control: 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: celsius off-grid start | [['21.5', 707], ['22.0', 716]] | [['21.5', 707], ['22.0', 716]] | Passed |
| control: 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 |
| control: scenario 2 | [['21.5', 707], ['21.0', 698], ['20.5', 689], ['21.0', 698], ['20.5', 689], ['20.0', 680], ['20.5', 689]] | [['21.5', 707], ['21.0', 698], ['20.5', 689], ['21.0', 698], ['20.5', 689], ['20.0', 680], ['20.5', 689]] | Passed |
SHA-256 / e55be2a355222d1f471c765e5cf39256512fc340814bb6eb7c88df3ac5933408
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: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: 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: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: 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]]],
['control: scenario 2',
['C', ['down', 'down', 'down', 'up', 'down', 'down', 'up'], 716],
[['21.5', 707],
['21.0', 698],
['20.5', 689],
['21.0', 698],
['20.5', 689],
['20.0', 680],
['20.5', 689]]]],
[['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 22',
['C', ['down', 'down', 'up', 'down', 'down'], 445],
[['7.0', 450], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.0', 450]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['control: 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 4',
['C', ['down', 'up', 'down', 'down', 'down', 'down', 'down', 'up'], 445],
[['7.0', 450],
['7.5', 455],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.0', 450],
['7.5', 455]]],
['regression: scenario 25',
['C', ['down', 'down', 'down', 'up', 'down', 'up', 'up', 'up', 'down'], 905],
[['32.0', 896],
['31.5', 887],
['31.0', 878],
['31.5', 887],
['31.0', 878],
['31.5', 887],
['32.0', 896],
['32.0', 900],
['31.5', 887]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 6',
['C', ['up', 'up', 'down', 'down', 'down', 'down', 'down', 'down'], 905],
[['32.0', 900],
['32.0', 900],
['31.5', 887],
['31.0', 878],
['30.5', 869],
['30.0', 860],
['29.5', 851],
['29.0', 842]]],
['regression: scenario 18',
['C', ['up', 'up', 'down', 'up', 'up', 'down', 'down', 'up'], 905],
[['32.0', 900],
['32.0', 900],
['31.5', 887],
['32.0', 896],
['32.0', 900],
['31.5', 887],
['31.0', 878],
['31.5', 887]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: celsius half-step display', ['C', ['up'], 707], [['22.0', 716]]]],
[['regression: scenario 9',
['C', ['up', 'down', 'down', 'up', 'down', 'up'], 445],
[['7.5', 455], ['7.0', 450], ['7.0', 450], ['7.5', 455], ['7.0', 450], ['7.5', 455]]],
['regression: scenario 28',
['C', ['up', 'down', 'down', 'up', 'down'], 905],
[['32.0', 900], ['31.5', 887], ['31.0', 878], ['31.5', 887], ['31.0', 878]]],
['control: 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]]],
['regression: upper clamp', ['F', ['up'], 895], [['90', 900]]],
['control: celsius off-grid start', ['C', ['down', 'up'], 716], [['21.5', 707], ['22.0', 716]]],
['regression: celsius lower clamp',
['C', ['down', 'down', 'up'], 452],
[['7.0', 450], ['7.0', 450], ['7.5', 455]]],
['control: 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: upper clamp | [['90', 900]] | [['90', 900]] | Passed |
| regression: celsius lower clamp | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | [['7.0', 450], ['7.0', 450], ['7.5', 455]] | Passed |
| control: 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: celsius off-grid start | [['21.5', 707], ['22.0', 716]] | [['21.5', 707], ['22.0', 716]] | Passed |
| control: 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 |
| control: scenario 2 | [['21.5', 707], ['21.0', 698], ['20.5', 689], ['21.0', 698], ['20.5', 689], ['20.0', 680], ['20.5', 689]] | [['21.5', 707], ['21.0', 698], ['20.5', 689], ['21.0', 698], ['20.5', 689], ['20.0', 680], ['20.5', 689]] | Passed |
SHA-256 / 4eb7718fa6937f8d35f2d1f2c25950f1e4c06eea5a395f68e0f7dfd08e306c52
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.480091+00:00.
Case digest / 92140d7feaa9237a1a80368055376db383230e8a1f98ded7cead44623f171ccc