FA-92276 / PLC ladder logic scan cycles / Open access
Two-hand control re-arms when only one button is released · case 01
Tying down one button and pulsing the other still starts the press.
ROOT CAUSE
Releasing a single button re-arms the start permission.
VERIFIED REPAIR
Re-arm only when both buttons are released.
Unsuccessful approach: Re-arming on a single release while restarting the window from that scan still defeats anti-tie-down.
Case contract
Two-hand press control sampled per scan; samples is a list of [t_ms, left, right]. The press may start only when both buttons become pressed within window ms of the first one being pressed (inclusive), starting from a state where both were released. Releasing either button stops the press, and both buttons must then be released before a new start is allowed (anti-tie-down). Return the run output per scan.
Why this case matters
Ladder programs are executed as repeated scans; each defect here changes what a rung, timer, counter or data-table instruction reports on a particular scan, which is how commissioning and field faults are actually observed.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(window, samples):
first_press = None
armed = True
run = False
out = []
for t, left, right in samples:
left = bool(left)
right = bool(right)
if not left and not right:
armed = True
first_press = None
run = False
elif left and right:
if not run:
begin = t if first_press is None else first_press
if armed and t - begin <= window:
run = True
armed = False
else:
run = False
armed = True
if first_press is None:
first_press = t
out.append(run)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['control: scenario 1',
[200,
[[0, False, False],
[100, True, True],
[400, True, True],
[500, True, True],
[650, True, True],
[850, True, True],
[1050, True, False],
[1100, False, False],
[1300, True, True],
[1600, True, True]]],
[False, True, True, True, True, True, False, False, True, True]],
['regression: scenario 2',
[200,
[[0, True, True],
[50, True, False],
[200, True, True],
[250, False, False],
[300, False, True],
[400, False, False],
[450, False, False],
[550, False, True]]],
[True, False, False, False, False, False, False, False]],
['control: scenario 3',
[200,
[[0, True, False],
[150, True, True],
[200, True, True],
[400, False, False],
[500, True, True],
[700, True, True],
[850, True, False],
[1000, True, False],
[1100, False, False],
[1400, True, False],
[1450, False, True],
[1600, True, True],
[1700, True, True]]],
[False, True, True, False, True, True, False, False, False, False, False, True, True]]],
[['regression: scenario 1',
[250,
[[0, True, True],
[50, True, True],
[200, False, True],
[300, True, True],
[600, True, True],
[750, False, True],
[850, False, False],
[1000, False, True],
[1100, True, True]]],
[True, True, False, False, False, False, False, False, True]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['control: scenario 2',
[250,
[[0, True, True],
[50, False, True],
[200, False, False],
[250, True, False],
[400, True, True],
[500, True, True],
[650, False, False],
[700, True, True],
[850, False, False],
[1000, False, True],
[1150, False, True],
[1250, False, True],
[1550, False, False]]],
[True, False, False, False, True, True, False, True, False, False, False, False, False]],
['control: scenario 3',
[500,
[[0, True, False],
[300, False, False],
[400, False, True],
[500, False, True],
[800, True, True],
[950, False, False],
[1050, False, False],
[1350, False, False],
[1450, False, True],
[1600, False, True],
[1650, True, True],
[1800, False, True],
[1850, False, True]]],
[False, False, False, False, True, False, False, False, False, False, True, False, False]]],
[['regression: scenario 6',
[500,
[[0, True, False],
[100, True, True],
[250, False, True],
[400, True, True],
[450, False, False],
[600, False, False],
[900, True, True],
[1200, True, True],
[1350, False, False],
[1550, False, False],
[1850, False, True],
[1900, True, True],
[1950, True, True]]],
[False, True, False, False, False, False, True, True, False, False, False, True, True]],
['regression: scenario 3',
[250,
[[0, False, False],
[100, True, False],
[400, True, False],
[700, True, False],
[900, True, True],
[1200, True, False],
[1400, False, False],
[1700, True, False],
[1750, True, True],
[1950, False, False],
[2100, False, False],
[2250, False, True]]],
[False, False, False, False, False, False, False, False, True, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: scenario 1',
[200,
[[0, False, True],
[100, False, False],
[250, False, False],
[300, True, False],
[400, True, False],
[450, False, True],
[550, True, False],
[700, True, False],
[800, False, True]]],
[False, False, False, False, False, False, False, False, False]]],
[['regression: scenario 14',
[500,
[[0, True, True],
[100, False, True],
[200, False, True],
[400, False, True],
[450, True, True],
[550, True, False],
[700, True, False],
[800, False, False]]],
[True, False, False, False, False, False, False, False]],
['regression: scenario 5',
[500,
[[0, True, False],
[300, False, False],
[350, True, True],
[450, True, True],
[550, True, True],
[750, False, True],
[900, True, True],
[1000, False, False],
[1050, True, True],
[1350, False, True],
[1650, True, True],
[1950, True, False],
[2000, False, False]]],
[False, False, True, True, True, False, False, False, True, False, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: scenario 1',
[250,
[[0, False, True],
[200, False, False],
[250, True, False],
[550, True, False],
[650, True, True],
[700, True, False],
[900, True, True],
[1100, True, False],
[1200, False, True],
[1300, True, False],
[1450, False, True],
[1650, False, False],
[1800, True, True]]],
[False, False, False, False, False, False, False, False, False, False, False, False, True]]],
[['regression: scenario 48',
[500,
[[0, False, False],
[50, True, True],
[350, True, True],
[650, False, False],
[800, False, True],
[850, True, True],
[1150, False, False],
[1450, True, True],
[1650, True, False],
[1700, True, True],
[1850, False, False],
[2000, False, False],
[2150, False, False],
[2350, False, True]]],
[False, True, True, False, False, True, False, True, False, False, False, False, False, False]],
['regression: scenario 14',
[200,
[[0, True, True],
[150, False, True],
[200, False, True],
[350, False, True],
[550, True, True],
[750, False, False],
[950, False, False],
[1000, True, True],
[1100, True, False],
[1150, False, True]]],
[True, False, False, False, False, False, False, True, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: scenario 1',
[500,
[[0, True, False],
[150, True, False],
[250, True, False],
[300, False, False],
[350, False, True],
[650, False, True],
[850, False, False],
[1150, False, False],
[1350, True, False],
[1650, True, False],
[1800, True, False],
[1850, True, False],
[1900, False, True]]],
[False, False, False, False, False, False, False, False, False, False, False, False, False]]]]
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: simultaneous press then release one | [True, False, True] | [True, False, False] | Failed |
| regression: tie down one button | [False, True, False, False, False, False] | [False, True, False, False, False, False] | Passed |
| control: normal cycle | [False, True, True, False, False, True] | [False, True, True, False, False, True] | Passed |
| control: late second hand | [False, False, False, False, True] | [False, False, False, False, True] | Passed |
| control: exact window | [False, True, False] | [False, True, False] | Passed |
| control: scenario 1 | [False, True, True, True, True, True, False, False, True, True] | [False, True, True, True, True, True, False, False, True, True] | Passed |
| regression: scenario 2 | [True, False, True, False, False, False, False, False] | [True, False, False, False, False, False, False, False] | Failed |
| control: scenario 3 | [False, True, True, False, True, True, False, False, False, False, False, True, True] | [False, True, True, False, True, True, False, False, False, False, False, True, True] | Passed |
SHA-256 / 2dd74d5f1c04904ef6c868313b45313bb371b15c5f2fc879da619fbdfc9766fc
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(window, samples):
first_press = None
armed = True
run = False
out = []
for t, left, right in samples:
left = bool(left)
right = bool(right)
if not left and not right:
armed = True
first_press = None
run = False
elif left and right:
if not run:
begin = t if first_press is None else first_press
if armed and t - begin <= window:
run = True
armed = False
else:
run = False
armed = True
first_press = t
if first_press is None:
first_press = t
out.append(run)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['control: scenario 1',
[200,
[[0, False, False],
[100, True, True],
[400, True, True],
[500, True, True],
[650, True, True],
[850, True, True],
[1050, True, False],
[1100, False, False],
[1300, True, True],
[1600, True, True]]],
[False, True, True, True, True, True, False, False, True, True]],
['regression: scenario 2',
[200,
[[0, True, True],
[50, True, False],
[200, True, True],
[250, False, False],
[300, False, True],
[400, False, False],
[450, False, False],
[550, False, True]]],
[True, False, False, False, False, False, False, False]],
['control: scenario 3',
[200,
[[0, True, False],
[150, True, True],
[200, True, True],
[400, False, False],
[500, True, True],
[700, True, True],
[850, True, False],
[1000, True, False],
[1100, False, False],
[1400, True, False],
[1450, False, True],
[1600, True, True],
[1700, True, True]]],
[False, True, True, False, True, True, False, False, False, False, False, True, True]]],
[['regression: scenario 1',
[250,
[[0, True, True],
[50, True, True],
[200, False, True],
[300, True, True],
[600, True, True],
[750, False, True],
[850, False, False],
[1000, False, True],
[1100, True, True]]],
[True, True, False, False, False, False, False, False, True]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['control: scenario 2',
[250,
[[0, True, True],
[50, False, True],
[200, False, False],
[250, True, False],
[400, True, True],
[500, True, True],
[650, False, False],
[700, True, True],
[850, False, False],
[1000, False, True],
[1150, False, True],
[1250, False, True],
[1550, False, False]]],
[True, False, False, False, True, True, False, True, False, False, False, False, False]],
['control: scenario 3',
[500,
[[0, True, False],
[300, False, False],
[400, False, True],
[500, False, True],
[800, True, True],
[950, False, False],
[1050, False, False],
[1350, False, False],
[1450, False, True],
[1600, False, True],
[1650, True, True],
[1800, False, True],
[1850, False, True]]],
[False, False, False, False, True, False, False, False, False, False, True, False, False]]],
[['regression: scenario 6',
[500,
[[0, True, False],
[100, True, True],
[250, False, True],
[400, True, True],
[450, False, False],
[600, False, False],
[900, True, True],
[1200, True, True],
[1350, False, False],
[1550, False, False],
[1850, False, True],
[1900, True, True],
[1950, True, True]]],
[False, True, False, False, False, False, True, True, False, False, False, True, True]],
['regression: scenario 3',
[250,
[[0, False, False],
[100, True, False],
[400, True, False],
[700, True, False],
[900, True, True],
[1200, True, False],
[1400, False, False],
[1700, True, False],
[1750, True, True],
[1950, False, False],
[2100, False, False],
[2250, False, True]]],
[False, False, False, False, False, False, False, False, True, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: scenario 1',
[200,
[[0, False, True],
[100, False, False],
[250, False, False],
[300, True, False],
[400, True, False],
[450, False, True],
[550, True, False],
[700, True, False],
[800, False, True]]],
[False, False, False, False, False, False, False, False, False]]],
[['regression: scenario 14',
[500,
[[0, True, True],
[100, False, True],
[200, False, True],
[400, False, True],
[450, True, True],
[550, True, False],
[700, True, False],
[800, False, False]]],
[True, False, False, False, False, False, False, False]],
['regression: scenario 5',
[500,
[[0, True, False],
[300, False, False],
[350, True, True],
[450, True, True],
[550, True, True],
[750, False, True],
[900, True, True],
[1000, False, False],
[1050, True, True],
[1350, False, True],
[1650, True, True],
[1950, True, False],
[2000, False, False]]],
[False, False, True, True, True, False, False, False, True, False, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: scenario 1',
[250,
[[0, False, True],
[200, False, False],
[250, True, False],
[550, True, False],
[650, True, True],
[700, True, False],
[900, True, True],
[1100, True, False],
[1200, False, True],
[1300, True, False],
[1450, False, True],
[1650, False, False],
[1800, True, True]]],
[False, False, False, False, False, False, False, False, False, False, False, False, True]]],
[['regression: scenario 48',
[500,
[[0, False, False],
[50, True, True],
[350, True, True],
[650, False, False],
[800, False, True],
[850, True, True],
[1150, False, False],
[1450, True, True],
[1650, True, False],
[1700, True, True],
[1850, False, False],
[2000, False, False],
[2150, False, False],
[2350, False, True]]],
[False, True, True, False, False, True, False, True, False, False, False, False, False, False]],
['regression: scenario 14',
[200,
[[0, True, True],
[150, False, True],
[200, False, True],
[350, False, True],
[550, True, True],
[750, False, False],
[950, False, False],
[1000, True, True],
[1100, True, False],
[1150, False, True]]],
[True, False, False, False, False, False, False, True, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: scenario 1',
[500,
[[0, True, False],
[150, True, False],
[250, True, False],
[300, False, False],
[350, False, True],
[650, False, True],
[850, False, False],
[1150, False, False],
[1350, True, False],
[1650, True, False],
[1800, True, False],
[1850, True, False],
[1900, False, True]]],
[False, False, False, False, False, False, False, False, False, False, False, False, False]]]]
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: simultaneous press then release one | [True, False, True] | [True, False, False] | Failed |
| regression: tie down one button | [False, True, False, True, False, True] | [False, True, False, False, False, False] | Failed |
| control: normal cycle | [False, True, True, False, False, True] | [False, True, True, False, False, True] | Passed |
| control: late second hand | [False, False, False, False, True] | [False, False, False, False, True] | Passed |
| control: exact window | [False, True, False] | [False, True, False] | Passed |
| control: scenario 1 | [False, True, True, True, True, True, False, False, True, True] | [False, True, True, True, True, True, False, False, True, True] | Passed |
| regression: scenario 2 | [True, False, True, False, False, False, False, False] | [True, False, False, False, False, False, False, False] | Failed |
| control: scenario 3 | [False, True, True, False, True, True, False, False, False, False, False, True, True] | [False, True, True, False, True, True, False, False, False, False, False, True, True] | Passed |
SHA-256 / 3938ba9714ea2c8db6124fc4796f692e1dad25ee5a4bc76b63e9496cb4a08f3a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(window, samples):
first_press = None
armed = True
run = False
out = []
for t, left, right in samples:
left = bool(left)
right = bool(right)
if not left and not right:
armed = True
first_press = None
run = False
elif left and right:
if not run:
begin = t if first_press is None else first_press
if armed and t - begin <= window:
run = True
armed = False
else:
run = False
if first_press is None:
first_press = t
out.append(run)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['control: scenario 1',
[200,
[[0, False, False],
[100, True, True],
[400, True, True],
[500, True, True],
[650, True, True],
[850, True, True],
[1050, True, False],
[1100, False, False],
[1300, True, True],
[1600, True, True]]],
[False, True, True, True, True, True, False, False, True, True]],
['regression: scenario 2',
[200,
[[0, True, True],
[50, True, False],
[200, True, True],
[250, False, False],
[300, False, True],
[400, False, False],
[450, False, False],
[550, False, True]]],
[True, False, False, False, False, False, False, False]],
['control: scenario 3',
[200,
[[0, True, False],
[150, True, True],
[200, True, True],
[400, False, False],
[500, True, True],
[700, True, True],
[850, True, False],
[1000, True, False],
[1100, False, False],
[1400, True, False],
[1450, False, True],
[1600, True, True],
[1700, True, True]]],
[False, True, True, False, True, True, False, False, False, False, False, True, True]]],
[['regression: scenario 1',
[250,
[[0, True, True],
[50, True, True],
[200, False, True],
[300, True, True],
[600, True, True],
[750, False, True],
[850, False, False],
[1000, False, True],
[1100, True, True]]],
[True, True, False, False, False, False, False, False, True]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['control: scenario 2',
[250,
[[0, True, True],
[50, False, True],
[200, False, False],
[250, True, False],
[400, True, True],
[500, True, True],
[650, False, False],
[700, True, True],
[850, False, False],
[1000, False, True],
[1150, False, True],
[1250, False, True],
[1550, False, False]]],
[True, False, False, False, True, True, False, True, False, False, False, False, False]],
['control: scenario 3',
[500,
[[0, True, False],
[300, False, False],
[400, False, True],
[500, False, True],
[800, True, True],
[950, False, False],
[1050, False, False],
[1350, False, False],
[1450, False, True],
[1600, False, True],
[1650, True, True],
[1800, False, True],
[1850, False, True]]],
[False, False, False, False, True, False, False, False, False, False, True, False, False]]],
[['regression: scenario 6',
[500,
[[0, True, False],
[100, True, True],
[250, False, True],
[400, True, True],
[450, False, False],
[600, False, False],
[900, True, True],
[1200, True, True],
[1350, False, False],
[1550, False, False],
[1850, False, True],
[1900, True, True],
[1950, True, True]]],
[False, True, False, False, False, False, True, True, False, False, False, True, True]],
['regression: scenario 3',
[250,
[[0, False, False],
[100, True, False],
[400, True, False],
[700, True, False],
[900, True, True],
[1200, True, False],
[1400, False, False],
[1700, True, False],
[1750, True, True],
[1950, False, False],
[2100, False, False],
[2250, False, True]]],
[False, False, False, False, False, False, False, False, True, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: scenario 1',
[200,
[[0, False, True],
[100, False, False],
[250, False, False],
[300, True, False],
[400, True, False],
[450, False, True],
[550, True, False],
[700, True, False],
[800, False, True]]],
[False, False, False, False, False, False, False, False, False]]],
[['regression: scenario 14',
[500,
[[0, True, True],
[100, False, True],
[200, False, True],
[400, False, True],
[450, True, True],
[550, True, False],
[700, True, False],
[800, False, False]]],
[True, False, False, False, False, False, False, False]],
['regression: scenario 5',
[500,
[[0, True, False],
[300, False, False],
[350, True, True],
[450, True, True],
[550, True, True],
[750, False, True],
[900, True, True],
[1000, False, False],
[1050, True, True],
[1350, False, True],
[1650, True, True],
[1950, True, False],
[2000, False, False]]],
[False, False, True, True, True, False, False, False, True, False, False, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: scenario 1',
[250,
[[0, False, True],
[200, False, False],
[250, True, False],
[550, True, False],
[650, True, True],
[700, True, False],
[900, True, True],
[1100, True, False],
[1200, False, True],
[1300, True, False],
[1450, False, True],
[1650, False, False],
[1800, True, True]]],
[False, False, False, False, False, False, False, False, False, False, False, False, True]]],
[['regression: scenario 48',
[500,
[[0, False, False],
[50, True, True],
[350, True, True],
[650, False, False],
[800, False, True],
[850, True, True],
[1150, False, False],
[1450, True, True],
[1650, True, False],
[1700, True, True],
[1850, False, False],
[2000, False, False],
[2150, False, False],
[2350, False, True]]],
[False, True, True, False, False, True, False, True, False, False, False, False, False, False]],
['regression: scenario 14',
[200,
[[0, True, True],
[150, False, True],
[200, False, True],
[350, False, True],
[550, True, True],
[750, False, False],
[950, False, False],
[1000, True, True],
[1100, True, False],
[1150, False, True]]],
[True, False, False, False, False, False, False, True, False, False]],
['control: normal cycle',
[500,
[[0, True, False],
[300, True, True],
[400, True, True],
[500, False, True],
[600, False, False],
[700, True, True]]],
[False, True, True, False, False, True]],
['control: late second hand',
[200, [[0, True, False], [300, True, True], [400, False, False], [500, True, False], [600, True, True]]],
[False, False, False, False, True]],
['control: exact window',
[200, [[0, False, True], [200, True, True], [300, False, False]]],
[False, True, False]],
['regression: tie down one button',
[200,
[[0, True, False],
[100, True, True],
[200, True, False],
[300, True, True],
[400, True, False],
[450, True, True]]],
[False, True, False, False, False, False]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['control: scenario 1',
[500,
[[0, True, False],
[150, True, False],
[250, True, False],
[300, False, False],
[350, False, True],
[650, False, True],
[850, False, False],
[1150, False, False],
[1350, True, False],
[1650, True, False],
[1800, True, False],
[1850, True, False],
[1900, False, True]]],
[False, False, False, False, False, False, False, False, False, False, False, False, False]]]]
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: simultaneous press then release one | [True, False, False] | [True, False, False] | Passed |
| regression: tie down one button | [False, True, False, False, False, False] | [False, True, False, False, False, False] | Passed |
| control: normal cycle | [False, True, True, False, False, True] | [False, True, True, False, False, True] | Passed |
| control: late second hand | [False, False, False, False, True] | [False, False, False, False, True] | Passed |
| control: exact window | [False, True, False] | [False, True, False] | Passed |
| control: scenario 1 | [False, True, True, True, True, True, False, False, True, True] | [False, True, True, True, True, True, False, False, True, True] | Passed |
| regression: scenario 2 | [True, False, False, False, False, False, False, False] | [True, False, False, False, False, False, False, False] | Passed |
| control: scenario 3 | [False, True, True, False, True, True, False, False, False, False, False, True, True] | [False, True, True, False, True, True, False, False, False, False, False, True, True] | Passed |
SHA-256 / 61ee98a624f8faa3bc459564b3e68de721b56894f221c70457b685d2f61630a0
Verification & scope
A deterministic bounded teaching model of one PLC instruction or rung pattern evaluated scan by scan. The stated contract is a stipulated toy convention, not a claim of conformance to any vendor controller or IEC 61131-3 runtime. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:44.297614+00:00.
Case digest / 9bd2c76e87f0ebba262f941596452097538baf5e18d8b5ccece64c7e4c5a78d0