FAILURE MAP
← Case archive

FA-92841 / EV charging session scheduling / Open access

Time-of-use cheapest slot plan: departure slot exclusivity · case 01

Energy is planned into the slot in which the driver has already unplugged.

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

ROOT CAUSE

The departure index is treated as an occupied slot, so the window runs one slot past the plug-out time.

THE FAILURE

The departure index is treated as an occupied slot, so the window runs one slot past the plug-out time.

Unsuccessful approach: Clipping to len(prices)-1 fixes nothing for early departures and drops the final tariff slot when the car stays past the horizon.

Case contract

prices[i] is the tariff in cents/kWh of 15-minute slot i. The car is present for slots arrive..depart-1 (depart exclusive) clipped to the tariff horizon; arrive may be negative. Each slot delivers at most max_w//4 Wh. Fill the cheapest slots first (equal price: earlier slot first), the last slot partially. Return [[slot, wh] sorted by slot, cost in millicents (sum wh*price), unmet wh]. need_wh<=0 or max_w<=0 returns [[], 0, max(need_wh,0)].

Why this case matters

Depot, workplace and public EV chargers schedule sessions against prices, circuit limits and departure deadlines; a wrong decision silently strands a driver or overloads a feeder.

1 / The failure

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

N = 1
observations = []
def solve(prices, arrive, depart, need_wh, max_w):
    if need_wh <= 0 or max_w <= 0:
        return [[], 0, max(need_wh, 0)]
    slot_wh = max_w // 4
    lo = max(arrive, 0)
    hi = min(depart + 1, len(prices))
    window = list(range(lo, hi))
    ranked = sorted(window, key=lambda s: (prices[s], s))
    plan = []
    left = need_wh
    for s in ranked:
        if left <= 0:
            break
        take = min(slot_wh, left)
        plan.append([s, take])
        left -= take
    plan.sort()
    cost = sum(wh * prices[s] for s, wh in plan)
    return [plan, cost, left]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity', [[15, 30, 12, 12, 10, 8, 12, 30], 0, 3, 7000, 11000],
   [[[0, 2750], [1, 1500], [2, 2750]], 119250, 0]],
  ['regression: departure slot exclusivity (partial repair)', [[20, 8, 15, 15], 3, 6, 1500, 11000],
   [[[3, 1500]], 22500, 0]],
  ['control 1', [[25, 30, 20, 12, 12], 2, 2, 20000, 11000], [[], 0, 20000]],
  ['control 2', [[8, 15, 12, 10, 8, 25, 12, 8, 8, 20, 20, 25, 12, 8], -3, 15, 7000, 3700],
   [[[0, 925], [2, 925], [3, 925], [4, 925], [6, 525], [7, 925], [8, 925], [13, 925]], 63650, 0]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[30, 12, 30, 8, 15, 25, 8, 15, 15, 12, 8], 8, 9, 7000, 3700], [[[8, 925]], 13875, 6075]],
  ['regression: departure slot exclusivity (partial repair)',
   [[25, 30, 25, 25, 30, 15, 15, 8, 8, 12, 20, 30, 12, 30], 12, 16, 9000, 3700],
   [[[12, 925], [13, 925]], 38850, 7150]],
  ['control 1', [[30, 12, 20, 30, 25, 30, 12, 30, 20, 10, 20, 8, 15], 7, 12, 4000, 11000],
   [[[9, 1250], [11, 2750]], 34500, 0]],
  ['control 2', [[12, 12, 25, 30, 8, 20, 12, 12], 3, 11, 9000, 3700],
   [[[3, 925], [4, 925], [5, 925], [6, 925], [7, 925]], 75850, 4375]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[15, 30, 20, 12, 12, 15, 20, 30, 15, 12, 30, 8, 25, 8], 7, 8, 4000, 3700],
   [[[7, 925]], 27750, 3075]],
  ['regression: departure slot exclusivity (partial repair)',
   [[10, 25, 25, 30, 8, 12, 25, 8, 25, 25], 3, 13, 9000, 3700],
   [[[3, 925], [4, 925], [5, 925], [6, 925], [7, 925], [8, 925], [9, 925]], 123025, 2525]],
  ['control 1', [[12, 10, 15, 25, 30], 4, 4, 12500, 7400], [[], 0, 12500]],
  ['control 2', [[30, 12, 15, 12, 30, 15, 12, 20, 20], -2, 1, 9000, 0], [[], 0, 9000]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[8, 12, 8, 12, 10, 12, 25, 15, 25], 6, 6, 20000, 11000], [[], 0, 20000]],
  ['regression: departure slot exclusivity (partial repair)',
   [[12, 15, 8, 8, 12, 10, 25, 10, 12, 8, 15, 10, 10], 12, 16, 4000, 7400],
   [[[12, 1850]], 18500, 2150]],
  ['control 1', [[12, 20, 8, 15, 10, 8, 25, 12, 30, 12], 2, 12, 20000, 11000],
   [[[2, 2750], [3, 2750], [4, 2750], [5, 2750], [6, 2750], [7, 2750], [8, 750], [9, 2750]], 270000,
    0]],
  ['control 2', [[30, 30, 20, 12, 12, 15, 12, 10], -1, 8, 7000, 7400],
   [[[3, 1850], [4, 1850], [6, 1450], [7, 1850]], 80300, 0]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[25, 20, 15, 30, 30, 20, 12, 8, 20, 30], 6, 7, 7000, 7400], [[[6, 1850]], 22200, 5150]],
  ['regression: departure slot exclusivity (partial repair)',
   [[8, 25, 15, 12, 12, 12, 20, 25, 10, 12, 20, 20], 10, 14, 9000, 11000],
   [[[10, 2750], [11, 2750]], 110000, 3500]],
  ['control 1', [[12, 12, 8, 15, 10, 12, 20, 15, 12, 10, 12, 12, 15, 15], 12, 13, 1500, 3700],
   [[[12, 925]], 13875, 575]],
  ['control 2', [[10, 12, 10, 8, 10, 12, 12], 2, 5, 4000, 11000],
   [[[2, 1250], [3, 2750]], 34500, 0]]]]
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
boundary: nothing needed[[], 0, 0][[], 0, 0]Passed
boundary: empty window[[[2, 1850]], 55500, 3150][[], 0, 5000]Failed
boundary: departure beyond tariff horizon[[[1, 1850], [2, 1850], [3, 1300]], 63000, 0][[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]Passed
regression: cheap slot at departure[[[0, 1150], [2, 1850]], 43750, 0][[[0, 1850], [1, 1150]], 90000, 0]Failed
regression: departure slot exclusivity[[[0, 1500], [2, 2750], [3, 2750]], 88500, 0][[[0, 2750], [1, 1500], [2, 2750]], 119250, 0]Failed
regression: departure slot exclusivity (partial repair)[[[3, 1500]], 22500, 0][[[3, 1500]], 22500, 0]Passed
control 1[[[2, 2750]], 55000, 17250][[], 0, 20000]Failed
control 2[[[0, 925], [2, 925], [3, 925], [4, 925], [6, 525], [7, 925], [8, 925], [13, 925]], 63650, 0][[[0, 925], [2, 925], [3, 925], [4, 925], [6, 525], [7, 925], [8, 925], [13, 925]], 63650, 0]Passed

SHA-256 / f143842656894a3c433bdba538e22613cc81dbfeca1a8da8fe756dc777dd8ae8

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(prices, arrive, depart, need_wh, max_w):
    if need_wh <= 0 or max_w <= 0:
        return [[], 0, max(need_wh, 0)]
    slot_wh = max_w // 4
    lo = max(arrive, 0)
    hi = min(depart, len(prices) - 1)
    window = list(range(lo, hi))
    ranked = sorted(window, key=lambda s: (prices[s], s))
    plan = []
    left = need_wh
    for s in ranked:
        if left <= 0:
            break
        take = min(slot_wh, left)
        plan.append([s, take])
        left -= take
    plan.sort()
    cost = sum(wh * prices[s] for s, wh in plan)
    return [plan, cost, left]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity', [[15, 30, 12, 12, 10, 8, 12, 30], 0, 3, 7000, 11000],
   [[[0, 2750], [1, 1500], [2, 2750]], 119250, 0]],
  ['regression: departure slot exclusivity (partial repair)', [[20, 8, 15, 15], 3, 6, 1500, 11000],
   [[[3, 1500]], 22500, 0]],
  ['control 1', [[25, 30, 20, 12, 12], 2, 2, 20000, 11000], [[], 0, 20000]],
  ['control 2', [[8, 15, 12, 10, 8, 25, 12, 8, 8, 20, 20, 25, 12, 8], -3, 15, 7000, 3700],
   [[[0, 925], [2, 925], [3, 925], [4, 925], [6, 525], [7, 925], [8, 925], [13, 925]], 63650, 0]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[30, 12, 30, 8, 15, 25, 8, 15, 15, 12, 8], 8, 9, 7000, 3700], [[[8, 925]], 13875, 6075]],
  ['regression: departure slot exclusivity (partial repair)',
   [[25, 30, 25, 25, 30, 15, 15, 8, 8, 12, 20, 30, 12, 30], 12, 16, 9000, 3700],
   [[[12, 925], [13, 925]], 38850, 7150]],
  ['control 1', [[30, 12, 20, 30, 25, 30, 12, 30, 20, 10, 20, 8, 15], 7, 12, 4000, 11000],
   [[[9, 1250], [11, 2750]], 34500, 0]],
  ['control 2', [[12, 12, 25, 30, 8, 20, 12, 12], 3, 11, 9000, 3700],
   [[[3, 925], [4, 925], [5, 925], [6, 925], [7, 925]], 75850, 4375]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[15, 30, 20, 12, 12, 15, 20, 30, 15, 12, 30, 8, 25, 8], 7, 8, 4000, 3700],
   [[[7, 925]], 27750, 3075]],
  ['regression: departure slot exclusivity (partial repair)',
   [[10, 25, 25, 30, 8, 12, 25, 8, 25, 25], 3, 13, 9000, 3700],
   [[[3, 925], [4, 925], [5, 925], [6, 925], [7, 925], [8, 925], [9, 925]], 123025, 2525]],
  ['control 1', [[12, 10, 15, 25, 30], 4, 4, 12500, 7400], [[], 0, 12500]],
  ['control 2', [[30, 12, 15, 12, 30, 15, 12, 20, 20], -2, 1, 9000, 0], [[], 0, 9000]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[8, 12, 8, 12, 10, 12, 25, 15, 25], 6, 6, 20000, 11000], [[], 0, 20000]],
  ['regression: departure slot exclusivity (partial repair)',
   [[12, 15, 8, 8, 12, 10, 25, 10, 12, 8, 15, 10, 10], 12, 16, 4000, 7400],
   [[[12, 1850]], 18500, 2150]],
  ['control 1', [[12, 20, 8, 15, 10, 8, 25, 12, 30, 12], 2, 12, 20000, 11000],
   [[[2, 2750], [3, 2750], [4, 2750], [5, 2750], [6, 2750], [7, 2750], [8, 750], [9, 2750]], 270000,
    0]],
  ['control 2', [[30, 30, 20, 12, 12, 15, 12, 10], -1, 8, 7000, 7400],
   [[[3, 1850], [4, 1850], [6, 1450], [7, 1850]], 80300, 0]]],
 [['boundary: nothing needed', [[10, 20, 30], 0, 3, 0, 7400], [[], 0, 0]],
  ['boundary: empty window', [[10, 20, 30], 2, 2, 5000, 7400], [[], 0, 5000]],
  ['boundary: departure beyond tariff horizon', [[30, 10, 10, 20], 1, 9, 5000, 7400],
   [[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]],
  ['regression: cheap slot at departure', [[30, 30, 5, 5], 0, 2, 3000, 7400],
   [[[0, 1850], [1, 1150]], 90000, 0]],
  ['regression: departure slot exclusivity',
   [[25, 20, 15, 30, 30, 20, 12, 8, 20, 30], 6, 7, 7000, 7400], [[[6, 1850]], 22200, 5150]],
  ['regression: departure slot exclusivity (partial repair)',
   [[8, 25, 15, 12, 12, 12, 20, 25, 10, 12, 20, 20], 10, 14, 9000, 11000],
   [[[10, 2750], [11, 2750]], 110000, 3500]],
  ['control 1', [[12, 12, 8, 15, 10, 12, 20, 15, 12, 10, 12, 12, 15, 15], 12, 13, 1500, 3700],
   [[[12, 925]], 13875, 575]],
  ['control 2', [[10, 12, 10, 8, 10, 12, 12], 2, 5, 4000, 11000],
   [[[2, 1250], [3, 2750]], 34500, 0]]]]
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
boundary: nothing needed[[], 0, 0][[], 0, 0]Passed
boundary: empty window[[], 0, 5000][[], 0, 5000]Passed
boundary: departure beyond tariff horizon[[[1, 1850], [2, 1850]], 37000, 1300][[[1, 1850], [2, 1850], [3, 1300]], 63000, 0]Failed
regression: cheap slot at departure[[[0, 1850], [1, 1150]], 90000, 0][[[0, 1850], [1, 1150]], 90000, 0]Passed
regression: departure slot exclusivity[[[0, 2750], [1, 1500], [2, 2750]], 119250, 0][[[0, 2750], [1, 1500], [2, 2750]], 119250, 0]Passed
regression: departure slot exclusivity (partial repair)[[], 0, 1500][[[3, 1500]], 22500, 0]Failed
control 1[[], 0, 20000][[], 0, 20000]Passed
control 2[[[0, 925], [2, 925], [3, 925], [4, 925], [6, 925], [7, 925], [8, 925], [12, 525]], 67350, 0][[[0, 925], [2, 925], [3, 925], [4, 925], [6, 525], [7, 925], [8, 925], [13, 925]], 63650, 0]Failed

SHA-256 / 4ce3608dd75d342d7a92436e397738a05eb5668f155053c14cc17968a9115e08

HELD IN THE MEMBER ARCHIVE

The verified repair and its recorded checks are member-only.

This mechanism has 8 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.

Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.

Member access is invitation-based. Sign in with your invited account to inspect the repair.

Sign in to the archive ↗

Verification & scope

Deterministic stipulated toy contract for teaching; no claim of conformance with any standard, vendor protocol or production controller. 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:49.746562+00:00.

Case digest / 302a18b0ad0312cfded88a1420ee0ca3dc06dec2534d0a709e0a19340e90c839