FAILURE MAP
← Case archive

FA-31386 / Keyboard interactions / Open access

Sticky modifier latch lifecycle: Input ownership loss clears only one source of effective modifiers · case 01

The event trace violates the blur reset rule and produces incorrect keyboard state or command output.

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

ROOT CAUSE

Input ownership loss clears only one source of effective modifiers.

VERIFIED REPAIR

Use the contract transition `latches.clear(); physical.clear() return` at the blur reset fault site; preserve the other state transitions.

Unsuccessful approach: The partial repair changes this transition to physical.clear() return, which still violates the model contract on the explicit regression traces.

Case contract

Events [kind,key]. Tap of modifier cycles absent -> once -> locked -> absent. Modifier down/up sets physical state independently. Character emits [key,sorted effective modifiers] then consumes all once latches but preserves locked and physical modifiers. Cancel clears latches only; blur clears both. Return emitted events, latches and physical state. 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):
        latches={}; physical=set(); out=[]
        for kind,key in c:
            if kind=='tap':
                state=latches.get(key,0)
                next_state=(state+1)%3
                if next_state: latches[key]=next_state
                else: latches.pop(key,None)
            elif kind=='down': physical.add(key)
            elif kind=='up': physical.discard(key)
            elif kind=='char':
                effective=physical | set(latches)
                out.append([key,sorted(effective)])
                latches={k:v for k,v in latches.items() if v==2}
            elif kind=='cancel': latches.clear()
            elif kind=='blur':
                latches.clear()
        return [out,sorted([[k,v] for k,v in latches.items()]),sorted(physical)]
    return [run(c) for c in cases]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('sticky-modifiers scenario 0', solve([[]] * N), [[[], [], []]] * N)
check('sticky-modifiers scenario 1', solve([[['tap', 'Shift'], ['char', 'A'], ['char', 'B']]] * N), [[[['A', ['Shift']], ['B', []]], [], []]] * N)
check('sticky-modifiers scenario 2', solve([[['tap', 'Shift'], ['tap', 'Shift'], ['char', 'A'], ['char', 'B']]] * N), [[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]] * N)
check('sticky-modifiers scenario 3', solve([[['tap', 'Shift'], ['tap', 'Shift'], ['tap', 'Alt'], ['tap', 'Shift'], ['char', 'A']]] * N), [[[['A', ['Alt']]], [], []]] * N)
check('sticky-modifiers scenario 4', solve([[['down', 'Control'], ['down', 'Alt'], ['down', 'Alt'], ['char', 'A']]] * N), [[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]] * N)
check('sticky-modifiers scenario 5', solve([[['down', 'Control'], ['down', 'Alt'], ['up', 'Alt'], ['char', 'A']]] * N), [[[['A', ['Control']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 6', solve([[['down', 'Control'], ['tap', 'Shift'], ['char', 'A']]] * N), [[[['A', ['Control', 'Shift']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 7', solve([[['down', 'Control'], ['tap', 'Shift'], ['cancel', ''], ['char', 'A']]] * N), [[[['A', ['Control']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 8', solve([[['down', 'Control'], ['tap', 'Shift'], ['blur', '']]] * 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
sticky-modifiers scenario 0[[[], [], []]][[[], [], []]]Passed
sticky-modifiers scenario 1[[[['A', ['Shift']], ['B', []]], [], []]][[[['A', ['Shift']], ['B', []]], [], []]]Passed
sticky-modifiers scenario 2[[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]][[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]]Passed
sticky-modifiers scenario 3[[[['A', ['Alt']]], [], []]][[[['A', ['Alt']]], [], []]]Passed
sticky-modifiers scenario 4[[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]][[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]]Passed
sticky-modifiers scenario 5[[[['A', ['Control']]], [], ['Control']]][[[['A', ['Control']]], [], ['Control']]]Passed
sticky-modifiers scenario 6[[[['A', ['Control', 'Shift']]], [], ['Control']]][[[['A', ['Control', 'Shift']]], [], ['Control']]]Passed
sticky-modifiers scenario 7[[[['A', ['Control']]], [], ['Control']]][[[['A', ['Control']]], [], ['Control']]]Passed
sticky-modifiers scenario 8[[[], [], ['Control']]][[[], [], []]]Failed

SHA-256 / 554ef826a65ae48e7e2741d9cdf253bfa63ab32a08da94900cf615b6d808ff4d

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):
        latches={}; physical=set(); out=[]
        for kind,key in c:
            if kind=='tap':
                state=latches.get(key,0)
                next_state=(state+1)%3
                if next_state: latches[key]=next_state
                else: latches.pop(key,None)
            elif kind=='down': physical.add(key)
            elif kind=='up': physical.discard(key)
            elif kind=='char':
                effective=physical | set(latches)
                out.append([key,sorted(effective)])
                latches={k:v for k,v in latches.items() if v==2}
            elif kind=='cancel': latches.clear()
            elif kind=='blur':
                physical.clear()
        return [out,sorted([[k,v] for k,v in latches.items()]),sorted(physical)]
    return [run(c) for c in cases]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('sticky-modifiers scenario 0', solve([[]] * N), [[[], [], []]] * N)
check('sticky-modifiers scenario 1', solve([[['tap', 'Shift'], ['char', 'A'], ['char', 'B']]] * N), [[[['A', ['Shift']], ['B', []]], [], []]] * N)
check('sticky-modifiers scenario 2', solve([[['tap', 'Shift'], ['tap', 'Shift'], ['char', 'A'], ['char', 'B']]] * N), [[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]] * N)
check('sticky-modifiers scenario 3', solve([[['tap', 'Shift'], ['tap', 'Shift'], ['tap', 'Alt'], ['tap', 'Shift'], ['char', 'A']]] * N), [[[['A', ['Alt']]], [], []]] * N)
check('sticky-modifiers scenario 4', solve([[['down', 'Control'], ['down', 'Alt'], ['down', 'Alt'], ['char', 'A']]] * N), [[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]] * N)
check('sticky-modifiers scenario 5', solve([[['down', 'Control'], ['down', 'Alt'], ['up', 'Alt'], ['char', 'A']]] * N), [[[['A', ['Control']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 6', solve([[['down', 'Control'], ['tap', 'Shift'], ['char', 'A']]] * N), [[[['A', ['Control', 'Shift']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 7', solve([[['down', 'Control'], ['tap', 'Shift'], ['cancel', ''], ['char', 'A']]] * N), [[[['A', ['Control']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 8', solve([[['down', 'Control'], ['tap', 'Shift'], ['blur', '']]] * 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
sticky-modifiers scenario 0[[[], [], []]][[[], [], []]]Passed
sticky-modifiers scenario 1[[[['A', ['Shift']], ['B', []]], [], []]][[[['A', ['Shift']], ['B', []]], [], []]]Passed
sticky-modifiers scenario 2[[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]][[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]]Passed
sticky-modifiers scenario 3[[[['A', ['Alt']]], [], []]][[[['A', ['Alt']]], [], []]]Passed
sticky-modifiers scenario 4[[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]][[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]]Passed
sticky-modifiers scenario 5[[[['A', ['Control']]], [], ['Control']]][[[['A', ['Control']]], [], ['Control']]]Passed
sticky-modifiers scenario 6[[[['A', ['Control', 'Shift']]], [], ['Control']]][[[['A', ['Control', 'Shift']]], [], ['Control']]]Passed
sticky-modifiers scenario 7[[[['A', ['Control']]], [], ['Control']]][[[['A', ['Control']]], [], ['Control']]]Passed
sticky-modifiers scenario 8[[[], [['Shift', 1]], []]][[[], [], []]]Failed

SHA-256 / fdc6ad99eb5108f5046ed7a4ce3371438f6ce281f2a1336b756ab02ea4a2af36

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):
        latches={}; physical=set(); out=[]
        for kind,key in c:
            if kind=='tap':
                state=latches.get(key,0)
                next_state=(state+1)%3
                if next_state: latches[key]=next_state
                else: latches.pop(key,None)
            elif kind=='down': physical.add(key)
            elif kind=='up': physical.discard(key)
            elif kind=='char':
                effective=physical | set(latches)
                out.append([key,sorted(effective)])
                latches={k:v for k,v in latches.items() if v==2}
            elif kind=='cancel': latches.clear()
            elif kind=='blur':
                latches.clear(); physical.clear()
        return [out,sorted([[k,v] for k,v in latches.items()]),sorted(physical)]
    return [run(c) for c in cases]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('sticky-modifiers scenario 0', solve([[]] * N), [[[], [], []]] * N)
check('sticky-modifiers scenario 1', solve([[['tap', 'Shift'], ['char', 'A'], ['char', 'B']]] * N), [[[['A', ['Shift']], ['B', []]], [], []]] * N)
check('sticky-modifiers scenario 2', solve([[['tap', 'Shift'], ['tap', 'Shift'], ['char', 'A'], ['char', 'B']]] * N), [[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]] * N)
check('sticky-modifiers scenario 3', solve([[['tap', 'Shift'], ['tap', 'Shift'], ['tap', 'Alt'], ['tap', 'Shift'], ['char', 'A']]] * N), [[[['A', ['Alt']]], [], []]] * N)
check('sticky-modifiers scenario 4', solve([[['down', 'Control'], ['down', 'Alt'], ['down', 'Alt'], ['char', 'A']]] * N), [[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]] * N)
check('sticky-modifiers scenario 5', solve([[['down', 'Control'], ['down', 'Alt'], ['up', 'Alt'], ['char', 'A']]] * N), [[[['A', ['Control']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 6', solve([[['down', 'Control'], ['tap', 'Shift'], ['char', 'A']]] * N), [[[['A', ['Control', 'Shift']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 7', solve([[['down', 'Control'], ['tap', 'Shift'], ['cancel', ''], ['char', 'A']]] * N), [[[['A', ['Control']]], [], ['Control']]] * N)
check('sticky-modifiers scenario 8', solve([[['down', 'Control'], ['tap', 'Shift'], ['blur', '']]] * 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
sticky-modifiers scenario 0[[[], [], []]][[[], [], []]]Passed
sticky-modifiers scenario 1[[[['A', ['Shift']], ['B', []]], [], []]][[[['A', ['Shift']], ['B', []]], [], []]]Passed
sticky-modifiers scenario 2[[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]][[[['A', ['Shift']], ['B', ['Shift']]], [['Shift', 2]], []]]Passed
sticky-modifiers scenario 3[[[['A', ['Alt']]], [], []]][[[['A', ['Alt']]], [], []]]Passed
sticky-modifiers scenario 4[[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]][[[['A', ['Alt', 'Control']]], [], ['Alt', 'Control']]]Passed
sticky-modifiers scenario 5[[[['A', ['Control']]], [], ['Control']]][[[['A', ['Control']]], [], ['Control']]]Passed
sticky-modifiers scenario 6[[[['A', ['Control', 'Shift']]], [], ['Control']]][[[['A', ['Control', 'Shift']]], [], ['Control']]]Passed
sticky-modifiers scenario 7[[[['A', ['Control']]], [], ['Control']]][[[['A', ['Control']]], [], ['Control']]]Passed
sticky-modifiers scenario 8[[[], [], []]][[[], [], []]]Passed

SHA-256 / 44669353d0bf76253c49ddc9a65ea6e0f914c3a6987af48e8f08857354bc93bb

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

Case digest / 3ab3377eca33adbb54dc124a050b34fc5a053ff9180f6ea19d1c7548a4a32f68