FAILURE MAP
← Case archive

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

Garage hourly fee with grace and daily maximum: the remainder of a multi-day stay is not capped · case 01

Garage hourly fee with grace and daily maximum returns a wrong result when the remainder of a multi-day stay is not capped.

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

ROOT CAUSE

The partial day after full days is billed hourly without the daily maximum.

VERIFIED REPAIR

Restore the partial day cap rule so that the step reads `min(x['daily_max'], -(-rem // 60) * x['hourly'])`.

Unsuccessful approach: Capping the whole stay hours once double counts the full days for multi-day stays.

Case contract

Input {entry, exit (absolute minutes), grace, hourly, daily_max, lost_ticket, lost_fee}; cents. Exit before entry is 'invalid'. A stay no longer than grace is free (grace is not deducted from longer stays). Otherwise each full 1440-minute block costs daily_max and the remainder costs hourly per started hour, capped at daily_max. A lost ticket pays the greater of the computed fee and lost_fee.

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):
    if x['exit'] < x['entry']:
        return 'invalid'
    dur = x['exit'] - x['entry']
    if dur <= x['grace']:
        fee = 0
    else:
        days, rem = divmod(dur, 1440)
        fee = days * x['daily_max'] + -(-rem // 60) * x['hourly']
    if x['lost_ticket']:
        fee = max(fee, x['lost_fee'])
    return fee
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': 1945, 'exit': 6562, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 5500), ({'entry': 567, 'exit': 963, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 1362, 'exit': 5675, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 6000), ({'entry': 731, 'exit': 2171, 'grace': 0, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1335, 'exit': 1738, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2000)], [({'entry': 132, 'exit': 552, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2100), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 465, 'exit': 1605, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2000), ({'entry': 53, 'exit': 593, 'grace': 10, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1903, 'exit': 4783, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 4000), ({'entry': 2049, 'exit': 2559, 'grace': 15, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 2400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500)], [({'entry': 2226, 'exit': 4482, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 3000), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 918, 'exit': 3798, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 4800), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1152, 'exit': 1160, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 2128, 'exit': 3328, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 1705, 'exit': 3954, 'grace': 10, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800)], [({'entry': 2136, 'exit': 3576, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2000), ({'entry': 359, 'exit': 377, 'grace': 0, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 358, 'exit': 898, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1365, 'exit': 3758, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 411, 'exit': 4074, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 7200), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350)], [({'entry': 2534, 'exit': 2857, 'grace': 10, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 163, 'exit': 3043, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 2508, 'exit': 6945, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 7800), ({'entry': 950, 'exit': 1518, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2204, 'exit': 4608, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 4000), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0)]]
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 055005500Passed
fee oracle 131502400Failed
fee oracle 213501350Passed
fee oracle 3112006000Failed
fee oracle 425002500Passed
fee oracle 500Passed
fee oracle 625002500Passed
fee oracle 721002000Failed

SHA-256 / c4539eefd002df26d8a66e5c78890d1df35d8b9052185bb0eb71a95d93f63c13

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    if x['exit'] < x['entry']:
        return 'invalid'
    dur = x['exit'] - x['entry']
    if dur <= x['grace']:
        fee = 0
    else:
        days, rem = divmod(dur, 1440)
        fee = days * x['daily_max'] + min(x['daily_max'], -(-dur // 60) * x['hourly'])
    if x['lost_ticket']:
        fee = max(fee, x['lost_fee'])
    return fee
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': 1945, 'exit': 6562, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 5500), ({'entry': 567, 'exit': 963, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 1362, 'exit': 5675, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 6000), ({'entry': 731, 'exit': 2171, 'grace': 0, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1335, 'exit': 1738, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2000)], [({'entry': 132, 'exit': 552, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2100), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 465, 'exit': 1605, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2000), ({'entry': 53, 'exit': 593, 'grace': 10, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1903, 'exit': 4783, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 4000), ({'entry': 2049, 'exit': 2559, 'grace': 15, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 2400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500)], [({'entry': 2226, 'exit': 4482, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 3000), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 918, 'exit': 3798, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 4800), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1152, 'exit': 1160, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 2128, 'exit': 3328, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 1705, 'exit': 3954, 'grace': 10, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800)], [({'entry': 2136, 'exit': 3576, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2000), ({'entry': 359, 'exit': 377, 'grace': 0, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 358, 'exit': 898, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1365, 'exit': 3758, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 411, 'exit': 4074, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 7200), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350)], [({'entry': 2534, 'exit': 2857, 'grace': 10, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 163, 'exit': 3043, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 2508, 'exit': 6945, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 7800), ({'entry': 950, 'exit': 1518, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2204, 'exit': 4608, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 4000), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0)]]
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 060005500Failed
fee oracle 124002400Passed
fee oracle 213501350Passed
fee oracle 360006000Passed
fee oracle 440002500Failed
fee oracle 500Passed
fee oracle 625002500Passed
fee oracle 720002000Passed

SHA-256 / 1f8f995171a5f9328d8a81014581a4440f36039342febbbbddafc2863f1ea6eb

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    if x['exit'] < x['entry']:
        return 'invalid'
    dur = x['exit'] - x['entry']
    if dur <= x['grace']:
        fee = 0
    else:
        days, rem = divmod(dur, 1440)
        fee = days * x['daily_max'] + min(x['daily_max'], -(-rem // 60) * x['hourly'])
    if x['lost_ticket']:
        fee = max(fee, x['lost_fee'])
    return fee
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'entry': 1945, 'exit': 6562, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 5500), ({'entry': 567, 'exit': 963, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 1362, 'exit': 5675, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 6000), ({'entry': 731, 'exit': 2171, 'grace': 0, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1335, 'exit': 1738, 'grace': 20, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2000)], [({'entry': 132, 'exit': 552, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2100), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 465, 'exit': 1605, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2000), ({'entry': 53, 'exit': 593, 'grace': 10, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1903, 'exit': 4783, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 4000), ({'entry': 2049, 'exit': 2559, 'grace': 15, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 2400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500)], [({'entry': 2226, 'exit': 4482, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 3000), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 918, 'exit': 3798, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 4800), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1152, 'exit': 1160, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 2128, 'exit': 3328, 'grace': 0, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 1705, 'exit': 3954, 'grace': 10, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800)], [({'entry': 2136, 'exit': 3576, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2000), ({'entry': 359, 'exit': 377, 'grace': 0, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 358, 'exit': 898, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1365, 'exit': 3758, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 411, 'exit': 4074, 'grace': 0, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 7200), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350)], [({'entry': 2534, 'exit': 2857, 'grace': 10, 'hourly': 450, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 163, 'exit': 3043, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 2508, 'exit': 6945, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 7800), ({'entry': 950, 'exit': 1518, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2204, 'exit': 4608, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 4000), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0)]]
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 055005500Passed
fee oracle 124002400Passed
fee oracle 213501350Passed
fee oracle 360006000Passed
fee oracle 425002500Passed
fee oracle 500Passed
fee oracle 625002500Passed
fee oracle 720002000Passed

SHA-256 / 289205c7e1ec8786d4b2bf96218057d237f504972415b2e9ea86a778b9279d78

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

Case digest / 8af27812578d7c37a1d9e32b798de54db8921d69ee1e643e9b21636b0cbdf08e