FAILURE MAP
← Case archive

FA-93901 / Shift rostering labor rules / Open access

Exactly ten-hour spread earns the spread premium · case 01

A day spanning precisely ten hours is paid an extra hour.

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

ROOT CAUSE

The spread premium comparison is inclusive at 600 minutes.

VERIFIED REPAIR

Pay the spread premium only when the spread strictly exceeds 600 minutes.

Unsuccessful approach: Also requiring a split denies the premium to continuous shifts longer than ten hours.

Case contract

Worked segments [start, end] of one workday (unordered, possibly overlapping) and a minimum wage in cents. Overlapping or touching segments merge. Spread is last end minus first start. Each gap longer than 60 minutes is a split. A spread over 600 minutes earns one hour of minimum wage and replaces any split premium; otherwise each split earns min_wage // 2, for at most two splits. Return [spread, splits, premium].

Why this case matters

Spread-of-hours and split-shift premiums are frequent roster-pay rules that depend on correct interval handling.

1 / The failure

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

N = 1
observations = []
def solve(segments, min_wage):
    seg = sorted(segments)
    merged = []
    for s, e in seg:
        if merged and s <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], e)
        else:
            merged.append([s, e])
    spread = merged[-1][1] - merged[0][0]
    splits = sum(1 for a, b in zip(merged, merged[1:]) if b[0] - a[1] > 60)
    if spread >= 600:
        premium = min_wage
    else:
        premium = (min_wage // 2) * min(splits, 2)
    return [spread, splits, premium]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: spread threshold requires split 1', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[720, 960], [360, 480]], 1600], [600, 1, 800]),
  ('partial repair guard 3', [[[810, 1110], [480, 780]], 1520], [630, 0, 1520]),
  ('boundary control 4', [[[420, 1020]], 1600], [600, 0, 0]),
  ('boundary control 5', [[[420, 480], [550, 600], [700, 760], [840, 900], [980, 1010]], 1655],
   [590, 4, 1654]),
  ('normal control 6', [[[720, 900], [420, 720], [961, 1141]], 1520], [721, 1, 1520]),
  ('normal control 7', [[[1079, 1319], [960, 1020], [420, 540], [630, 870]], 1600], [899, 2, 1600]),
  ('normal control 8', [[[1169, 1349], [810, 930], [990, 1110], [480, 780]], 1600], [869, 0, 1600])],
 [('regression: spread threshold requires split 1', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
  ('regression variant: spread threshold requires split 2', [[[420, 600], [660, 780], [840, 1020]], 1701],
   [600, 0, 0]),
  ('partial repair guard 3', [[[719, 839], [1108, 1168], [480, 660], [898, 1078]], 1600], [688, 0, 1600]),
  ('boundary control 4', [[[900, 1000], [420, 600], [700, 800]], 1520], [580, 2, 1520]),
  ('normal control 5', [[[960, 1140], [970, 980], [480, 780]], 1520], [660, 1, 1520]),
  ('normal control 6', [[[420, 540], [570, 630], [691, 991], [510, 570]], 1600], [571, 1, 800]),
  ('normal control 7', [[[1261, 1441], [430, 440], [780, 1020], [420, 660], [1081, 1261]], 1600],
   [1021, 2, 1600]),
  ('normal control 8', [[[480, 720], [780, 840]], 1655], [360, 0, 0])],
 [('regression: spread threshold requires split 1', [[[420, 600], [660, 780], [840, 1020]], 1701],
   [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[660, 960], [370, 380], [360, 600]], 1600],
   [600, 0, 0]),
  ('partial repair guard 3', [[[1169, 1349], [810, 930], [990, 1110], [480, 780]], 1600], [869, 0, 1600]),
  ('boundary control 4', [[[600, 630], [700, 730], [800, 830], [900, 930]], 1701], [330, 3, 1700]),
  ('boundary control 5', [[[420, 1021]], 1600], [601, 0, 1600]),
  ('normal control 6', [[[600, 780], [420, 480], [430, 440]], 1600], [360, 1, 800]),
  ('normal control 7', [[[1021, 1261], [781, 961], [480, 720]], 1600], [781, 1, 1600]),
  ('normal control 8', [[[810, 930], [1170, 1470], [420, 720]], 1655], [1050, 2, 1655])],
 [('regression: spread threshold requires split 1', [[[420, 1020]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
  ('partial repair guard 3', [[[600, 780], [1139, 1379], [839, 1079]], 1701], [779, 0, 1701]),
  ('boundary control 4', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
  ('boundary control 5', [[[420, 450], [520, 550], [620, 650], [720, 750]], 1600], [330, 3, 1600]),
  ('normal control 6', [[[480, 600]], 1655], [120, 0, 0]),
  ('normal control 7', [[[480, 720], [900, 1020]], 1600], [540, 1, 800]),
  ('normal control 8', [[[600, 840]], 1520], [240, 0, 0])],
 [('regression: spread threshold requires split 1', [[[660, 960], [360, 600]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[780, 960], [360, 540]], 1655], [600, 1, 827]),
  ('partial repair guard 3', [[[810, 1110], [480, 780]], 1520], [630, 0, 1520]),
  ('boundary control 4', [[[600, 900], [610, 620], [1000, 1100]], 1655], [500, 1, 827]),
  ('boundary control 5', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
  ('normal control 6', [[[1019, 1259], [1320, 1440], [720, 960], [360, 540]], 1655], [1080, 2, 1655]),
  ('normal control 7', [[[420, 600], [430, 440]], 1701], [180, 0, 0]),
  ('normal control 8', [[[1440, 1500], [1080, 1200], [870, 1110], [480, 780]], 1655], [1020, 2, 1655])]]
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: spread threshold requires split 1[600, 0, 1600][600, 0, 0]Failed
regression variant: spread threshold requires split 2[600, 1, 1600][600, 1, 800]Failed
partial repair guard 3[630, 0, 1520][630, 0, 1520]Passed
boundary control 4[600, 0, 1600][600, 0, 0]Failed
boundary control 5[590, 4, 1654][590, 4, 1654]Passed
normal control 6[721, 1, 1520][721, 1, 1520]Passed
normal control 7[899, 2, 1600][899, 2, 1600]Passed
normal control 8[869, 0, 1600][869, 0, 1600]Passed

SHA-256 / ea772d78da46ba1173fd104cbf63f75aeae387d49cb53d6d2464ab3bd23cda0f

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(segments, min_wage):
    seg = sorted(segments)
    merged = []
    for s, e in seg:
        if merged and s <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], e)
        else:
            merged.append([s, e])
    spread = merged[-1][1] - merged[0][0]
    splits = sum(1 for a, b in zip(merged, merged[1:]) if b[0] - a[1] > 60)
    if spread > 600 and splits:
        premium = min_wage
    else:
        premium = (min_wage // 2) * min(splits, 2)
    return [spread, splits, premium]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: spread threshold requires split 1', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[720, 960], [360, 480]], 1600], [600, 1, 800]),
  ('partial repair guard 3', [[[810, 1110], [480, 780]], 1520], [630, 0, 1520]),
  ('boundary control 4', [[[420, 1020]], 1600], [600, 0, 0]),
  ('boundary control 5', [[[420, 480], [550, 600], [700, 760], [840, 900], [980, 1010]], 1655],
   [590, 4, 1654]),
  ('normal control 6', [[[720, 900], [420, 720], [961, 1141]], 1520], [721, 1, 1520]),
  ('normal control 7', [[[1079, 1319], [960, 1020], [420, 540], [630, 870]], 1600], [899, 2, 1600]),
  ('normal control 8', [[[1169, 1349], [810, 930], [990, 1110], [480, 780]], 1600], [869, 0, 1600])],
 [('regression: spread threshold requires split 1', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
  ('regression variant: spread threshold requires split 2', [[[420, 600], [660, 780], [840, 1020]], 1701],
   [600, 0, 0]),
  ('partial repair guard 3', [[[719, 839], [1108, 1168], [480, 660], [898, 1078]], 1600], [688, 0, 1600]),
  ('boundary control 4', [[[900, 1000], [420, 600], [700, 800]], 1520], [580, 2, 1520]),
  ('normal control 5', [[[960, 1140], [970, 980], [480, 780]], 1520], [660, 1, 1520]),
  ('normal control 6', [[[420, 540], [570, 630], [691, 991], [510, 570]], 1600], [571, 1, 800]),
  ('normal control 7', [[[1261, 1441], [430, 440], [780, 1020], [420, 660], [1081, 1261]], 1600],
   [1021, 2, 1600]),
  ('normal control 8', [[[480, 720], [780, 840]], 1655], [360, 0, 0])],
 [('regression: spread threshold requires split 1', [[[420, 600], [660, 780], [840, 1020]], 1701],
   [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[660, 960], [370, 380], [360, 600]], 1600],
   [600, 0, 0]),
  ('partial repair guard 3', [[[1169, 1349], [810, 930], [990, 1110], [480, 780]], 1600], [869, 0, 1600]),
  ('boundary control 4', [[[600, 630], [700, 730], [800, 830], [900, 930]], 1701], [330, 3, 1700]),
  ('boundary control 5', [[[420, 1021]], 1600], [601, 0, 1600]),
  ('normal control 6', [[[600, 780], [420, 480], [430, 440]], 1600], [360, 1, 800]),
  ('normal control 7', [[[1021, 1261], [781, 961], [480, 720]], 1600], [781, 1, 1600]),
  ('normal control 8', [[[810, 930], [1170, 1470], [420, 720]], 1655], [1050, 2, 1655])],
 [('regression: spread threshold requires split 1', [[[420, 1020]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
  ('partial repair guard 3', [[[600, 780], [1139, 1379], [839, 1079]], 1701], [779, 0, 1701]),
  ('boundary control 4', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
  ('boundary control 5', [[[420, 450], [520, 550], [620, 650], [720, 750]], 1600], [330, 3, 1600]),
  ('normal control 6', [[[480, 600]], 1655], [120, 0, 0]),
  ('normal control 7', [[[480, 720], [900, 1020]], 1600], [540, 1, 800]),
  ('normal control 8', [[[600, 840]], 1520], [240, 0, 0])],
 [('regression: spread threshold requires split 1', [[[660, 960], [360, 600]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[780, 960], [360, 540]], 1655], [600, 1, 827]),
  ('partial repair guard 3', [[[810, 1110], [480, 780]], 1520], [630, 0, 1520]),
  ('boundary control 4', [[[600, 900], [610, 620], [1000, 1100]], 1655], [500, 1, 827]),
  ('boundary control 5', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
  ('normal control 6', [[[1019, 1259], [1320, 1440], [720, 960], [360, 540]], 1655], [1080, 2, 1655]),
  ('normal control 7', [[[420, 600], [430, 440]], 1701], [180, 0, 0]),
  ('normal control 8', [[[1440, 1500], [1080, 1200], [870, 1110], [480, 780]], 1655], [1020, 2, 1655])]]
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: spread threshold requires split 1[600, 0, 0][600, 0, 0]Passed
regression variant: spread threshold requires split 2[600, 1, 800][600, 1, 800]Passed
partial repair guard 3[630, 0, 0][630, 0, 1520]Failed
boundary control 4[600, 0, 0][600, 0, 0]Passed
boundary control 5[590, 4, 1654][590, 4, 1654]Passed
normal control 6[721, 1, 1520][721, 1, 1520]Passed
normal control 7[899, 2, 1600][899, 2, 1600]Passed
normal control 8[869, 0, 0][869, 0, 1600]Failed

SHA-256 / 7d45b32b6fb04029717cd5aa696d5f5974fd4e966fd9c6a67dbef2602c5c3982

3 / The verified repair

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

N = 1
observations = []
def solve(segments, min_wage):
    seg = sorted(segments)
    merged = []
    for s, e in seg:
        if merged and s <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], e)
        else:
            merged.append([s, e])
    spread = merged[-1][1] - merged[0][0]
    splits = sum(1 for a, b in zip(merged, merged[1:]) if b[0] - a[1] > 60)
    if spread > 600:
        premium = min_wage
    else:
        premium = (min_wage // 2) * min(splits, 2)
    return [spread, splits, premium]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: spread threshold requires split 1', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[720, 960], [360, 480]], 1600], [600, 1, 800]),
  ('partial repair guard 3', [[[810, 1110], [480, 780]], 1520], [630, 0, 1520]),
  ('boundary control 4', [[[420, 1020]], 1600], [600, 0, 0]),
  ('boundary control 5', [[[420, 480], [550, 600], [700, 760], [840, 900], [980, 1010]], 1655],
   [590, 4, 1654]),
  ('normal control 6', [[[720, 900], [420, 720], [961, 1141]], 1520], [721, 1, 1520]),
  ('normal control 7', [[[1079, 1319], [960, 1020], [420, 540], [630, 870]], 1600], [899, 2, 1600]),
  ('normal control 8', [[[1169, 1349], [810, 930], [990, 1110], [480, 780]], 1600], [869, 0, 1600])],
 [('regression: spread threshold requires split 1', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
  ('regression variant: spread threshold requires split 2', [[[420, 600], [660, 780], [840, 1020]], 1701],
   [600, 0, 0]),
  ('partial repair guard 3', [[[719, 839], [1108, 1168], [480, 660], [898, 1078]], 1600], [688, 0, 1600]),
  ('boundary control 4', [[[900, 1000], [420, 600], [700, 800]], 1520], [580, 2, 1520]),
  ('normal control 5', [[[960, 1140], [970, 980], [480, 780]], 1520], [660, 1, 1520]),
  ('normal control 6', [[[420, 540], [570, 630], [691, 991], [510, 570]], 1600], [571, 1, 800]),
  ('normal control 7', [[[1261, 1441], [430, 440], [780, 1020], [420, 660], [1081, 1261]], 1600],
   [1021, 2, 1600]),
  ('normal control 8', [[[480, 720], [780, 840]], 1655], [360, 0, 0])],
 [('regression: spread threshold requires split 1', [[[420, 600], [660, 780], [840, 1020]], 1701],
   [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[660, 960], [370, 380], [360, 600]], 1600],
   [600, 0, 0]),
  ('partial repair guard 3', [[[1169, 1349], [810, 930], [990, 1110], [480, 780]], 1600], [869, 0, 1600]),
  ('boundary control 4', [[[600, 630], [700, 730], [800, 830], [900, 930]], 1701], [330, 3, 1700]),
  ('boundary control 5', [[[420, 1021]], 1600], [601, 0, 1600]),
  ('normal control 6', [[[600, 780], [420, 480], [430, 440]], 1600], [360, 1, 800]),
  ('normal control 7', [[[1021, 1261], [781, 961], [480, 720]], 1600], [781, 1, 1600]),
  ('normal control 8', [[[810, 930], [1170, 1470], [420, 720]], 1655], [1050, 2, 1655])],
 [('regression: spread threshold requires split 1', [[[420, 1020]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[480, 720], [781, 1080]], 1600], [600, 1, 800]),
  ('partial repair guard 3', [[[600, 780], [1139, 1379], [839, 1079]], 1701], [779, 0, 1701]),
  ('boundary control 4', [[[420, 600], [660, 780], [840, 1020]], 1701], [600, 0, 0]),
  ('boundary control 5', [[[420, 450], [520, 550], [620, 650], [720, 750]], 1600], [330, 3, 1600]),
  ('normal control 6', [[[480, 600]], 1655], [120, 0, 0]),
  ('normal control 7', [[[480, 720], [900, 1020]], 1600], [540, 1, 800]),
  ('normal control 8', [[[600, 840]], 1520], [240, 0, 0])],
 [('regression: spread threshold requires split 1', [[[660, 960], [360, 600]], 1600], [600, 0, 0]),
  ('regression variant: spread threshold requires split 2', [[[780, 960], [360, 540]], 1655], [600, 1, 827]),
  ('partial repair guard 3', [[[810, 1110], [480, 780]], 1520], [630, 0, 1520]),
  ('boundary control 4', [[[600, 900], [610, 620], [1000, 1100]], 1655], [500, 1, 827]),
  ('boundary control 5', [[[480, 720], [780, 1080]], 1600], [600, 0, 0]),
  ('normal control 6', [[[1019, 1259], [1320, 1440], [720, 960], [360, 540]], 1655], [1080, 2, 1655]),
  ('normal control 7', [[[420, 600], [430, 440]], 1701], [180, 0, 0]),
  ('normal control 8', [[[1440, 1500], [1080, 1200], [870, 1110], [480, 780]], 1655], [1020, 2, 1655])]]
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: spread threshold requires split 1[600, 0, 0][600, 0, 0]Passed
regression variant: spread threshold requires split 2[600, 1, 800][600, 1, 800]Passed
partial repair guard 3[630, 0, 1520][630, 0, 1520]Passed
boundary control 4[600, 0, 0][600, 0, 0]Passed
boundary control 5[590, 4, 1654][590, 4, 1654]Passed
normal control 6[721, 1, 1520][721, 1, 1520]Passed
normal control 7[899, 2, 1600][899, 2, 1600]Passed
normal control 8[869, 0, 1600][869, 0, 1600]Passed

SHA-256 / 44ea18630b5c12473528f9a9fbdd31a89bffc1b97ab91513dafaf8c26c675ab2

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

Case digest / bb9d96fecff5d34398949918e916075ba0e1e1ef7566d47f3363733b517da8ab