FAILURE MAP
← Case archive

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

Street meter time purchase: the time limit is measured from the extension start · case 01

Street meter time purchase returns a wrong result when the time limit is measured from the extension start.

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

ROOT CAUSE

Stacking purchases pushes expiry beyond the posted time limit.

VERIFIED REPAIR

Restore the maximum stay limit rule so that the step reads `base + x['max_minutes']`.

Unsuccessful approach: Measuring the limit from now wrongly shortens purchases made before enforcement starts.

Case contract

Input {now, expiry (or null), paid_cents, rate_per_hour, max_minutes, enf_start, enf_end, increment}. Only whole increments are credited. At or after enforcement end nothing is sold: [null, 0]. Paid time starts at the later of now, enforcement start and any unexpired expiry; it ends at the earliest of start + purchased minutes (floor), max(now, enf_start) + max_minutes, and enf_end. Accepted cents = min(credited, ceil(used minutes * rate/60)). Return [new expiry, accepted cents].

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):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, start + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 532, 'paid_cents': 270, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 114]), ({'now': 646, 'expiry': 691, 'paid_cents': 468, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [766, 188]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 182, 'expiry': 191, 'paid_cents': 258, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 42, 'expiry': None, 'paid_cents': 361, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 591, 'expiry': 603, 'paid_cents': 339, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [651, 80])], [({'now': 798, 'expiry': 823, 'paid_cents': 558, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [858, 88]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 257, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [582, 255]), ({'now': 180, 'expiry': None, 'paid_cents': 342, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 436, 'expiry': 444, 'paid_cents': 152, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 564, 'paid_cents': 165, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 453, 'expiry': None, 'paid_cents': 545, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 480, 'expiry': 548, 'paid_cents': 563, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 130]), ({'now': 396, 'expiry': None, 'paid_cents': 250, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 150]), ({'now': 480, 'expiry': 429, 'paid_cents': 333, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [579, 330]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 527, 'paid_cents': 369, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 44])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 514, 'paid_cents': 358, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 65]), ({'now': 480, 'expiry': 556, 'paid_cents': 47, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [556, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1000, 'expiry': 966, 'paid_cents': 599, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 267]), ({'now': 228, 'expiry': 243, 'paid_cents': 594, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 16, 'expiry': None, 'paid_cents': 295, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [568, 294])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 914, 'expiry': 980, 'paid_cents': 538, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [980, 0]), ({'now': 758, 'expiry': 791, 'paid_cents': 478, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [981, 475]), ({'now': 413, 'expiry': None, 'paid_cents': 368, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 701, 'expiry': 740, 'paid_cents': 590, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 135]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 62, 'expiry': None, 'paid_cents': 358, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 350])]]
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[652, 200][600, 114]Failed
fee oracle 1[811, 300][766, 188]Failed
fee oracle 2[None, 0][None, 0]Passed
fee oracle 3[540, 200][540, 200]Passed
fee oracle 4[1080, 100][1080, 100]Passed
fee oracle 5[540, 250][540, 250]Passed
fee oracle 6[540, 200][540, 200]Passed
fee oracle 7[663, 100][651, 80]Failed

SHA-256 / 116dc3cd054e25b5d24a388aee310c4b1e6c9c05eb3b81d9119d40478d04df5b

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, now + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 532, 'paid_cents': 270, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 114]), ({'now': 646, 'expiry': 691, 'paid_cents': 468, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [766, 188]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 182, 'expiry': 191, 'paid_cents': 258, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 42, 'expiry': None, 'paid_cents': 361, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 591, 'expiry': 603, 'paid_cents': 339, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [651, 80])], [({'now': 798, 'expiry': 823, 'paid_cents': 558, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [858, 88]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 257, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [582, 255]), ({'now': 180, 'expiry': None, 'paid_cents': 342, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 436, 'expiry': 444, 'paid_cents': 152, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 564, 'paid_cents': 165, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 453, 'expiry': None, 'paid_cents': 545, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 480, 'expiry': 548, 'paid_cents': 563, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 130]), ({'now': 396, 'expiry': None, 'paid_cents': 250, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 150]), ({'now': 480, 'expiry': 429, 'paid_cents': 333, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [579, 330]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 527, 'paid_cents': 369, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 44])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 514, 'paid_cents': 358, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 65]), ({'now': 480, 'expiry': 556, 'paid_cents': 47, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [556, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1000, 'expiry': 966, 'paid_cents': 599, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 267]), ({'now': 228, 'expiry': 243, 'paid_cents': 594, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 16, 'expiry': None, 'paid_cents': 295, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [568, 294])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 914, 'expiry': 980, 'paid_cents': 538, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [980, 0]), ({'now': 758, 'expiry': 791, 'paid_cents': 478, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [981, 475]), ({'now': 413, 'expiry': None, 'paid_cents': 368, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 701, 'expiry': 740, 'paid_cents': 590, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 135]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 62, 'expiry': None, 'paid_cents': 358, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 350])]]
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[600, 114][600, 114]Passed
fee oracle 1[766, 188][766, 188]Passed
fee oracle 2[None, 0][None, 0]Passed
fee oracle 3[540, 200][540, 200]Passed
fee oracle 4[1080, 100][1080, 100]Passed
fee oracle 5[480, 0][540, 250]Failed
fee oracle 6[480, 0][540, 200]Failed
fee oracle 7[651, 80][651, 80]Passed

SHA-256 / 71db3986b2912b548d0153d79cec19df90b464308e83c39b406f6fe3299c5b54

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    inc = x['increment']
    c = x['paid_cents'] - x['paid_cents'] % inc
    now = x['now']
    if now >= x['enf_end']:
        return [None, 0]
    base = max(now, x['enf_start'])
    start = base if x['expiry'] is None else max(base, x['expiry'])
    mins = c * 60 // x['rate_per_hour']
    end = min(start + mins, base + x['max_minutes'], x['enf_end'])
    used = max(0, end - start)
    cost = min(c, -(-used * x['rate_per_hour'] // 60))
    return [max(end, start), cost]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[({'now': 480, 'expiry': 532, 'paid_cents': 270, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 114]), ({'now': 646, 'expiry': 691, 'paid_cents': 468, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [766, 188]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 182, 'expiry': 191, 'paid_cents': 258, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 42, 'expiry': None, 'paid_cents': 361, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 591, 'expiry': 603, 'paid_cents': 339, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [651, 80])], [({'now': 798, 'expiry': 823, 'paid_cents': 558, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [858, 88]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': None, 'paid_cents': 257, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [582, 255]), ({'now': 180, 'expiry': None, 'paid_cents': 342, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 436, 'expiry': 444, 'paid_cents': 152, 'rate_per_hour': 100, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 100]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 480, 'expiry': 564, 'paid_cents': 165, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 0])], [({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 453, 'expiry': None, 'paid_cents': 545, 'rate_per_hour': 100, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [720, 400]), ({'now': 480, 'expiry': 548, 'paid_cents': 563, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [600, 130]), ({'now': 396, 'expiry': None, 'paid_cents': 250, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 150]), ({'now': 480, 'expiry': 429, 'paid_cents': 333, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [579, 330]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 527, 'paid_cents': 369, 'rate_per_hour': 200, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 44])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 480, 'expiry': 514, 'paid_cents': 358, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 65]), ({'now': 480, 'expiry': 556, 'paid_cents': 47, 'rate_per_hour': 150, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [556, 0]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 1000, 'expiry': 966, 'paid_cents': 599, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [1080, 267]), ({'now': 228, 'expiry': 243, 'paid_cents': 594, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [540, 250]), ({'now': 1080, 'expiry': 1100, 'paid_cents': 100, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [None, 0]), ({'now': 16, 'expiry': None, 'paid_cents': 295, 'rate_per_hour': 200, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [568, 294])], [({'now': 1040, 'expiry': None, 'paid_cents': 333, 'rate_per_hour': 150, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [1080, 100]), ({'now': 914, 'expiry': 980, 'paid_cents': 538, 'rate_per_hour': 250, 'max_minutes': 60, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [980, 0]), ({'now': 758, 'expiry': 791, 'paid_cents': 478, 'rate_per_hour': 150, 'max_minutes': 240, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [981, 475]), ({'now': 413, 'expiry': None, 'paid_cents': 368, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [585, 350]), ({'now': 701, 'expiry': 740, 'paid_cents': 590, 'rate_per_hour': 100, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [821, 135]), ({'now': 600, 'expiry': 650, 'paid_cents': 400, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 5}, [720, 234]), ({'now': 420, 'expiry': None, 'paid_cents': 200, 'rate_per_hour': 200, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [540, 200]), ({'now': 62, 'expiry': None, 'paid_cents': 358, 'rate_per_hour': 250, 'max_minutes': 120, 'enf_start': 480, 'enf_end': 1080, 'increment': 25}, [564, 350])]]
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[600, 114][600, 114]Passed
fee oracle 1[766, 188][766, 188]Passed
fee oracle 2[None, 0][None, 0]Passed
fee oracle 3[540, 200][540, 200]Passed
fee oracle 4[1080, 100][1080, 100]Passed
fee oracle 5[540, 250][540, 250]Passed
fee oracle 6[540, 200][540, 200]Passed
fee oracle 7[651, 80][651, 80]Passed

SHA-256 / 2ad34048def842d0a20ffdc33f9bf617b48e25f6ad3ea6ced01fe660f0a1e8d3

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

Case digest / 3ace017914820a9833c751fb2457a4c48a50f0e51d00684b9a24ca97b9bf6c9e