FAILURE MAP
← Case archive

FA-94241 / Shift rostering labor rules / Open access

Weekend standby rate uses a Sunday-first week · case 01

Monday standby is paid at the weekend rate and Saturday at the weekday rate.

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

ROOT CAUSE

Day numbers are interpreted with Sunday as day 0.

VERIFIED REPAIR

Monday is day 0; days 5 and 6 are the weekend.

Unsuccessful approach: Classifying all minutes by the standby start day misprices standby that crosses into the weekend.

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 not in (0, 6) 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: weekend standby day 1', [[0, 60], [], 1800], 600),
  ('regression variant: weekend standby day 2', [[9660, 10260], [], 2400], 10800),
  ('partial repair guard 3', [[9720, 11160], [], 3000], 27000),
  ('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', [[9660, 9900], [], 2400], 4800),
  ('normal control 8', [[1080, 1320], [[1128, 1248], [1263, 1323]], 2400], 8040)],
 [('regression: weekend standby day 1', [[0, 60], [[0, 30], [15, 45]], 1800], 1500),
  ('regression variant: weekend standby day 2', [[8520, 9120], [[8907, 9027], [8591, 8711]], 1999], 13993),
  ('partial repair guard 3', [[6780, 8220], [], 2400], 26000),
  ('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
  ('normal control 6', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
  ('normal control 7', [[9720, 9960], [], 3000], 6000),
  ('normal control 8', [[7080, 7320], [[7306, 7366], [7148, 7268]], 3000], 11083)],
 [('regression: weekend standby day 1', [[7200, 7260], [], 1800], 900),
  ('regression variant: weekend standby day 2', [[1320, 2760], [[2547, 2637]], 2400], 21600),
  ('partial repair guard 3', [[7080, 7320], [[7235, 7295], [7196, 7316], [7251, 7281]], 1800], 4820),
  ('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('normal control 5', [[8280, 8880], [[8248, 8368]], 1999], 12527),
  ('normal control 6', [[2880, 4320], [[3310, 3340], [2947, 3007]], 3000], 27000),
  ('normal control 7', [[4200, 4800], [], 1800], 6000),
  ('normal control 8', [[8220, 9660], [], 3000], 36000)],
 [('regression: weekend standby day 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('regression variant: weekend standby day 2', [[8220, 9060], [[9056, 9146]], 1800], 15240),
  ('partial repair guard 3', [[6780, 7620], [[7367, 7457], [7321, 7381]], 1999], 13926),
  ('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
  ('normal control 6', [[2460, 3300], [[3157, 3217]], 1999], 10661),
  ('normal control 7', [[5340, 5940], [], 2400], 8000),
  ('normal control 8', [[5400, 6240], [], 1800], 8400)],
 [('regression: weekend standby day 1', [[7200, 8640], [[8470, 8590], [8025, 8145]], 2400], 33600),
  ('regression variant: weekend standby day 2', [[7200, 8040], [], 3000], 21000),
  ('partial repair guard 3', [[7080, 7680], [], 3000], 14000),
  ('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('boundary control 5', [[7200, 7260], [], 1800], 900),
  ('normal control 6', [[9660, 11100], [[10811, 10901], [11009, 11099], [9752, 9842]], 1800], 21450),
  ('normal control 7', [[1080, 2520], [[1687, 1717]], 3000], 25000),
  ('normal control 8', [[8520, 9360], [[8580, 8640], [8559, 8649]], 1800], 13950)]]
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: weekend standby day 1900600Failed
regression variant: weekend standby day 21200010800Failed
partial repair guard 33600027000Failed
boundary control 432003267Failed
boundary control 5600900Failed
normal control 61860015400Failed
normal control 748004800Passed
normal control 884608040Failed

SHA-256 / 6605313e844dbe96652997d62319221ac49f6b401f6360e7fc5ea3a38b1870c6

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 (s0 // 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: weekend standby day 1', [[0, 60], [], 1800], 600),
  ('regression variant: weekend standby day 2', [[9660, 10260], [], 2400], 10800),
  ('partial repair guard 3', [[9720, 11160], [], 3000], 27000),
  ('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', [[9660, 9900], [], 2400], 4800),
  ('normal control 8', [[1080, 1320], [[1128, 1248], [1263, 1323]], 2400], 8040)],
 [('regression: weekend standby day 1', [[0, 60], [[0, 30], [15, 45]], 1800], 1500),
  ('regression variant: weekend standby day 2', [[8520, 9120], [[8907, 9027], [8591, 8711]], 1999], 13993),
  ('partial repair guard 3', [[6780, 8220], [], 2400], 26000),
  ('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
  ('normal control 6', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
  ('normal control 7', [[9720, 9960], [], 3000], 6000),
  ('normal control 8', [[7080, 7320], [[7306, 7366], [7148, 7268]], 3000], 11083)],
 [('regression: weekend standby day 1', [[7200, 7260], [], 1800], 900),
  ('regression variant: weekend standby day 2', [[1320, 2760], [[2547, 2637]], 2400], 21600),
  ('partial repair guard 3', [[7080, 7320], [[7235, 7295], [7196, 7316], [7251, 7281]], 1800], 4820),
  ('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('normal control 5', [[8280, 8880], [[8248, 8368]], 1999], 12527),
  ('normal control 6', [[2880, 4320], [[3310, 3340], [2947, 3007]], 3000], 27000),
  ('normal control 7', [[4200, 4800], [], 1800], 6000),
  ('normal control 8', [[8220, 9660], [], 3000], 36000)],
 [('regression: weekend standby day 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('regression variant: weekend standby day 2', [[8220, 9060], [[9056, 9146]], 1800], 15240),
  ('partial repair guard 3', [[6780, 7620], [[7367, 7457], [7321, 7381]], 1999], 13926),
  ('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
  ('normal control 6', [[2460, 3300], [[3157, 3217]], 1999], 10661),
  ('normal control 7', [[5340, 5940], [], 2400], 8000),
  ('normal control 8', [[5400, 6240], [], 1800], 8400)],
 [('regression: weekend standby day 1', [[7200, 8640], [[8470, 8590], [8025, 8145]], 2400], 33600),
  ('regression variant: weekend standby day 2', [[7200, 8040], [], 3000], 21000),
  ('partial repair guard 3', [[7080, 7680], [], 3000], 14000),
  ('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('boundary control 5', [[7200, 7260], [], 1800], 900),
  ('normal control 6', [[9660, 11100], [[10811, 10901], [11009, 11099], [9752, 9842]], 1800], 21450),
  ('normal control 7', [[1080, 2520], [[1687, 1717]], 3000], 25000),
  ('normal control 8', [[8520, 9360], [[8580, 8640], [8559, 8649]], 1800], 13950)]]
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: weekend standby day 1600600Passed
regression variant: weekend standby day 21200010800Failed
partial repair guard 33600027000Failed
boundary control 432003267Failed
boundary control 5900900Passed
normal control 61860015400Failed
normal control 748004800Passed
normal control 880408040Passed

SHA-256 / 178ab0e7b2f05d4a7b97215c66a6cd136605624fe87b80f36dcce6b5c08bd258

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: weekend standby day 1', [[0, 60], [], 1800], 600),
  ('regression variant: weekend standby day 2', [[9660, 10260], [], 2400], 10800),
  ('partial repair guard 3', [[9720, 11160], [], 3000], 27000),
  ('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', [[9660, 9900], [], 2400], 4800),
  ('normal control 8', [[1080, 1320], [[1128, 1248], [1263, 1323]], 2400], 8040)],
 [('regression: weekend standby day 1', [[0, 60], [[0, 30], [15, 45]], 1800], 1500),
  ('regression variant: weekend standby day 2', [[8520, 9120], [[8907, 9027], [8591, 8711]], 1999], 13993),
  ('partial repair guard 3', [[6780, 8220], [], 2400], 26000),
  ('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
  ('normal control 6', [[7080, 7320], [[7181, 7241], [7166, 7226]], 2400], 5727),
  ('normal control 7', [[9720, 9960], [], 3000], 6000),
  ('normal control 8', [[7080, 7320], [[7306, 7366], [7148, 7268]], 3000], 11083)],
 [('regression: weekend standby day 1', [[7200, 7260], [], 1800], 900),
  ('regression variant: weekend standby day 2', [[1320, 2760], [[2547, 2637]], 2400], 21600),
  ('partial repair guard 3', [[7080, 7320], [[7235, 7295], [7196, 7316], [7251, 7281]], 1800], 4820),
  ('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('normal control 5', [[8280, 8880], [[8248, 8368]], 1999], 12527),
  ('normal control 6', [[2880, 4320], [[3310, 3340], [2947, 3007]], 3000], 27000),
  ('normal control 7', [[4200, 4800], [], 1800], 6000),
  ('normal control 8', [[8220, 9660], [], 3000], 36000)],
 [('regression: weekend standby day 1', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('regression variant: weekend standby day 2', [[8220, 9060], [[9056, 9146]], 1800], 15240),
  ('partial repair guard 3', [[6780, 7620], [[7367, 7457], [7321, 7381]], 1999], 13926),
  ('boundary control 4', [[0, 60], [], 1800], 600), ('boundary control 5', [[0, 3], [], 30], 1),
  ('normal control 6', [[2460, 3300], [[3157, 3217]], 1999], 10661),
  ('normal control 7', [[5340, 5940], [], 2400], 8000),
  ('normal control 8', [[5400, 6240], [], 1800], 8400)],
 [('regression: weekend standby day 1', [[7200, 8640], [[8470, 8590], [8025, 8145]], 2400], 33600),
  ('regression variant: weekend standby day 2', [[7200, 8040], [], 3000], 21000),
  ('partial repair guard 3', [[7080, 7680], [], 3000], 14000),
  ('boundary control 4', [[7140, 7260], [[7190, 7250]], 2400], 3267),
  ('boundary control 5', [[7200, 7260], [], 1800], 900),
  ('normal control 6', [[9660, 11100], [[10811, 10901], [11009, 11099], [9752, 9842]], 1800], 21450),
  ('normal control 7', [[1080, 2520], [[1687, 1717]], 3000], 25000),
  ('normal control 8', [[8520, 9360], [[8580, 8640], [8559, 8649]], 1800], 13950)]]
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: weekend standby day 1600600Passed
regression variant: weekend standby day 21080010800Passed
partial repair guard 32700027000Passed
boundary control 432673267Passed
boundary control 5900900Passed
normal control 61540015400Passed
normal control 748004800Passed
normal control 880408040Passed

SHA-256 / 030dd8a4eaa42f02db5cbf8e4ce5765d3dfee307459b2262cdcec7245bedaca4

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.580092+00:00.

Case digest / fb1e2ff5ff6c0afb5d21233e63291c889b85e4ea9aad08015044d4235716707a