FAILURE MAP
← Case archive

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.

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

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 fixtureActualExpectedOutcome
regression: standby callout deduction 119501500Failed
regression variant: standby callout deduction 21800016750Failed
partial repair guard 3105008750Failed
boundary control 444003267Failed
boundary control 5900900Passed
normal control 61530014535Failed
normal control 72280022373Failed
normal control 81360012800Failed

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 fixtureActualExpectedOutcome
regression: standby callout deduction 115001500Passed
regression variant: standby callout deduction 21800016750Failed
partial repair guard 3105008750Failed
boundary control 442673267Failed
boundary control 5900900Passed
normal control 61530014535Failed
normal control 72237322373Passed
normal control 81280012800Passed

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 fixtureActualExpectedOutcome
regression: standby callout deduction 115001500Passed
regression variant: standby callout deduction 21675016750Passed
partial repair guard 387508750Passed
boundary control 432673267Passed
boundary control 5900900Passed
normal control 61453514535Passed
normal control 72237322373Passed
normal control 81280012800Passed

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