FA-93241 / EV charging session scheduling / Open access
Contiguous cheap charging blocks: run accumulator reset · case 01
Separate cheap periods are merged into one long run that includes expensive gaps.
ROOT CAUSE
The current-run accumulator is not cleared at an expensive slot.
VERIFIED REPAIR
Start a fresh run after each expensive slot.
Unsuccessful approach: Appending a copy still never clears the accumulator.
Case contract
Slots with price <= threshold form runs of consecutive slots; runs shorter than min_len are ignored to avoid contactor cycling. Slots of qualifying runs are taken chronologically until need_slots are chosen. If still short, the remaining cheapest unchosen slots (ties: earlier) are added regardless of runs and fallback is True. Return [sorted slots, fallback].
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, threshold, min_len, need_slots):
runs = []
cur = []
for i, p in enumerate(prices):
if p <= threshold:
cur.append(i)
else:
if cur:
runs.append(cur)
if cur:
runs.append(cur)
chosen = []
for run in runs:
if len(run) >= min_len:
for i in run:
if len(chosen) < need_slots:
chosen.append(i)
fallback = False
if len(chosen) < need_slots:
fallback = True
rest = sorted((p, i) for i, p in enumerate(prices) if i not in chosen)
for p, i in rest[:need_slots - len(chosen)]:
chosen.append(i)
return [sorted(chosen), fallback]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[0, 12, 8, 15, 10, 30, 5, 5, 5, 5, 8, 20, 15, 12, 8, 8], 8, 2, 3], [[6, 7, 8], False]],
['regression: run accumulator reset (partial repair)',
[[-2, -2, 10, 8, 0, 5, -2, 8, 20, -2, -2], 0, 1, 7], [[0, 1, 4, 5, 6, 9, 10], True]],
['control 1', [[5, 20, 15, 10, -2], 10, 3, 5], [[0, 1, 2, 3, 4], True]],
['control 2', [[30, 30, 0, -2, 20, 8, 5, 20, 0, 20], 0, 4, 1], [[3], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[20, 5, 15, 12, 15, 30, 5, 20, 0, 20, 10, 0, 5, -2, 10], 12, 4, 6],
[[8, 10, 11, 12, 13, 14], True]],
['regression: run accumulator reset (partial repair)',
[[20, 8, 10, 30, -2, 0, 12, 20, 8, 8], 12, 4, 1], [[4], True]],
['control 1', [[30, -2, 5, 12, 8, 20, 20, 20], 12, 1, 7], [[1, 2, 3, 4, 5, 6, 7], True]],
['control 2', [[30, 8, 15, 0, 15], 8, 2, 2], [[1, 3], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset', [[20, 15, 10, 30, 12, 30, -2, 10, 15, 30, 5, 20], 12, 4, 2],
[[6, 10], True]],
['regression: run accumulator reset (partial repair)', [[30, 8, 12, 30, 30, 5, 5, 12], 8, 1, 5],
[[1, 2, 5, 6, 7], True]],
['control 1', [[20, -2, 10, 8, 15, 10, 0, 8, 12, 5, 8, 0, 8], 0, 1, 1], [[1], False]],
['control 2', [[-2, 8, 8, 12, -2, -2, -2, 10, 12, 15], 0, 2, 7], [[0, 1, 2, 4, 5, 6, 7], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[10, 30, 5, 20, 0, 8, 20, 10, 30, 30, 20, 30, 8, 30, 10], 12, 3, 2], [[2, 4], True]],
['regression: run accumulator reset (partial repair)',
[[-2, -2, 15, -2, 5, 10, 5, 20, -2, 30, 20, 5, 10], 0, 4, 2], [[0, 1], True]],
['control 1', [[-2, 12, 30, 10, 10, 10, 15, 0, 15, 10, -2, 20, 0], 12, 4, 5],
[[0, 3, 7, 10, 12], True]],
['control 2', [[10, 30, 15, 20, -2, 20, 10, 20, -2, 8, 5, 0, 20], 8, 3, 1], [[8], False]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset', [[-2, 10, 30, 12, 8, 0, 8, 30, -2, 12, -2, 0, 12], 8, 2, 7],
[[0, 4, 5, 6, 8, 10, 11], True]],
['regression: run accumulator reset (partial repair)',
[[8, -2, 30, 12, 12, 0, -2, 12, 20, 15, 5, 12, 0, 15, -2], 0, 1, 8],
[[0, 1, 3, 5, 6, 10, 12, 14], True]],
['control 1', [[20, 15, 8, 8, -2, 8, -2, 30, 12, 30, 10], 10, 2, 1], [[2], False]],
['control 2', [[5, 12, 20, 0, 15, -2], 12, 4, 6], [[0, 1, 2, 3, 4, 5], True]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| boundary: run ending at horizon | [[2, 3], False] | [[2, 3], False] | Passed |
| boundary: run exactly min_len | [[0, 1], False] | [[0, 1], False] | Passed |
| boundary: nothing needed | [[], False] | [[], False] | Passed |
| regression: run accumulator reset | [[0, 2, 6], False] | [[6, 7, 8], False] | Failed |
| regression: run accumulator reset (partial repair) | [[0, 0, 1, 4, 6, 9, 10], False] | [[0, 1, 4, 5, 6, 9, 10], True] | Failed |
| control 1 | [[0, 0, 3, 3, 4], False] | [[0, 1, 2, 3, 4], True] | Failed |
| control 2 | [[3], True] | [[3], True] | Passed |
SHA-256 / aae7b0c79341f167565083c647d5a59e76adbb3ab714075bad747c6a87734f6f
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(prices, threshold, min_len, need_slots):
runs = []
cur = []
for i, p in enumerate(prices):
if p <= threshold:
cur.append(i)
else:
if cur:
runs.append(list(cur))
if cur:
runs.append(cur)
chosen = []
for run in runs:
if len(run) >= min_len:
for i in run:
if len(chosen) < need_slots:
chosen.append(i)
fallback = False
if len(chosen) < need_slots:
fallback = True
rest = sorted((p, i) for i, p in enumerate(prices) if i not in chosen)
for p, i in rest[:need_slots - len(chosen)]:
chosen.append(i)
return [sorted(chosen), fallback]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[0, 12, 8, 15, 10, 30, 5, 5, 5, 5, 8, 20, 15, 12, 8, 8], 8, 2, 3], [[6, 7, 8], False]],
['regression: run accumulator reset (partial repair)',
[[-2, -2, 10, 8, 0, 5, -2, 8, 20, -2, -2], 0, 1, 7], [[0, 1, 4, 5, 6, 9, 10], True]],
['control 1', [[5, 20, 15, 10, -2], 10, 3, 5], [[0, 1, 2, 3, 4], True]],
['control 2', [[30, 30, 0, -2, 20, 8, 5, 20, 0, 20], 0, 4, 1], [[3], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[20, 5, 15, 12, 15, 30, 5, 20, 0, 20, 10, 0, 5, -2, 10], 12, 4, 6],
[[8, 10, 11, 12, 13, 14], True]],
['regression: run accumulator reset (partial repair)',
[[20, 8, 10, 30, -2, 0, 12, 20, 8, 8], 12, 4, 1], [[4], True]],
['control 1', [[30, -2, 5, 12, 8, 20, 20, 20], 12, 1, 7], [[1, 2, 3, 4, 5, 6, 7], True]],
['control 2', [[30, 8, 15, 0, 15], 8, 2, 2], [[1, 3], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset', [[20, 15, 10, 30, 12, 30, -2, 10, 15, 30, 5, 20], 12, 4, 2],
[[6, 10], True]],
['regression: run accumulator reset (partial repair)', [[30, 8, 12, 30, 30, 5, 5, 12], 8, 1, 5],
[[1, 2, 5, 6, 7], True]],
['control 1', [[20, -2, 10, 8, 15, 10, 0, 8, 12, 5, 8, 0, 8], 0, 1, 1], [[1], False]],
['control 2', [[-2, 8, 8, 12, -2, -2, -2, 10, 12, 15], 0, 2, 7], [[0, 1, 2, 4, 5, 6, 7], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[10, 30, 5, 20, 0, 8, 20, 10, 30, 30, 20, 30, 8, 30, 10], 12, 3, 2], [[2, 4], True]],
['regression: run accumulator reset (partial repair)',
[[-2, -2, 15, -2, 5, 10, 5, 20, -2, 30, 20, 5, 10], 0, 4, 2], [[0, 1], True]],
['control 1', [[-2, 12, 30, 10, 10, 10, 15, 0, 15, 10, -2, 20, 0], 12, 4, 5],
[[0, 3, 7, 10, 12], True]],
['control 2', [[10, 30, 15, 20, -2, 20, 10, 20, -2, 8, 5, 0, 20], 8, 3, 1], [[8], False]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset', [[-2, 10, 30, 12, 8, 0, 8, 30, -2, 12, -2, 0, 12], 8, 2, 7],
[[0, 4, 5, 6, 8, 10, 11], True]],
['regression: run accumulator reset (partial repair)',
[[8, -2, 30, 12, 12, 0, -2, 12, 20, 15, 5, 12, 0, 15, -2], 0, 1, 8],
[[0, 1, 3, 5, 6, 10, 12, 14], True]],
['control 1', [[20, 15, 8, 8, -2, 8, -2, 30, 12, 30, 10], 10, 2, 1], [[2], False]],
['control 2', [[5, 12, 20, 0, 15, -2], 12, 4, 6], [[0, 1, 2, 3, 4, 5], True]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| boundary: run ending at horizon | [[2, 3], False] | [[2, 3], False] | Passed |
| boundary: run exactly min_len | [[0, 1], False] | [[0, 1], False] | Passed |
| boundary: nothing needed | [[], False] | [[], False] | Passed |
| regression: run accumulator reset | [[0, 0, 2], False] | [[6, 7, 8], False] | Failed |
| regression: run accumulator reset (partial repair) | [[0, 0, 0, 1, 1, 1, 4], False] | [[0, 1, 4, 5, 6, 9, 10], True] | Failed |
| control 1 | [[0, 1, 2, 3, 4], True] | [[0, 1, 2, 3, 4], True] | Passed |
| control 2 | [[3], True] | [[3], True] | Passed |
SHA-256 / 08a7abcf5337399b9c3783ec20fd1af2e6c5f524d54da63c62cc4fa1f7bc5c40
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(prices, threshold, min_len, need_slots):
runs = []
cur = []
for i, p in enumerate(prices):
if p <= threshold:
cur.append(i)
else:
if cur:
runs.append(cur)
cur = []
if cur:
runs.append(cur)
chosen = []
for run in runs:
if len(run) >= min_len:
for i in run:
if len(chosen) < need_slots:
chosen.append(i)
fallback = False
if len(chosen) < need_slots:
fallback = True
rest = sorted((p, i) for i, p in enumerate(prices) if i not in chosen)
for p, i in rest[:need_slots - len(chosen)]:
chosen.append(i)
return [sorted(chosen), fallback]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[0, 12, 8, 15, 10, 30, 5, 5, 5, 5, 8, 20, 15, 12, 8, 8], 8, 2, 3], [[6, 7, 8], False]],
['regression: run accumulator reset (partial repair)',
[[-2, -2, 10, 8, 0, 5, -2, 8, 20, -2, -2], 0, 1, 7], [[0, 1, 4, 5, 6, 9, 10], True]],
['control 1', [[5, 20, 15, 10, -2], 10, 3, 5], [[0, 1, 2, 3, 4], True]],
['control 2', [[30, 30, 0, -2, 20, 8, 5, 20, 0, 20], 0, 4, 1], [[3], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[20, 5, 15, 12, 15, 30, 5, 20, 0, 20, 10, 0, 5, -2, 10], 12, 4, 6],
[[8, 10, 11, 12, 13, 14], True]],
['regression: run accumulator reset (partial repair)',
[[20, 8, 10, 30, -2, 0, 12, 20, 8, 8], 12, 4, 1], [[4], True]],
['control 1', [[30, -2, 5, 12, 8, 20, 20, 20], 12, 1, 7], [[1, 2, 3, 4, 5, 6, 7], True]],
['control 2', [[30, 8, 15, 0, 15], 8, 2, 2], [[1, 3], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset', [[20, 15, 10, 30, 12, 30, -2, 10, 15, 30, 5, 20], 12, 4, 2],
[[6, 10], True]],
['regression: run accumulator reset (partial repair)', [[30, 8, 12, 30, 30, 5, 5, 12], 8, 1, 5],
[[1, 2, 5, 6, 7], True]],
['control 1', [[20, -2, 10, 8, 15, 10, 0, 8, 12, 5, 8, 0, 8], 0, 1, 1], [[1], False]],
['control 2', [[-2, 8, 8, 12, -2, -2, -2, 10, 12, 15], 0, 2, 7], [[0, 1, 2, 4, 5, 6, 7], True]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset',
[[10, 30, 5, 20, 0, 8, 20, 10, 30, 30, 20, 30, 8, 30, 10], 12, 3, 2], [[2, 4], True]],
['regression: run accumulator reset (partial repair)',
[[-2, -2, 15, -2, 5, 10, 5, 20, -2, 30, 20, 5, 10], 0, 4, 2], [[0, 1], True]],
['control 1', [[-2, 12, 30, 10, 10, 10, 15, 0, 15, 10, -2, 20, 0], 12, 4, 5],
[[0, 3, 7, 10, 12], True]],
['control 2', [[10, 30, 15, 20, -2, 20, 10, 20, -2, 8, 5, 0, 20], 8, 3, 1], [[8], False]]],
[['boundary: run ending at horizon', [[20, 20, 5, 5], 8, 2, 2], [[2, 3], False]],
['boundary: run exactly min_len', [[5, 5, 20, 5], 8, 2, 2], [[0, 1], False]],
['boundary: nothing needed', [[5, 5, 5], 8, 1, 0], [[], False]],
['regression: run accumulator reset', [[-2, 10, 30, 12, 8, 0, 8, 30, -2, 12, -2, 0, 12], 8, 2, 7],
[[0, 4, 5, 6, 8, 10, 11], True]],
['regression: run accumulator reset (partial repair)',
[[8, -2, 30, 12, 12, 0, -2, 12, 20, 15, 5, 12, 0, 15, -2], 0, 1, 8],
[[0, 1, 3, 5, 6, 10, 12, 14], True]],
['control 1', [[20, 15, 8, 8, -2, 8, -2, 30, 12, 30, 10], 10, 2, 1], [[2], False]],
['control 2', [[5, 12, 20, 0, 15, -2], 12, 4, 6], [[0, 1, 2, 3, 4, 5], True]]]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| boundary: run ending at horizon | [[2, 3], False] | [[2, 3], False] | Passed |
| boundary: run exactly min_len | [[0, 1], False] | [[0, 1], False] | Passed |
| boundary: nothing needed | [[], False] | [[], False] | Passed |
| regression: run accumulator reset | [[6, 7, 8], False] | [[6, 7, 8], False] | Passed |
| regression: run accumulator reset (partial repair) | [[0, 1, 4, 5, 6, 9, 10], True] | [[0, 1, 4, 5, 6, 9, 10], True] | Passed |
| control 1 | [[0, 1, 2, 3, 4], True] | [[0, 1, 2, 3, 4], True] | Passed |
| control 2 | [[3], True] | [[3], True] | Passed |
SHA-256 / c90de4a1de81ea7980d448ad128af8dd1b2d6c65a38522d066e54e8b1d543e9c
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:53.340546+00:00.
Case digest / 6107a3320e5171d1c2d5a134e715d27aa6c0fa63a9b0198d236b2b4e0616fd92