FA-31171 / Keyboard interactions / Open access
Per-key double tap recognizer: Double-tap acceptance excludes the deadline or compares the absolute time · case 01
The event trace violates the inclusive window rule and produces incorrect keyboard state or command output.
ROOT CAUSE
Double-tap acceptance excludes the deadline or compares the absolute time.
VERIFIED REPAIR
Use the contract transition `now-previous<=window` at the inclusive window fault site; preserve the other state transitions.
Unsuccessful approach: The partial repair changes this transition to now<=window, which still violates the model contract on the explicit regression traces.
Case contract
Case [window,events] with nondecreasing timestamps and events [kind,key,time]. Down repeats do not count. A matched up produces tap and may complete a double if the prior release of the same key is within the inclusive window. Completing a double consumes that prior release. Cancel clears that key, blur clears all state. Return doubles [key,time], held keys and remembered release times. Inputs are finite ordered event traces; return the stated deterministic state. Batch entries are independent. N varies the number of independent input transactions.
Why this case matters
Controlled keyboard event processing model for debugging application event logic.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(cases):
def run(c):
window,events=c
held=set(); last={}; doubles=[]; taps=0
for kind,key,now in events:
if kind=='down': held.add(key)
elif kind=='up' and key in held:
held.discard(key)
taps+=1
previous=last.get(key)
if previous is not None and now-previous<window:
doubles.append([key,now])
last.pop(key,None)
else:
last[key]=now
elif kind=='cancel':
held.discard(key); last.pop(key,None)
elif kind=='blur':
held.clear(); last.clear()
return [doubles,sorted(held),sorted([[k,v] for k,v in last.items()]),taps]
return [run(c) for c in cases]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('double-tap scenario 0', solve([[4, []]] * N), [[[], [], [], 0]] * N)
check('double-tap scenario 1', solve([[4, [['down', 'A', 0], ['up', 'B', 1]]]] * N), [[[], ['A'], [], 0]] * N)
check('double-tap scenario 2', solve([[4, [['up', 'A', 1]]]] * N), [[[], [], [], 0]] * N)
check('double-tap scenario 3', solve([[4, [['down', 'A', 0], ['up', 'A', 0], ['down', 'A', 2], ['up', 'A', 4]]]] * N), [[[['A', 4]], [], [], 2]] * N)
check('double-tap scenario 4', solve([[4, [['down', 'A', 10], ['up', 'A', 11], ['down', 'B', 12], ['up', 'B', 13]]]] * N), [[[], [], [['A', 11], ['B', 13]], 2]] * N)
check('double-tap scenario 5', solve([[4, [['down', 'A', 1], ['down', 'A', 1], ['up', 'A', 2]]]] * N), [[[], [], [['A', 2]], 1]] * N)
check('double-tap scenario 6', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'A', 10], ['up', 'A', 11], ['down', 'A', 12], ['up', 'A', 13]]]] * N), [[[['A', 13]], [], [], 3]] * N)
check('double-tap scenario 7', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'A', 2], ['up', 'A', 3], ['down', 'A', 4], ['up', 'A', 5]]]] * N), [[[['A', 3]], [], [['A', 5]], 3]] * N)
check('double-tap scenario 8', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'B', 2], ['up', 'B', 3], ['down', 'A', 3], ['up', 'A', 4]]]] * N), [[[['A', 4]], [], [['B', 3]], 3]] * N)
check('double-tap scenario 9', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'B', 2], ['blur', '', 3]]]] * N), [[[], [], [], 1]] * N)
check('double-tap scenario 10', solve([[4, [['down', 'A', 0], ['down', 'B', 1], ['up', 'A', 2]]]] * N), [[[], ['B'], [['A', 2]], 1]] * N)
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| double-tap scenario 0 | [[[], [], [], 0]] | [[[], [], [], 0]] | Passed |
| double-tap scenario 1 | [[[], ['A'], [], 0]] | [[[], ['A'], [], 0]] | Passed |
| double-tap scenario 2 | [[[], [], [], 0]] | [[[], [], [], 0]] | Passed |
| double-tap scenario 3 | [[[], [], [['A', 4]], 2]] | [[[['A', 4]], [], [], 2]] | Failed |
| double-tap scenario 4 | [[[], [], [['A', 11], ['B', 13]], 2]] | [[[], [], [['A', 11], ['B', 13]], 2]] | Passed |
| double-tap scenario 5 | [[[], [], [['A', 2]], 1]] | [[[], [], [['A', 2]], 1]] | Passed |
| double-tap scenario 6 | [[[['A', 13]], [], [], 3]] | [[[['A', 13]], [], [], 3]] | Passed |
| double-tap scenario 7 | [[[['A', 3]], [], [['A', 5]], 3]] | [[[['A', 3]], [], [['A', 5]], 3]] | Passed |
| double-tap scenario 8 | [[[['A', 4]], [], [['B', 3]], 3]] | [[[['A', 4]], [], [['B', 3]], 3]] | Passed |
| double-tap scenario 9 | [[[], [], [], 1]] | [[[], [], [], 1]] | Passed |
| double-tap scenario 10 | [[[], ['B'], [['A', 2]], 1]] | [[[], ['B'], [['A', 2]], 1]] | Passed |
SHA-256 / 64e3e0e5d89bf4a3c74685072880a0d27238140b914161400b60c60ebc550054
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(cases):
def run(c):
window,events=c
held=set(); last={}; doubles=[]; taps=0
for kind,key,now in events:
if kind=='down': held.add(key)
elif kind=='up' and key in held:
held.discard(key)
taps+=1
previous=last.get(key)
if previous is not None and now<=window:
doubles.append([key,now])
last.pop(key,None)
else:
last[key]=now
elif kind=='cancel':
held.discard(key); last.pop(key,None)
elif kind=='blur':
held.clear(); last.clear()
return [doubles,sorted(held),sorted([[k,v] for k,v in last.items()]),taps]
return [run(c) for c in cases]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('double-tap scenario 0', solve([[4, []]] * N), [[[], [], [], 0]] * N)
check('double-tap scenario 1', solve([[4, [['down', 'A', 0], ['up', 'B', 1]]]] * N), [[[], ['A'], [], 0]] * N)
check('double-tap scenario 2', solve([[4, [['up', 'A', 1]]]] * N), [[[], [], [], 0]] * N)
check('double-tap scenario 3', solve([[4, [['down', 'A', 0], ['up', 'A', 0], ['down', 'A', 2], ['up', 'A', 4]]]] * N), [[[['A', 4]], [], [], 2]] * N)
check('double-tap scenario 4', solve([[4, [['down', 'A', 10], ['up', 'A', 11], ['down', 'B', 12], ['up', 'B', 13]]]] * N), [[[], [], [['A', 11], ['B', 13]], 2]] * N)
check('double-tap scenario 5', solve([[4, [['down', 'A', 1], ['down', 'A', 1], ['up', 'A', 2]]]] * N), [[[], [], [['A', 2]], 1]] * N)
check('double-tap scenario 6', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'A', 10], ['up', 'A', 11], ['down', 'A', 12], ['up', 'A', 13]]]] * N), [[[['A', 13]], [], [], 3]] * N)
check('double-tap scenario 7', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'A', 2], ['up', 'A', 3], ['down', 'A', 4], ['up', 'A', 5]]]] * N), [[[['A', 3]], [], [['A', 5]], 3]] * N)
check('double-tap scenario 8', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'B', 2], ['up', 'B', 3], ['down', 'A', 3], ['up', 'A', 4]]]] * N), [[[['A', 4]], [], [['B', 3]], 3]] * N)
check('double-tap scenario 9', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'B', 2], ['blur', '', 3]]]] * N), [[[], [], [], 1]] * N)
check('double-tap scenario 10', solve([[4, [['down', 'A', 0], ['down', 'B', 1], ['up', 'A', 2]]]] * N), [[[], ['B'], [['A', 2]], 1]] * N)
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| double-tap scenario 0 | [[[], [], [], 0]] | [[[], [], [], 0]] | Passed |
| double-tap scenario 1 | [[[], ['A'], [], 0]] | [[[], ['A'], [], 0]] | Passed |
| double-tap scenario 2 | [[[], [], [], 0]] | [[[], [], [], 0]] | Passed |
| double-tap scenario 3 | [[[['A', 4]], [], [], 2]] | [[[['A', 4]], [], [], 2]] | Passed |
| double-tap scenario 4 | [[[], [], [['A', 11], ['B', 13]], 2]] | [[[], [], [['A', 11], ['B', 13]], 2]] | Passed |
| double-tap scenario 5 | [[[], [], [['A', 2]], 1]] | [[[], [], [['A', 2]], 1]] | Passed |
| double-tap scenario 6 | [[[], [], [['A', 13]], 3]] | [[[['A', 13]], [], [], 3]] | Failed |
| double-tap scenario 7 | [[[['A', 3]], [], [['A', 5]], 3]] | [[[['A', 3]], [], [['A', 5]], 3]] | Passed |
| double-tap scenario 8 | [[[['A', 4]], [], [['B', 3]], 3]] | [[[['A', 4]], [], [['B', 3]], 3]] | Passed |
| double-tap scenario 9 | [[[], [], [], 1]] | [[[], [], [], 1]] | Passed |
| double-tap scenario 10 | [[[], ['B'], [['A', 2]], 1]] | [[[], ['B'], [['A', 2]], 1]] | Passed |
SHA-256 / 1a65534751870cf5a92771f7367716899c4ae59b7448ecea216c4306f081782f
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(cases):
def run(c):
window,events=c
held=set(); last={}; doubles=[]; taps=0
for kind,key,now in events:
if kind=='down': held.add(key)
elif kind=='up' and key in held:
held.discard(key)
taps+=1
previous=last.get(key)
if previous is not None and now-previous<=window:
doubles.append([key,now])
last.pop(key,None)
else:
last[key]=now
elif kind=='cancel':
held.discard(key); last.pop(key,None)
elif kind=='blur':
held.clear(); last.clear()
return [doubles,sorted(held),sorted([[k,v] for k,v in last.items()]),taps]
return [run(c) for c in cases]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('double-tap scenario 0', solve([[4, []]] * N), [[[], [], [], 0]] * N)
check('double-tap scenario 1', solve([[4, [['down', 'A', 0], ['up', 'B', 1]]]] * N), [[[], ['A'], [], 0]] * N)
check('double-tap scenario 2', solve([[4, [['up', 'A', 1]]]] * N), [[[], [], [], 0]] * N)
check('double-tap scenario 3', solve([[4, [['down', 'A', 0], ['up', 'A', 0], ['down', 'A', 2], ['up', 'A', 4]]]] * N), [[[['A', 4]], [], [], 2]] * N)
check('double-tap scenario 4', solve([[4, [['down', 'A', 10], ['up', 'A', 11], ['down', 'B', 12], ['up', 'B', 13]]]] * N), [[[], [], [['A', 11], ['B', 13]], 2]] * N)
check('double-tap scenario 5', solve([[4, [['down', 'A', 1], ['down', 'A', 1], ['up', 'A', 2]]]] * N), [[[], [], [['A', 2]], 1]] * N)
check('double-tap scenario 6', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'A', 10], ['up', 'A', 11], ['down', 'A', 12], ['up', 'A', 13]]]] * N), [[[['A', 13]], [], [], 3]] * N)
check('double-tap scenario 7', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'A', 2], ['up', 'A', 3], ['down', 'A', 4], ['up', 'A', 5]]]] * N), [[[['A', 3]], [], [['A', 5]], 3]] * N)
check('double-tap scenario 8', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'B', 2], ['up', 'B', 3], ['down', 'A', 3], ['up', 'A', 4]]]] * N), [[[['A', 4]], [], [['B', 3]], 3]] * N)
check('double-tap scenario 9', solve([[4, [['down', 'A', 0], ['up', 'A', 1], ['down', 'B', 2], ['blur', '', 3]]]] * N), [[[], [], [], 1]] * N)
check('double-tap scenario 10', solve([[4, [['down', 'A', 0], ['down', 'B', 1], ['up', 'A', 2]]]] * N), [[[], ['B'], [['A', 2]], 1]] * N)
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| double-tap scenario 0 | [[[], [], [], 0]] | [[[], [], [], 0]] | Passed |
| double-tap scenario 1 | [[[], ['A'], [], 0]] | [[[], ['A'], [], 0]] | Passed |
| double-tap scenario 2 | [[[], [], [], 0]] | [[[], [], [], 0]] | Passed |
| double-tap scenario 3 | [[[['A', 4]], [], [], 2]] | [[[['A', 4]], [], [], 2]] | Passed |
| double-tap scenario 4 | [[[], [], [['A', 11], ['B', 13]], 2]] | [[[], [], [['A', 11], ['B', 13]], 2]] | Passed |
| double-tap scenario 5 | [[[], [], [['A', 2]], 1]] | [[[], [], [['A', 2]], 1]] | Passed |
| double-tap scenario 6 | [[[['A', 13]], [], [], 3]] | [[[['A', 13]], [], [], 3]] | Passed |
| double-tap scenario 7 | [[[['A', 3]], [], [['A', 5]], 3]] | [[[['A', 3]], [], [['A', 5]], 3]] | Passed |
| double-tap scenario 8 | [[[['A', 4]], [], [['B', 3]], 3]] | [[[['A', 4]], [], [['B', 3]], 3]] | Passed |
| double-tap scenario 9 | [[[], [], [], 1]] | [[[], [], [], 1]] | Passed |
| double-tap scenario 10 | [[[], ['B'], [['A', 2]], 1]] | [[[], ['B'], [['A', 2]], 1]] | Passed |
SHA-256 / 996bacd31ad41881e54cbca4d0eab47d384d1b6adac1e7258fa4b3224482c7f1
Verification & scope
Offline stipulated event model, not a browser implementation or web standard conformance claim. 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:41:59.665953+00:00.
Case digest / c7f570a5c8c79ce16004e2dd0b92c936d9f5c40c54d40b33adda57a463d0dfe3