FA-93911 / Shift rostering labor rules / Open access
One-hour meal gap counted as a split shift · case 01
A standard 60-minute unpaid break triggers a split premium.
ROOT CAUSE
The split gap test is inclusive of 60 minutes.
VERIFIED REPAIR
Only gaps longer than 60 minutes are splits.
Unsuccessful approach: Measuring the gap from segment start to next start counts nearly every multi-segment day as split.
Case contract
Worked segments [start, end] of one workday (unordered, possibly overlapping) and a minimum wage in cents. Overlapping or touching segments merge. Spread is last end minus first start. Each gap longer than 60 minutes is a split. A spread over 600 minutes earns one hour of minimum wage and replaces any split premium; otherwise each split earns min_wage // 2, for at most two splits. Return [spread, splits, premium].
Why this case matters
Spread-of-hours and split-shift premiums are frequent roster-pay rules that depend on correct interval handling.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(segments, min_wage):
seg = sorted(segments)
merged = []
for s, e in seg:
if merged and s <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], e)
else:
merged.append([s, e])
spread = merged[-1][1] - merged[0][0]
splits = sum(1 for a, b in zip(merged, merged[1:]) if b[0] - a[1] >= 60)
if spread > 600:
premium = min_wage
else:
premium = (min_wage // 2) * min(splits, 2)
return [spread, splits, premium]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: split gap threshold 1', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
('regression variant: split gap threshold 2',
[[[480, 540], [630, 870], [930, 1170], [1170, 1470], [490, 500]], 1701], [990, 1, 1701]),
('partial repair guard 3', [[[870, 1110], [1170, 1290], [480, 540], [720, 780]], 1655], [810, 2, 1655]),
('boundary control 4', [[[420, 1020]], 1600], [600, 0, 0]),
('boundary control 5', [[[420, 480], [550, 600], [700, 760], [840, 900], [980, 1010]], 1655],
[590, 4, 1654]),
('normal control 6', [[[810, 870], [840, 1020], [600, 720], [990, 1290]], 1600], [690, 1, 1600]),
('normal control 7', [[[630, 690], [760, 770], [720, 780], [750, 810], [480, 660]], 1600], [330, 0, 0]),
('normal control 8', [[[601, 841], [480, 540], [1081, 1261], [931, 991]], 1701], [781, 3, 1701])],
[('regression: split gap threshold 1', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
('regression variant: split gap threshold 2', [[[899, 959], [420, 720], [779, 839]], 1701], [539, 0, 0]),
('partial repair guard 3', [[[610, 620], [360, 540], [600, 900]], 1655], [540, 0, 0]),
('boundary control 4', [[[900, 1000], [420, 600], [700, 800]], 1520], [580, 2, 1520]),
('boundary control 5', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
('normal control 6', [[[450, 510], [360, 420], [370, 380]], 1520], [150, 0, 0]),
('normal control 7', [[[960, 1140], [420, 480], [480, 720]], 1520], [720, 1, 1520]),
('normal control 8', [[[420, 600], [659, 719], [749, 869]], 1655], [449, 0, 0])],
[('regression: split gap threshold 1', [[[600, 780], [1139, 1379], [839, 1079]], 1701], [779, 0, 1701]),
('regression variant: split gap threshold 2', [[[1080, 1140], [420, 600], [660, 960], [1140, 1320]], 1600],
[900, 1, 1600]),
('partial repair guard 3', [[[420, 480], [570, 870], [430, 440], [929, 1229]], 1655], [809, 1, 1655]),
('boundary control 4', [[[600, 630], [700, 730], [800, 830], [900, 930]], 1701], [330, 3, 1700]),
('boundary control 5', [[[420, 1021]], 1600], [601, 0, 1600]),
('normal control 6', [[[570, 810], [900, 1200], [910, 920], [420, 540]], 1701], [780, 1, 1701]),
('normal control 7', [[[420, 600], [840, 1080]], 1600], [660, 1, 1600]),
('normal control 8', [[[690, 750], [480, 720]], 1701], [270, 0, 0])],
[('regression: split gap threshold 1', [[[1079, 1319], [1378, 1618], [720, 1020], [600, 660]], 1701],
[1018, 0, 1701]),
('regression variant: split gap threshold 2', [[[1021, 1261], [781, 961], [480, 720]], 1600],
[781, 1, 1600]),
('partial repair guard 3', [[[570, 810], [900, 1200], [910, 920], [420, 540]], 1701], [780, 1, 1701]),
('boundary control 4', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
('boundary control 5', [[[420, 450], [520, 550], [620, 650], [720, 750]], 1600], [330, 3, 1600]),
('normal control 6', [[[750, 810], [900, 1140], [1110, 1230], [480, 780]], 1600], [750, 1, 1600]),
('normal control 7', [[[430, 440], [420, 720]], 1655], [300, 0, 0]),
('normal control 8', [[[480, 540], [630, 870], [930, 1170], [1170, 1470], [490, 500]], 1701],
[990, 1, 1701])],
[('regression: split gap threshold 1', [[[1110, 1410], [600, 900], [990, 1050], [1471, 1771]], 1600],
[1171, 2, 1600]),
('regression variant: split gap threshold 2', [[[1351, 1591], [1021, 1261], [600, 840], [900, 960]], 1701],
[991, 2, 1701]),
('partial repair guard 3', [[[720, 900], [490, 500], [480, 660]], 1655], [420, 0, 0]),
('boundary control 4', [[[600, 900], [610, 620], [1000, 1100]], 1655], [500, 1, 827]),
('boundary control 5', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
('normal control 6', [[[600, 840]], 1520], [240, 0, 0]),
('normal control 7', [[[360, 420], [420, 540]], 1520], [180, 0, 0]),
('normal control 8', [[[1081, 1321], [720, 1020], [360, 600]], 1655], [961, 2, 1655])]]
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: split gap threshold 1 | [600, 1, 800] | [600, 0, 0] | Failed |
| regression variant: split gap threshold 2 | [990, 2, 1701] | [990, 1, 1701] | Failed |
| partial repair guard 3 | [810, 3, 1655] | [810, 2, 1655] | Failed |
| boundary control 4 | [600, 0, 0] | [600, 0, 0] | Passed |
| boundary control 5 | [590, 4, 1654] | [590, 4, 1654] | Passed |
| normal control 6 | [690, 1, 1600] | [690, 1, 1600] | Passed |
| normal control 7 | [330, 0, 0] | [330, 0, 0] | Passed |
| normal control 8 | [781, 3, 1701] | [781, 3, 1701] | Passed |
SHA-256 / 76ba0199e3a495553bc54b9142c9ba430d601980b0d43aa5d1278df470049426
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(segments, min_wage):
seg = sorted(segments)
merged = []
for s, e in seg:
if merged and s <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], e)
else:
merged.append([s, e])
spread = merged[-1][1] - merged[0][0]
splits = sum(1 for a, b in zip(merged, merged[1:]) if b[0] - a[0] > 60)
if spread > 600:
premium = min_wage
else:
premium = (min_wage // 2) * min(splits, 2)
return [spread, splits, premium]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: split gap threshold 1', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
('regression variant: split gap threshold 2',
[[[480, 540], [630, 870], [930, 1170], [1170, 1470], [490, 500]], 1701], [990, 1, 1701]),
('partial repair guard 3', [[[870, 1110], [1170, 1290], [480, 540], [720, 780]], 1655], [810, 2, 1655]),
('boundary control 4', [[[420, 1020]], 1600], [600, 0, 0]),
('boundary control 5', [[[420, 480], [550, 600], [700, 760], [840, 900], [980, 1010]], 1655],
[590, 4, 1654]),
('normal control 6', [[[810, 870], [840, 1020], [600, 720], [990, 1290]], 1600], [690, 1, 1600]),
('normal control 7', [[[630, 690], [760, 770], [720, 780], [750, 810], [480, 660]], 1600], [330, 0, 0]),
('normal control 8', [[[601, 841], [480, 540], [1081, 1261], [931, 991]], 1701], [781, 3, 1701])],
[('regression: split gap threshold 1', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
('regression variant: split gap threshold 2', [[[899, 959], [420, 720], [779, 839]], 1701], [539, 0, 0]),
('partial repair guard 3', [[[610, 620], [360, 540], [600, 900]], 1655], [540, 0, 0]),
('boundary control 4', [[[900, 1000], [420, 600], [700, 800]], 1520], [580, 2, 1520]),
('boundary control 5', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
('normal control 6', [[[450, 510], [360, 420], [370, 380]], 1520], [150, 0, 0]),
('normal control 7', [[[960, 1140], [420, 480], [480, 720]], 1520], [720, 1, 1520]),
('normal control 8', [[[420, 600], [659, 719], [749, 869]], 1655], [449, 0, 0])],
[('regression: split gap threshold 1', [[[600, 780], [1139, 1379], [839, 1079]], 1701], [779, 0, 1701]),
('regression variant: split gap threshold 2', [[[1080, 1140], [420, 600], [660, 960], [1140, 1320]], 1600],
[900, 1, 1600]),
('partial repair guard 3', [[[420, 480], [570, 870], [430, 440], [929, 1229]], 1655], [809, 1, 1655]),
('boundary control 4', [[[600, 630], [700, 730], [800, 830], [900, 930]], 1701], [330, 3, 1700]),
('boundary control 5', [[[420, 1021]], 1600], [601, 0, 1600]),
('normal control 6', [[[570, 810], [900, 1200], [910, 920], [420, 540]], 1701], [780, 1, 1701]),
('normal control 7', [[[420, 600], [840, 1080]], 1600], [660, 1, 1600]),
('normal control 8', [[[690, 750], [480, 720]], 1701], [270, 0, 0])],
[('regression: split gap threshold 1', [[[1079, 1319], [1378, 1618], [720, 1020], [600, 660]], 1701],
[1018, 0, 1701]),
('regression variant: split gap threshold 2', [[[1021, 1261], [781, 961], [480, 720]], 1600],
[781, 1, 1600]),
('partial repair guard 3', [[[570, 810], [900, 1200], [910, 920], [420, 540]], 1701], [780, 1, 1701]),
('boundary control 4', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
('boundary control 5', [[[420, 450], [520, 550], [620, 650], [720, 750]], 1600], [330, 3, 1600]),
('normal control 6', [[[750, 810], [900, 1140], [1110, 1230], [480, 780]], 1600], [750, 1, 1600]),
('normal control 7', [[[430, 440], [420, 720]], 1655], [300, 0, 0]),
('normal control 8', [[[480, 540], [630, 870], [930, 1170], [1170, 1470], [490, 500]], 1701],
[990, 1, 1701])],
[('regression: split gap threshold 1', [[[1110, 1410], [600, 900], [990, 1050], [1471, 1771]], 1600],
[1171, 2, 1600]),
('regression variant: split gap threshold 2', [[[1351, 1591], [1021, 1261], [600, 840], [900, 960]], 1701],
[991, 2, 1701]),
('partial repair guard 3', [[[720, 900], [490, 500], [480, 660]], 1655], [420, 0, 0]),
('boundary control 4', [[[600, 900], [610, 620], [1000, 1100]], 1655], [500, 1, 827]),
('boundary control 5', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
('normal control 6', [[[600, 840]], 1520], [240, 0, 0]),
('normal control 7', [[[360, 420], [420, 540]], 1520], [180, 0, 0]),
('normal control 8', [[[1081, 1321], [720, 1020], [360, 600]], 1655], [961, 2, 1655])]]
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: split gap threshold 1 | [600, 1, 800] | [600, 0, 0] | Failed |
| regression variant: split gap threshold 2 | [990, 2, 1701] | [990, 1, 1701] | Failed |
| partial repair guard 3 | [810, 3, 1655] | [810, 2, 1655] | Failed |
| boundary control 4 | [600, 0, 0] | [600, 0, 0] | Passed |
| boundary control 5 | [590, 4, 1654] | [590, 4, 1654] | Passed |
| normal control 6 | [690, 1, 1600] | [690, 1, 1600] | Passed |
| normal control 7 | [330, 1, 800] | [330, 0, 0] | Failed |
| normal control 8 | [781, 3, 1701] | [781, 3, 1701] | Passed |
SHA-256 / e327a47503309b377c901eebac688e820ff7767372c652b1a3060cac9df2ad8c
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(segments, min_wage):
seg = sorted(segments)
merged = []
for s, e in seg:
if merged and s <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], e)
else:
merged.append([s, e])
spread = merged[-1][1] - merged[0][0]
splits = sum(1 for a, b in zip(merged, merged[1:]) if b[0] - a[1] > 60)
if spread > 600:
premium = min_wage
else:
premium = (min_wage // 2) * min(splits, 2)
return [spread, splits, premium]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: split gap threshold 1', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
('regression variant: split gap threshold 2',
[[[480, 540], [630, 870], [930, 1170], [1170, 1470], [490, 500]], 1701], [990, 1, 1701]),
('partial repair guard 3', [[[870, 1110], [1170, 1290], [480, 540], [720, 780]], 1655], [810, 2, 1655]),
('boundary control 4', [[[420, 1020]], 1600], [600, 0, 0]),
('boundary control 5', [[[420, 480], [550, 600], [700, 760], [840, 900], [980, 1010]], 1655],
[590, 4, 1654]),
('normal control 6', [[[810, 870], [840, 1020], [600, 720], [990, 1290]], 1600], [690, 1, 1600]),
('normal control 7', [[[630, 690], [760, 770], [720, 780], [750, 810], [480, 660]], 1600], [330, 0, 0]),
('normal control 8', [[[601, 841], [480, 540], [1081, 1261], [931, 991]], 1701], [781, 3, 1701])],
[('regression: split gap threshold 1', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
('regression variant: split gap threshold 2', [[[899, 959], [420, 720], [779, 839]], 1701], [539, 0, 0]),
('partial repair guard 3', [[[610, 620], [360, 540], [600, 900]], 1655], [540, 0, 0]),
('boundary control 4', [[[900, 1000], [420, 600], [700, 800]], 1520], [580, 2, 1520]),
('boundary control 5', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
('normal control 6', [[[450, 510], [360, 420], [370, 380]], 1520], [150, 0, 0]),
('normal control 7', [[[960, 1140], [420, 480], [480, 720]], 1520], [720, 1, 1520]),
('normal control 8', [[[420, 600], [659, 719], [749, 869]], 1655], [449, 0, 0])],
[('regression: split gap threshold 1', [[[600, 780], [1139, 1379], [839, 1079]], 1701], [779, 0, 1701]),
('regression variant: split gap threshold 2', [[[1080, 1140], [420, 600], [660, 960], [1140, 1320]], 1600],
[900, 1, 1600]),
('partial repair guard 3', [[[420, 480], [570, 870], [430, 440], [929, 1229]], 1655], [809, 1, 1655]),
('boundary control 4', [[[600, 630], [700, 730], [800, 830], [900, 930]], 1701], [330, 3, 1700]),
('boundary control 5', [[[420, 1021]], 1600], [601, 0, 1600]),
('normal control 6', [[[570, 810], [900, 1200], [910, 920], [420, 540]], 1701], [780, 1, 1701]),
('normal control 7', [[[420, 600], [840, 1080]], 1600], [660, 1, 1600]),
('normal control 8', [[[690, 750], [480, 720]], 1701], [270, 0, 0])],
[('regression: split gap threshold 1', [[[1079, 1319], [1378, 1618], [720, 1020], [600, 660]], 1701],
[1018, 0, 1701]),
('regression variant: split gap threshold 2', [[[1021, 1261], [781, 961], [480, 720]], 1600],
[781, 1, 1600]),
('partial repair guard 3', [[[570, 810], [900, 1200], [910, 920], [420, 540]], 1701], [780, 1, 1701]),
('boundary control 4', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
('boundary control 5', [[[420, 450], [520, 550], [620, 650], [720, 750]], 1600], [330, 3, 1600]),
('normal control 6', [[[750, 810], [900, 1140], [1110, 1230], [480, 780]], 1600], [750, 1, 1600]),
('normal control 7', [[[430, 440], [420, 720]], 1655], [300, 0, 0]),
('normal control 8', [[[480, 540], [630, 870], [930, 1170], [1170, 1470], [490, 500]], 1701],
[990, 1, 1701])],
[('regression: split gap threshold 1', [[[1110, 1410], [600, 900], [990, 1050], [1471, 1771]], 1600],
[1171, 2, 1600]),
('regression variant: split gap threshold 2', [[[1351, 1591], [1021, 1261], [600, 840], [900, 960]], 1701],
[991, 2, 1701]),
('partial repair guard 3', [[[720, 900], [490, 500], [480, 660]], 1655], [420, 0, 0]),
('boundary control 4', [[[600, 900], [610, 620], [1000, 1100]], 1655], [500, 1, 827]),
('boundary control 5', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
('normal control 6', [[[600, 840]], 1520], [240, 0, 0]),
('normal control 7', [[[360, 420], [420, 540]], 1520], [180, 0, 0]),
('normal control 8', [[[1081, 1321], [720, 1020], [360, 600]], 1655], [961, 2, 1655])]]
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: split gap threshold 1 | [600, 0, 0] | [600, 0, 0] | Passed |
| regression variant: split gap threshold 2 | [990, 1, 1701] | [990, 1, 1701] | Passed |
| partial repair guard 3 | [810, 2, 1655] | [810, 2, 1655] | Passed |
| boundary control 4 | [600, 0, 0] | [600, 0, 0] | Passed |
| boundary control 5 | [590, 4, 1654] | [590, 4, 1654] | Passed |
| normal control 6 | [690, 1, 1600] | [690, 1, 1600] | Passed |
| normal control 7 | [330, 0, 0] | [330, 0, 0] | Passed |
| normal control 8 | [781, 3, 1701] | [781, 3, 1701] | Passed |
SHA-256 / ddc1366f7e2f0bd51a0353ec710d5240913e3667debc0e294a349410e536c301
Verification & scope
Stipulated toy labor rule for a bounded roster model; it is not legal advice and does not claim conformance with any jurisdiction, award, or collective agreement. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:59.449876+00:00.
Case digest / 20ee40cdb0bc70c12e384d341fb4c31c92d74e9d0a1cbc77428d3577ac68493c