FA-94246 / Shift rostering labor rules / Open access
Standby allowance truncated to the cent · case 01
Allowances with half a cent or more lose a cent.
ROOT CAUSE
The exact amount is floor-divided.
VERIFIED REPAIR
Round half up once on the exact amount.
Unsuccessful approach: Rounding units to whole minutes of full pay first distorts the fractional standby rates.
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):
if t in busy:
continue
units += 20 if (t // 1440) % 7 < 5 else 30
units += 60 * len(busy)
return units * rate // 3600
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: allowance rounding 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('regression variant: allowance rounding 2', [[5400, 6240], [], 1999], 9329),
('partial repair guard 3', [[1080, 1320], [[1310, 1340]], 1999], 3554),
('boundary control 4', [[7200, 7260], [], 1800], 900),
('normal control 5', [[1440, 2880], [[2236, 2356]], 1999], 18657),
('normal control 6', [[2880, 3720], [[3367, 3457]], 3000], 17000),
('normal control 7', [[2880, 4320], [[3679, 3739], [3048, 3138], [3606, 3726]], 1800], 18860),
('normal control 8', [[1320, 2760], [[2547, 2637]], 2400], 21600)],
[('regression: allowance rounding 1', [[0, 1], [], 90], 1),
('regression variant: allowance rounding 2', [[0, 1440], [[1320, 1440], [1406, 1526], [1262, 1322]], 1999],
22811),
('partial repair guard 3', [[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[2760, 3000], [[2936, 2966], [2726, 2816]], 2400], 6853),
('normal control 7', [[5760, 6360], [[6008, 6038], [6344, 6434]], 1800], 9140),
('normal control 8', [[8520, 8760], [[8576, 8696], [8730, 8850], [8520, 8580]], 2400], 12520)],
[('regression: allowance rounding 1', [[0, 3], [], 30], 1),
('regression variant: allowance rounding 2',
[[3900, 5340], [[4265, 4355], [4658, 4688], [4876, 4906]], 1999], 19324),
('partial repair guard 3', [[7200, 8040], [[7671, 7731], [7689, 7809], [7240, 7360]], 1999], 18291),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[9660, 11100], [[11072, 11162], [9928, 9958]], 2400], 25827),
('normal control 7', [[1020, 2460], [[1085, 1115]], 2400], 20000),
('normal control 8', [[4200, 4800], [[4321, 4351], [4378, 4468]], 1800], 8400)],
[('regression: allowance rounding 1', [[1080, 1320], [[1310, 1340]], 1999], 3554),
('regression variant: allowance rounding 2', [[5340, 6180], [[5928, 6018], [6161, 6281]], 2400], 18147),
('partial repair guard 3', [[9960, 11400], [[10425, 10545]], 1999], 19324),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[5340, 5940], [], 2400], 8000),
('normal control 7', [[9720, 10560], [[9952, 10042]], 2400], 15400),
('normal control 8', [[2880, 3120], [[3022, 3112]], 1999], 4664)],
[('regression: allowance rounding 1', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
('regression variant: allowance rounding 2', [[5760, 6000], [[5707, 5767]], 1999], 4587),
('partial repair guard 3', [[9960, 10200], [[10055, 10145], [10110, 10200]], 1999], 6413),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[5640, 6240], [[5947, 6007], [5613, 5733], [5788, 5908]], 2400], 16360),
('normal control 7', [[9720, 9960], [[9734, 9764]], 2400], 5400),
('normal control 8', [[9660, 11100], [], 1999], 18324)]]
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: allowance rounding 1 | 3266 | 3267 | Failed |
| regression variant: allowance rounding 2 | 9328 | 9329 | Failed |
| partial repair guard 3 | 3553 | 3554 | Failed |
| boundary control 4 | 900 | 900 | Passed |
| normal control 5 | 18657 | 18657 | Passed |
| normal control 6 | 17000 | 17000 | Passed |
| normal control 7 | 18860 | 18860 | Passed |
| normal control 8 | 21600 | 21600 | Passed |
SHA-256 / b61f2e42afc95e44687bad4f7da3062bba498fd06838a8e2907abe42974deac0
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:
continue
units += 20 if (t // 1440) % 7 < 5 else 30
units += 60 * len(busy)
return (units + 30) // 60 * rate // 60
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: allowance rounding 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('regression variant: allowance rounding 2', [[5400, 6240], [], 1999], 9329),
('partial repair guard 3', [[1080, 1320], [[1310, 1340]], 1999], 3554),
('boundary control 4', [[7200, 7260], [], 1800], 900),
('normal control 5', [[1440, 2880], [[2236, 2356]], 1999], 18657),
('normal control 6', [[2880, 3720], [[3367, 3457]], 3000], 17000),
('normal control 7', [[2880, 4320], [[3679, 3739], [3048, 3138], [3606, 3726]], 1800], 18860),
('normal control 8', [[1320, 2760], [[2547, 2637]], 2400], 21600)],
[('regression: allowance rounding 1', [[0, 1], [], 90], 1),
('regression variant: allowance rounding 2', [[0, 1440], [[1320, 1440], [1406, 1526], [1262, 1322]], 1999],
22811),
('partial repair guard 3', [[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[2760, 3000], [[2936, 2966], [2726, 2816]], 2400], 6853),
('normal control 7', [[5760, 6360], [[6008, 6038], [6344, 6434]], 1800], 9140),
('normal control 8', [[8520, 8760], [[8576, 8696], [8730, 8850], [8520, 8580]], 2400], 12520)],
[('regression: allowance rounding 1', [[0, 3], [], 30], 1),
('regression variant: allowance rounding 2',
[[3900, 5340], [[4265, 4355], [4658, 4688], [4876, 4906]], 1999], 19324),
('partial repair guard 3', [[7200, 8040], [[7671, 7731], [7689, 7809], [7240, 7360]], 1999], 18291),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[9660, 11100], [[11072, 11162], [9928, 9958]], 2400], 25827),
('normal control 7', [[1020, 2460], [[1085, 1115]], 2400], 20000),
('normal control 8', [[4200, 4800], [[4321, 4351], [4378, 4468]], 1800], 8400)],
[('regression: allowance rounding 1', [[1080, 1320], [[1310, 1340]], 1999], 3554),
('regression variant: allowance rounding 2', [[5340, 6180], [[5928, 6018], [6161, 6281]], 2400], 18147),
('partial repair guard 3', [[9960, 11400], [[10425, 10545]], 1999], 19324),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[5340, 5940], [], 2400], 8000),
('normal control 7', [[9720, 10560], [[9952, 10042]], 2400], 15400),
('normal control 8', [[2880, 3120], [[3022, 3112]], 1999], 4664)],
[('regression: allowance rounding 1', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
('regression variant: allowance rounding 2', [[5760, 6000], [[5707, 5767]], 1999], 4587),
('partial repair guard 3', [[9960, 10200], [[10055, 10145], [10110, 10200]], 1999], 6413),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[5640, 6240], [[5947, 6007], [5613, 5733], [5788, 5908]], 2400], 16360),
('normal control 7', [[9720, 9960], [[9734, 9764]], 2400], 5400),
('normal control 8', [[9660, 11100], [], 1999], 18324)]]
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: allowance rounding 1 | 3280 | 3267 | Failed |
| regression variant: allowance rounding 2 | 9328 | 9329 | Failed |
| partial repair guard 3 | 3564 | 3554 | Failed |
| boundary control 4 | 900 | 900 | Passed |
| normal control 5 | 18657 | 18657 | Passed |
| normal control 6 | 17000 | 17000 | Passed |
| normal control 7 | 18870 | 18860 | Failed |
| normal control 8 | 21600 | 21600 | Passed |
SHA-256 / fe7894d040fc155f566f040f875f1dda18af7cb56cc49eae965b0462dfa3029b
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: allowance rounding 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('regression variant: allowance rounding 2', [[5400, 6240], [], 1999], 9329),
('partial repair guard 3', [[1080, 1320], [[1310, 1340]], 1999], 3554),
('boundary control 4', [[7200, 7260], [], 1800], 900),
('normal control 5', [[1440, 2880], [[2236, 2356]], 1999], 18657),
('normal control 6', [[2880, 3720], [[3367, 3457]], 3000], 17000),
('normal control 7', [[2880, 4320], [[3679, 3739], [3048, 3138], [3606, 3726]], 1800], 18860),
('normal control 8', [[1320, 2760], [[2547, 2637]], 2400], 21600)],
[('regression: allowance rounding 1', [[0, 1], [], 90], 1),
('regression variant: allowance rounding 2', [[0, 1440], [[1320, 1440], [1406, 1526], [1262, 1322]], 1999],
22811),
('partial repair guard 3', [[6840, 7680], [[7144, 7264], [6829, 6919], [6824, 6854]], 1999], 16592),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[2760, 3000], [[2936, 2966], [2726, 2816]], 2400], 6853),
('normal control 7', [[5760, 6360], [[6008, 6038], [6344, 6434]], 1800], 9140),
('normal control 8', [[8520, 8760], [[8576, 8696], [8730, 8850], [8520, 8580]], 2400], 12520)],
[('regression: allowance rounding 1', [[0, 3], [], 30], 1),
('regression variant: allowance rounding 2',
[[3900, 5340], [[4265, 4355], [4658, 4688], [4876, 4906]], 1999], 19324),
('partial repair guard 3', [[7200, 8040], [[7671, 7731], [7689, 7809], [7240, 7360]], 1999], 18291),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[9660, 11100], [[11072, 11162], [9928, 9958]], 2400], 25827),
('normal control 7', [[1020, 2460], [[1085, 1115]], 2400], 20000),
('normal control 8', [[4200, 4800], [[4321, 4351], [4378, 4468]], 1800], 8400)],
[('regression: allowance rounding 1', [[1080, 1320], [[1310, 1340]], 1999], 3554),
('regression variant: allowance rounding 2', [[5340, 6180], [[5928, 6018], [6161, 6281]], 2400], 18147),
('partial repair guard 3', [[9960, 11400], [[10425, 10545]], 1999], 19324),
('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
('normal control 6', [[5340, 5940], [], 2400], 8000),
('normal control 7', [[9720, 10560], [[9952, 10042]], 2400], 15400),
('normal control 8', [[2880, 3120], [[3022, 3112]], 1999], 4664)],
[('regression: allowance rounding 1', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
('regression variant: allowance rounding 2', [[5760, 6000], [[5707, 5767]], 1999], 4587),
('partial repair guard 3', [[9960, 10200], [[10055, 10145], [10110, 10200]], 1999], 6413),
('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
('boundary control 5', [[7200, 7260], [], 1800], 900),
('normal control 6', [[5640, 6240], [[5947, 6007], [5613, 5733], [5788, 5908]], 2400], 16360),
('normal control 7', [[9720, 9960], [[9734, 9764]], 2400], 5400),
('normal control 8', [[9660, 11100], [], 1999], 18324)]]
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: allowance rounding 1 | 3267 | 3267 | Passed |
| regression variant: allowance rounding 2 | 9329 | 9329 | Passed |
| partial repair guard 3 | 3554 | 3554 | Passed |
| boundary control 4 | 900 | 900 | Passed |
| normal control 5 | 18657 | 18657 | Passed |
| normal control 6 | 17000 | 17000 | Passed |
| normal control 7 | 18860 | 18860 | Passed |
| normal control 8 | 21600 | 21600 | Passed |
SHA-256 / 2b4b1ff7d7e82837629ac9e3ce5bfe03f7451e8b4d4533f1117c077eb9ebab10
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.624244+00:00.
Case digest / bf08510f4b57956bbc7a1c31ef7609be694b1aefb3e8f85c044b1914e7529dbb