FA-67041 / Railway interlocking logic / Open access
Absolute block bell-code working: clearing point condition · case 01
Line clear is given while the clearing point beyond the home signal is fouled.
ROOT CAUSE
The acceptance ignores the clearing point.
VERIFIED REPAIR
Accept an offer only from normal with the clearing point clear.
Unsuccessful approach: Allowing re-acceptance from line_clear silently swallows a duplicate offer that should be refused.
Case contract
A block section between two boxes is normal, line_clear or train_on_line. An offer is accepted (line_clear) only from normal with the clearing point clear, otherwise alarm refused. A departure without line clear raises unauthorised-departure; any departure puts the train on line. arrive_complete returns train_on_line to normal; arrive_no_tail raises tail-missing and keeps the block occupied. cancel only withdraws line_clear. cp_blocked/cp_clear set the clearing point.
Why this case matters
Interlocking logic decides whether trains may be given authority; a wrong decision at this point either grants unsafe movements or strands traffic.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
state = 'normal'
cp = True
alarms = []
for ev in x['events']:
if ev == 'cp_blocked':
cp = False
elif ev == 'cp_clear':
cp = True
elif ev == 'offer':
if state == 'normal':
state = 'line_clear'
else:
alarms.append('refused')
elif ev == 'depart':
if state != 'line_clear':
alarms.append('unauthorised-departure')
state = 'train_on_line'
elif ev == 'arrive_complete':
if state == 'train_on_line':
state = 'normal'
elif ev == 'arrive_no_tail':
alarms.append('tail-missing')
elif ev == 'cancel':
if state == 'line_clear':
state = 'normal'
return {'state': state, 'alarms': alarms}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('sampled regression 10', {'events': ['arrive_complete', 'cp_blocked', 'offer', 'arrive_complete', 'depart']}, {'state': 'train_on_line', 'alarms': ['refused', 'unauthorised-departure']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('control 4', {'events': ['cancel', 'arrive_no_tail', 'offer', 'offer', 'depart', 'arrive_no_tail']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused', 'tail-missing']}), ('boundary: offer with clearing point fouled', {'events': ['cp_blocked', 'offer', 'cp_clear', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('control 1', {'events': ['cp_blocked', 'depart', 'arrive_no_tail', 'cp_clear', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure']}), ('control 7', {'events': ['offer', 'arrive_no_tail', 'arrive_complete', 'cp_clear', 'cp_clear', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['tail-missing', 'tail-missing']}), ('control 13', {'events': ['depart', 'offer', 'arrive_complete', 'arrive_complete', 'cp_blocked', 'cp_clear', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure']})], [('sampled regression 33', {'events': ['arrive_no_tail', 'cp_blocked', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['tail-missing', 'refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 76', {'events': ['offer', 'offer', 'cp_blocked', 'cancel', 'cp_blocked', 'cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused', 'refused']}), ('control 46', {'events': ['offer', 'offer', 'arrive_complete', 'arrive_no_tail', 'cp_clear', 'arrive_complete', 'cancel', 'arrive_no_tail', 'offer', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['refused', 'tail-missing', 'tail-missing', 'tail-missing']}), ('boundary: second train into occupied block', {'events': ['offer', 'depart', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure']}), ('control 12', {'events': ['depart', 'cp_clear', 'arrive_complete', 'depart', 'cp_blocked']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'unauthorised-departure']}), ('control 15', {'events': ['arrive_complete', 'cp_clear', 'cancel']}, {'state': 'normal', 'alarms': []}), ('control 18', {'events': ['cp_clear', 'depart', 'offer', 'cp_blocked']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'refused']})], [('sampled regression 36', {'events': ['cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 53', {'events': ['offer', 'offer', 'arrive_complete', 'arrive_complete', 'offer', 'cancel', 'cancel', 'cp_blocked', 'arrive_no_tail', 'offer']}, {'state': 'normal', 'alarms': ['refused', 'refused', 'tail-missing', 'refused']}), ('control 69', {'events': ['offer', 'offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused', 'refused']}), ('boundary: train arrives without tail lamp', {'events': ['offer', 'depart', 'arrive_no_tail', 'offer']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused']}), ('control 23', {'events': ['depart', 'cp_blocked', 'arrive_no_tail', 'offer', 'cp_clear']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'tail-missing', 'refused']}), ('control 26', {'events': ['cp_clear', 'offer', 'cp_clear', 'cp_clear', 'cp_blocked', 'cp_clear', 'cp_blocked', 'depart']}, {'state': 'train_on_line', 'alarms': []}), ('control 29', {'events': ['arrive_complete', 'depart', 'offer', 'depart', 'arrive_complete', 'arrive_no_tail', 'cp_clear']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure', 'tail-missing']})], [('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 36', {'events': ['cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('control 11', {'events': ['arrive_no_tail', 'offer', 'offer', 'arrive_complete', 'cancel', 'arrive_no_tail', 'depart', 'cp_blocked', 'cp_clear']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused', 'tail-missing', 'unauthorised-departure']}), ('boundary: cancel of line clear', {'events': ['offer', 'cancel', 'offer']}, {'state': 'line_clear', 'alarms': []}), ('control 34', {'events': ['cp_blocked', 'arrive_no_tail', 'cancel', 'cp_clear', 'offer', 'arrive_complete', 'cp_blocked']}, {'state': 'line_clear', 'alarms': ['tail-missing']}), ('control 37', {'events': ['offer', 'depart', 'cp_clear', 'cp_clear']}, {'state': 'train_on_line', 'alarms': []}), ('control 40', {'events': ['arrive_complete', 'cp_clear', 'arrive_no_tail']}, {'state': 'normal', 'alarms': ['tail-missing']})], [('sampled regression 51', {'events': ['cp_clear', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 72', {'events': ['offer', 'depart', 'arrive_complete', 'cp_blocked', 'offer', 'offer', 'arrive_no_tail', 'cancel']}, {'state': 'normal', 'alarms': ['refused', 'refused', 'tail-missing']}), ('control 49', {'events': ['offer', 'cp_clear', 'offer', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['refused', 'tail-missing']}), ('boundary: offer with clearing point fouled', {'events': ['cp_blocked', 'offer', 'cp_clear', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('control 48', {'events': ['cp_clear', 'cp_blocked', 'arrive_no_tail', 'depart', 'offer', 'offer', 'arrive_no_tail', 'offer']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'unauthorised-departure', 'refused', 'refused', 'tail-missing', 'refused']}), ('control 54', {'events': ['offer', 'arrive_no_tail', 'offer', 'depart', 'arrive_complete', 'offer', 'cp_blocked']}, {'state': 'line_clear', 'alarms': ['tail-missing', 'refused']})]]
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 |
|---|---|---|---|
| sampled regression 10 | {'alarms': [], 'state': 'train_on_line'} | {'alarms': ['refused', 'unauthorised-departure'], 'state': 'train_on_line'} | Failed |
| boundary: second offer while line clear | {'alarms': ['refused'], 'state': 'line_clear'} | {'alarms': ['refused'], 'state': 'line_clear'} | Passed |
| sampled regression 45 | {'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused'], 'state': 'line_clear'} | {'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused'], 'state': 'normal'} | Failed |
| control 4 | {'alarms': ['tail-missing', 'refused', 'tail-missing'], 'state': 'train_on_line'} | {'alarms': ['tail-missing', 'refused', 'tail-missing'], 'state': 'train_on_line'} | Passed |
| boundary: offer with clearing point fouled | {'alarms': ['refused'], 'state': 'line_clear'} | {'alarms': ['refused'], 'state': 'line_clear'} | Passed |
| control 1 | {'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
| control 7 | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'line_clear'} | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'line_clear'} | Passed |
| control 13 | {'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
SHA-256 / 8d523ad0f309c6052dce42567df0af69f490491a0435f9018c9094a25b0e25ec
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
state = 'normal'
cp = True
alarms = []
for ev in x['events']:
if ev == 'cp_blocked':
cp = False
elif ev == 'cp_clear':
cp = True
elif ev == 'offer':
if state != 'train_on_line' and cp:
state = 'line_clear'
else:
alarms.append('refused')
elif ev == 'depart':
if state != 'line_clear':
alarms.append('unauthorised-departure')
state = 'train_on_line'
elif ev == 'arrive_complete':
if state == 'train_on_line':
state = 'normal'
elif ev == 'arrive_no_tail':
alarms.append('tail-missing')
elif ev == 'cancel':
if state == 'line_clear':
state = 'normal'
return {'state': state, 'alarms': alarms}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('sampled regression 10', {'events': ['arrive_complete', 'cp_blocked', 'offer', 'arrive_complete', 'depart']}, {'state': 'train_on_line', 'alarms': ['refused', 'unauthorised-departure']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('control 4', {'events': ['cancel', 'arrive_no_tail', 'offer', 'offer', 'depart', 'arrive_no_tail']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused', 'tail-missing']}), ('boundary: offer with clearing point fouled', {'events': ['cp_blocked', 'offer', 'cp_clear', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('control 1', {'events': ['cp_blocked', 'depart', 'arrive_no_tail', 'cp_clear', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure']}), ('control 7', {'events': ['offer', 'arrive_no_tail', 'arrive_complete', 'cp_clear', 'cp_clear', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['tail-missing', 'tail-missing']}), ('control 13', {'events': ['depart', 'offer', 'arrive_complete', 'arrive_complete', 'cp_blocked', 'cp_clear', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure']})], [('sampled regression 33', {'events': ['arrive_no_tail', 'cp_blocked', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['tail-missing', 'refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 76', {'events': ['offer', 'offer', 'cp_blocked', 'cancel', 'cp_blocked', 'cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused', 'refused']}), ('control 46', {'events': ['offer', 'offer', 'arrive_complete', 'arrive_no_tail', 'cp_clear', 'arrive_complete', 'cancel', 'arrive_no_tail', 'offer', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['refused', 'tail-missing', 'tail-missing', 'tail-missing']}), ('boundary: second train into occupied block', {'events': ['offer', 'depart', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure']}), ('control 12', {'events': ['depart', 'cp_clear', 'arrive_complete', 'depart', 'cp_blocked']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'unauthorised-departure']}), ('control 15', {'events': ['arrive_complete', 'cp_clear', 'cancel']}, {'state': 'normal', 'alarms': []}), ('control 18', {'events': ['cp_clear', 'depart', 'offer', 'cp_blocked']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'refused']})], [('sampled regression 36', {'events': ['cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 53', {'events': ['offer', 'offer', 'arrive_complete', 'arrive_complete', 'offer', 'cancel', 'cancel', 'cp_blocked', 'arrive_no_tail', 'offer']}, {'state': 'normal', 'alarms': ['refused', 'refused', 'tail-missing', 'refused']}), ('control 69', {'events': ['offer', 'offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused', 'refused']}), ('boundary: train arrives without tail lamp', {'events': ['offer', 'depart', 'arrive_no_tail', 'offer']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused']}), ('control 23', {'events': ['depart', 'cp_blocked', 'arrive_no_tail', 'offer', 'cp_clear']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'tail-missing', 'refused']}), ('control 26', {'events': ['cp_clear', 'offer', 'cp_clear', 'cp_clear', 'cp_blocked', 'cp_clear', 'cp_blocked', 'depart']}, {'state': 'train_on_line', 'alarms': []}), ('control 29', {'events': ['arrive_complete', 'depart', 'offer', 'depart', 'arrive_complete', 'arrive_no_tail', 'cp_clear']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure', 'tail-missing']})], [('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 36', {'events': ['cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('control 11', {'events': ['arrive_no_tail', 'offer', 'offer', 'arrive_complete', 'cancel', 'arrive_no_tail', 'depart', 'cp_blocked', 'cp_clear']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused', 'tail-missing', 'unauthorised-departure']}), ('boundary: cancel of line clear', {'events': ['offer', 'cancel', 'offer']}, {'state': 'line_clear', 'alarms': []}), ('control 34', {'events': ['cp_blocked', 'arrive_no_tail', 'cancel', 'cp_clear', 'offer', 'arrive_complete', 'cp_blocked']}, {'state': 'line_clear', 'alarms': ['tail-missing']}), ('control 37', {'events': ['offer', 'depart', 'cp_clear', 'cp_clear']}, {'state': 'train_on_line', 'alarms': []}), ('control 40', {'events': ['arrive_complete', 'cp_clear', 'arrive_no_tail']}, {'state': 'normal', 'alarms': ['tail-missing']})], [('sampled regression 51', {'events': ['cp_clear', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 72', {'events': ['offer', 'depart', 'arrive_complete', 'cp_blocked', 'offer', 'offer', 'arrive_no_tail', 'cancel']}, {'state': 'normal', 'alarms': ['refused', 'refused', 'tail-missing']}), ('control 49', {'events': ['offer', 'cp_clear', 'offer', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['refused', 'tail-missing']}), ('boundary: offer with clearing point fouled', {'events': ['cp_blocked', 'offer', 'cp_clear', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('control 48', {'events': ['cp_clear', 'cp_blocked', 'arrive_no_tail', 'depart', 'offer', 'offer', 'arrive_no_tail', 'offer']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'unauthorised-departure', 'refused', 'refused', 'tail-missing', 'refused']}), ('control 54', {'events': ['offer', 'arrive_no_tail', 'offer', 'depart', 'arrive_complete', 'offer', 'cp_blocked']}, {'state': 'line_clear', 'alarms': ['tail-missing', 'refused']})]]
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 |
|---|---|---|---|
| sampled regression 10 | {'alarms': ['refused', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['refused', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
| boundary: second offer while line clear | {'alarms': [], 'state': 'line_clear'} | {'alarms': ['refused'], 'state': 'line_clear'} | Failed |
| sampled regression 45 | {'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused'], 'state': 'normal'} | {'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused'], 'state': 'normal'} | Passed |
| control 4 | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'train_on_line'} | {'alarms': ['tail-missing', 'refused', 'tail-missing'], 'state': 'train_on_line'} | Failed |
| boundary: offer with clearing point fouled | {'alarms': ['refused'], 'state': 'line_clear'} | {'alarms': ['refused'], 'state': 'line_clear'} | Passed |
| control 1 | {'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
| control 7 | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'line_clear'} | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'line_clear'} | Passed |
| control 13 | {'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
SHA-256 / 5c98dcc52b2bedd402847b42edc24d464d853f0a397008bda78509551d7b3cca
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
state = 'normal'
cp = True
alarms = []
for ev in x['events']:
if ev == 'cp_blocked':
cp = False
elif ev == 'cp_clear':
cp = True
elif ev == 'offer':
if state == 'normal' and cp:
state = 'line_clear'
else:
alarms.append('refused')
elif ev == 'depart':
if state != 'line_clear':
alarms.append('unauthorised-departure')
state = 'train_on_line'
elif ev == 'arrive_complete':
if state == 'train_on_line':
state = 'normal'
elif ev == 'arrive_no_tail':
alarms.append('tail-missing')
elif ev == 'cancel':
if state == 'line_clear':
state = 'normal'
return {'state': state, 'alarms': alarms}
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('sampled regression 10', {'events': ['arrive_complete', 'cp_blocked', 'offer', 'arrive_complete', 'depart']}, {'state': 'train_on_line', 'alarms': ['refused', 'unauthorised-departure']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('control 4', {'events': ['cancel', 'arrive_no_tail', 'offer', 'offer', 'depart', 'arrive_no_tail']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused', 'tail-missing']}), ('boundary: offer with clearing point fouled', {'events': ['cp_blocked', 'offer', 'cp_clear', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('control 1', {'events': ['cp_blocked', 'depart', 'arrive_no_tail', 'cp_clear', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure']}), ('control 7', {'events': ['offer', 'arrive_no_tail', 'arrive_complete', 'cp_clear', 'cp_clear', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['tail-missing', 'tail-missing']}), ('control 13', {'events': ['depart', 'offer', 'arrive_complete', 'arrive_complete', 'cp_blocked', 'cp_clear', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure']})], [('sampled regression 33', {'events': ['arrive_no_tail', 'cp_blocked', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['tail-missing', 'refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 76', {'events': ['offer', 'offer', 'cp_blocked', 'cancel', 'cp_blocked', 'cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused', 'refused']}), ('control 46', {'events': ['offer', 'offer', 'arrive_complete', 'arrive_no_tail', 'cp_clear', 'arrive_complete', 'cancel', 'arrive_no_tail', 'offer', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['refused', 'tail-missing', 'tail-missing', 'tail-missing']}), ('boundary: second train into occupied block', {'events': ['offer', 'depart', 'depart']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure']}), ('control 12', {'events': ['depart', 'cp_clear', 'arrive_complete', 'depart', 'cp_blocked']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'unauthorised-departure']}), ('control 15', {'events': ['arrive_complete', 'cp_clear', 'cancel']}, {'state': 'normal', 'alarms': []}), ('control 18', {'events': ['cp_clear', 'depart', 'offer', 'cp_blocked']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'refused']})], [('sampled regression 36', {'events': ['cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 53', {'events': ['offer', 'offer', 'arrive_complete', 'arrive_complete', 'offer', 'cancel', 'cancel', 'cp_blocked', 'arrive_no_tail', 'offer']}, {'state': 'normal', 'alarms': ['refused', 'refused', 'tail-missing', 'refused']}), ('control 69', {'events': ['offer', 'offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused', 'refused']}), ('boundary: train arrives without tail lamp', {'events': ['offer', 'depart', 'arrive_no_tail', 'offer']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused']}), ('control 23', {'events': ['depart', 'cp_blocked', 'arrive_no_tail', 'offer', 'cp_clear']}, {'state': 'train_on_line', 'alarms': ['unauthorised-departure', 'tail-missing', 'refused']}), ('control 26', {'events': ['cp_clear', 'offer', 'cp_clear', 'cp_clear', 'cp_blocked', 'cp_clear', 'cp_blocked', 'depart']}, {'state': 'train_on_line', 'alarms': []}), ('control 29', {'events': ['arrive_complete', 'depart', 'offer', 'depart', 'arrive_complete', 'arrive_no_tail', 'cp_clear']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure', 'tail-missing']})], [('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 36', {'events': ['cp_blocked', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('control 11', {'events': ['arrive_no_tail', 'offer', 'offer', 'arrive_complete', 'cancel', 'arrive_no_tail', 'depart', 'cp_blocked', 'cp_clear']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'refused', 'tail-missing', 'unauthorised-departure']}), ('boundary: cancel of line clear', {'events': ['offer', 'cancel', 'offer']}, {'state': 'line_clear', 'alarms': []}), ('control 34', {'events': ['cp_blocked', 'arrive_no_tail', 'cancel', 'cp_clear', 'offer', 'arrive_complete', 'cp_blocked']}, {'state': 'line_clear', 'alarms': ['tail-missing']}), ('control 37', {'events': ['offer', 'depart', 'cp_clear', 'cp_clear']}, {'state': 'train_on_line', 'alarms': []}), ('control 40', {'events': ['arrive_complete', 'cp_clear', 'arrive_no_tail']}, {'state': 'normal', 'alarms': ['tail-missing']})], [('sampled regression 51', {'events': ['cp_clear', 'cp_blocked', 'offer']}, {'state': 'normal', 'alarms': ['refused']}), ('boundary: second offer while line clear', {'events': ['offer', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 72', {'events': ['offer', 'depart', 'arrive_complete', 'cp_blocked', 'offer', 'offer', 'arrive_no_tail', 'cancel']}, {'state': 'normal', 'alarms': ['refused', 'refused', 'tail-missing']}), ('control 49', {'events': ['offer', 'cp_clear', 'offer', 'arrive_no_tail']}, {'state': 'line_clear', 'alarms': ['refused', 'tail-missing']}), ('boundary: offer with clearing point fouled', {'events': ['cp_blocked', 'offer', 'cp_clear', 'offer']}, {'state': 'line_clear', 'alarms': ['refused']}), ('sampled regression 45', {'events': ['depart', 'depart', 'depart', 'cp_blocked', 'depart', 'offer', 'arrive_complete', 'offer']}, {'state': 'normal', 'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused']}), ('control 48', {'events': ['cp_clear', 'cp_blocked', 'arrive_no_tail', 'depart', 'offer', 'offer', 'arrive_no_tail', 'offer']}, {'state': 'train_on_line', 'alarms': ['tail-missing', 'unauthorised-departure', 'refused', 'refused', 'tail-missing', 'refused']}), ('control 54', {'events': ['offer', 'arrive_no_tail', 'offer', 'depart', 'arrive_complete', 'offer', 'cp_blocked']}, {'state': 'line_clear', 'alarms': ['tail-missing', 'refused']})]]
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 |
|---|---|---|---|
| sampled regression 10 | {'alarms': ['refused', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['refused', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
| boundary: second offer while line clear | {'alarms': ['refused'], 'state': 'line_clear'} | {'alarms': ['refused'], 'state': 'line_clear'} | Passed |
| sampled regression 45 | {'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused'], 'state': 'normal'} | {'alarms': ['unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'unauthorised-departure', 'refused', 'refused'], 'state': 'normal'} | Passed |
| control 4 | {'alarms': ['tail-missing', 'refused', 'tail-missing'], 'state': 'train_on_line'} | {'alarms': ['tail-missing', 'refused', 'tail-missing'], 'state': 'train_on_line'} | Passed |
| boundary: offer with clearing point fouled | {'alarms': ['refused'], 'state': 'line_clear'} | {'alarms': ['refused'], 'state': 'line_clear'} | Passed |
| control 1 | {'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['unauthorised-departure', 'tail-missing', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
| control 7 | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'line_clear'} | {'alarms': ['tail-missing', 'tail-missing'], 'state': 'line_clear'} | Passed |
| control 13 | {'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure'], 'state': 'train_on_line'} | {'alarms': ['unauthorised-departure', 'refused', 'unauthorised-departure'], 'state': 'train_on_line'} | Passed |
SHA-256 / 4375f8bfe1cc3e5cf4c25f4dda60168e4fdad8f44142a981f21916685bb81400
Verification & scope
Stipulated toy interlocking contract for a bounded teaching model; it makes no claim of conformance to any railway signalling standard and omits real safety cases. 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:47:49.273932+00:00.
Case digest / 4e04c717792e0caddc43a4c081654908e5b18b00981dd7f6ec9c5978f56ba99c