FAILURE MAP
← Case archive

FA-31831 / Keyboard interactions / Open access

Tap-hold dual-role keyboard key: Foreign release is replaced with modifier release or dropped · case 01

The event trace violates the foreign release rule and produces incorrect keyboard state or command output.

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

ROOT CAUSE

Foreign release is replaced with modifier release or dropped.

VERIFIED REPAIR

Use the contract transition `elif kind=='up': out.append(['up',key])` at the foreign release fault site; preserve the other state transitions.

Unsuccessful approach: The partial repair changes this transition to elif kind=='up': pass, 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',modifier])
            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 fixtureActualExpectedOutcome
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', 'Control'], ['up', 'Control']], None]][[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]]Failed
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', 'Control']], None]][[[['down', 'A'], ['up', 'A']], None]]Failed

SHA-256 / 86851f7108ed2145705498967d5c63c677304c679073eadff75ebb2bae595941

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': pass
            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 fixtureActualExpectedOutcome
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', 'Control']], None]][[[['down', 'Control'], ['down', 'A'], ['down', 'B'], ['up', 'A'], ['up', 'Control']], None]]Failed
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']], None]][[[['down', 'A'], ['up', 'A']], None]]Failed

SHA-256 / aeccdd0807285f9653101637c6ea1be27a202e03cb647b3d6d078e344962cccd

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 fixtureActualExpectedOutcome
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.035503+00:00.

Case digest / f49f5557ae4df4d96a97c60ec5e9f0363418197a344e5b6bbf1ee2ad6173c372