FA-68321 / Traffic signal timing plans / Open access
Overlap phase indication: a parent in yellow keeps the overlap green · case 01
Overlap phase indication returns a wrong result when a parent in yellow keeps the overlap green.
ROOT CAUSE
Yellow is treated as green for parents, so the overlap skips its own yellow and goes straight to red after the parent clears.
VERIFIED REPAIR
Restore the parent green test rule so that the step reads `if any(cur.get(p) == 'G' for p in par):`.
Unsuccessful approach: Checking only the lowest parent ignores the other parents.
Case contract
Input {parents, not_during, state: [[phase, G|Y|RC|R]] for timing phases, next: phases to be served next}. The overlap is red while any not_during phase is green. Otherwise it is green while any parent is green. If a parent is terminating (Y or RC) the overlap stays green when any next phase is a parent; otherwise it is yellow if any terminating parent is in yellow, else red. Otherwise red.
Why this case matters
Signal timing arithmetic is exact and integer or rational; a wrong rule silently produces unsafe or inefficient timing plans.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
par = set(x['parents'])
nd = set(x['not_during'])
cur = {ph: iv for ph, iv in x['state']}
if any(cur.get(p) == 'G' for p in nd):
return 'R'
if any(cur.get(p) in ('G', 'Y') for p in par):
return 'G'
terminating = [p for p in sorted(par) if cur.get(p) in ('Y', 'RC')]
if terminating:
if any(p in par for p in x['next']):
return 'G'
if any(cur[p] == 'Y' for p in terminating):
return 'Y'
return 'R'
return 'R'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [1, 3, 4], 'not_during': [], 'state': [[8, 'Y'], [7, 'G'], [3, 'Y']], 'next': []}, 'Y'), ({'parents': [1, 2, 3], 'not_during': [], 'state': [[3, 'G']], 'next': [6, 7]}, 'G'), ({'parents': [1, 7], 'not_during': [4, 6], 'state': [[2, 'G'], [1, 'Y'], [8, 'R']], 'next': [8]}, 'Y'), ({'parents': [2, 5, 7], 'not_during': [3, 8], 'state': [[8, 'R'], [5, 'G']], 'next': [4]}, 'G'), ({'parents': [5, 7], 'not_during': [8], 'state': [[4, 'RC']], 'next': [2]}, 'R'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y')], [({'parents': [3, 4, 7], 'not_during': [], 'state': [[3, 'Y']], 'next': [2, 8]}, 'Y'), ({'parents': [1, 2, 8], 'not_during': [6], 'state': [[8, 'G'], [4, 'Y']], 'next': [3]}, 'G'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [8], 'not_during': [], 'state': [[7, 'Y'], [3, 'G'], [1, 'R']], 'next': [4]}, 'R'), ({'parents': [4, 5, 7], 'not_during': [2], 'state': [[5, 'Y'], [2, 'RC'], [1, 'Y']], 'next': []}, 'Y'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [1, 2, 4], 'not_during': [3, 6], 'state': [[5, 'RC'], [2, 'G'], [1, 'RC']], 'next': [5]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G')], [({'parents': [2, 6, 8], 'not_during': [], 'state': [[7, 'G'], [8, 'G'], [1, 'Y']], 'next': [3, 5]}, 'G'), ({'parents': [3, 4, 5], 'not_during': [], 'state': [[4, 'G']], 'next': [6]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [2], 'not_during': [7], 'state': [[2, 'Y'], [6, 'R']], 'next': [3, 7]}, 'Y'), ({'parents': [2], 'not_during': [3, 6], 'state': [[7, 'G'], [2, 'G'], [6, 'Y']], 'next': [1, 4]}, 'G'), ({'parents': [5], 'not_during': [2, 4], 'state': [[1, 'R'], [5, 'Y'], [8, 'R']], 'next': []}, 'Y'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R')], [({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [5, 6, 8], 'not_during': [4], 'state': [[6, 'G']], 'next': [5, 8]}, 'G'), ({'parents': [3, 4, 6], 'not_during': [], 'state': [[7, 'Y'], [6, 'Y']], 'next': [2]}, 'Y'), ({'parents': [6], 'not_during': [], 'state': [[6, 'R'], [5, 'RC']], 'next': []}, 'R'), ({'parents': [4, 5, 8], 'not_during': [2, 3], 'state': [[6, 'G'], [5, 'G'], [8, 'Y']], 'next': [3]}, 'G'), ({'parents': [3, 4, 6], 'not_during': [], 'state': [[1, 'Y'], [4, 'RC'], [3, 'Y']], 'next': []}, 'Y'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R')], [({'parents': [1, 3, 8], 'not_during': [], 'state': [[2, 'G'], [4, 'Y'], [6, 'R']], 'next': [1]}, 'R'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [1, 7], 'not_during': [2, 5], 'state': [[7, 'G'], [3, 'Y']], 'next': []}, 'G'), ({'parents': [4, 5], 'not_during': [], 'state': [[5, 'Y'], [6, 'G']], 'next': []}, 'Y'), ({'parents': [3, 4], 'not_during': [], 'state': [[4, 'G'], [7, 'G'], [5, 'RC']], 'next': [2]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [2, 6], 'not_during': [4], 'state': [[3, 'G'], [2, 'Y'], [4, 'R']], 'next': []}, 'Y')]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check('timing oracle' + ' %d' % i, 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 |
|---|---|---|---|
| timing oracle 0 | G | G | Passed |
| timing oracle 1 | G | Y | Failed |
| timing oracle 2 | G | G | Passed |
| timing oracle 3 | G | Y | Failed |
| timing oracle 4 | G | G | Passed |
| timing oracle 5 | R | R | Passed |
| timing oracle 6 | R | R | Passed |
| timing oracle 7 | G | Y | Failed |
SHA-256 / 287cefcc66d5def1605ad9ad4ac885f826fd6948c63d02818b1d0cf7275b0515
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
par = set(x['parents'])
nd = set(x['not_during'])
cur = {ph: iv for ph, iv in x['state']}
if any(cur.get(p) == 'G' for p in nd):
return 'R'
if cur.get(min(par)) == 'G':
return 'G'
terminating = [p for p in sorted(par) if cur.get(p) in ('Y', 'RC')]
if terminating:
if any(p in par for p in x['next']):
return 'G'
if any(cur[p] == 'Y' for p in terminating):
return 'Y'
return 'R'
return 'R'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [1, 3, 4], 'not_during': [], 'state': [[8, 'Y'], [7, 'G'], [3, 'Y']], 'next': []}, 'Y'), ({'parents': [1, 2, 3], 'not_during': [], 'state': [[3, 'G']], 'next': [6, 7]}, 'G'), ({'parents': [1, 7], 'not_during': [4, 6], 'state': [[2, 'G'], [1, 'Y'], [8, 'R']], 'next': [8]}, 'Y'), ({'parents': [2, 5, 7], 'not_during': [3, 8], 'state': [[8, 'R'], [5, 'G']], 'next': [4]}, 'G'), ({'parents': [5, 7], 'not_during': [8], 'state': [[4, 'RC']], 'next': [2]}, 'R'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y')], [({'parents': [3, 4, 7], 'not_during': [], 'state': [[3, 'Y']], 'next': [2, 8]}, 'Y'), ({'parents': [1, 2, 8], 'not_during': [6], 'state': [[8, 'G'], [4, 'Y']], 'next': [3]}, 'G'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [8], 'not_during': [], 'state': [[7, 'Y'], [3, 'G'], [1, 'R']], 'next': [4]}, 'R'), ({'parents': [4, 5, 7], 'not_during': [2], 'state': [[5, 'Y'], [2, 'RC'], [1, 'Y']], 'next': []}, 'Y'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [1, 2, 4], 'not_during': [3, 6], 'state': [[5, 'RC'], [2, 'G'], [1, 'RC']], 'next': [5]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G')], [({'parents': [2, 6, 8], 'not_during': [], 'state': [[7, 'G'], [8, 'G'], [1, 'Y']], 'next': [3, 5]}, 'G'), ({'parents': [3, 4, 5], 'not_during': [], 'state': [[4, 'G']], 'next': [6]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [2], 'not_during': [7], 'state': [[2, 'Y'], [6, 'R']], 'next': [3, 7]}, 'Y'), ({'parents': [2], 'not_during': [3, 6], 'state': [[7, 'G'], [2, 'G'], [6, 'Y']], 'next': [1, 4]}, 'G'), ({'parents': [5], 'not_during': [2, 4], 'state': [[1, 'R'], [5, 'Y'], [8, 'R']], 'next': []}, 'Y'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R')], [({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [5, 6, 8], 'not_during': [4], 'state': [[6, 'G']], 'next': [5, 8]}, 'G'), ({'parents': [3, 4, 6], 'not_during': [], 'state': [[7, 'Y'], [6, 'Y']], 'next': [2]}, 'Y'), ({'parents': [6], 'not_during': [], 'state': [[6, 'R'], [5, 'RC']], 'next': []}, 'R'), ({'parents': [4, 5, 8], 'not_during': [2, 3], 'state': [[6, 'G'], [5, 'G'], [8, 'Y']], 'next': [3]}, 'G'), ({'parents': [3, 4, 6], 'not_during': [], 'state': [[1, 'Y'], [4, 'RC'], [3, 'Y']], 'next': []}, 'Y'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R')], [({'parents': [1, 3, 8], 'not_during': [], 'state': [[2, 'G'], [4, 'Y'], [6, 'R']], 'next': [1]}, 'R'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [1, 7], 'not_during': [2, 5], 'state': [[7, 'G'], [3, 'Y']], 'next': []}, 'G'), ({'parents': [4, 5], 'not_during': [], 'state': [[5, 'Y'], [6, 'G']], 'next': []}, 'Y'), ({'parents': [3, 4], 'not_during': [], 'state': [[4, 'G'], [7, 'G'], [5, 'RC']], 'next': [2]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [2, 6], 'not_during': [4], 'state': [[3, 'G'], [2, 'Y'], [4, 'R']], 'next': []}, 'Y')]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check('timing oracle' + ' %d' % i, 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 |
|---|---|---|---|
| timing oracle 0 | G | G | Passed |
| timing oracle 1 | Y | Y | Passed |
| timing oracle 2 | R | G | Failed |
| timing oracle 3 | Y | Y | Passed |
| timing oracle 4 | R | G | Failed |
| timing oracle 5 | R | R | Passed |
| timing oracle 6 | R | R | Passed |
| timing oracle 7 | Y | Y | Passed |
SHA-256 / d01cd98d8a0b023f21f32e493574f205d869a8ebfdff1bdaa59feac178a83c5a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
par = set(x['parents'])
nd = set(x['not_during'])
cur = {ph: iv for ph, iv in x['state']}
if any(cur.get(p) == 'G' for p in nd):
return 'R'
if any(cur.get(p) == 'G' for p in par):
return 'G'
terminating = [p for p in sorted(par) if cur.get(p) in ('Y', 'RC')]
if terminating:
if any(p in par for p in x['next']):
return 'G'
if any(cur[p] == 'Y' for p in terminating):
return 'Y'
return 'R'
return 'R'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [1, 3, 4], 'not_during': [], 'state': [[8, 'Y'], [7, 'G'], [3, 'Y']], 'next': []}, 'Y'), ({'parents': [1, 2, 3], 'not_during': [], 'state': [[3, 'G']], 'next': [6, 7]}, 'G'), ({'parents': [1, 7], 'not_during': [4, 6], 'state': [[2, 'G'], [1, 'Y'], [8, 'R']], 'next': [8]}, 'Y'), ({'parents': [2, 5, 7], 'not_during': [3, 8], 'state': [[8, 'R'], [5, 'G']], 'next': [4]}, 'G'), ({'parents': [5, 7], 'not_during': [8], 'state': [[4, 'RC']], 'next': [2]}, 'R'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y')], [({'parents': [3, 4, 7], 'not_during': [], 'state': [[3, 'Y']], 'next': [2, 8]}, 'Y'), ({'parents': [1, 2, 8], 'not_during': [6], 'state': [[8, 'G'], [4, 'Y']], 'next': [3]}, 'G'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [8], 'not_during': [], 'state': [[7, 'Y'], [3, 'G'], [1, 'R']], 'next': [4]}, 'R'), ({'parents': [4, 5, 7], 'not_during': [2], 'state': [[5, 'Y'], [2, 'RC'], [1, 'Y']], 'next': []}, 'Y'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [1, 2, 4], 'not_during': [3, 6], 'state': [[5, 'RC'], [2, 'G'], [1, 'RC']], 'next': [5]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G')], [({'parents': [2, 6, 8], 'not_during': [], 'state': [[7, 'G'], [8, 'G'], [1, 'Y']], 'next': [3, 5]}, 'G'), ({'parents': [3, 4, 5], 'not_during': [], 'state': [[4, 'G']], 'next': [6]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [2], 'not_during': [7], 'state': [[2, 'Y'], [6, 'R']], 'next': [3, 7]}, 'Y'), ({'parents': [2], 'not_during': [3, 6], 'state': [[7, 'G'], [2, 'G'], [6, 'Y']], 'next': [1, 4]}, 'G'), ({'parents': [5], 'not_during': [2, 4], 'state': [[1, 'R'], [5, 'Y'], [8, 'R']], 'next': []}, 'Y'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R')], [({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [5, 6, 8], 'not_during': [4], 'state': [[6, 'G']], 'next': [5, 8]}, 'G'), ({'parents': [3, 4, 6], 'not_during': [], 'state': [[7, 'Y'], [6, 'Y']], 'next': [2]}, 'Y'), ({'parents': [6], 'not_during': [], 'state': [[6, 'R'], [5, 'RC']], 'next': []}, 'R'), ({'parents': [4, 5, 8], 'not_during': [2, 3], 'state': [[6, 'G'], [5, 'G'], [8, 'Y']], 'next': [3]}, 'G'), ({'parents': [3, 4, 6], 'not_during': [], 'state': [[1, 'Y'], [4, 'RC'], [3, 'Y']], 'next': []}, 'Y'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R')], [({'parents': [1, 3, 8], 'not_during': [], 'state': [[2, 'G'], [4, 'Y'], [6, 'R']], 'next': [1]}, 'R'), ({'parents': [4, 5], 'not_during': [], 'state': [[4, 'RC'], [5, 'Y']], 'next': [1]}, 'Y'), ({'parents': [2, 3], 'not_during': [5], 'state': [[3, 'G'], [5, 'G']], 'next': []}, 'R'), ({'parents': [1, 7], 'not_during': [2, 5], 'state': [[7, 'G'], [3, 'Y']], 'next': []}, 'G'), ({'parents': [4, 5], 'not_during': [], 'state': [[5, 'Y'], [6, 'G']], 'next': []}, 'Y'), ({'parents': [3, 4], 'not_during': [], 'state': [[4, 'G'], [7, 'G'], [5, 'RC']], 'next': [2]}, 'G'), ({'parents': [2, 3], 'not_during': [], 'state': [[2, 'RC'], [6, 'RC']], 'next': [3, 7]}, 'G'), ({'parents': [2, 6], 'not_during': [4], 'state': [[3, 'G'], [2, 'Y'], [4, 'R']], 'next': []}, 'Y')]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check('timing oracle' + ' %d' % i, 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 |
|---|---|---|---|
| timing oracle 0 | G | G | Passed |
| timing oracle 1 | Y | Y | Passed |
| timing oracle 2 | G | G | Passed |
| timing oracle 3 | Y | Y | Passed |
| timing oracle 4 | G | G | Passed |
| timing oracle 5 | R | R | Passed |
| timing oracle 6 | R | R | Passed |
| timing oracle 7 | Y | Y | Passed |
SHA-256 / 721b4084a10a787b0a6836193eeb19b838a958296c8b89f2293722939049caaf
Verification & scope
A deterministic, bounded toy model with a stipulated contract; it makes no claim of conformance to any agency manual or standard. 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:48:01.153159+00:00.
Case digest / a80a544574e6cc15d40dd1aea9e5d7b0976c2be268085882fbb14fd5b1482eb3