FAILURE MAP
← Case archive

FA-68701 / Toll and parking fee computation / Open access

EV charging session and idle fee: the grace period is only a threshold · case 01

EV charging session and idle fee returns a wrong result when the grace period is only a threshold.

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

ROOT CAUSE

Idle fees start the instant charging completes.

VERIFIED REPAIR

Restore the idle grace rule so that the step reads `max(0, x['unplug'] - x['charge_end'] - x['idle_grace'])`.

Unsuccessful approach: Once the grace is exceeded the whole idle period is billed, so grace minutes are charged.

Case contract

Input {plug_in, charge_end, unplug (minutes), wh, price_per_kwh, idle_grace, idle_rate (per started 10 min), idle_cap, session_fee, min_fee}. Energy = ceil(wh*price/1000). Idle minutes = max(0, unplug - charge_end - grace); idle fee = min(idle_cap, started 10-minute blocks * idle_rate). Total = max(min_fee, session_fee + energy + idle). Return {energy, idle, total}.

Why this case matters

Fee engines bill customers in integer cents; a wrong boundary, rounding stage or cap scope silently over- or under-charges.

1 / The failure

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

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = max(0, x['unplug'] - x['charge_end'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 400, 'charge_end': 430, 'unplug': 440, 'wh': 26712, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 935, 'idle': 0, 'total': 1085}), ({'plug_in': 443, 'charge_end': 621, 'unplug': 1021, 'wh': 32003, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1121, 'idle': 3000, 'total': 4221}), ({'plug_in': 178, 'charge_end': 431, 'unplug': 831, 'wh': 23652, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 828, 'idle': 1000, 'total': 1928}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 24, 'charge_end': 82, 'unplug': 82, 'wh': 29231, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1316, 'idle': 0, 'total': 1416}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 321, 'charge_end': 549, 'unplug': 949, 'wh': 16186, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 470, 'idle': 1950, 'total': 2420}), ({'plug_in': 445, 'charge_end': 719, 'unplug': 744, 'wh': 13785, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 400, 'idle': 200, 'total': 600})], [({'plug_in': 143, 'charge_end': 442, 'unplug': 842, 'wh': 33904, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 984, 'idle': 1000, 'total': 2134}), ({'plug_in': 468, 'charge_end': 648, 'unplug': 648, 'wh': 29211, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1315, 'idle': 0, 'total': 1465}), ({'plug_in': 244, 'charge_end': 536, 'unplug': 736, 'wh': 50241, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1457, 'idle': 1000, 'total': 2607}), ({'plug_in': 217, 'charge_end': 487, 'unplug': 687, 'wh': 40392, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2101, 'idle': 950, 'total': 3051}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 417, 'charge_end': 599, 'unplug': 604, 'wh': 38888, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1128, 'idle': 0, 'total': 1278}), ({'plug_in': 468, 'charge_end': 517, 'unplug': 542, 'wh': 35685, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1606, 'idle': 100, 'total': 1706})], [({'plug_in': 533, 'charge_end': 668, 'unplug': 684, 'wh': 20000, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1040, 'idle': 50, 'total': 1240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 548, 'charge_end': 679, 'unplug': 679, 'wh': 46438, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2415, 'idle': 0, 'total': 2565}), ({'plug_in': 295, 'charge_end': 514, 'unplug': 539, 'wh': 58607, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2052, 'idle': 100, 'total': 2252}), ({'plug_in': 270, 'charge_end': 560, 'unplug': 620, 'wh': 13483, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 392, 'idle': 250, 'total': 642}), ({'plug_in': 503, 'charge_end': 781, 'unplug': 1181, 'wh': 23844, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 835, 'idle': 1000, 'total': 1935}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 422, 'charge_end': 604, 'unplug': 1004, 'wh': 58741, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2644, 'idle': 1000, 'total': 3644})], [({'plug_in': 141, 'charge_end': 394, 'unplug': 410, 'wh': 26923, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1400, 'idle': 100, 'total': 1500}), ({'plug_in': 87, 'charge_end': 194, 'unplug': 194, 'wh': 20119, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 906, 'idle': 0, 'total': 1006}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 121, 'charge_end': 230, 'unplug': 230, 'wh': 2855, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 129, 'idle': 0, 'total': 500}), ({'plug_in': 116, 'charge_end': 342, 'unplug': 357, 'wh': 45320, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2040, 'idle': 100, 'total': 2240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 304, 'charge_end': 530, 'unplug': 555, 'wh': 5327, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 240, 'idle': 40, 'total': 500}), ({'plug_in': 338, 'charge_end': 429, 'unplug': 429, 'wh': 18347, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 533, 'idle': 0, 'total': 683})], [({'plug_in': 391, 'charge_end': 417, 'unplug': 432, 'wh': 50407, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1765, 'idle': 50, 'total': 1815}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 547, 'charge_end': 782, 'unplug': 782, 'wh': 23444, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1055, 'idle': 0, 'total': 1205}), ({'plug_in': 424, 'charge_end': 695, 'unplug': 1095, 'wh': 19996, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 580, 'idle': 2000, 'total': 2730}), ({'plug_in': 596, 'charge_end': 767, 'unplug': 967, 'wh': 39532, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1779, 'idle': 1000, 'total': 2929}), ({'plug_in': 445, 'charge_end': 682, 'unplug': 698, 'wh': 57925, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1680, 'idle': 40, 'total': 1870}), ({'plug_in': 555, 'charge_end': 609, 'unplug': 634, 'wh': 10270, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 360, 'idle': 200, 'total': 660}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, 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
fee oracle 0{'energy': 935, 'idle': 40, 'total': 1125}{'energy': 935, 'idle': 0, 'total': 1085}Failed
fee oracle 1{'energy': 1121, 'idle': 3000, 'total': 4221}{'energy': 1121, 'idle': 3000, 'total': 4221}Passed
fee oracle 2{'energy': 828, 'idle': 1000, 'total': 1928}{'energy': 828, 'idle': 1000, 'total': 1928}Passed
fee oracle 3{'energy': 438, 'idle': 100, 'total': 638}{'energy': 438, 'idle': 50, 'total': 588}Failed
fee oracle 4{'energy': 1316, 'idle': 0, 'total': 1416}{'energy': 1316, 'idle': 0, 'total': 1416}Passed
fee oracle 5{'energy': 24, 'idle': 1000, 'total': 1024}{'energy': 24, 'idle': 1000, 'total': 1024}Passed
fee oracle 6{'energy': 470, 'idle': 2000, 'total': 2470}{'energy': 470, 'idle': 1950, 'total': 2420}Failed
fee oracle 7{'energy': 400, 'idle': 300, 'total': 700}{'energy': 400, 'idle': 200, 'total': 600}Failed

SHA-256 / 732ec46ebec221fe96f24d60b5b9ac99b81e856eacb263456e93ee5f3278052d

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = (x['unplug'] - x['charge_end'] if x['unplug'] - x['charge_end'] > x['idle_grace'] else 0)
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 400, 'charge_end': 430, 'unplug': 440, 'wh': 26712, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 935, 'idle': 0, 'total': 1085}), ({'plug_in': 443, 'charge_end': 621, 'unplug': 1021, 'wh': 32003, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1121, 'idle': 3000, 'total': 4221}), ({'plug_in': 178, 'charge_end': 431, 'unplug': 831, 'wh': 23652, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 828, 'idle': 1000, 'total': 1928}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 24, 'charge_end': 82, 'unplug': 82, 'wh': 29231, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1316, 'idle': 0, 'total': 1416}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 321, 'charge_end': 549, 'unplug': 949, 'wh': 16186, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 470, 'idle': 1950, 'total': 2420}), ({'plug_in': 445, 'charge_end': 719, 'unplug': 744, 'wh': 13785, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 400, 'idle': 200, 'total': 600})], [({'plug_in': 143, 'charge_end': 442, 'unplug': 842, 'wh': 33904, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 984, 'idle': 1000, 'total': 2134}), ({'plug_in': 468, 'charge_end': 648, 'unplug': 648, 'wh': 29211, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1315, 'idle': 0, 'total': 1465}), ({'plug_in': 244, 'charge_end': 536, 'unplug': 736, 'wh': 50241, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1457, 'idle': 1000, 'total': 2607}), ({'plug_in': 217, 'charge_end': 487, 'unplug': 687, 'wh': 40392, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2101, 'idle': 950, 'total': 3051}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 417, 'charge_end': 599, 'unplug': 604, 'wh': 38888, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1128, 'idle': 0, 'total': 1278}), ({'plug_in': 468, 'charge_end': 517, 'unplug': 542, 'wh': 35685, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1606, 'idle': 100, 'total': 1706})], [({'plug_in': 533, 'charge_end': 668, 'unplug': 684, 'wh': 20000, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1040, 'idle': 50, 'total': 1240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 548, 'charge_end': 679, 'unplug': 679, 'wh': 46438, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2415, 'idle': 0, 'total': 2565}), ({'plug_in': 295, 'charge_end': 514, 'unplug': 539, 'wh': 58607, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2052, 'idle': 100, 'total': 2252}), ({'plug_in': 270, 'charge_end': 560, 'unplug': 620, 'wh': 13483, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 392, 'idle': 250, 'total': 642}), ({'plug_in': 503, 'charge_end': 781, 'unplug': 1181, 'wh': 23844, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 835, 'idle': 1000, 'total': 1935}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 422, 'charge_end': 604, 'unplug': 1004, 'wh': 58741, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2644, 'idle': 1000, 'total': 3644})], [({'plug_in': 141, 'charge_end': 394, 'unplug': 410, 'wh': 26923, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1400, 'idle': 100, 'total': 1500}), ({'plug_in': 87, 'charge_end': 194, 'unplug': 194, 'wh': 20119, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 906, 'idle': 0, 'total': 1006}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 121, 'charge_end': 230, 'unplug': 230, 'wh': 2855, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 129, 'idle': 0, 'total': 500}), ({'plug_in': 116, 'charge_end': 342, 'unplug': 357, 'wh': 45320, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2040, 'idle': 100, 'total': 2240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 304, 'charge_end': 530, 'unplug': 555, 'wh': 5327, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 240, 'idle': 40, 'total': 500}), ({'plug_in': 338, 'charge_end': 429, 'unplug': 429, 'wh': 18347, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 533, 'idle': 0, 'total': 683})], [({'plug_in': 391, 'charge_end': 417, 'unplug': 432, 'wh': 50407, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1765, 'idle': 50, 'total': 1815}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 547, 'charge_end': 782, 'unplug': 782, 'wh': 23444, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1055, 'idle': 0, 'total': 1205}), ({'plug_in': 424, 'charge_end': 695, 'unplug': 1095, 'wh': 19996, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 580, 'idle': 2000, 'total': 2730}), ({'plug_in': 596, 'charge_end': 767, 'unplug': 967, 'wh': 39532, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1779, 'idle': 1000, 'total': 2929}), ({'plug_in': 445, 'charge_end': 682, 'unplug': 698, 'wh': 57925, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1680, 'idle': 40, 'total': 1870}), ({'plug_in': 555, 'charge_end': 609, 'unplug': 634, 'wh': 10270, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 360, 'idle': 200, 'total': 660}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, 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
fee oracle 0{'energy': 935, 'idle': 0, 'total': 1085}{'energy': 935, 'idle': 0, 'total': 1085}Passed
fee oracle 1{'energy': 1121, 'idle': 3000, 'total': 4221}{'energy': 1121, 'idle': 3000, 'total': 4221}Passed
fee oracle 2{'energy': 828, 'idle': 1000, 'total': 1928}{'energy': 828, 'idle': 1000, 'total': 1928}Passed
fee oracle 3{'energy': 438, 'idle': 100, 'total': 638}{'energy': 438, 'idle': 50, 'total': 588}Failed
fee oracle 4{'energy': 1316, 'idle': 0, 'total': 1416}{'energy': 1316, 'idle': 0, 'total': 1416}Passed
fee oracle 5{'energy': 24, 'idle': 1000, 'total': 1024}{'energy': 24, 'idle': 1000, 'total': 1024}Passed
fee oracle 6{'energy': 470, 'idle': 2000, 'total': 2470}{'energy': 470, 'idle': 1950, 'total': 2420}Failed
fee oracle 7{'energy': 400, 'idle': 300, 'total': 700}{'energy': 400, 'idle': 200, 'total': 600}Failed

SHA-256 / 1d0a889154dbddb0d3164b546121f6ff6fae3767165bdc46f1e0b09ff561de3e

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    energy = -(-x['wh'] * x['price_per_kwh'] // 1000)
    idle = max(0, x['unplug'] - x['charge_end'] - x['idle_grace'])
    idle_fee = min(x['idle_cap'], -(-idle // 10) * x['idle_rate'])
    total = x['session_fee'] + energy + idle_fee
    return {'energy': energy, 'idle': idle_fee, 'total': max(x['min_fee'], total)}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'plug_in': 400, 'charge_end': 430, 'unplug': 440, 'wh': 26712, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 935, 'idle': 0, 'total': 1085}), ({'plug_in': 443, 'charge_end': 621, 'unplug': 1021, 'wh': 32003, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 1121, 'idle': 3000, 'total': 4221}), ({'plug_in': 178, 'charge_end': 431, 'unplug': 831, 'wh': 23652, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 828, 'idle': 1000, 'total': 1928}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 24, 'charge_end': 82, 'unplug': 82, 'wh': 29231, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 300}, {'energy': 1316, 'idle': 0, 'total': 1416}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 321, 'charge_end': 549, 'unplug': 949, 'wh': 16186, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 470, 'idle': 1950, 'total': 2420}), ({'plug_in': 445, 'charge_end': 719, 'unplug': 744, 'wh': 13785, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 400, 'idle': 200, 'total': 600})], [({'plug_in': 143, 'charge_end': 442, 'unplug': 842, 'wh': 33904, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 984, 'idle': 1000, 'total': 2134}), ({'plug_in': 468, 'charge_end': 648, 'unplug': 648, 'wh': 29211, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1315, 'idle': 0, 'total': 1465}), ({'plug_in': 244, 'charge_end': 536, 'unplug': 736, 'wh': 50241, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1457, 'idle': 1000, 'total': 2607}), ({'plug_in': 217, 'charge_end': 487, 'unplug': 687, 'wh': 40392, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2101, 'idle': 950, 'total': 3051}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 417, 'charge_end': 599, 'unplug': 604, 'wh': 38888, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1128, 'idle': 0, 'total': 1278}), ({'plug_in': 468, 'charge_end': 517, 'unplug': 542, 'wh': 35685, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1606, 'idle': 100, 'total': 1706})], [({'plug_in': 533, 'charge_end': 668, 'unplug': 684, 'wh': 20000, 'price_per_kwh': 52, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 1040, 'idle': 50, 'total': 1240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 548, 'charge_end': 679, 'unplug': 679, 'wh': 46438, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 2415, 'idle': 0, 'total': 2565}), ({'plug_in': 295, 'charge_end': 514, 'unplug': 539, 'wh': 58607, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2052, 'idle': 100, 'total': 2252}), ({'plug_in': 270, 'charge_end': 560, 'unplug': 620, 'wh': 13483, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 3000, 'session_fee': 0, 'min_fee': 300}, {'energy': 392, 'idle': 250, 'total': 642}), ({'plug_in': 503, 'charge_end': 781, 'unplug': 1181, 'wh': 23844, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 835, 'idle': 1000, 'total': 1935}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 422, 'charge_end': 604, 'unplug': 1004, 'wh': 58741, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 300}, {'energy': 2644, 'idle': 1000, 'total': 3644})], [({'plug_in': 141, 'charge_end': 394, 'unplug': 410, 'wh': 26923, 'price_per_kwh': 52, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1400, 'idle': 100, 'total': 1500}), ({'plug_in': 87, 'charge_end': 194, 'unplug': 194, 'wh': 20119, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 500}, {'energy': 906, 'idle': 0, 'total': 1006}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 121, 'charge_end': 230, 'unplug': 230, 'wh': 2855, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 3000, 'session_fee': 100, 'min_fee': 500}, {'energy': 129, 'idle': 0, 'total': 500}), ({'plug_in': 116, 'charge_end': 342, 'unplug': 357, 'wh': 45320, 'price_per_kwh': 45, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 100, 'min_fee': 200}, {'energy': 2040, 'idle': 100, 'total': 2240}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588}), ({'plug_in': 304, 'charge_end': 530, 'unplug': 555, 'wh': 5327, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 500}, {'energy': 240, 'idle': 40, 'total': 500}), ({'plug_in': 338, 'charge_end': 429, 'unplug': 429, 'wh': 18347, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 533, 'idle': 0, 'total': 683})], [({'plug_in': 391, 'charge_end': 417, 'unplug': 432, 'wh': 50407, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 0, 'min_fee': 500}, {'energy': 1765, 'idle': 50, 'total': 1815}), ({'plug_in': 30, 'charge_end': 45, 'unplug': 400, 'wh': 800, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 0, 'min_fee': 500}, {'energy': 24, 'idle': 1000, 'total': 1024}), ({'plug_in': 547, 'charge_end': 782, 'unplug': 782, 'wh': 23444, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 300}, {'energy': 1055, 'idle': 0, 'total': 1205}), ({'plug_in': 424, 'charge_end': 695, 'unplug': 1095, 'wh': 19996, 'price_per_kwh': 29, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 150, 'min_fee': 200}, {'energy': 580, 'idle': 2000, 'total': 2730}), ({'plug_in': 596, 'charge_end': 767, 'unplug': 967, 'wh': 39532, 'price_per_kwh': 45, 'idle_grace': 15, 'idle_rate': 100, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1779, 'idle': 1000, 'total': 2929}), ({'plug_in': 445, 'charge_end': 682, 'unplug': 698, 'wh': 57925, 'price_per_kwh': 29, 'idle_grace': 10, 'idle_rate': 40, 'idle_cap': 1000, 'session_fee': 150, 'min_fee': 200}, {'energy': 1680, 'idle': 40, 'total': 1870}), ({'plug_in': 555, 'charge_end': 609, 'unplug': 634, 'wh': 10270, 'price_per_kwh': 35, 'idle_grace': 10, 'idle_rate': 100, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 360, 'idle': 200, 'total': 660}), ({'plug_in': 0, 'charge_end': 90, 'unplug': 106, 'wh': 12500, 'price_per_kwh': 35, 'idle_grace': 15, 'idle_rate': 50, 'idle_cap': 2000, 'session_fee': 100, 'min_fee': 300}, {'energy': 438, 'idle': 50, 'total': 588})]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check('fee oracle' + ' %d' % i, 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
fee oracle 0{'energy': 935, 'idle': 0, 'total': 1085}{'energy': 935, 'idle': 0, 'total': 1085}Passed
fee oracle 1{'energy': 1121, 'idle': 3000, 'total': 4221}{'energy': 1121, 'idle': 3000, 'total': 4221}Passed
fee oracle 2{'energy': 828, 'idle': 1000, 'total': 1928}{'energy': 828, 'idle': 1000, 'total': 1928}Passed
fee oracle 3{'energy': 438, 'idle': 50, 'total': 588}{'energy': 438, 'idle': 50, 'total': 588}Passed
fee oracle 4{'energy': 1316, 'idle': 0, 'total': 1416}{'energy': 1316, 'idle': 0, 'total': 1416}Passed
fee oracle 5{'energy': 24, 'idle': 1000, 'total': 1024}{'energy': 24, 'idle': 1000, 'total': 1024}Passed
fee oracle 6{'energy': 470, 'idle': 1950, 'total': 2420}{'energy': 470, 'idle': 1950, 'total': 2420}Passed
fee oracle 7{'energy': 400, 'idle': 200, 'total': 600}{'energy': 400, 'idle': 200, 'total': 600}Passed

SHA-256 / 3e2583d4f905bcc0e00b887d6077940bb00167c2075f1133fe57eb2339249558

Verification & scope

A deterministic, bounded toy model with a stipulated contract; it makes no claim of conformance to any agency manual or standard. 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:48:04.742485+00:00.

Case digest / 33d44bbd69f2c567d377a838e2fe2ee03eeae378123693aa229c36f69415030a