FA-31146 / Keyboard interactions / Open access
Per-key double tap recognizer: An unmatched release counts as a tap even without its own down · case 01
The event trace violates the matched release rule and produces incorrect keyboard state or command output.
ROOT CAUSE
An unmatched release counts as a tap even without its own down.
VERIFIED REPAIR
Use the contract transition `elif kind=='up' and key in held:` at the matched release fault site; preserve the other state transitions.
Unsuccessful approach: The partial repair changes this transition to elif kind=='up' and bool(held):, 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':
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'], [['B', 1]], 1]] | [[[], ['A'], [], 0]] | Failed |
| double-tap scenario 2 | [[[], [], [['A', 1]], 1]] | [[[], [], [], 0]] | Failed |
| 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 / c274f49fe660c86de18e1ed98966a982e4e591c32d0fe49ef8be91d63f8210a8
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 bool(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'], [['B', 1]], 1]] | [[[], ['A'], [], 0]] | Failed |
| 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 / d8cdc086177fb46e69202e0cdee26d72511ace9c70005f009783b7aaac358278
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.321104+00:00.
Case digest / 731d714601f50e8d7cbf03a29b3292e1669a30dca910fb662c1f90c1c98b1fee