FA-94236 / Shift rostering labor rules / Open access
Callout minutes also earn the standby allowance · case 01
Minutes worked on a callout are paid both full rate and standby allowance.
ROOT CAUSE
Standby minutes are not reduced by callout minutes.
VERIFIED REPAIR
Skip standby allowance for any minute covered by a callout.
Unsuccessful approach: Deducting only on weekdays still double pays weekend callouts.
Case contract
Standby window [start, end], callout intervals and hourly rate in cents; minutes from Monday 00:00. Callout minutes (union of callouts, inside or outside standby) pay full rate. Standby minutes not in a callout pay a third of rate on weekdays and half on Saturday/Sunday (days 5-6). Return total cents, computed exactly and rounded half up once.
Why this case matters
On-call allowances interact with callout pay; double payment and wrong day attribution are common.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(standby, callouts, rate):
s0, e0 = standby
busy = set()
for s, e in callouts:
busy.update(range(s, e))
units = 0
for t in range(s0, e0):
units += 20 if (t // 1440) % 7 < 5 else 30
units += 60 * len(busy)
return (units * rate + 1800) // 3600
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: standby callout deduction 1', [[0, 60], [[0, 30], [15, 45]], 1800], 1500),
('regression variant: standby callout deduction 2', [[8220, 8820], [[8770, 8830]], 3000], 16750),
('partial repair guard 3', [[8640, 8880], [[8620, 8650], [8695, 8725], [8811, 8841]], 3000], 8750),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[8220, 9060], [[9009, 9099]], 1800], 14535),
('normal control 7', [[2520, 3960], [[3928, 4018]], 2400], 22373),
('normal control 8', [[5760, 6600], [[5769, 5799], [6053, 6083]], 2400], 12800)],
[('regression: standby callout deduction 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('regression variant: standby callout deduction 2',
[[8520, 8760], [[8576, 8696], [8730, 8850], [8520, 8580]], 2400], 12520),
('partial repair guard 3', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[4200, 4800], [[4456, 4576], [4414, 4504], [4798, 4828]], 3000], 16867),
('normal control 7', [[6780, 8220], [], 2400], 26000),
('normal control 8', [[3960, 4200], [], 1800], 2400)],
[('regression: standby callout deduction 1', [[7200, 8640], [[8470, 8590], [8025, 8145]], 2400], 33600),
('regression variant: standby callout deduction 2', [[8220, 8820], [[8187, 8247], [8269, 8299]], 1800],
10845),
('partial repair guard 3', [[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[9720, 10560], [[9952, 10042]], 2400], 15400),
('normal control 7', [[5640, 6480], [], 1999], 9329),
('normal control 8', [[3900, 4740], [[4707, 4767], [4227, 4317], [4059, 4179]], 3000], 23450)],
[('regression: standby callout deduction 1', [[0, 240], [[127, 217], [236, 266]], 3000], 8433),
('regression variant: standby callout deduction 2',
[[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('partial repair guard 3', [[8220, 9060], [[9056, 9146]], 1800], 15240),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[2760, 3600], [[2751, 2871], [3164, 3284]], 1999], 14759),
('normal control 7', [[5640, 7080], [[6495, 6615], [6688, 6748]], 3000], 30000),
('normal control 8', [[2460, 3060], [[2442, 2502], [2640, 2700]], 1999], 9529)],
[('regression: standby callout deduction 1', [[5340, 5940], [[5348, 5378], [5293, 5353]], 1999], 9073),
('regression variant: standby callout deduction 2', [[8520, 8760], [[8600, 8630], [8558, 8588]], 3000],
7500),
('partial repair guard 3', [[9720, 10560], [[9889, 10009]], 3000], 20000),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[5760, 6360], [[5953, 6043]], 1999], 8662),
('normal control 7', [[1320, 1920], [[1501, 1621], [1371, 1431], [1314, 1404]], 1800], 10800),
('normal control 8', [[2760, 3600], [[2751, 2871], [3164, 3284]], 1999], 14759)]]
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: standby callout deduction 1 | 1950 | 1500 | Failed |
| regression variant: standby callout deduction 2 | 18000 | 16750 | Failed |
| partial repair guard 3 | 10500 | 8750 | Failed |
| boundary control 4 | 4400 | 3267 | Failed |
| boundary control 5 | 900 | 900 | Passed |
| normal control 6 | 15300 | 14535 | Failed |
| normal control 7 | 22800 | 22373 | Failed |
| normal control 8 | 13600 | 12800 | Failed |
SHA-256 / f927cc6383b3558e5bd735175a7566c0f035cb4b8f77f0bf0f1b0495903b3a7c
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(standby, callouts, rate):
s0, e0 = standby
busy = set()
for s, e in callouts:
busy.update(range(s, e))
units = 0
for t in range(s0, e0):
if t in busy and (t // 1440) % 7 < 5:
continue
units += 20 if (t // 1440) % 7 < 5 else 30
units += 60 * len(busy)
return (units * rate + 1800) // 3600
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: standby callout deduction 1', [[0, 60], [[0, 30], [15, 45]], 1800], 1500),
('regression variant: standby callout deduction 2', [[8220, 8820], [[8770, 8830]], 3000], 16750),
('partial repair guard 3', [[8640, 8880], [[8620, 8650], [8695, 8725], [8811, 8841]], 3000], 8750),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[8220, 9060], [[9009, 9099]], 1800], 14535),
('normal control 7', [[2520, 3960], [[3928, 4018]], 2400], 22373),
('normal control 8', [[5760, 6600], [[5769, 5799], [6053, 6083]], 2400], 12800)],
[('regression: standby callout deduction 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('regression variant: standby callout deduction 2',
[[8520, 8760], [[8576, 8696], [8730, 8850], [8520, 8580]], 2400], 12520),
('partial repair guard 3', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[4200, 4800], [[4456, 4576], [4414, 4504], [4798, 4828]], 3000], 16867),
('normal control 7', [[6780, 8220], [], 2400], 26000),
('normal control 8', [[3960, 4200], [], 1800], 2400)],
[('regression: standby callout deduction 1', [[7200, 8640], [[8470, 8590], [8025, 8145]], 2400], 33600),
('regression variant: standby callout deduction 2', [[8220, 8820], [[8187, 8247], [8269, 8299]], 1800],
10845),
('partial repair guard 3', [[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[9720, 10560], [[9952, 10042]], 2400], 15400),
('normal control 7', [[5640, 6480], [], 1999], 9329),
('normal control 8', [[3900, 4740], [[4707, 4767], [4227, 4317], [4059, 4179]], 3000], 23450)],
[('regression: standby callout deduction 1', [[0, 240], [[127, 217], [236, 266]], 3000], 8433),
('regression variant: standby callout deduction 2',
[[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('partial repair guard 3', [[8220, 9060], [[9056, 9146]], 1800], 15240),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[2760, 3600], [[2751, 2871], [3164, 3284]], 1999], 14759),
('normal control 7', [[5640, 7080], [[6495, 6615], [6688, 6748]], 3000], 30000),
('normal control 8', [[2460, 3060], [[2442, 2502], [2640, 2700]], 1999], 9529)],
[('regression: standby callout deduction 1', [[5340, 5940], [[5348, 5378], [5293, 5353]], 1999], 9073),
('regression variant: standby callout deduction 2', [[8520, 8760], [[8600, 8630], [8558, 8588]], 3000],
7500),
('partial repair guard 3', [[9720, 10560], [[9889, 10009]], 3000], 20000),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[5760, 6360], [[5953, 6043]], 1999], 8662),
('normal control 7', [[1320, 1920], [[1501, 1621], [1371, 1431], [1314, 1404]], 1800], 10800),
('normal control 8', [[2760, 3600], [[2751, 2871], [3164, 3284]], 1999], 14759)]]
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: standby callout deduction 1 | 1500 | 1500 | Passed |
| regression variant: standby callout deduction 2 | 18000 | 16750 | Failed |
| partial repair guard 3 | 10500 | 8750 | Failed |
| boundary control 4 | 4267 | 3267 | Failed |
| boundary control 5 | 900 | 900 | Passed |
| normal control 6 | 15300 | 14535 | Failed |
| normal control 7 | 22373 | 22373 | Passed |
| normal control 8 | 12800 | 12800 | Passed |
SHA-256 / dff5d484e2bc5c957accda632dce323959d3d224d6f0332d8583295e063a7528
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(standby, callouts, rate):
s0, e0 = standby
busy = set()
for s, e in callouts:
busy.update(range(s, e))
units = 0
for t in range(s0, e0):
if t in busy:
continue
units += 20 if (t // 1440) % 7 < 5 else 30
units += 60 * len(busy)
return (units * rate + 1800) // 3600
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: standby callout deduction 1', [[0, 60], [[0, 30], [15, 45]], 1800], 1500),
('regression variant: standby callout deduction 2', [[8220, 8820], [[8770, 8830]], 3000], 16750),
('partial repair guard 3', [[8640, 8880], [[8620, 8650], [8695, 8725], [8811, 8841]], 3000], 8750),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[8220, 9060], [[9009, 9099]], 1800], 14535),
('normal control 7', [[2520, 3960], [[3928, 4018]], 2400], 22373),
('normal control 8', [[5760, 6600], [[5769, 5799], [6053, 6083]], 2400], 12800)],
[('regression: standby callout deduction 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('regression variant: standby callout deduction 2',
[[8520, 8760], [[8576, 8696], [8730, 8850], [8520, 8580]], 2400], 12520),
('partial repair guard 3', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[4200, 4800], [[4456, 4576], [4414, 4504], [4798, 4828]], 3000], 16867),
('normal control 7', [[6780, 8220], [], 2400], 26000),
('normal control 8', [[3960, 4200], [], 1800], 2400)],
[('regression: standby callout deduction 1', [[7200, 8640], [[8470, 8590], [8025, 8145]], 2400], 33600),
('regression variant: standby callout deduction 2', [[8220, 8820], [[8187, 8247], [8269, 8299]], 1800],
10845),
('partial repair guard 3', [[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[9720, 10560], [[9952, 10042]], 2400], 15400),
('normal control 7', [[5640, 6480], [], 1999], 9329),
('normal control 8', [[3900, 4740], [[4707, 4767], [4227, 4317], [4059, 4179]], 3000], 23450)],
[('regression: standby callout deduction 1', [[0, 240], [[127, 217], [236, 266]], 3000], 8433),
('regression variant: standby callout deduction 2',
[[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('partial repair guard 3', [[8220, 9060], [[9056, 9146]], 1800], 15240),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[2760, 3600], [[2751, 2871], [3164, 3284]], 1999], 14759),
('normal control 7', [[5640, 7080], [[6495, 6615], [6688, 6748]], 3000], 30000),
('normal control 8', [[2460, 3060], [[2442, 2502], [2640, 2700]], 1999], 9529)],
[('regression: standby callout deduction 1', [[5340, 5940], [[5348, 5378], [5293, 5353]], 1999], 9073),
('regression variant: standby callout deduction 2', [[8520, 8760], [[8600, 8630], [8558, 8588]], 3000],
7500),
('partial repair guard 3', [[9720, 10560], [[9889, 10009]], 3000], 20000),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[5760, 6360], [[5953, 6043]], 1999], 8662),
('normal control 7', [[1320, 1920], [[1501, 1621], [1371, 1431], [1314, 1404]], 1800], 10800),
('normal control 8', [[2760, 3600], [[2751, 2871], [3164, 3284]], 1999], 14759)]]
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: standby callout deduction 1 | 1500 | 1500 | Passed |
| regression variant: standby callout deduction 2 | 16750 | 16750 | Passed |
| partial repair guard 3 | 8750 | 8750 | Passed |
| boundary control 4 | 3267 | 3267 | Passed |
| boundary control 5 | 900 | 900 | Passed |
| normal control 6 | 14535 | 14535 | Passed |
| normal control 7 | 22373 | 22373 | Passed |
| normal control 8 | 12800 | 12800 | Passed |
SHA-256 / 29f55791c02ddfc12bcdea7ccd40f3182a1a2a52c6756a96a1a3ab8dc3bd6f50
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:52:02.577376+00:00.
Case digest / d3228b0501c53593c160c25179c91df95cdad2949f216d97a2f6279ce95d088e