FA-31836 / Keyboard interactions / Open access
Tap-hold dual-role keyboard key: Cancellation releases a modifier that was never pressed or leaves a promoted modifier stuck · case 01
The event trace violates the cancel promoted rule and produces incorrect keyboard state or command output.
ROOT CAUSE
Cancellation releases a modifier that was never pressed or leaves a promoted modifier stuck.
VERIFIED REPAIR
Use the contract transition `if pending is not None and pending[1]: out.append(['up',modifier])` at the cancel promoted fault site; preserve the other state transitions.
Unsuccessful approach: The partial repair changes this transition to if pending is not None and not pending[1]: out.append(['up',modifier]), which still violates the model contract on the explicit regression traces.
Case contract
Case [dual,tap,modifier,threshold,events]. Events [kind,key,time]. Dual down arms tap; repeated dual downs ignored. A foreign down or threshold tick promotes pending dual into modifier down before forwarding foreign down. Dual up emits tap when pending, modifier up when promoted. Cancel releases a promoted modifier and cancels pending tap. Foreign up is forwarded. Return emitted [phase,key] and pending [start,promoted] or None. 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):
dual,tap,modifier,threshold,events=c
pending=None; out=[]
for kind,key,now in events:
if kind=='down' and key==dual:
if pending is None: pending=[now,False]
elif kind=='down':
if pending is not None and not pending[1]:
out.append(['down',modifier]); pending[1]=True
out.append(['down',key])
elif kind=='tick':
if pending is not None and not pending[1] and now-pending[0]>=threshold:
out.append(['down',modifier]); pending[1]=True
elif kind=='up' and key==dual:
if pending is not None:
if pending[1]: out.append(['up',modifier])
else: out.extend([['down',tap],['up',tap]])
pending=None
elif kind=='up': out.append(['up',key])
elif kind=='cancel':
if pending is not None: out.append(['up',modifier])
pending=None
return [out,pending]
return [run(c) for c in cases]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('dual-role-key scenario 0', solve([['X', 'Escape', 'Control', 5, []]] * N), [[[], None]] * N)
check('dual-role-key scenario 1', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['up', 'X', 11]]]] * N), [[[['down', 'Escape'], ['up', 'Escape']], None]] * N)
check('dual-role-key scenario 2', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['down', 'A', 11], ['down', 'B', 12], ['up', 'A', 13], ['up', 'X', 14]]]] * N), [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 3', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 12]]]] * N), [[[], [10, False]]] * N)
check('dual-role-key scenario 4', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 15], ['tick', '', 16], ['up', 'X', 17]]]] * N), [[[['down', 'Control'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 5', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['down', 'X', 13], ['tick', '', 15]]]] * N), [[[['down', 'Control']], [10, True]]] * N)
check('dual-role-key scenario 6', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['cancel', '', 11]]]] * N), [[[], None]] * N)
check('dual-role-key scenario 7', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 15], ['cancel', '', 16]]]] * N), [[[['down', 'Control'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 8', solve([['X', 'Escape', 'Control', 5, [['down', 'A', 10], ['up', 'A', 11]]]] * N), [[[['down', 'A'], ['up', 'A']], None]] * 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 |
|---|---|---|---|
| dual-role-key scenario 0 | [[[], None]] | [[[], None]] | Passed |
| dual-role-key scenario 1 | [[[['down', 'Escape'], ['up', 'Escape']], None]] | [[[['down', 'Escape'], ['up', 'Escape']], None]] | Passed |
| dual-role-key scenario 2 | [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 3 | [[[], [10, False]]] | [[[], [10, False]]] | Passed |
| dual-role-key scenario 4 | [[[['down', 'Control'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 5 | [[[['down', 'Control']], [10, True]]] | [[[['down', 'Control']], [10, True]]] | Passed |
| dual-role-key scenario 6 | [[[['up', 'Control']], None]] | [[[], None]] | Failed |
| dual-role-key scenario 7 | [[[['down', 'Control'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 8 | [[[['down', 'A'], ['up', 'A']], None]] | [[[['down', 'A'], ['up', 'A']], None]] | Passed |
SHA-256 / c3806e49dc34b28a3df659c15ca97152ab46336bc17b78814011769cb4763242
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):
dual,tap,modifier,threshold,events=c
pending=None; out=[]
for kind,key,now in events:
if kind=='down' and key==dual:
if pending is None: pending=[now,False]
elif kind=='down':
if pending is not None and not pending[1]:
out.append(['down',modifier]); pending[1]=True
out.append(['down',key])
elif kind=='tick':
if pending is not None and not pending[1] and now-pending[0]>=threshold:
out.append(['down',modifier]); pending[1]=True
elif kind=='up' and key==dual:
if pending is not None:
if pending[1]: out.append(['up',modifier])
else: out.extend([['down',tap],['up',tap]])
pending=None
elif kind=='up': out.append(['up',key])
elif kind=='cancel':
if pending is not None and not pending[1]: out.append(['up',modifier])
pending=None
return [out,pending]
return [run(c) for c in cases]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('dual-role-key scenario 0', solve([['X', 'Escape', 'Control', 5, []]] * N), [[[], None]] * N)
check('dual-role-key scenario 1', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['up', 'X', 11]]]] * N), [[[['down', 'Escape'], ['up', 'Escape']], None]] * N)
check('dual-role-key scenario 2', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['down', 'A', 11], ['down', 'B', 12], ['up', 'A', 13], ['up', 'X', 14]]]] * N), [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 3', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 12]]]] * N), [[[], [10, False]]] * N)
check('dual-role-key scenario 4', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 15], ['tick', '', 16], ['up', 'X', 17]]]] * N), [[[['down', 'Control'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 5', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['down', 'X', 13], ['tick', '', 15]]]] * N), [[[['down', 'Control']], [10, True]]] * N)
check('dual-role-key scenario 6', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['cancel', '', 11]]]] * N), [[[], None]] * N)
check('dual-role-key scenario 7', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 15], ['cancel', '', 16]]]] * N), [[[['down', 'Control'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 8', solve([['X', 'Escape', 'Control', 5, [['down', 'A', 10], ['up', 'A', 11]]]] * N), [[[['down', 'A'], ['up', 'A']], None]] * 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 |
|---|---|---|---|
| dual-role-key scenario 0 | [[[], None]] | [[[], None]] | Passed |
| dual-role-key scenario 1 | [[[['down', 'Escape'], ['up', 'Escape']], None]] | [[[['down', 'Escape'], ['up', 'Escape']], None]] | Passed |
| dual-role-key scenario 2 | [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 3 | [[[], [10, False]]] | [[[], [10, False]]] | Passed |
| dual-role-key scenario 4 | [[[['down', 'Control'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 5 | [[[['down', 'Control']], [10, True]]] | [[[['down', 'Control']], [10, True]]] | Passed |
| dual-role-key scenario 6 | [[[['up', 'Control']], None]] | [[[], None]] | Failed |
| dual-role-key scenario 7 | [[[['down', 'Control']], None]] | [[[['down', 'Control'], ['up', 'Control']], None]] | Failed |
| dual-role-key scenario 8 | [[[['down', 'A'], ['up', 'A']], None]] | [[[['down', 'A'], ['up', 'A']], None]] | Passed |
SHA-256 / db1b7ffa3a665336c698c7c9530c39d665ac0a64f11977e5d3e2f3ccc97c5599
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):
dual,tap,modifier,threshold,events=c
pending=None; out=[]
for kind,key,now in events:
if kind=='down' and key==dual:
if pending is None: pending=[now,False]
elif kind=='down':
if pending is not None and not pending[1]:
out.append(['down',modifier]); pending[1]=True
out.append(['down',key])
elif kind=='tick':
if pending is not None and not pending[1] and now-pending[0]>=threshold:
out.append(['down',modifier]); pending[1]=True
elif kind=='up' and key==dual:
if pending is not None:
if pending[1]: out.append(['up',modifier])
else: out.extend([['down',tap],['up',tap]])
pending=None
elif kind=='up': out.append(['up',key])
elif kind=='cancel':
if pending is not None and pending[1]: out.append(['up',modifier])
pending=None
return [out,pending]
return [run(c) for c in cases]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('dual-role-key scenario 0', solve([['X', 'Escape', 'Control', 5, []]] * N), [[[], None]] * N)
check('dual-role-key scenario 1', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['up', 'X', 11]]]] * N), [[[['down', 'Escape'], ['up', 'Escape']], None]] * N)
check('dual-role-key scenario 2', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['down', 'A', 11], ['down', 'B', 12], ['up', 'A', 13], ['up', 'X', 14]]]] * N), [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 3', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 12]]]] * N), [[[], [10, False]]] * N)
check('dual-role-key scenario 4', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 15], ['tick', '', 16], ['up', 'X', 17]]]] * N), [[[['down', 'Control'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 5', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['down', 'X', 13], ['tick', '', 15]]]] * N), [[[['down', 'Control']], [10, True]]] * N)
check('dual-role-key scenario 6', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['cancel', '', 11]]]] * N), [[[], None]] * N)
check('dual-role-key scenario 7', solve([['X', 'Escape', 'Control', 5, [['down', 'X', 10], ['tick', '', 15], ['cancel', '', 16]]]] * N), [[[['down', 'Control'], ['up', 'Control']], None]] * N)
check('dual-role-key scenario 8', solve([['X', 'Escape', 'Control', 5, [['down', 'A', 10], ['up', 'A', 11]]]] * N), [[[['down', 'A'], ['up', 'A']], None]] * 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 |
|---|---|---|---|
| dual-role-key scenario 0 | [[[], None]] | [[[], None]] | Passed |
| dual-role-key scenario 1 | [[[['down', 'Escape'], ['up', 'Escape']], None]] | [[[['down', 'Escape'], ['up', 'Escape']], None]] | Passed |
| dual-role-key scenario 2 | [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 3 | [[[], [10, False]]] | [[[], [10, False]]] | Passed |
| dual-role-key scenario 4 | [[[['down', 'Control'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 5 | [[[['down', 'Control']], [10, True]]] | [[[['down', 'Control']], [10, True]]] | Passed |
| dual-role-key scenario 6 | [[[], None]] | [[[], None]] | Passed |
| dual-role-key scenario 7 | [[[['down', 'Control'], ['up', 'Control']], None]] | [[[['down', 'Control'], ['up', 'Control']], None]] | Passed |
| dual-role-key scenario 8 | [[[['down', 'A'], ['up', 'A']], None]] | [[[['down', 'A'], ['up', 'A']], None]] | Passed |
SHA-256 / e4ae9417af0a85ce6dd64e5b58c1836d8a021ae82f911ba9c19a8130ed4a1a26
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:42:06.080557+00:00.
Case digest / 2704aea46100a9e3a75842edee131dbb21ca1a3920b0f1ccd2463d18422d281e