FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
regression: allowance rounding 132663267Failed
regression variant: allowance rounding 293289329Failed
partial repair guard 335533554Failed
boundary control 4900900Passed
normal control 51865718657Passed
normal control 61700017000Passed
normal control 71886018860Passed
normal control 82160021600Passed

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 fixtureActualExpectedOutcome
regression: allowance rounding 132803267Failed
regression variant: allowance rounding 293289329Failed
partial repair guard 335643554Failed
boundary control 4900900Passed
normal control 51865718657Passed
normal control 61700017000Passed
normal control 71887018860Failed
normal control 82160021600Passed

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 fixtureActualExpectedOutcome
regression: allowance rounding 132673267Passed
regression variant: allowance rounding 293299329Passed
partial repair guard 335543554Passed
boundary control 4900900Passed
normal control 51865718657Passed
normal control 61700017000Passed
normal control 71886018860Passed
normal control 82160021600Passed

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