FA-66876 / Railway interlocking logic / Open access
Four-aspect signal chain evaluation: diverging aspect cap · case 01
A diverging junction signal displays a preliminary caution or green.
ROOT CAUSE
The approach-control cap only lowers G, so a double yellow is still shown over the turnout.
THE FAILURE
The approach-control cap only lowers G, so a double yellow is still shown over the turnout.
Unsuccessful approach: Capping only YY lets a green be shown over the diverging route.
Case contract
Signals 0..n-1 each protect one block; occupied[i] is the block after signal i. Evaluate from the far end: a signal shows R if its block is occupied, else Y if the next signal is R, R if the next signal is DARK, YY if the next is Y, otherwise G. Beyond the last signal the next aspect is G on an open line and R at a buffer stop. A diverging signal is capped at Y. A signal whose red lamp has failed shows DARK instead of R.
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):
occ = x['occupied']
n = len(occ)
fail = set(x['lamp_fail'])
div = set(x['diverging'])
nxt = 'G' if x['open_end'] else 'R'
out = [None] * n
for i in range(n - 1, -1, -1):
if occ[i]:
a = 'R'
elif nxt in ('R', 'DARK'):
a = 'R' if nxt == 'DARK' else 'Y'
elif nxt == 'Y':
a = 'YY'
else:
a = 'G'
if i in div and a == 'G':
a = 'Y'
if a == 'R' and i in fail:
a = 'DARK'
out[i] = a
nxt = a
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 24', {'occupied': [False, False, False, True], 'lamp_fail': [1], 'diverging': [1], 'open_end': True}, ['YY', 'Y', 'Y', 'R']), ('control 9', {'occupied': [False, False, False, False, True, False, False], 'lamp_fail': [1], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'YY', 'Y', 'R', 'G', 'G']), ('boundary: dark signal ahead', {'occupied': [False, True], 'lamp_fail': [1], 'diverging': [], 'open_end': True}, ['R', 'DARK']), ('control 1', {'occupied': [False, True, False, False, False, False], 'lamp_fail': [0], 'diverging': [], 'open_end': False}, ['Y', 'R', 'G', 'G', 'YY', 'Y']), ('control 4', {'occupied': [False, True], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['Y', 'R']), ('control 7', {'occupied': [True, False, False, True], 'lamp_fail': [], 'diverging': [3], 'open_end': True}, ['R', 'YY', 'Y', 'R'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 64', {'occupied': [False, False, True, False, False, True], 'lamp_fail': [4], 'diverging': [0], 'open_end': False}, ['Y', 'Y', 'R', 'YY', 'Y', 'R']), ('control 34', {'occupied': [False, False, False, False, False, False, False], 'lamp_fail': [], 'diverging': [1], 'open_end': False}, ['YY', 'Y', 'G', 'G', 'G', 'YY', 'Y']), ('boundary: buffer stop at the end', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['G', 'YY', 'Y']), ('control 12', {'occupied': [False, False], 'lamp_fail': [0], 'diverging': [], 'open_end': False}, ['YY', 'Y']), ('control 15', {'occupied': [False, True], 'lamp_fail': [], 'diverging': [], 'open_end': True}, ['Y', 'R']), ('control 18', {'occupied': [False, False], 'lamp_fail': [], 'diverging': [], 'open_end': True}, ['G', 'G'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 28', {'occupied': [False, False, False, True, False, False, False], 'lamp_fail': [6], 'diverging': [1, 5], 'open_end': True}, ['YY', 'Y', 'Y', 'R', 'YY', 'Y', 'G']), ('control 44', {'occupied': [False, False], 'lamp_fail': [0, 1], 'diverging': [1], 'open_end': True}, ['YY', 'Y']), ('boundary: buffer stop at the end', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['G', 'YY', 'Y']), ('control 23', {'occupied': [True, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['R', 'Y', 'R']), ('control 26', {'occupied': [False, False, False, True], 'lamp_fail': [], 'diverging': [3], 'open_end': False}, ['G', 'YY', 'Y', 'R']), ('control 29', {'occupied': [False, True], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['Y', 'R'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('control 58', {'occupied': [True, False], 'lamp_fail': [0], 'diverging': [0, 1], 'open_end': True}, ['DARK', 'Y']), ('boundary: train two blocks ahead', {'occupied': [False, False, True, False], 'lamp_fail': [], 'diverging': [], 'open_end': True}, ['YY', 'Y', 'R', 'G']), ('boundary: dark signal at the buffer end', {'occupied': [False, False], 'lamp_fail': [1], 'diverging': [], 'open_end': False}, ['YY', 'Y']), ('control 34', {'occupied': [False, False, False, False, False, False, False], 'lamp_fail': [], 'diverging': [1], 'open_end': False}, ['YY', 'Y', 'G', 'G', 'G', 'YY', 'Y']), ('control 37', {'occupied': [False, False, False, False], 'lamp_fail': [0], 'diverging': [0, 3], 'open_end': True}, ['Y', 'G', 'YY', 'Y']), ('control 40', {'occupied': [False, False, False, False], 'lamp_fail': [0], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G', 'G'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 39', {'occupied': [False, False, True, False, False, True], 'lamp_fail': [0, 4], 'diverging': [3], 'open_end': False}, ['YY', 'Y', 'R', 'Y', 'Y', 'R']), ('control 21', {'occupied': [True, False, True, False, False, False], 'lamp_fail': [1, 5], 'diverging': [1, 3], 'open_end': True}, ['R', 'Y', 'R', 'Y', 'G', 'G']), ('boundary: dark signal ahead', {'occupied': [False, True], 'lamp_fail': [1], 'diverging': [], 'open_end': True}, ['R', 'DARK']), ('control 45', {'occupied': [True, False, False, True], 'lamp_fail': [3], 'diverging': [1], 'open_end': True}, ['R', 'Y', 'R', 'DARK']), ('control 48', {'occupied': [False, False, False, False], 'lamp_fail': [1], 'diverging': [2], 'open_end': True}, ['G', 'YY', 'Y', 'G']), ('control 51', {'occupied': [False, False, False, True], 'lamp_fail': [], 'diverging': [0, 2, 3], 'open_end': True}, ['Y', 'YY', 'Y', 'R'])]]
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: diverging junction behind a double yellow | ['YY', 'Y', 'R'] | ['Y', 'Y', 'R'] | Failed |
| boundary: diverging junction behind a green | ['Y', 'G', 'G'] | ['Y', 'G', 'G'] | Passed |
| sampled regression 24 | ['G', 'YY', 'Y', 'R'] | ['YY', 'Y', 'Y', 'R'] | Failed |
| control 9 | ['Y', 'G', 'YY', 'Y', 'R', 'G', 'G'] | ['Y', 'G', 'YY', 'Y', 'R', 'G', 'G'] | Passed |
| boundary: dark signal ahead | ['R', 'DARK'] | ['R', 'DARK'] | Passed |
| control 1 | ['Y', 'R', 'G', 'G', 'YY', 'Y'] | ['Y', 'R', 'G', 'G', 'YY', 'Y'] | Passed |
| control 4 | ['Y', 'R'] | ['Y', 'R'] | Passed |
| control 7 | ['R', 'YY', 'Y', 'R'] | ['R', 'YY', 'Y', 'R'] | Passed |
SHA-256 / 7f316bf6cb48444f8bc837be1b07da3c7c83fead2b876ac3ad3a98b59331c787
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
occ = x['occupied']
n = len(occ)
fail = set(x['lamp_fail'])
div = set(x['diverging'])
nxt = 'G' if x['open_end'] else 'R'
out = [None] * n
for i in range(n - 1, -1, -1):
if occ[i]:
a = 'R'
elif nxt in ('R', 'DARK'):
a = 'R' if nxt == 'DARK' else 'Y'
elif nxt == 'Y':
a = 'YY'
else:
a = 'G'
if i in div and a == 'YY':
a = 'Y'
if a == 'R' and i in fail:
a = 'DARK'
out[i] = a
nxt = a
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 24', {'occupied': [False, False, False, True], 'lamp_fail': [1], 'diverging': [1], 'open_end': True}, ['YY', 'Y', 'Y', 'R']), ('control 9', {'occupied': [False, False, False, False, True, False, False], 'lamp_fail': [1], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'YY', 'Y', 'R', 'G', 'G']), ('boundary: dark signal ahead', {'occupied': [False, True], 'lamp_fail': [1], 'diverging': [], 'open_end': True}, ['R', 'DARK']), ('control 1', {'occupied': [False, True, False, False, False, False], 'lamp_fail': [0], 'diverging': [], 'open_end': False}, ['Y', 'R', 'G', 'G', 'YY', 'Y']), ('control 4', {'occupied': [False, True], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['Y', 'R']), ('control 7', {'occupied': [True, False, False, True], 'lamp_fail': [], 'diverging': [3], 'open_end': True}, ['R', 'YY', 'Y', 'R'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 64', {'occupied': [False, False, True, False, False, True], 'lamp_fail': [4], 'diverging': [0], 'open_end': False}, ['Y', 'Y', 'R', 'YY', 'Y', 'R']), ('control 34', {'occupied': [False, False, False, False, False, False, False], 'lamp_fail': [], 'diverging': [1], 'open_end': False}, ['YY', 'Y', 'G', 'G', 'G', 'YY', 'Y']), ('boundary: buffer stop at the end', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['G', 'YY', 'Y']), ('control 12', {'occupied': [False, False], 'lamp_fail': [0], 'diverging': [], 'open_end': False}, ['YY', 'Y']), ('control 15', {'occupied': [False, True], 'lamp_fail': [], 'diverging': [], 'open_end': True}, ['Y', 'R']), ('control 18', {'occupied': [False, False], 'lamp_fail': [], 'diverging': [], 'open_end': True}, ['G', 'G'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 28', {'occupied': [False, False, False, True, False, False, False], 'lamp_fail': [6], 'diverging': [1, 5], 'open_end': True}, ['YY', 'Y', 'Y', 'R', 'YY', 'Y', 'G']), ('control 44', {'occupied': [False, False], 'lamp_fail': [0, 1], 'diverging': [1], 'open_end': True}, ['YY', 'Y']), ('boundary: buffer stop at the end', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['G', 'YY', 'Y']), ('control 23', {'occupied': [True, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['R', 'Y', 'R']), ('control 26', {'occupied': [False, False, False, True], 'lamp_fail': [], 'diverging': [3], 'open_end': False}, ['G', 'YY', 'Y', 'R']), ('control 29', {'occupied': [False, True], 'lamp_fail': [], 'diverging': [], 'open_end': False}, ['Y', 'R'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('control 58', {'occupied': [True, False], 'lamp_fail': [0], 'diverging': [0, 1], 'open_end': True}, ['DARK', 'Y']), ('boundary: train two blocks ahead', {'occupied': [False, False, True, False], 'lamp_fail': [], 'diverging': [], 'open_end': True}, ['YY', 'Y', 'R', 'G']), ('boundary: dark signal at the buffer end', {'occupied': [False, False], 'lamp_fail': [1], 'diverging': [], 'open_end': False}, ['YY', 'Y']), ('control 34', {'occupied': [False, False, False, False, False, False, False], 'lamp_fail': [], 'diverging': [1], 'open_end': False}, ['YY', 'Y', 'G', 'G', 'G', 'YY', 'Y']), ('control 37', {'occupied': [False, False, False, False], 'lamp_fail': [0], 'diverging': [0, 3], 'open_end': True}, ['Y', 'G', 'YY', 'Y']), ('control 40', {'occupied': [False, False, False, False], 'lamp_fail': [0], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G', 'G'])], [('regression: diverging junction behind a double yellow', {'occupied': [False, False, True], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'Y', 'R']), ('boundary: diverging junction behind a green', {'occupied': [False, False, False], 'lamp_fail': [], 'diverging': [0], 'open_end': True}, ['Y', 'G', 'G']), ('sampled regression 39', {'occupied': [False, False, True, False, False, True], 'lamp_fail': [0, 4], 'diverging': [3], 'open_end': False}, ['YY', 'Y', 'R', 'Y', 'Y', 'R']), ('control 21', {'occupied': [True, False, True, False, False, False], 'lamp_fail': [1, 5], 'diverging': [1, 3], 'open_end': True}, ['R', 'Y', 'R', 'Y', 'G', 'G']), ('boundary: dark signal ahead', {'occupied': [False, True], 'lamp_fail': [1], 'diverging': [], 'open_end': True}, ['R', 'DARK']), ('control 45', {'occupied': [True, False, False, True], 'lamp_fail': [3], 'diverging': [1], 'open_end': True}, ['R', 'Y', 'R', 'DARK']), ('control 48', {'occupied': [False, False, False, False], 'lamp_fail': [1], 'diverging': [2], 'open_end': True}, ['G', 'YY', 'Y', 'G']), ('control 51', {'occupied': [False, False, False, True], 'lamp_fail': [], 'diverging': [0, 2, 3], 'open_end': True}, ['Y', 'YY', 'Y', 'R'])]]
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: diverging junction behind a double yellow | ['Y', 'Y', 'R'] | ['Y', 'Y', 'R'] | Passed |
| boundary: diverging junction behind a green | ['G', 'G', 'G'] | ['Y', 'G', 'G'] | Failed |
| sampled regression 24 | ['YY', 'Y', 'Y', 'R'] | ['YY', 'Y', 'Y', 'R'] | Passed |
| control 9 | ['G', 'G', 'YY', 'Y', 'R', 'G', 'G'] | ['Y', 'G', 'YY', 'Y', 'R', 'G', 'G'] | Failed |
| boundary: dark signal ahead | ['R', 'DARK'] | ['R', 'DARK'] | Passed |
| control 1 | ['Y', 'R', 'G', 'G', 'YY', 'Y'] | ['Y', 'R', 'G', 'G', 'YY', 'Y'] | Passed |
| control 4 | ['Y', 'R'] | ['Y', 'R'] | Passed |
| control 7 | ['R', 'YY', 'Y', 'R'] | ['R', 'YY', 'Y', 'R'] | Passed |
SHA-256 / 591a68ed6e22e141d402626ac6830943b629a7dce86dae1c1ffe1ee93ebddb9d
HELD IN THE MEMBER ARCHIVE
The verified repair and its recorded checks are member-only.
This mechanism has 8 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.
Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.
Member access is invitation-based. Sign in with your invited account to inspect the repair.
Sign in to the archive ↗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:47.730242+00:00.
Case digest / 726cac81eb7209c2d1f5b38dc13ac0652e484c3b849bba6fc64846ce6f633952