FAILURE MAP
← Case archive

FA-94226 / Shift rostering labor rules / Open access

Exactly sixteen hours in 24 flagged as fatigue breach · case 01

Double shifts totalling 16 hours are rejected.

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

ROOT CAUSE

The 24-hour limit comparison is inclusive.

VERIFIED REPAIR

Flag only totals above 960 minutes.

Unsuccessful approach: Comparing whole hours ignores 1 to 59 excess minutes.

Case contract

Shifts [start, end] in minutes. For window W, evaluate windows [t, t + W) anchored at each shift start and sum the minutes of all shifts clipped to the window; report the maximum. Return [max in any 24h window, max in any 168h window, violations] where more than 960 minutes in 24h or more than 3600 minutes in 168h is a violation.

Why this case matters

Rolling-window fatigue limits catch hours concentrated across calendar-day boundaries.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(shifts):
    starts = sorted(s for s, e in shifts)
    def worst(W):
        best = 0
        for t in starts:
            tot = sum(max(0, min(e, t + W) - max(s, t)) for s, e in shifts)
            best = max(best, tot)
        return best
    m24, m168 = worst(1440), worst(10080)
    v = []
    if m24 >= 960:
        v.append('24h')
    if m168 > 3600:
        v.append('7d')
    return [m24, m168, v]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: daily limit units 1', [[[0, 480], [480, 960]]], [960, 960, []]),
  ('regression variant: daily limit units 2',
   [[[1740, 2340], [540, 1020], [2820, 3420], [4140, 4620], [5100, 5700], [7140, 7740], [0, 480]]],
   [960, 3840, ['7d']]),
  ('partial repair guard 3', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[1000, 1500], [0, 480]]], [920, 980, []]),
  ('normal control 5', [[[1260, 1980], [780, 1260], [2700, 3300], [0, 720], [3540, 4140]]],
   [1380, 3120, ['24h']]),
  ('normal control 6', [[[300, 780], [3600, 4080], [4140, 4620], [1680, 2160]]], [960, 1920, []]),
  ('normal control 7', [[[3060, 3660], [2400, 3000], [3900, 4260], [0, 480], [960, 1680]]],
   [1200, 2760, ['24h']]),
  ('normal control 8', [[[2460, 3060], [300, 780], [3540, 4140], [1020, 1740]]], [1200, 2400, ['24h']])],
 [('regression: daily limit units 1', [[[2220, 2580], [1560, 2160], [300, 660]]], [960, 1320, []]),
  ('regression variant: daily limit units 2',
   [[[4020, 4740], [300, 660], [6600, 7080], [2820, 3300], [5640, 6120], [1560, 1920]]], [960, 2880, []]),
  ('partial repair guard 3', [[[0, 1010]]], [1010, 1010, ['24h']]),
  ('boundary control 4', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 5', [[[1000, 1500], [0, 480]]], [920, 980, []]),
  ('normal control 6', [[[0, 360], [1800, 2280]]], [480, 840, []]),
  ('normal control 7', [[[300, 1020], [4260, 4740], [3480, 4200], [1260, 1620], [3060, 3420]]],
   [1320, 2640, ['24h']]),
  ('normal control 8', [[[0, 600], [1080, 1800], [2040, 2640], [3120, 3720]]], [1200, 2520, ['24h']])],
 [('regression: daily limit units 1', [[[300, 900], [1380, 1740]]], [960, 960, []]),
  ('regression variant: daily limit units 2', [[[660, 1260], [300, 660]]], [960, 960, []]),
  ('partial repair guard 3', [[[0, 480], [480, 961]]], [961, 961, ['24h']]),
  ('boundary control 4', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('normal control 5', [[[3720, 4200], [2220, 2820], [300, 780]]], [600, 1560, []]),
  ('normal control 6', [[[4680, 5280], [3960, 4440], [2640, 3240], [1440, 1920], [0, 720], [5520, 6240]]],
   [1200, 3600, ['24h']]),
  ('normal control 7', [[[300, 900], [1380, 1860]]], [960, 1080, []]),
  ('normal control 8',
   [[[960, 1680], [6480, 6840], [3360, 3720], [7740, 8340], [5160, 5760], [0, 480], [1920, 2640]]],
   [1200, 3840, ['24h', '7d']])],
 [('regression: daily limit units 1', [[[2520, 3120], [2100, 2460], [300, 660]]], [960, 1320, []]),
  ('regression variant: daily limit units 2', [[[300, 660], [2580, 2940], [1140, 1860]]], [960, 1440, []]),
  ('partial repair guard 3', [[[0, 500], [500, 1000]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[0, 720], [1440, 2160], [2880, 3600], [4320, 5040], [5760, 6480]]],
   [720, 3600, []]),
  ('boundary control 5', [[[0, 480], [480, 961]]], [961, 961, ['24h']]),
  ('normal control 6', [[[3120, 3720], [1200, 1800], [1860, 2220], [0, 720]]], [960, 2280, []]),
  ('normal control 7',
   [[[4380, 4740], [6180, 6540], [300, 1020], [4980, 5460], [3540, 4140], [2460, 3060], [7440, 7800]]],
   [960, 3480, []]),
  ('normal control 8',
   [[[2520, 2880], [7080, 7680], [0, 600], [4800, 5280], [6000, 6360], [600, 1080], [3600, 4320]]],
   [1080, 3600, ['24h']])],
 [('regression: daily limit units 1',
   [[[5700, 6180], [3960, 4320], [4380, 4980], [0, 480], [1920, 2400], [2880, 3480]]], [960, 3000, []]),
  ('regression variant: daily limit units 2', [[[300, 660], [900, 1500]]], [960, 960, []]),
  ('partial repair guard 3', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[0, 1010]]], [1010, 1010, ['24h']]),
  ('boundary control 5', [[[0, 720], [1440, 2160], [2880, 3600], [4320, 5040], [5760, 6480]]],
   [720, 3600, []]),
  ('normal control 6',
   [[[1200, 1800], [480, 1200], [0, 480], [4500, 5220], [7140, 7620], [2880, 3600], [5940, 6420],
     [2280, 2880]]],
   [1440, 4800, ['24h', '7d']]),
  ('normal control 7', [[[4140, 4620], [1800, 2520], [0, 360], [2760, 3240]]], [1200, 2040, ['24h']]),
  ('normal control 8', [[[300, 780], [840, 1560]]], [1200, 1200, ['24h']])]]
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: daily limit units 1[960, 960, ['24h']][960, 960, []]Failed
regression variant: daily limit units 2[960, 3840, ['24h', '7d']][960, 3840, ['7d']]Failed
partial repair guard 3[1000, 1000, ['24h']][1000, 1000, ['24h']]Passed
boundary control 4[920, 980, []][920, 980, []]Passed
normal control 5[1380, 3120, ['24h']][1380, 3120, ['24h']]Passed
normal control 6[960, 1920, ['24h']][960, 1920, []]Failed
normal control 7[1200, 2760, ['24h']][1200, 2760, ['24h']]Passed
normal control 8[1200, 2400, ['24h']][1200, 2400, ['24h']]Passed

SHA-256 / 0debfdb3522fc7ede3755a39b83de5b11627ac7f45e0eb30c05264bd58c88a2a

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(shifts):
    starts = sorted(s for s, e in shifts)
    def worst(W):
        best = 0
        for t in starts:
            tot = sum(max(0, min(e, t + W) - max(s, t)) for s, e in shifts)
            best = max(best, tot)
        return best
    m24, m168 = worst(1440), worst(10080)
    v = []
    if m24 // 60 > 16:
        v.append('24h')
    if m168 > 3600:
        v.append('7d')
    return [m24, m168, v]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: daily limit units 1', [[[0, 480], [480, 960]]], [960, 960, []]),
  ('regression variant: daily limit units 2',
   [[[1740, 2340], [540, 1020], [2820, 3420], [4140, 4620], [5100, 5700], [7140, 7740], [0, 480]]],
   [960, 3840, ['7d']]),
  ('partial repair guard 3', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[1000, 1500], [0, 480]]], [920, 980, []]),
  ('normal control 5', [[[1260, 1980], [780, 1260], [2700, 3300], [0, 720], [3540, 4140]]],
   [1380, 3120, ['24h']]),
  ('normal control 6', [[[300, 780], [3600, 4080], [4140, 4620], [1680, 2160]]], [960, 1920, []]),
  ('normal control 7', [[[3060, 3660], [2400, 3000], [3900, 4260], [0, 480], [960, 1680]]],
   [1200, 2760, ['24h']]),
  ('normal control 8', [[[2460, 3060], [300, 780], [3540, 4140], [1020, 1740]]], [1200, 2400, ['24h']])],
 [('regression: daily limit units 1', [[[2220, 2580], [1560, 2160], [300, 660]]], [960, 1320, []]),
  ('regression variant: daily limit units 2',
   [[[4020, 4740], [300, 660], [6600, 7080], [2820, 3300], [5640, 6120], [1560, 1920]]], [960, 2880, []]),
  ('partial repair guard 3', [[[0, 1010]]], [1010, 1010, ['24h']]),
  ('boundary control 4', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 5', [[[1000, 1500], [0, 480]]], [920, 980, []]),
  ('normal control 6', [[[0, 360], [1800, 2280]]], [480, 840, []]),
  ('normal control 7', [[[300, 1020], [4260, 4740], [3480, 4200], [1260, 1620], [3060, 3420]]],
   [1320, 2640, ['24h']]),
  ('normal control 8', [[[0, 600], [1080, 1800], [2040, 2640], [3120, 3720]]], [1200, 2520, ['24h']])],
 [('regression: daily limit units 1', [[[300, 900], [1380, 1740]]], [960, 960, []]),
  ('regression variant: daily limit units 2', [[[660, 1260], [300, 660]]], [960, 960, []]),
  ('partial repair guard 3', [[[0, 480], [480, 961]]], [961, 961, ['24h']]),
  ('boundary control 4', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('normal control 5', [[[3720, 4200], [2220, 2820], [300, 780]]], [600, 1560, []]),
  ('normal control 6', [[[4680, 5280], [3960, 4440], [2640, 3240], [1440, 1920], [0, 720], [5520, 6240]]],
   [1200, 3600, ['24h']]),
  ('normal control 7', [[[300, 900], [1380, 1860]]], [960, 1080, []]),
  ('normal control 8',
   [[[960, 1680], [6480, 6840], [3360, 3720], [7740, 8340], [5160, 5760], [0, 480], [1920, 2640]]],
   [1200, 3840, ['24h', '7d']])],
 [('regression: daily limit units 1', [[[2520, 3120], [2100, 2460], [300, 660]]], [960, 1320, []]),
  ('regression variant: daily limit units 2', [[[300, 660], [2580, 2940], [1140, 1860]]], [960, 1440, []]),
  ('partial repair guard 3', [[[0, 500], [500, 1000]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[0, 720], [1440, 2160], [2880, 3600], [4320, 5040], [5760, 6480]]],
   [720, 3600, []]),
  ('boundary control 5', [[[0, 480], [480, 961]]], [961, 961, ['24h']]),
  ('normal control 6', [[[3120, 3720], [1200, 1800], [1860, 2220], [0, 720]]], [960, 2280, []]),
  ('normal control 7',
   [[[4380, 4740], [6180, 6540], [300, 1020], [4980, 5460], [3540, 4140], [2460, 3060], [7440, 7800]]],
   [960, 3480, []]),
  ('normal control 8',
   [[[2520, 2880], [7080, 7680], [0, 600], [4800, 5280], [6000, 6360], [600, 1080], [3600, 4320]]],
   [1080, 3600, ['24h']])],
 [('regression: daily limit units 1',
   [[[5700, 6180], [3960, 4320], [4380, 4980], [0, 480], [1920, 2400], [2880, 3480]]], [960, 3000, []]),
  ('regression variant: daily limit units 2', [[[300, 660], [900, 1500]]], [960, 960, []]),
  ('partial repair guard 3', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[0, 1010]]], [1010, 1010, ['24h']]),
  ('boundary control 5', [[[0, 720], [1440, 2160], [2880, 3600], [4320, 5040], [5760, 6480]]],
   [720, 3600, []]),
  ('normal control 6',
   [[[1200, 1800], [480, 1200], [0, 480], [4500, 5220], [7140, 7620], [2880, 3600], [5940, 6420],
     [2280, 2880]]],
   [1440, 4800, ['24h', '7d']]),
  ('normal control 7', [[[4140, 4620], [1800, 2520], [0, 360], [2760, 3240]]], [1200, 2040, ['24h']]),
  ('normal control 8', [[[300, 780], [840, 1560]]], [1200, 1200, ['24h']])]]
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: daily limit units 1[960, 960, []][960, 960, []]Passed
regression variant: daily limit units 2[960, 3840, ['7d']][960, 3840, ['7d']]Passed
partial repair guard 3[1000, 1000, []][1000, 1000, ['24h']]Failed
boundary control 4[920, 980, []][920, 980, []]Passed
normal control 5[1380, 3120, ['24h']][1380, 3120, ['24h']]Passed
normal control 6[960, 1920, []][960, 1920, []]Passed
normal control 7[1200, 2760, ['24h']][1200, 2760, ['24h']]Passed
normal control 8[1200, 2400, ['24h']][1200, 2400, ['24h']]Passed

SHA-256 / 808ed4c1b0a4f212b30a5a2d1a39385b049eb5c2f4016be03e2f4e02fa3611c3

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(shifts):
    starts = sorted(s for s, e in shifts)
    def worst(W):
        best = 0
        for t in starts:
            tot = sum(max(0, min(e, t + W) - max(s, t)) for s, e in shifts)
            best = max(best, tot)
        return best
    m24, m168 = worst(1440), worst(10080)
    v = []
    if m24 > 960:
        v.append('24h')
    if m168 > 3600:
        v.append('7d')
    return [m24, m168, v]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: daily limit units 1', [[[0, 480], [480, 960]]], [960, 960, []]),
  ('regression variant: daily limit units 2',
   [[[1740, 2340], [540, 1020], [2820, 3420], [4140, 4620], [5100, 5700], [7140, 7740], [0, 480]]],
   [960, 3840, ['7d']]),
  ('partial repair guard 3', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[1000, 1500], [0, 480]]], [920, 980, []]),
  ('normal control 5', [[[1260, 1980], [780, 1260], [2700, 3300], [0, 720], [3540, 4140]]],
   [1380, 3120, ['24h']]),
  ('normal control 6', [[[300, 780], [3600, 4080], [4140, 4620], [1680, 2160]]], [960, 1920, []]),
  ('normal control 7', [[[3060, 3660], [2400, 3000], [3900, 4260], [0, 480], [960, 1680]]],
   [1200, 2760, ['24h']]),
  ('normal control 8', [[[2460, 3060], [300, 780], [3540, 4140], [1020, 1740]]], [1200, 2400, ['24h']])],
 [('regression: daily limit units 1', [[[2220, 2580], [1560, 2160], [300, 660]]], [960, 1320, []]),
  ('regression variant: daily limit units 2',
   [[[4020, 4740], [300, 660], [6600, 7080], [2820, 3300], [5640, 6120], [1560, 1920]]], [960, 2880, []]),
  ('partial repair guard 3', [[[0, 1010]]], [1010, 1010, ['24h']]),
  ('boundary control 4', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 5', [[[1000, 1500], [0, 480]]], [920, 980, []]),
  ('normal control 6', [[[0, 360], [1800, 2280]]], [480, 840, []]),
  ('normal control 7', [[[300, 1020], [4260, 4740], [3480, 4200], [1260, 1620], [3060, 3420]]],
   [1320, 2640, ['24h']]),
  ('normal control 8', [[[0, 600], [1080, 1800], [2040, 2640], [3120, 3720]]], [1200, 2520, ['24h']])],
 [('regression: daily limit units 1', [[[300, 900], [1380, 1740]]], [960, 960, []]),
  ('regression variant: daily limit units 2', [[[660, 1260], [300, 660]]], [960, 960, []]),
  ('partial repair guard 3', [[[0, 480], [480, 961]]], [961, 961, ['24h']]),
  ('boundary control 4', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('normal control 5', [[[3720, 4200], [2220, 2820], [300, 780]]], [600, 1560, []]),
  ('normal control 6', [[[4680, 5280], [3960, 4440], [2640, 3240], [1440, 1920], [0, 720], [5520, 6240]]],
   [1200, 3600, ['24h']]),
  ('normal control 7', [[[300, 900], [1380, 1860]]], [960, 1080, []]),
  ('normal control 8',
   [[[960, 1680], [6480, 6840], [3360, 3720], [7740, 8340], [5160, 5760], [0, 480], [1920, 2640]]],
   [1200, 3840, ['24h', '7d']])],
 [('regression: daily limit units 1', [[[2520, 3120], [2100, 2460], [300, 660]]], [960, 1320, []]),
  ('regression variant: daily limit units 2', [[[300, 660], [2580, 2940], [1140, 1860]]], [960, 1440, []]),
  ('partial repair guard 3', [[[0, 500], [500, 1000]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[0, 720], [1440, 2160], [2880, 3600], [4320, 5040], [5760, 6480]]],
   [720, 3600, []]),
  ('boundary control 5', [[[0, 480], [480, 961]]], [961, 961, ['24h']]),
  ('normal control 6', [[[3120, 3720], [1200, 1800], [1860, 2220], [0, 720]]], [960, 2280, []]),
  ('normal control 7',
   [[[4380, 4740], [6180, 6540], [300, 1020], [4980, 5460], [3540, 4140], [2460, 3060], [7440, 7800]]],
   [960, 3480, []]),
  ('normal control 8',
   [[[2520, 2880], [7080, 7680], [0, 600], [4800, 5280], [6000, 6360], [600, 1080], [3600, 4320]]],
   [1080, 3600, ['24h']])],
 [('regression: daily limit units 1',
   [[[5700, 6180], [3960, 4320], [4380, 4980], [0, 480], [1920, 2400], [2880, 3480]]], [960, 3000, []]),
  ('regression variant: daily limit units 2', [[[300, 660], [900, 1500]]], [960, 960, []]),
  ('partial repair guard 3', [[[100, 700], [900, 1300]]], [1000, 1000, ['24h']]),
  ('boundary control 4', [[[0, 1010]]], [1010, 1010, ['24h']]),
  ('boundary control 5', [[[0, 720], [1440, 2160], [2880, 3600], [4320, 5040], [5760, 6480]]],
   [720, 3600, []]),
  ('normal control 6',
   [[[1200, 1800], [480, 1200], [0, 480], [4500, 5220], [7140, 7620], [2880, 3600], [5940, 6420],
     [2280, 2880]]],
   [1440, 4800, ['24h', '7d']]),
  ('normal control 7', [[[4140, 4620], [1800, 2520], [0, 360], [2760, 3240]]], [1200, 2040, ['24h']]),
  ('normal control 8', [[[300, 780], [840, 1560]]], [1200, 1200, ['24h']])]]
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: daily limit units 1[960, 960, []][960, 960, []]Passed
regression variant: daily limit units 2[960, 3840, ['7d']][960, 3840, ['7d']]Passed
partial repair guard 3[1000, 1000, ['24h']][1000, 1000, ['24h']]Passed
boundary control 4[920, 980, []][920, 980, []]Passed
normal control 5[1380, 3120, ['24h']][1380, 3120, ['24h']]Passed
normal control 6[960, 1920, []][960, 1920, []]Passed
normal control 7[1200, 2760, ['24h']][1200, 2760, ['24h']]Passed
normal control 8[1200, 2400, ['24h']][1200, 2400, ['24h']]Passed

SHA-256 / a0b548183ead4a487c3ea5725a0edde3a002d32135b5a1c9a5eb9973eb352fb6

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

Case digest / 330ae337335a1af2802d360ebeaacf915d43284232a61c459336ddc9d0190b96