FAILURE MAP
← Case archive

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

Garage hourly fee with grace and daily maximum: the grace period is deducted from every stay · case 01

Garage hourly fee with grace and daily maximum returns a wrong result when the grace period is deducted from every stay.

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

ROOT CAUSE

Grace is treated as a free allowance subtracted from longer stays, so stays just over an hour boundary lose an hour of charge.

VERIFIED REPAIR

Restore the grace handling rule so that the step reads `days, rem = divmod(dur, 1440)`.

Unsuccessful approach: Deducting grace only from sub-day stays still discounts ordinary short stays that exceeded grace.

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 - x['grace'], 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': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1632, 'exit': 3252, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 2400), ({'entry': 2554, 'exit': 4295, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 1901, 'exit': 2083, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 800), ({'entry': 2535, 'exit': 2780, 'grace': 10, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 1000), ({'entry': 2829, 'exit': 3340, 'grace': 20, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 1800), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, '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': 1167, 'exit': 1504, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1800), ({'entry': 2822, 'exit': 3008, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 800), ({'entry': 1104, 'exit': 1533, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1858, 'exit': 1884, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 450), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2627, 'exit': 2930, 'grace': 20, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 2400)], [({'entry': 1596, 'exit': 1839, 'grace': 10, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2000), ({'entry': 1635, 'exit': 1861, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1200), ({'entry': 804, 'exit': 2244, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1006, 'exit': 1076, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 900), ({'entry': 2160, 'exit': 6481, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 7400)], [({'entry': 2742, 'exit': 4255, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2900), ({'entry': 414, 'exit': 2095, 'grace': 20, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 3000), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 271, 'exit': 685, 'grace': 15, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1400), ({'entry': 2163, 'exit': 2233, 'grace': 15, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 600), ({'entry': 1405, 'exit': 1831, 'grace': 10, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1600), ({'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': 2752, 'exit': 3173, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1055, 'exit': 1487, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1600), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 2422, 'exit': 2542, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 600), ({'entry': 2869, 'exit': 3243, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 1400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 857, 'exit': 1248, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 1500)]]
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 026002600Passed
fee oracle 124002400Passed
fee oracle 246504800Failed
fee oracle 3600800Failed
fee oracle 48001000Failed
fee oracle 518001800Passed
fee oracle 625002500Passed
fee oracle 700Passed

SHA-256 / 43393a21905b2550bac9f5244a1f2949013d3ac546bab0855de8a7d20922a24a

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 - x['grace'], 1440) if dur < 1440 else 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': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1632, 'exit': 3252, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 2400), ({'entry': 2554, 'exit': 4295, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 1901, 'exit': 2083, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 800), ({'entry': 2535, 'exit': 2780, 'grace': 10, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 1000), ({'entry': 2829, 'exit': 3340, 'grace': 20, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 1800), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, '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': 1167, 'exit': 1504, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1800), ({'entry': 2822, 'exit': 3008, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 800), ({'entry': 1104, 'exit': 1533, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1858, 'exit': 1884, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 450), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2627, 'exit': 2930, 'grace': 20, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 2400)], [({'entry': 1596, 'exit': 1839, 'grace': 10, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2000), ({'entry': 1635, 'exit': 1861, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1200), ({'entry': 804, 'exit': 2244, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1006, 'exit': 1076, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 900), ({'entry': 2160, 'exit': 6481, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 7400)], [({'entry': 2742, 'exit': 4255, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2900), ({'entry': 414, 'exit': 2095, 'grace': 20, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 3000), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 271, 'exit': 685, 'grace': 15, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1400), ({'entry': 2163, 'exit': 2233, 'grace': 15, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 600), ({'entry': 1405, 'exit': 1831, 'grace': 10, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1600), ({'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': 2752, 'exit': 3173, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1055, 'exit': 1487, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1600), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 2422, 'exit': 2542, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 600), ({'entry': 2869, 'exit': 3243, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 1400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 857, 'exit': 1248, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 1500)]]
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 026002600Passed
fee oracle 124002400Passed
fee oracle 248004800Passed
fee oracle 3600800Failed
fee oracle 48001000Failed
fee oracle 518001800Passed
fee oracle 625002500Passed
fee oracle 700Passed

SHA-256 / 356571a0034cadf88b115aee64374dc3589f4e562d71a8a039b86ba2e3c13886

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': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1632, 'exit': 3252, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 2400), ({'entry': 2554, 'exit': 4295, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 4800), ({'entry': 1901, 'exit': 2083, 'grace': 20, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 800), ({'entry': 2535, 'exit': 2780, 'grace': 10, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 1000), ({'entry': 2829, 'exit': 3340, 'grace': 20, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 1800), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, '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': 1167, 'exit': 1504, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1800), ({'entry': 2822, 'exit': 3008, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 800), ({'entry': 1104, 'exit': 1533, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 2400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 1858, 'exit': 1884, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 450), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 2627, 'exit': 2930, 'grace': 20, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 2400)], [({'entry': 1596, 'exit': 1839, 'grace': 10, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2000), ({'entry': 1635, 'exit': 1861, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1200), ({'entry': 804, 'exit': 2244, 'grace': 0, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 1006, 'exit': 1076, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 900), ({'entry': 2160, 'exit': 6481, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 7400)], [({'entry': 2742, 'exit': 4255, 'grace': 15, 'hourly': 450, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 2900), ({'entry': 414, 'exit': 2095, 'grace': 20, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': True, 'lost_fee': 2500}, 3000), ({'entry': 60, 'exit': 240, 'grace': 15, 'hourly': 450, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1350), ({'entry': 271, 'exit': 685, 'grace': 15, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 1400), ({'entry': 2163, 'exit': 2233, 'grace': 15, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 600), ({'entry': 1405, 'exit': 1831, 'grace': 10, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1600), ({'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': 2752, 'exit': 3173, 'grace': 15, 'hourly': 200, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 2500}, 1500), ({'entry': 100, 'exit': 115, 'grace': 15, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 0), ({'entry': 1055, 'exit': 1487, 'grace': 15, 'hourly': 200, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 3000}, 1600), ({'entry': 0, 'exit': 1560, 'grace': 10, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': False, 'lost_fee': 2500}, 2600), ({'entry': 2422, 'exit': 2542, 'grace': 20, 'hourly': 300, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 3000}, 600), ({'entry': 2869, 'exit': 3243, 'grace': 20, 'hourly': 200, 'daily_max': 2400, 'lost_ticket': False, 'lost_fee': 2500}, 1400), ({'entry': 500, 'exit': 500, 'grace': 0, 'hourly': 300, 'daily_max': 2000, 'lost_ticket': True, 'lost_fee': 2500}, 2500), ({'entry': 857, 'exit': 1248, 'grace': 0, 'hourly': 300, 'daily_max': 1500, 'lost_ticket': False, 'lost_fee': 3000}, 1500)]]
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 026002600Passed
fee oracle 124002400Passed
fee oracle 248004800Passed
fee oracle 3800800Passed
fee oracle 410001000Passed
fee oracle 518001800Passed
fee oracle 625002500Passed
fee oracle 700Passed

SHA-256 / aedea6b04c70f11b750ec694f3782c2e479ab4a0a5e8461ebfa1b5e5013657c6

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

Case digest / f191c53fdfafdd31a723087e30db2e069bcba4deb8987f43b0b86134f348f7ca