FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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