FA-92286 / PLC ladder logic scan cycles / Open access
Two-hand press keeps running after one hand is removed · case 01
The press stroke continues with only one button held.
ROOT CAUSE
The single-button branch does not drop the run output.
VERIFIED REPAIR
Drop the run output whenever either button is released.
Unsuccessful approach: Dropping run only when a first-press time was recorded misses presses started simultaneously.
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:
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: 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]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['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: 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]],
['regression: 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: 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: 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: 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: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: 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]],
['regression: 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: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: scenario 5',
[500,
[[0, True, False],
[150, False, False],
[350, True, False],
[500, False, False],
[800, True, True],
[950, False, False],
[1000, True, True],
[1200, True, False],
[1250, False, True],
[1300, False, True],
[1450, True, True],
[1650, True, False],
[1950, False, False],
[2250, False, True]]],
[False, False, False, False, True, False, True, False, False, False, False, False, False, False]],
['regression: 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 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 2',
[250,
[[0, False, False],
[150, False, False],
[350, False, False],
[400, True, True],
[600, True, False],
[750, False, True],
[1050, True, True],
[1150, True, True],
[1350, True, False],
[1400, False, False],
[1550, True, False]]],
[False, False, False, True, False, False, False, False, False, False, False]]],
[['regression: scenario 3',
[200,
[[0, False, False],
[300, False, False],
[350, False, True],
[650, True, False],
[700, False, False],
[1000, False, True],
[1050, False, False],
[1150, False, True],
[1300, True, True],
[1600, False, True],
[1700, False, True],
[1900, False, False],
[2200, True, True],
[2250, False, True]]],
[False, False, False, False, False, False, False, False, True, False, False, False, True, False]],
['regression: scenario 8',
[250,
[[0, False, True],
[50, False, False],
[200, True, True],
[350, False, False],
[450, True, True],
[500, False, True],
[650, False, True],
[800, False, False],
[900, False, False],
[1000, True, True],
[1150, False, True],
[1450, False, True],
[1500, True, True]]],
[False, False, True, False, True, False, False, False, False, True, False, False, False]],
['regression: 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',
[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 3',
[200,
[[0, False, False],
[300, False, False],
[400, True, False],
[600, True, True],
[750, True, True],
[800, True, True],
[850, True, False],
[1000, True, True],
[1150, False, False],
[1450, False, False]]],
[False, False, False, True, True, True, False, False, False, False]],
['regression: scenario 9',
[250,
[[0, True, False],
[300, True, True],
[500, False, False],
[700, False, False],
[1000, False, True],
[1150, True, True],
[1450, True, True],
[1600, False, True],
[1900, False, False],
[2050, True, True],
[2200, True, False],
[2350, True, False]]],
[False, False, False, False, False, True, True, False, False, True, False, False]],
['regression: 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: normal cycle | [False, True, True, True, False, True] | [False, True, True, False, False, True] | Failed |
| regression: simultaneous press then release one | [True, True, True] | [True, False, False] | Failed |
| 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 |
| regression: tie down one button | [False, True, True, True, True, True] | [False, True, False, False, False, False] | Failed |
| regression: scenario 1 | [False, True, True, True, True, True, True, False, True, True] | [False, True, True, True, True, True, False, False, True, True] | Failed |
| regression: scenario 2 | [True, True, True, False, False, False, False, False] | [True, False, False, False, False, False, False, False] | Failed |
| regression: scenario 3 | [False, True, True, False, True, True, True, True, False, False, False, True, True] | [False, True, True, False, True, True, False, False, False, False, False, True, True] | Failed |
SHA-256 / a3e472127f75da82293bc1fd941ea041cc879366ab676dcdd0a2a10f60d37eb6
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 = run and first_press is None
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: 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]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['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: 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]],
['regression: 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: 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: 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: 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: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: 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]],
['regression: 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: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: scenario 5',
[500,
[[0, True, False],
[150, False, False],
[350, True, False],
[500, False, False],
[800, True, True],
[950, False, False],
[1000, True, True],
[1200, True, False],
[1250, False, True],
[1300, False, True],
[1450, True, True],
[1650, True, False],
[1950, False, False],
[2250, False, True]]],
[False, False, False, False, True, False, True, False, False, False, False, False, False, False]],
['regression: 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 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 2',
[250,
[[0, False, False],
[150, False, False],
[350, False, False],
[400, True, True],
[600, True, False],
[750, False, True],
[1050, True, True],
[1150, True, True],
[1350, True, False],
[1400, False, False],
[1550, True, False]]],
[False, False, False, True, False, False, False, False, False, False, False]]],
[['regression: scenario 3',
[200,
[[0, False, False],
[300, False, False],
[350, False, True],
[650, True, False],
[700, False, False],
[1000, False, True],
[1050, False, False],
[1150, False, True],
[1300, True, True],
[1600, False, True],
[1700, False, True],
[1900, False, False],
[2200, True, True],
[2250, False, True]]],
[False, False, False, False, False, False, False, False, True, False, False, False, True, False]],
['regression: scenario 8',
[250,
[[0, False, True],
[50, False, False],
[200, True, True],
[350, False, False],
[450, True, True],
[500, False, True],
[650, False, True],
[800, False, False],
[900, False, False],
[1000, True, True],
[1150, False, True],
[1450, False, True],
[1500, True, True]]],
[False, False, True, False, True, False, False, False, False, True, False, False, False]],
['regression: 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',
[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 3',
[200,
[[0, False, False],
[300, False, False],
[400, True, False],
[600, True, True],
[750, True, True],
[800, True, True],
[850, True, False],
[1000, True, True],
[1150, False, False],
[1450, False, False]]],
[False, False, False, True, True, True, False, False, False, False]],
['regression: scenario 9',
[250,
[[0, True, False],
[300, True, True],
[500, False, False],
[700, False, False],
[1000, False, True],
[1150, True, True],
[1450, True, True],
[1600, False, True],
[1900, False, False],
[2050, True, True],
[2200, True, False],
[2350, True, False]]],
[False, False, False, False, False, True, True, False, False, True, False, False]],
['regression: 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: normal cycle | [False, True, True, False, False, True] | [False, True, True, False, False, True] | Passed |
| regression: simultaneous press then release one | [True, True, True] | [True, False, False] | Failed |
| 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 |
| regression: tie down one button | [False, True, False, False, False, False] | [False, True, False, False, False, False] | Passed |
| regression: scenario 1 | [False, True, True, True, True, True, True, False, True, True] | [False, True, True, True, True, True, False, False, True, True] | Failed |
| regression: scenario 2 | [True, True, True, False, False, False, False, False] | [True, False, False, False, False, False, False, False] | Failed |
| regression: scenario 3 | [False, True, True, False, True, True, True, False, False, False, False, True, True] | [False, True, True, False, True, True, False, False, False, False, False, True, True] | Failed |
SHA-256 / 23727c2add1fe8b971dfd277379179afb140f93bf679fe764eb1aa9842b6ed82
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: 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]],
['regression: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['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: 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]],
['regression: 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: 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: 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: 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: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: 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]],
['regression: 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: simultaneous press then release one',
[200, [[0, True, True], [100, True, False], [200, True, True]]],
[True, False, False]],
['regression: scenario 5',
[500,
[[0, True, False],
[150, False, False],
[350, True, False],
[500, False, False],
[800, True, True],
[950, False, False],
[1000, True, True],
[1200, True, False],
[1250, False, True],
[1300, False, True],
[1450, True, True],
[1650, True, False],
[1950, False, False],
[2250, False, True]]],
[False, False, False, False, True, False, True, False, False, False, False, False, False, False]],
['regression: 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 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 2',
[250,
[[0, False, False],
[150, False, False],
[350, False, False],
[400, True, True],
[600, True, False],
[750, False, True],
[1050, True, True],
[1150, True, True],
[1350, True, False],
[1400, False, False],
[1550, True, False]]],
[False, False, False, True, False, False, False, False, False, False, False]]],
[['regression: scenario 3',
[200,
[[0, False, False],
[300, False, False],
[350, False, True],
[650, True, False],
[700, False, False],
[1000, False, True],
[1050, False, False],
[1150, False, True],
[1300, True, True],
[1600, False, True],
[1700, False, True],
[1900, False, False],
[2200, True, True],
[2250, False, True]]],
[False, False, False, False, False, False, False, False, True, False, False, False, True, False]],
['regression: scenario 8',
[250,
[[0, False, True],
[50, False, False],
[200, True, True],
[350, False, False],
[450, True, True],
[500, False, True],
[650, False, True],
[800, False, False],
[900, False, False],
[1000, True, True],
[1150, False, True],
[1450, False, True],
[1500, True, True]]],
[False, False, True, False, True, False, False, False, False, True, False, False, False]],
['regression: 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',
[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 3',
[200,
[[0, False, False],
[300, False, False],
[400, True, False],
[600, True, True],
[750, True, True],
[800, True, True],
[850, True, False],
[1000, True, True],
[1150, False, False],
[1450, False, False]]],
[False, False, False, True, True, True, False, False, False, False]],
['regression: scenario 9',
[250,
[[0, True, False],
[300, True, True],
[500, False, False],
[700, False, False],
[1000, False, True],
[1150, True, True],
[1450, True, True],
[1600, False, True],
[1900, False, False],
[2050, True, True],
[2200, True, False],
[2350, True, False]]],
[False, False, False, False, False, True, True, False, False, True, False, False]],
['regression: 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: normal cycle | [False, True, True, False, False, True] | [False, True, True, False, False, True] | Passed |
| regression: simultaneous press then release one | [True, False, False] | [True, False, False] | 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 |
| regression: tie down one button | [False, True, False, False, False, False] | [False, True, False, False, False, False] | Passed |
| regression: 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 |
| regression: 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 / 8fdd4086b1d84e8cc737d0c0a03f9ba3cfd6c2f7280755ea68a02ffbae09f103
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.337352+00:00.
Case digest / 64eaa34b3111ae6b5349c506930c9414c7dd220092d74bf46e7e467546ba3ff1