FA-31161 / Keyboard interactions / Open access
Per-key double tap recognizer: Double-tap matching borrows a release timestamp from a different key · case 01
The event trace violates the per key history rule and produces incorrect keyboard state or command output.
ROOT CAUSE
Double-tap matching borrows a release timestamp from a different key.
VERIFIED REPAIR
Use the contract transition `previous=last.get(key)` at the per key history fault site; preserve the other state transitions.
Unsuccessful approach: The partial repair changes this transition to previous=max(last.values(),default=None), 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=next(iter(last.values()),None)
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 | [[[['B', 13]], [], [['A', 11]], 2]] | [[[], [], [['A', 11], ['B', 13]], 2]] | Failed |
| 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 | [[[['B', 3], ['A', 4]], [], [], 3]] | [[[['A', 4]], [], [['B', 3]], 3]] | Failed |
| double-tap scenario 9 | [[[], [], [], 1]] | [[[], [], [], 1]] | Passed |
| double-tap scenario 10 | [[[], ['B'], [['A', 2]], 1]] | [[[], ['B'], [['A', 2]], 1]] | Passed |
SHA-256 / 0d0eb4e824f4fd7cecc26dab35f9cfe32f00fc3472f0e191b132150f8897276a
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=max(last.values(),default=None)
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 | [[[['B', 13]], [], [['A', 11]], 2]] | [[[], [], [['A', 11], ['B', 13]], 2]] | Failed |
| 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 | [[[['B', 3], ['A', 4]], [], [], 3]] | [[[['A', 4]], [], [['B', 3]], 3]] | Failed |
| double-tap scenario 9 | [[[], [], [], 1]] | [[[], [], [], 1]] | Passed |
| double-tap scenario 10 | [[[], ['B'], [['A', 2]], 1]] | [[[], ['B'], [['A', 2]], 1]] | Passed |
SHA-256 / f3a20f4884693f5d8b179cbbadb08e2b498c4a543189479c8e7d758bc3111883
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.575499+00:00.
Case digest / 89a88b4c8399b58f7051807fa768d8f4788a53ee0a2fd453a530381e38e03eab