FAILURE MAP
← Case archive

FA-73601 / Rate limiter algorithms / Open access

Escalating failure penalty box: ban still active at its end instant · case 01

A client retrying exactly when told the ban ends is still refused.

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

ROOT CAUSE

The ban check uses t <= end.

VERIFIED REPAIR

Lift the ban at t == end.

Unsuccessful approach: Measuring the ban from its end time extends every ban by another base period.

Case contract

Input {threshold, window_s, ban_s, events [[t, ok]]} with nondecreasing t. While t < ban end every event is "banned" and ignored. Failures are kept for window_s seconds (f > t - window_s). When the kept failures reach threshold the key is banned until t + ban_s * 2^strikes, strikes increments and the failure log is cleared; the event reports "ban-until-<end>". Otherwise failures report "fail" and successes "ok". Return the per-event results.

Why this case matters

Login and abuse throttles escalate bans after repeated failures; window, escalation and log-reset rules decide whether attackers are slowed and legitimate users recover.

1 / The failure

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

N = 1
observations = []
def solve(x):
    th = x['threshold']
    Wn = x['window_s']
    base = x['ban_s']
    fails = []
    until = None
    strikes = 0
    out = []
    for t, ok in x['events']:
        if until is not None and t <= until:
            out.append('banned')
            continue
        if ok:
            out.append('ok')
            continue
        fails = [f for f in fails if f > t - Wn] + [t]
        if len(fails) >= th:
            until = t + base * 2 ** strikes
            strikes += 1
            fails = []
            out.append('ban-until-%d' % until)
        else:
            out.append('fail')
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [31, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [71, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [23, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [12, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-17', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [9, False], [10, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [13, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[10, False],
               [13, False],
               [16, False],
               [20, False],
               [52, False],
               [53, False],
               [57, False],
               [74, False],
               [80, False],
               [87, False],
               [106, False],
               [115, True],
               [116, False],
               [119, True]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ban-until-31',
    'banned',
    'fail',
    'fail',
    'ban-until-87',
    'banned',
    'banned',
    'fail',
    'fail',
    'ok',
    'fail',
    'ok']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [32, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [72, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [24, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [13, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-18', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [10, False], [11, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [14, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[0, False],
               [23, False],
               [25, True],
               [34, False],
               [35, False],
               [49, False],
               [49, True],
               [60, False],
               [71, False],
               [83, False],
               [87, False],
               [87, False],
               [89, False],
               [117, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ok',
    'fail',
    'ban-until-50',
    'banned',
    'banned',
    'fail',
    'fail',
    'fail',
    'ban-until-117',
    'banned',
    'banned',
    'fail']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [33, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [73, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [25, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [14, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-19', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [11, False], [12, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [15, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[8, False],
               [30, False],
               [30, False],
               [39, True],
               [44, False],
               [45, True],
               [47, False],
               [58, True],
               [78, False],
               [78, False],
               [82, False],
               [90, False],
               [107, False],
               [119, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'fail',
    'ok',
    'ban-until-59',
    'banned',
    'banned',
    'banned',
    'fail',
    'fail',
    'ban-until-112',
    'banned',
    'banned',
    'fail']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [34, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'fail', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [74, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [26, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [15, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-20', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [12, False], [13, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [16, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[6, False],
               [17, False],
               [25, False],
               [51, False],
               [52, True],
               [68, True],
               [78, True],
               [79, False],
               [86, True],
               [90, False],
               [92, True],
               [96, False],
               [100, False],
               [110, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ban-until-40',
    'fail',
    'ok',
    'ok',
    'ok',
    'fail',
    'ok',
    'fail',
    'ok',
    'ban-until-126',
    'banned',
    'banned']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [35, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'fail', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [75, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [27, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [16, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-21', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [13, False], [14, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [17, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[11, True],
               [15, True],
               [29, False],
               [39, False],
               [45, False],
               [45, False],
               [46, False],
               [50, True],
               [54, False],
               [56, False],
               [58, True],
               [60, False],
               [83, False],
               [92, False]],
    'threshold': 3,
    'window_s': 20},
   ['ok',
    'ok',
    'fail',
    'fail',
    'ban-until-60',
    'banned',
    'banned',
    'banned',
    'banned',
    'banned',
    'banned',
    'fail',
    'fail',
    'fail']]]]
for label, args, expected in cases[N - 1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
ban and escalate['fail', 'fail', 'ban-until-12', 'banned', 'banned', 'fail', 'fail', 'ban-until-51', 'banned']['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']Failed
third ban escalates['ban-until-10', 'banned', 'ban-until-50', 'ok', 'ok']['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']Failed
ban end instant['fail', 'ban-until-21', 'banned', 'banned', 'fail']['fail', 'ban-until-21', 'banned', 'ok', 'fail']Failed
window prune boundary['fail', 'fail', 'ban-until-17', 'fail']['fail', 'fail', 'ban-until-17', 'fail']Passed
failures cleared by ban['fail', 'fail', 'ban-until-7', 'banned', 'fail', 'ok']['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']Failed
banned failures ignored['fail', 'ban-until-11', 'banned', 'banned', 'banned', 'ok']['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']Failed
random attempts['fail', 'fail', 'ban-until-31', 'banned', 'fail', 'fail', 'ban-until-87', 'banned', 'banned', 'banned', 'fail', 'ok', 'fail', 'ok']['fail', 'fail', 'ban-until-31', 'banned', 'fail', 'fail', 'ban-until-87', 'banned', 'banned', 'fail', 'fail', 'ok', 'fail', 'ok']Failed

SHA-256 / 42fd5ccfefaf9692a865f2f6c855f4e014a09290057417afdb9daa09d3475eb2

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    th = x['threshold']
    Wn = x['window_s']
    base = x['ban_s']
    fails = []
    until = None
    strikes = 0
    out = []
    for t, ok in x['events']:
        if until is not None and t - until < base:
            out.append('banned')
            continue
        if ok:
            out.append('ok')
            continue
        fails = [f for f in fails if f > t - Wn] + [t]
        if len(fails) >= th:
            until = t + base * 2 ** strikes
            strikes += 1
            fails = []
            out.append('ban-until-%d' % until)
        else:
            out.append('fail')
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [31, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [71, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [23, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [12, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-17', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [9, False], [10, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [13, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[10, False],
               [13, False],
               [16, False],
               [20, False],
               [52, False],
               [53, False],
               [57, False],
               [74, False],
               [80, False],
               [87, False],
               [106, False],
               [115, True],
               [116, False],
               [119, True]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ban-until-31',
    'banned',
    'fail',
    'fail',
    'ban-until-87',
    'banned',
    'banned',
    'fail',
    'fail',
    'ok',
    'fail',
    'ok']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [32, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [72, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [24, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [13, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-18', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [10, False], [11, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [14, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[0, False],
               [23, False],
               [25, True],
               [34, False],
               [35, False],
               [49, False],
               [49, True],
               [60, False],
               [71, False],
               [83, False],
               [87, False],
               [87, False],
               [89, False],
               [117, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ok',
    'fail',
    'ban-until-50',
    'banned',
    'banned',
    'fail',
    'fail',
    'fail',
    'ban-until-117',
    'banned',
    'banned',
    'fail']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [33, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [73, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [25, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [14, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-19', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [11, False], [12, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [15, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[8, False],
               [30, False],
               [30, False],
               [39, True],
               [44, False],
               [45, True],
               [47, False],
               [58, True],
               [78, False],
               [78, False],
               [82, False],
               [90, False],
               [107, False],
               [119, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'fail',
    'ok',
    'ban-until-59',
    'banned',
    'banned',
    'banned',
    'fail',
    'fail',
    'ban-until-112',
    'banned',
    'banned',
    'fail']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [34, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'fail', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [74, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [26, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [15, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-20', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [12, False], [13, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [16, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[6, False],
               [17, False],
               [25, False],
               [51, False],
               [52, True],
               [68, True],
               [78, True],
               [79, False],
               [86, True],
               [90, False],
               [92, True],
               [96, False],
               [100, False],
               [110, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ban-until-40',
    'fail',
    'ok',
    'ok',
    'ok',
    'fail',
    'ok',
    'fail',
    'ok',
    'ban-until-126',
    'banned',
    'banned']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [35, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'fail', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [75, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [27, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [16, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-21', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [13, False], [14, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [17, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[11, True],
               [15, True],
               [29, False],
               [39, False],
               [45, False],
               [45, False],
               [46, False],
               [50, True],
               [54, False],
               [56, False],
               [58, True],
               [60, False],
               [83, False],
               [92, False]],
    'threshold': 3,
    'window_s': 20},
   ['ok',
    'ok',
    'fail',
    'fail',
    'ban-until-60',
    'banned',
    'banned',
    'banned',
    'banned',
    'banned',
    'banned',
    'fail',
    'fail',
    'fail']]]]
for label, args, expected in cases[N - 1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
ban and escalate['fail', 'fail', 'ban-until-12', 'banned', 'banned', 'banned', 'banned', 'fail', 'ok']['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']Failed
third ban escalates['ban-until-10', 'banned', 'ban-until-50', 'ok', 'ok']['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']Failed
ban end instant['fail', 'ban-until-21', 'banned', 'banned', 'banned']['fail', 'ban-until-21', 'banned', 'ok', 'fail']Failed
window prune boundary['fail', 'fail', 'ban-until-17', 'fail']['fail', 'fail', 'ban-until-17', 'fail']Passed
failures cleared by ban['fail', 'fail', 'ban-until-7', 'banned', 'banned', 'banned']['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']Failed
banned failures ignored['fail', 'ban-until-11', 'banned', 'banned', 'banned', 'banned']['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']Failed
random attempts['fail', 'fail', 'ban-until-31', 'banned', 'fail', 'fail', 'ban-until-87', 'banned', 'banned', 'banned', 'fail', 'ok', 'fail', 'ok']['fail', 'fail', 'ban-until-31', 'banned', 'fail', 'fail', 'ban-until-87', 'banned', 'banned', 'fail', 'fail', 'ok', 'fail', 'ok']Failed

SHA-256 / cbd0ec44c57cb1d2f612d743b941fdcf47757fab710254c9296add38150f7352

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    th = x['threshold']
    Wn = x['window_s']
    base = x['ban_s']
    fails = []
    until = None
    strikes = 0
    out = []
    for t, ok in x['events']:
        if until is not None and t < until:
            out.append('banned')
            continue
        if ok:
            out.append('ok')
            continue
        fails = [f for f in fails if f > t - Wn] + [t]
        if len(fails) >= th:
            until = t + base * 2 ** strikes
            strikes += 1
            fails = []
            out.append('ban-until-%d' % until)
        else:
            out.append('fail')
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [31, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [71, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [23, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [12, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-17', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [9, False], [10, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [13, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[10, False],
               [13, False],
               [16, False],
               [20, False],
               [52, False],
               [53, False],
               [57, False],
               [74, False],
               [80, False],
               [87, False],
               [106, False],
               [115, True],
               [116, False],
               [119, True]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ban-until-31',
    'banned',
    'fail',
    'fail',
    'ban-until-87',
    'banned',
    'banned',
    'fail',
    'fail',
    'ok',
    'fail',
    'ok']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [32, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [72, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [24, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [13, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-18', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [10, False], [11, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [14, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[0, False],
               [23, False],
               [25, True],
               [34, False],
               [35, False],
               [49, False],
               [49, True],
               [60, False],
               [71, False],
               [83, False],
               [87, False],
               [87, False],
               [89, False],
               [117, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ok',
    'fail',
    'ban-until-50',
    'banned',
    'banned',
    'fail',
    'fail',
    'fail',
    'ban-until-117',
    'banned',
    'banned',
    'fail']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [33, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [73, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [25, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [14, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-19', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [11, False], [12, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [15, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[8, False],
               [30, False],
               [30, False],
               [39, True],
               [44, False],
               [45, True],
               [47, False],
               [58, True],
               [78, False],
               [78, False],
               [82, False],
               [90, False],
               [107, False],
               [119, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'fail',
    'ok',
    'ban-until-59',
    'banned',
    'banned',
    'banned',
    'fail',
    'fail',
    'ban-until-112',
    'banned',
    'banned',
    'fail']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [34, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'fail', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [74, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [26, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [15, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-20', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [12, False], [13, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [16, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[6, False],
               [17, False],
               [25, False],
               [51, False],
               [52, True],
               [68, True],
               [78, True],
               [79, False],
               [86, True],
               [90, False],
               [92, True],
               [96, False],
               [100, False],
               [110, False]],
    'threshold': 3,
    'window_s': 20},
   ['fail',
    'fail',
    'ban-until-40',
    'fail',
    'ok',
    'ok',
    'ok',
    'fail',
    'ok',
    'fail',
    'ok',
    'ban-until-126',
    'banned',
    'banned']]],
 [['ban and escalate',
   {'ban_s': 10,
    'events': [[0, False],
               [1, False],
               [2, False],
               [5, False],
               [12, False],
               [13, False],
               [14, False],
               [35, False],
               [40, True]],
    'threshold': 3,
    'window_s': 60},
   ['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'fail', 'ok']],
  ['third ban escalates',
   {'ban_s': 10,
    'events': [[0, False], [10, False], [30, False], [65, True], [75, True]],
    'threshold': 1,
    'window_s': 5},
   ['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']],
  ['ban end instant',
   {'ban_s': 20,
    'events': [[0, False], [1, False], [20, True], [21, True], [27, False]],
    'threshold': 2,
    'window_s': 30},
   ['fail', 'ban-until-21', 'banned', 'ok', 'fail']],
  ['window prune boundary',
   {'ban_s': 5,
    'events': [[0, False], [10, False], [16, False], [30, False]],
    'threshold': 2,
    'window_s': 10},
   ['fail', 'fail', 'ban-until-21', 'fail']],
  ['failures cleared by ban',
   {'ban_s': 5,
    'events': [[0, False], [1, False], [2, False], [7, False], [13, False], [14, True]],
    'threshold': 3,
    'window_s': 100},
   ['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']],
  ['banned failures ignored',
   {'ban_s': 10,
    'events': [[0, False], [1, False], [5, False], [6, False], [11, False], [17, True]],
    'threshold': 2,
    'window_s': 100},
   ['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']],
  ['random attempts',
   {'ban_s': 15,
    'events': [[11, True],
               [15, True],
               [29, False],
               [39, False],
               [45, False],
               [45, False],
               [46, False],
               [50, True],
               [54, False],
               [56, False],
               [58, True],
               [60, False],
               [83, False],
               [92, False]],
    'threshold': 3,
    'window_s': 20},
   ['ok',
    'ok',
    'fail',
    'fail',
    'ban-until-60',
    'banned',
    'banned',
    'banned',
    'banned',
    'banned',
    'banned',
    'fail',
    'fail',
    'fail']]]]
for label, args, expected in cases[N - 1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
ban and escalate['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']['fail', 'fail', 'ban-until-12', 'banned', 'fail', 'fail', 'ban-until-34', 'banned', 'ok']Passed
third ban escalates['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']['ban-until-10', 'ban-until-30', 'ban-until-70', 'banned', 'ok']Passed
ban end instant['fail', 'ban-until-21', 'banned', 'ok', 'fail']['fail', 'ban-until-21', 'banned', 'ok', 'fail']Passed
window prune boundary['fail', 'fail', 'ban-until-17', 'fail']['fail', 'fail', 'ban-until-17', 'fail']Passed
failures cleared by ban['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']['fail', 'fail', 'ban-until-7', 'fail', 'fail', 'ok']Passed
banned failures ignored['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']['fail', 'ban-until-11', 'banned', 'banned', 'fail', 'ok']Passed
random attempts['fail', 'fail', 'ban-until-31', 'banned', 'fail', 'fail', 'ban-until-87', 'banned', 'banned', 'fail', 'fail', 'ok', 'fail', 'ok']['fail', 'fail', 'ban-until-31', 'banned', 'fail', 'fail', 'ban-until-87', 'banned', 'banned', 'fail', 'fail', 'ok', 'fail', 'ok']Passed

SHA-256 / a85b760b01d176918edfe5266592a16ed76089f21492a4e80dce604d33d858da

Verification & scope

A deterministic, bounded teaching model with stipulated constants and pre-hashed or explicitly hashed inputs; it is not a production implementation and makes no claim of conformance to any library or paper beyond the stated contract. 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:49.095248+00:00.

Case digest / 58ab7d23a8f2dd3d3cc94fb7e0ea1c65eb31cf3ecde62ea436bb75bf950b2699