FA-93876 / Shift rostering labor rules / Open access
Double-time minutes also counted in the time-and-a-half band · case 01
Long days pay minutes past 12 hours at both 1.5x and 2x.
ROOT CAUSE
The 1.5x daily band is not capped at the 240 minutes between the 8- and 12-hour thresholds.
VERIFIED REPAIR
Cap the 1.5x daily band at 240 minutes.
Unsuccessful approach: Dropping the whole 1.5x band on days over 12 hours underpays hours 8 to 12.
Case contract
Seven daily worked minutes (Mon..Sun) and an hourly rate in cents. Daily: first 480 minutes regular, next 240 at 1.5x, beyond 720 at 2x. If all seven days are worked, day 7 pays its first 480 minutes at 1.5x and the rest at 2x. Regular minutes above 2400 in the week move to 1.5x (daily overtime minutes never count toward the weekly threshold). Pay is computed exactly and rounded half up to a cent once. Return [regular, ot15, ot2, pay_cents].
Why this case matters
Overtime classification mistakes are a classic payroll-roster defect: pyramiding, seventh-day rules and rounding stage all change what workers are paid.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(days, rate):
reg = ot15 = ot2 = 0
seventh = all(m > 0 for m in days)
for d, m in enumerate(days):
if d == 6 and seventh:
ot15 += min(m, 480)
ot2 += max(m - 480, 0)
continue
reg += min(m, 480)
ot15 += max(m - 480, 0)
ot2 += max(m - 720, 0)
excess = max(reg - 2400, 0)
reg -= excess
ot15 += excess
units = reg * rate * 2 + ot15 * rate * 3 + ot2 * rate * 4
pay = (units + 60) // 120
return [reg, ot15, ot2, pay]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: daily overtime band cap 1', [[900, 0, 0, 0, 0, 0, 0], 1725], [480, 240, 180, 34500]),
('regression variant: daily overtime band cap 2', [[480, 0, 480, 721, 720, 240, 720], 1810],
[2400, 960, 1, 115900]),
('partial repair guard 3', [[900, 720, 721, 600, 201, 720, 0], 2250], [2400, 1281, 181, 175631]),
('boundary control 4', [[480, 480, 480, 480, 480, 480, 0], 2000], [2400, 480, 0, 104000]),
('boundary control 5', [[9, 0, 0, 0, 0, 0, 0], 30], [9, 0, 0, 5]),
('normal control 6', [[600, 510, 300, 300, 510, 480, 300], 1725], [2400, 600, 0, 94875]),
('normal control 7', [[900, 721, 0, 900, 481, 450, 780], 30], [2400, 1411, 421, 2679]),
('normal control 8', [[600, 450, 721, 600, 481, 720, 480], 1500], [2400, 1651, 1, 121963])],
[('regression: daily overtime band cap 1', [[481, 119, 900, 256, 240, 20, 480], 2000],
[1595, 721, 180, 101217]),
('regression variant: daily overtime band cap 2', [[0, 240, 0, 540, 780, 0, 0], 30], [1200, 300, 60, 885]),
('partial repair guard 3', [[450, 0, 908, 480, 721, 540, 540], 2250], [2400, 1050, 189, 163238]),
('boundary control 4', [[0, 480, 480, 480, 480, 480, 480], 1500], [2400, 480, 0, 78000]),
('boundary control 5', [[480, 480, 480, 480, 480, 480, 0], 2000], [2400, 480, 0, 104000]),
('normal control 6', [[780, 480, 0, 240, 0, 0, 481], 1500], [1680, 241, 60, 54038]),
('normal control 7', [[510, 510, 480, 600, 480, 600, 480], 2000], [2400, 1260, 0, 143000]),
('normal control 8', [[721, 150, 480, 450, 504, 540, 450], 30], [2400, 894, 1, 1872])],
[('regression: daily overtime band cap 1', [[540, 450, 450, 900, 720, 240, 540], 2250],
[2400, 1200, 240, 175500]),
('regression variant: daily overtime band cap 2', [[480, 721, 240, 481, 498, 450, 481], 1725],
[2400, 949, 2, 110041]),
('partial repair guard 3', [[450, 780, 450, 480, 481, 481, 0], 1500], [2400, 662, 60, 87825]),
('boundary control 4', [[27, 0, 0, 0, 0, 0, 0], 30], [27, 0, 0, 14]),
('boundary control 5', [[0, 480, 480, 480, 480, 480, 480], 1500], [2400, 480, 0, 78000]),
('normal control 6', [[480, 480, 480, 480, 300, 480, 510], 90], [2400, 780, 30, 5445]),
('normal control 7', [[0, 37, 780, 0, 240, 240, 240], 30], [1237, 240, 60, 859]),
('normal control 8', [[900, 600, 900, 481, 737, 381, 780], 1500], [2400, 1702, 677, 157675])],
[('regression: daily overtime band cap 1', [[0, 721, 240, 480, 900, 480, 0], 2250],
[2160, 480, 181, 121575]),
('regression variant: daily overtime band cap 2', [[480, 600, 600, 720, 480, 780, 540], 2000],
[2400, 1680, 120, 172000]),
('partial repair guard 3', [[335, 540, 0, 900, 0, 540, 240], 1725], [2015, 360, 180, 83806]),
('boundary control 4', [[600, 600, 600, 600, 600, 0, 0], 1500], [2400, 600, 0, 82500]),
('boundary control 5', [[27, 0, 0, 0, 0, 0, 0], 30], [27, 0, 0, 14]),
('normal control 6', [[721, 150, 480, 450, 504, 540, 450], 30], [2400, 894, 1, 1872]),
('normal control 7', [[510, 480, 300, 300, 600, 300, 510], 1725], [2340, 630, 30, 96169]),
('normal control 8', [[300, 600, 510, 600, 510, 600, 600], 1810], [2400, 1200, 120, 133940])],
[('regression: daily overtime band cap 1', [[780, 780, 721, 450, 0, 0, 0], 2250], [1890, 720, 121, 120450]),
('regression variant: daily overtime band cap 2', [[480, 480, 481, 946, 720, 540, 450], 2000],
[2400, 1471, 226, 168617]),
('partial repair guard 3', [[0, 14, 481, 481, 720, 720, 900], 2250], [2400, 736, 180, 144900]),
('boundary control 4', [[540, 540, 540, 540, 540, 540, 60], 2000], [2400, 900, 0, 125000]),
('boundary control 5', [[600, 600, 600, 600, 600, 0, 0], 1500], [2400, 600, 0, 82500]),
('normal control 6', [[300, 480, 600, 300, 510, 510, 510], 2000], [2400, 780, 30, 121000]),
('normal control 7', [[300, 480, 600, 300, 480, 480, 480], 2000], [2400, 720, 0, 116000]),
('normal control 8', [[510, 480, 300, 510, 300, 480, 480], 30], [2400, 660, 0, 1695])]]
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: daily overtime band cap 1 | [480, 420, 180, 42263] | [480, 240, 180, 34500] | Failed |
| regression variant: daily overtime band cap 2 | [2400, 961, 1, 115946] | [2400, 960, 1, 115900] | Failed |
| partial repair guard 3 | [2400, 1462, 181, 185813] | [2400, 1281, 181, 175631] | Failed |
| boundary control 4 | [2400, 480, 0, 104000] | [2400, 480, 0, 104000] | Passed |
| boundary control 5 | [9, 0, 0, 5] | [9, 0, 0, 5] | Passed |
| normal control 6 | [2400, 600, 0, 94875] | [2400, 600, 0, 94875] | Passed |
| normal control 7 | [2400, 1832, 421, 2995] | [2400, 1411, 421, 2679] | Failed |
| normal control 8 | [2400, 1652, 1, 122000] | [2400, 1651, 1, 121963] | Failed |
SHA-256 / 1a4933728bd9cf7171185372208a142eb727f0452cf532c20ea3fe8222592f5b
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(days, rate):
reg = ot15 = ot2 = 0
seventh = all(m > 0 for m in days)
for d, m in enumerate(days):
if d == 6 and seventh:
ot15 += min(m, 480)
ot2 += max(m - 480, 0)
continue
reg += min(m, 480)
ot15 += min(max(m - 480, 0), 240) if m <= 720 else 0
ot2 += max(m - 720, 0)
excess = max(reg - 2400, 0)
reg -= excess
ot15 += excess
units = reg * rate * 2 + ot15 * rate * 3 + ot2 * rate * 4
pay = (units + 60) // 120
return [reg, ot15, ot2, pay]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: daily overtime band cap 1', [[900, 0, 0, 0, 0, 0, 0], 1725], [480, 240, 180, 34500]),
('regression variant: daily overtime band cap 2', [[480, 0, 480, 721, 720, 240, 720], 1810],
[2400, 960, 1, 115900]),
('partial repair guard 3', [[900, 720, 721, 600, 201, 720, 0], 2250], [2400, 1281, 181, 175631]),
('boundary control 4', [[480, 480, 480, 480, 480, 480, 0], 2000], [2400, 480, 0, 104000]),
('boundary control 5', [[9, 0, 0, 0, 0, 0, 0], 30], [9, 0, 0, 5]),
('normal control 6', [[600, 510, 300, 300, 510, 480, 300], 1725], [2400, 600, 0, 94875]),
('normal control 7', [[900, 721, 0, 900, 481, 450, 780], 30], [2400, 1411, 421, 2679]),
('normal control 8', [[600, 450, 721, 600, 481, 720, 480], 1500], [2400, 1651, 1, 121963])],
[('regression: daily overtime band cap 1', [[481, 119, 900, 256, 240, 20, 480], 2000],
[1595, 721, 180, 101217]),
('regression variant: daily overtime band cap 2', [[0, 240, 0, 540, 780, 0, 0], 30], [1200, 300, 60, 885]),
('partial repair guard 3', [[450, 0, 908, 480, 721, 540, 540], 2250], [2400, 1050, 189, 163238]),
('boundary control 4', [[0, 480, 480, 480, 480, 480, 480], 1500], [2400, 480, 0, 78000]),
('boundary control 5', [[480, 480, 480, 480, 480, 480, 0], 2000], [2400, 480, 0, 104000]),
('normal control 6', [[780, 480, 0, 240, 0, 0, 481], 1500], [1680, 241, 60, 54038]),
('normal control 7', [[510, 510, 480, 600, 480, 600, 480], 2000], [2400, 1260, 0, 143000]),
('normal control 8', [[721, 150, 480, 450, 504, 540, 450], 30], [2400, 894, 1, 1872])],
[('regression: daily overtime band cap 1', [[540, 450, 450, 900, 720, 240, 540], 2250],
[2400, 1200, 240, 175500]),
('regression variant: daily overtime band cap 2', [[480, 721, 240, 481, 498, 450, 481], 1725],
[2400, 949, 2, 110041]),
('partial repair guard 3', [[450, 780, 450, 480, 481, 481, 0], 1500], [2400, 662, 60, 87825]),
('boundary control 4', [[27, 0, 0, 0, 0, 0, 0], 30], [27, 0, 0, 14]),
('boundary control 5', [[0, 480, 480, 480, 480, 480, 480], 1500], [2400, 480, 0, 78000]),
('normal control 6', [[480, 480, 480, 480, 300, 480, 510], 90], [2400, 780, 30, 5445]),
('normal control 7', [[0, 37, 780, 0, 240, 240, 240], 30], [1237, 240, 60, 859]),
('normal control 8', [[900, 600, 900, 481, 737, 381, 780], 1500], [2400, 1702, 677, 157675])],
[('regression: daily overtime band cap 1', [[0, 721, 240, 480, 900, 480, 0], 2250],
[2160, 480, 181, 121575]),
('regression variant: daily overtime band cap 2', [[480, 600, 600, 720, 480, 780, 540], 2000],
[2400, 1680, 120, 172000]),
('partial repair guard 3', [[335, 540, 0, 900, 0, 540, 240], 1725], [2015, 360, 180, 83806]),
('boundary control 4', [[600, 600, 600, 600, 600, 0, 0], 1500], [2400, 600, 0, 82500]),
('boundary control 5', [[27, 0, 0, 0, 0, 0, 0], 30], [27, 0, 0, 14]),
('normal control 6', [[721, 150, 480, 450, 504, 540, 450], 30], [2400, 894, 1, 1872]),
('normal control 7', [[510, 480, 300, 300, 600, 300, 510], 1725], [2340, 630, 30, 96169]),
('normal control 8', [[300, 600, 510, 600, 510, 600, 600], 1810], [2400, 1200, 120, 133940])],
[('regression: daily overtime band cap 1', [[780, 780, 721, 450, 0, 0, 0], 2250], [1890, 720, 121, 120450]),
('regression variant: daily overtime band cap 2', [[480, 480, 481, 946, 720, 540, 450], 2000],
[2400, 1471, 226, 168617]),
('partial repair guard 3', [[0, 14, 481, 481, 720, 720, 900], 2250], [2400, 736, 180, 144900]),
('boundary control 4', [[540, 540, 540, 540, 540, 540, 60], 2000], [2400, 900, 0, 125000]),
('boundary control 5', [[600, 600, 600, 600, 600, 0, 0], 1500], [2400, 600, 0, 82500]),
('normal control 6', [[300, 480, 600, 300, 510, 510, 510], 2000], [2400, 780, 30, 121000]),
('normal control 7', [[300, 480, 600, 300, 480, 480, 480], 2000], [2400, 720, 0, 116000]),
('normal control 8', [[510, 480, 300, 510, 300, 480, 480], 30], [2400, 660, 0, 1695])]]
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: daily overtime band cap 1 | [480, 0, 180, 24150] | [480, 240, 180, 34500] | Failed |
| regression variant: daily overtime band cap 2 | [2400, 720, 1, 105040] | [2400, 960, 1, 115900] | Failed |
| partial repair guard 3 | [2400, 801, 181, 148631] | [2400, 1281, 181, 175631] | Failed |
| boundary control 4 | [2400, 480, 0, 104000] | [2400, 480, 0, 104000] | Passed |
| boundary control 5 | [9, 0, 0, 5] | [9, 0, 0, 5] | Passed |
| normal control 6 | [2400, 600, 0, 94875] | [2400, 600, 0, 94875] | Passed |
| normal control 7 | [2400, 451, 421, 1959] | [2400, 1411, 421, 2679] | Failed |
| normal control 8 | [2400, 1411, 1, 112963] | [2400, 1651, 1, 121963] | Failed |
SHA-256 / d4097d8a6d4735b52511e5801e6a12e9cb5d99a42681e350ce53a7fb0872511a
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(days, rate):
reg = ot15 = ot2 = 0
seventh = all(m > 0 for m in days)
for d, m in enumerate(days):
if d == 6 and seventh:
ot15 += min(m, 480)
ot2 += max(m - 480, 0)
continue
reg += min(m, 480)
ot15 += min(max(m - 480, 0), 240)
ot2 += max(m - 720, 0)
excess = max(reg - 2400, 0)
reg -= excess
ot15 += excess
units = reg * rate * 2 + ot15 * rate * 3 + ot2 * rate * 4
pay = (units + 60) // 120
return [reg, ot15, ot2, pay]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: daily overtime band cap 1', [[900, 0, 0, 0, 0, 0, 0], 1725], [480, 240, 180, 34500]),
('regression variant: daily overtime band cap 2', [[480, 0, 480, 721, 720, 240, 720], 1810],
[2400, 960, 1, 115900]),
('partial repair guard 3', [[900, 720, 721, 600, 201, 720, 0], 2250], [2400, 1281, 181, 175631]),
('boundary control 4', [[480, 480, 480, 480, 480, 480, 0], 2000], [2400, 480, 0, 104000]),
('boundary control 5', [[9, 0, 0, 0, 0, 0, 0], 30], [9, 0, 0, 5]),
('normal control 6', [[600, 510, 300, 300, 510, 480, 300], 1725], [2400, 600, 0, 94875]),
('normal control 7', [[900, 721, 0, 900, 481, 450, 780], 30], [2400, 1411, 421, 2679]),
('normal control 8', [[600, 450, 721, 600, 481, 720, 480], 1500], [2400, 1651, 1, 121963])],
[('regression: daily overtime band cap 1', [[481, 119, 900, 256, 240, 20, 480], 2000],
[1595, 721, 180, 101217]),
('regression variant: daily overtime band cap 2', [[0, 240, 0, 540, 780, 0, 0], 30], [1200, 300, 60, 885]),
('partial repair guard 3', [[450, 0, 908, 480, 721, 540, 540], 2250], [2400, 1050, 189, 163238]),
('boundary control 4', [[0, 480, 480, 480, 480, 480, 480], 1500], [2400, 480, 0, 78000]),
('boundary control 5', [[480, 480, 480, 480, 480, 480, 0], 2000], [2400, 480, 0, 104000]),
('normal control 6', [[780, 480, 0, 240, 0, 0, 481], 1500], [1680, 241, 60, 54038]),
('normal control 7', [[510, 510, 480, 600, 480, 600, 480], 2000], [2400, 1260, 0, 143000]),
('normal control 8', [[721, 150, 480, 450, 504, 540, 450], 30], [2400, 894, 1, 1872])],
[('regression: daily overtime band cap 1', [[540, 450, 450, 900, 720, 240, 540], 2250],
[2400, 1200, 240, 175500]),
('regression variant: daily overtime band cap 2', [[480, 721, 240, 481, 498, 450, 481], 1725],
[2400, 949, 2, 110041]),
('partial repair guard 3', [[450, 780, 450, 480, 481, 481, 0], 1500], [2400, 662, 60, 87825]),
('boundary control 4', [[27, 0, 0, 0, 0, 0, 0], 30], [27, 0, 0, 14]),
('boundary control 5', [[0, 480, 480, 480, 480, 480, 480], 1500], [2400, 480, 0, 78000]),
('normal control 6', [[480, 480, 480, 480, 300, 480, 510], 90], [2400, 780, 30, 5445]),
('normal control 7', [[0, 37, 780, 0, 240, 240, 240], 30], [1237, 240, 60, 859]),
('normal control 8', [[900, 600, 900, 481, 737, 381, 780], 1500], [2400, 1702, 677, 157675])],
[('regression: daily overtime band cap 1', [[0, 721, 240, 480, 900, 480, 0], 2250],
[2160, 480, 181, 121575]),
('regression variant: daily overtime band cap 2', [[480, 600, 600, 720, 480, 780, 540], 2000],
[2400, 1680, 120, 172000]),
('partial repair guard 3', [[335, 540, 0, 900, 0, 540, 240], 1725], [2015, 360, 180, 83806]),
('boundary control 4', [[600, 600, 600, 600, 600, 0, 0], 1500], [2400, 600, 0, 82500]),
('boundary control 5', [[27, 0, 0, 0, 0, 0, 0], 30], [27, 0, 0, 14]),
('normal control 6', [[721, 150, 480, 450, 504, 540, 450], 30], [2400, 894, 1, 1872]),
('normal control 7', [[510, 480, 300, 300, 600, 300, 510], 1725], [2340, 630, 30, 96169]),
('normal control 8', [[300, 600, 510, 600, 510, 600, 600], 1810], [2400, 1200, 120, 133940])],
[('regression: daily overtime band cap 1', [[780, 780, 721, 450, 0, 0, 0], 2250], [1890, 720, 121, 120450]),
('regression variant: daily overtime band cap 2', [[480, 480, 481, 946, 720, 540, 450], 2000],
[2400, 1471, 226, 168617]),
('partial repair guard 3', [[0, 14, 481, 481, 720, 720, 900], 2250], [2400, 736, 180, 144900]),
('boundary control 4', [[540, 540, 540, 540, 540, 540, 60], 2000], [2400, 900, 0, 125000]),
('boundary control 5', [[600, 600, 600, 600, 600, 0, 0], 1500], [2400, 600, 0, 82500]),
('normal control 6', [[300, 480, 600, 300, 510, 510, 510], 2000], [2400, 780, 30, 121000]),
('normal control 7', [[300, 480, 600, 300, 480, 480, 480], 2000], [2400, 720, 0, 116000]),
('normal control 8', [[510, 480, 300, 510, 300, 480, 480], 30], [2400, 660, 0, 1695])]]
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: daily overtime band cap 1 | [480, 240, 180, 34500] | [480, 240, 180, 34500] | Passed |
| regression variant: daily overtime band cap 2 | [2400, 960, 1, 115900] | [2400, 960, 1, 115900] | Passed |
| partial repair guard 3 | [2400, 1281, 181, 175631] | [2400, 1281, 181, 175631] | Passed |
| boundary control 4 | [2400, 480, 0, 104000] | [2400, 480, 0, 104000] | Passed |
| boundary control 5 | [9, 0, 0, 5] | [9, 0, 0, 5] | Passed |
| normal control 6 | [2400, 600, 0, 94875] | [2400, 600, 0, 94875] | Passed |
| normal control 7 | [2400, 1411, 421, 2679] | [2400, 1411, 421, 2679] | Passed |
| normal control 8 | [2400, 1651, 1, 121963] | [2400, 1651, 1, 121963] | Passed |
SHA-256 / 9c0c6a2344a7cdb173aee05c08ab7b7011d6f19d2e92de261a8233df53604077
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.141585+00:00.
Case digest / ba724f2a144fa0a063c545e84f324593271e19e9e683f27965c6c1ae56939ca9