FAILURE MAP
← Case archive

FA-31106 / Keyboard interactions / Open access

Tap versus hold command arbitration: Repeated ticks emit the hold command more than once · case 01

The event trace violates the single hold rule and produces incorrect keyboard state or command output.

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

ROOT CAUSE

Repeated ticks emit the hold command more than once.

VERIFIED REPAIR

Use the contract transition `if not sent and now-start` at the single hold fault site; preserve the other state transitions.

Unsuccessful approach: The partial repair changes this transition to if (not sent or now>start) and now-start, which still violates the model contract on the explicit regression traces.

Case contract

Case [threshold,events]. Events [kind,key,time]. Down arms an unarmed key. Tick emits hold once when age >= threshold. Up before threshold emits tap, at or after threshold emits hold if not yet emitted. Cancel removes just one key silently. Return output and pending [key,start,held] entries. 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):
        threshold,events=c
        pending={}; output=[]
        for kind,key,now in events:
            if kind=='down' and key not in pending:
                pending[key]=[now,False]
            elif kind=='tick':
                for k in sorted(pending):
                    start,sent=pending[k]
                    if now-start>=threshold:
                        output.append(['hold',k])
                        pending[k][1]=True
            elif kind=='up' and key in pending:
                start,sent=pending[key]
                if not sent:
                    output.append(['hold' if now-start>=threshold else 'tap',key])
                del pending[key]
            elif kind=='cancel':
                pending.pop(key,None)
        return [output,sorted([[k]+v for k,v in pending.items()])]
    return [run(c) for c in cases]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('hold-threshold scenario 0', solve([[5, []]] * N), [[[], []]] * N)
check('hold-threshold scenario 1', solve([[5, [['down', 'A', 10], ['tick', '', 12]]]] * N), [[[], [['A', 10, False]]]] * N)
check('hold-threshold scenario 2', solve([[5, [['down', 'A', 10], ['up', 'A', 12]]]] * N), [[[['tap', 'A']], []]] * N)
check('hold-threshold scenario 3', solve([[5, [['down', 'A', 10], ['up', 'A', 15]]]] * N), [[[['hold', 'A']], []]] * N)
check('hold-threshold scenario 4', solve([[5, [['down', 'A', 10], ['tick', '', 15], ['tick', '', 16], ['up', 'A', 17]]]] * N), [[[['hold', 'A']], []]] * N)
check('hold-threshold scenario 5', solve([[5, [['down', 'A', 10], ['down', 'A', 13], ['tick', '', 15]]]] * N), [[[['hold', 'A']], [['A', 10, True]]]] * N)
check('hold-threshold scenario 6', solve([[5, [['down', 'A', 10], ['down', 'B', 11], ['up', 'A', 12]]]] * N), [[[['tap', 'A']], [['B', 11, False]]]] * N)
check('hold-threshold scenario 7', solve([[5, [['down', 'A', 10], ['down', 'B', 11], ['cancel', 'A', 12], ['tick', '', 16]]]] * N), [[[['hold', 'B']], [['B', 11, True]]]] * N)
check('hold-threshold scenario 8', solve([[5, [['up', 'A', 100]]]] * N), [[[], []]] * 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
hold-threshold scenario 0[[[], []]][[[], []]]Passed
hold-threshold scenario 1[[[], [['A', 10, False]]]][[[], [['A', 10, False]]]]Passed
hold-threshold scenario 2[[[['tap', 'A']], []]][[[['tap', 'A']], []]]Passed
hold-threshold scenario 3[[[['hold', 'A']], []]][[[['hold', 'A']], []]]Passed
hold-threshold scenario 4[[[['hold', 'A'], ['hold', 'A']], []]][[[['hold', 'A']], []]]Failed
hold-threshold scenario 5[[[['hold', 'A']], [['A', 10, True]]]][[[['hold', 'A']], [['A', 10, True]]]]Passed
hold-threshold scenario 6[[[['tap', 'A']], [['B', 11, False]]]][[[['tap', 'A']], [['B', 11, False]]]]Passed
hold-threshold scenario 7[[[['hold', 'B']], [['B', 11, True]]]][[[['hold', 'B']], [['B', 11, True]]]]Passed
hold-threshold scenario 8[[[], []]][[[], []]]Passed

SHA-256 / 22e06018c42378ecfb0de53afa04db1c0873ef2eb1a7664cdf8e6815d784f226

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):
        threshold,events=c
        pending={}; output=[]
        for kind,key,now in events:
            if kind=='down' and key not in pending:
                pending[key]=[now,False]
            elif kind=='tick':
                for k in sorted(pending):
                    start,sent=pending[k]
                    if (not sent or now>start) and now-start>=threshold:
                        output.append(['hold',k])
                        pending[k][1]=True
            elif kind=='up' and key in pending:
                start,sent=pending[key]
                if not sent:
                    output.append(['hold' if now-start>=threshold else 'tap',key])
                del pending[key]
            elif kind=='cancel':
                pending.pop(key,None)
        return [output,sorted([[k]+v for k,v in pending.items()])]
    return [run(c) for c in cases]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('hold-threshold scenario 0', solve([[5, []]] * N), [[[], []]] * N)
check('hold-threshold scenario 1', solve([[5, [['down', 'A', 10], ['tick', '', 12]]]] * N), [[[], [['A', 10, False]]]] * N)
check('hold-threshold scenario 2', solve([[5, [['down', 'A', 10], ['up', 'A', 12]]]] * N), [[[['tap', 'A']], []]] * N)
check('hold-threshold scenario 3', solve([[5, [['down', 'A', 10], ['up', 'A', 15]]]] * N), [[[['hold', 'A']], []]] * N)
check('hold-threshold scenario 4', solve([[5, [['down', 'A', 10], ['tick', '', 15], ['tick', '', 16], ['up', 'A', 17]]]] * N), [[[['hold', 'A']], []]] * N)
check('hold-threshold scenario 5', solve([[5, [['down', 'A', 10], ['down', 'A', 13], ['tick', '', 15]]]] * N), [[[['hold', 'A']], [['A', 10, True]]]] * N)
check('hold-threshold scenario 6', solve([[5, [['down', 'A', 10], ['down', 'B', 11], ['up', 'A', 12]]]] * N), [[[['tap', 'A']], [['B', 11, False]]]] * N)
check('hold-threshold scenario 7', solve([[5, [['down', 'A', 10], ['down', 'B', 11], ['cancel', 'A', 12], ['tick', '', 16]]]] * N), [[[['hold', 'B']], [['B', 11, True]]]] * N)
check('hold-threshold scenario 8', solve([[5, [['up', 'A', 100]]]] * N), [[[], []]] * 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
hold-threshold scenario 0[[[], []]][[[], []]]Passed
hold-threshold scenario 1[[[], [['A', 10, False]]]][[[], [['A', 10, False]]]]Passed
hold-threshold scenario 2[[[['tap', 'A']], []]][[[['tap', 'A']], []]]Passed
hold-threshold scenario 3[[[['hold', 'A']], []]][[[['hold', 'A']], []]]Passed
hold-threshold scenario 4[[[['hold', 'A'], ['hold', 'A']], []]][[[['hold', 'A']], []]]Failed
hold-threshold scenario 5[[[['hold', 'A']], [['A', 10, True]]]][[[['hold', 'A']], [['A', 10, True]]]]Passed
hold-threshold scenario 6[[[['tap', 'A']], [['B', 11, False]]]][[[['tap', 'A']], [['B', 11, False]]]]Passed
hold-threshold scenario 7[[[['hold', 'B']], [['B', 11, True]]]][[[['hold', 'B']], [['B', 11, True]]]]Passed
hold-threshold scenario 8[[[], []]][[[], []]]Passed

SHA-256 / 4a0c83700b8f2d78a13d5737dccf8eba84b182759b91d376df99295769e23f30

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):
        threshold,events=c
        pending={}; output=[]
        for kind,key,now in events:
            if kind=='down' and key not in pending:
                pending[key]=[now,False]
            elif kind=='tick':
                for k in sorted(pending):
                    start,sent=pending[k]
                    if not sent and now-start>=threshold:
                        output.append(['hold',k])
                        pending[k][1]=True
            elif kind=='up' and key in pending:
                start,sent=pending[key]
                if not sent:
                    output.append(['hold' if now-start>=threshold else 'tap',key])
                del pending[key]
            elif kind=='cancel':
                pending.pop(key,None)
        return [output,sorted([[k]+v for k,v in pending.items()])]
    return [run(c) for c in cases]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('hold-threshold scenario 0', solve([[5, []]] * N), [[[], []]] * N)
check('hold-threshold scenario 1', solve([[5, [['down', 'A', 10], ['tick', '', 12]]]] * N), [[[], [['A', 10, False]]]] * N)
check('hold-threshold scenario 2', solve([[5, [['down', 'A', 10], ['up', 'A', 12]]]] * N), [[[['tap', 'A']], []]] * N)
check('hold-threshold scenario 3', solve([[5, [['down', 'A', 10], ['up', 'A', 15]]]] * N), [[[['hold', 'A']], []]] * N)
check('hold-threshold scenario 4', solve([[5, [['down', 'A', 10], ['tick', '', 15], ['tick', '', 16], ['up', 'A', 17]]]] * N), [[[['hold', 'A']], []]] * N)
check('hold-threshold scenario 5', solve([[5, [['down', 'A', 10], ['down', 'A', 13], ['tick', '', 15]]]] * N), [[[['hold', 'A']], [['A', 10, True]]]] * N)
check('hold-threshold scenario 6', solve([[5, [['down', 'A', 10], ['down', 'B', 11], ['up', 'A', 12]]]] * N), [[[['tap', 'A']], [['B', 11, False]]]] * N)
check('hold-threshold scenario 7', solve([[5, [['down', 'A', 10], ['down', 'B', 11], ['cancel', 'A', 12], ['tick', '', 16]]]] * N), [[[['hold', 'B']], [['B', 11, True]]]] * N)
check('hold-threshold scenario 8', solve([[5, [['up', 'A', 100]]]] * N), [[[], []]] * 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
hold-threshold scenario 0[[[], []]][[[], []]]Passed
hold-threshold scenario 1[[[], [['A', 10, False]]]][[[], [['A', 10, False]]]]Passed
hold-threshold scenario 2[[[['tap', 'A']], []]][[[['tap', 'A']], []]]Passed
hold-threshold scenario 3[[[['hold', 'A']], []]][[[['hold', 'A']], []]]Passed
hold-threshold scenario 4[[[['hold', 'A']], []]][[[['hold', 'A']], []]]Passed
hold-threshold scenario 5[[[['hold', 'A']], [['A', 10, True]]]][[[['hold', 'A']], [['A', 10, True]]]]Passed
hold-threshold scenario 6[[[['tap', 'A']], [['B', 11, False]]]][[[['tap', 'A']], [['B', 11, False]]]]Passed
hold-threshold scenario 7[[[['hold', 'B']], [['B', 11, True]]]][[[['hold', 'B']], [['B', 11, True]]]]Passed
hold-threshold scenario 8[[[], []]][[[], []]]Passed

SHA-256 / 3559c584388395b9ad7f21e5c69727bd641bfedebef31b5afa7ed39ccfc09a19

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

Case digest / 3ca590671a9b8d5dc7dc21fb2e5c0bbc9682493a31eb9486d2bacea5c7b09ee7