FAILURE MAP
← Case archive

FA-22796 / Autocomplete interactions / Open access

Undoable autocomplete acceptance: undo restores text but leaves accepted suggestion submitted · case 01

The undo-value completion transition produces an incorrect observable state.

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

ROOT CAUSE

At completion-undo/undo-value, the implementation evaluates `d['after']['value']` instead of preserving the specified completion transition.

VERIFIED REPAIR

Implement the undo-value contract using `d['before']['value']`.

Unsuccessful approach: The attempted repair uses `None`, which still violates a normal or boundary completion fixture.

Case contract

A completion acceptance is one undo transaction containing draft, committed identity and caret. Undo restores the pre-accept snapshot; redo restores the accepted snapshot. A subsequent edit discards redo, transient hover never enters the stack, and history snapshots own their data.

Why this case matters

Autocomplete couples draft text, suggestion identity and asynchronous presentation; this bounded lifecycle model isolates one observable interaction defect.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(event, d):
    if event == 'undo-text': return d['before']['text']
    if event == 'undo-value': return d['after']['value']
    if event == 'undo-caret': return d['before']['caret']
    if event == 'redo-text': return d['after']['text']
    if event == 'redo-value': return d['after']['value']
    if event == 'redo-caret': return d['after']['caret']
    if event == 'edit': return []
    if event == 'transaction': return d['undo']+[d['before']]
    if event == 'hover': return d['undo']
    if event == 'redo-divergence': return d['draft']==d['before']['text']
    return {'unchanged': True}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
normal = {'before':{'text':'pea','value':None,'caret':3},'after':{'text':'pear','value':'p1','caret':4},'undo':['u1'],'redo':['r1'],'hover':'p2','draft':'pear!','epoch':N}
boundary = {'before':{'text':'','value':'','caret':0},'after':{'text':'x','value':'x1','caret':1},'undo':[],'redo':[],'hover':None,'draft':'','epoch':N}
check('normal undo-text', solve('undo-text', normal), 'pea')
check('boundary undo-text', solve('undo-text', boundary), '')
check('normal undo-value', solve('undo-value', normal), None)
check('boundary undo-value', solve('undo-value', boundary), '')
check('normal undo-caret', solve('undo-caret', normal), 3)
check('boundary undo-caret', solve('undo-caret', boundary), 0)
check('normal redo-text', solve('redo-text', normal), 'pear')
check('boundary redo-text', solve('redo-text', boundary), 'x')
check('normal redo-value', solve('redo-value', normal), 'p1')
check('boundary redo-value', solve('redo-value', boundary), 'x1')
check('normal redo-caret', solve('redo-caret', normal), 4)
check('boundary redo-caret', solve('redo-caret', boundary), 1)
check('normal edit', solve('edit', normal), [])
check('boundary edit', solve('edit', boundary), [])
check('normal transaction', solve('transaction', normal), ['u1',{'text':'pea','value':None,'caret':3}])
check('boundary transaction', solve('transaction', boundary), [{'text':'','value':'','caret':0}])
check('normal hover', solve('hover', normal), ['u1'])
check('boundary hover', solve('hover', boundary), [])
check('normal redo-divergence', solve('redo-divergence', normal), False)
check('boundary redo-divergence', solve('redo-divergence', boundary), True)
variant = dict(normal, **{'after':{'text':'p'*N,'value':'p','caret':N}})
check('variant query or selection payload', solve('redo-text', variant), 'p'*N)
check('unknown completion event', solve('unrecognized', normal), {'unchanged': True})
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
normal undo-textpeapeaPassed
boundary undo-textPassed
normal undo-valuep1NoneFailed
boundary undo-valuex1Failed
normal undo-caret33Passed
boundary undo-caret00Passed
normal redo-textpearpearPassed
boundary redo-textxxPassed
normal redo-valuep1p1Passed
boundary redo-valuex1x1Passed
normal redo-caret44Passed
boundary redo-caret11Passed
normal edit[][]Passed
boundary edit[][]Passed
normal transaction['u1', {'caret': 3, 'text': 'pea', 'value': None}]['u1', {'caret': 3, 'text': 'pea', 'value': None}]Passed
boundary transaction[{'caret': 0, 'text': '', 'value': ''}][{'caret': 0, 'text': '', 'value': ''}]Passed
normal hover['u1']['u1']Passed
boundary hover[][]Passed
normal redo-divergenceFalseFalsePassed
boundary redo-divergenceTrueTruePassed
variant query or selection payloadppPassed
unknown completion event{'unchanged': True}{'unchanged': True}Passed

SHA-256 / c97456fe5fa852c98e4d098864ff68f69c21574c4aa47a5c2cd2f35c5d6e0f17

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(event, d):
    if event == 'undo-text': return d['before']['text']
    if event == 'undo-value': return None
    if event == 'undo-caret': return d['before']['caret']
    if event == 'redo-text': return d['after']['text']
    if event == 'redo-value': return d['after']['value']
    if event == 'redo-caret': return d['after']['caret']
    if event == 'edit': return []
    if event == 'transaction': return d['undo']+[d['before']]
    if event == 'hover': return d['undo']
    if event == 'redo-divergence': return d['draft']==d['before']['text']
    return {'unchanged': True}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
normal = {'before':{'text':'pea','value':None,'caret':3},'after':{'text':'pear','value':'p1','caret':4},'undo':['u1'],'redo':['r1'],'hover':'p2','draft':'pear!','epoch':N}
boundary = {'before':{'text':'','value':'','caret':0},'after':{'text':'x','value':'x1','caret':1},'undo':[],'redo':[],'hover':None,'draft':'','epoch':N}
check('normal undo-text', solve('undo-text', normal), 'pea')
check('boundary undo-text', solve('undo-text', boundary), '')
check('normal undo-value', solve('undo-value', normal), None)
check('boundary undo-value', solve('undo-value', boundary), '')
check('normal undo-caret', solve('undo-caret', normal), 3)
check('boundary undo-caret', solve('undo-caret', boundary), 0)
check('normal redo-text', solve('redo-text', normal), 'pear')
check('boundary redo-text', solve('redo-text', boundary), 'x')
check('normal redo-value', solve('redo-value', normal), 'p1')
check('boundary redo-value', solve('redo-value', boundary), 'x1')
check('normal redo-caret', solve('redo-caret', normal), 4)
check('boundary redo-caret', solve('redo-caret', boundary), 1)
check('normal edit', solve('edit', normal), [])
check('boundary edit', solve('edit', boundary), [])
check('normal transaction', solve('transaction', normal), ['u1',{'text':'pea','value':None,'caret':3}])
check('boundary transaction', solve('transaction', boundary), [{'text':'','value':'','caret':0}])
check('normal hover', solve('hover', normal), ['u1'])
check('boundary hover', solve('hover', boundary), [])
check('normal redo-divergence', solve('redo-divergence', normal), False)
check('boundary redo-divergence', solve('redo-divergence', boundary), True)
variant = dict(normal, **{'after':{'text':'p'*N,'value':'p','caret':N}})
check('variant query or selection payload', solve('redo-text', variant), 'p'*N)
check('unknown completion event', solve('unrecognized', normal), {'unchanged': True})
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
normal undo-textpeapeaPassed
boundary undo-textPassed
normal undo-valueNoneNonePassed
boundary undo-valueNoneFailed
normal undo-caret33Passed
boundary undo-caret00Passed
normal redo-textpearpearPassed
boundary redo-textxxPassed
normal redo-valuep1p1Passed
boundary redo-valuex1x1Passed
normal redo-caret44Passed
boundary redo-caret11Passed
normal edit[][]Passed
boundary edit[][]Passed
normal transaction['u1', {'caret': 3, 'text': 'pea', 'value': None}]['u1', {'caret': 3, 'text': 'pea', 'value': None}]Passed
boundary transaction[{'caret': 0, 'text': '', 'value': ''}][{'caret': 0, 'text': '', 'value': ''}]Passed
normal hover['u1']['u1']Passed
boundary hover[][]Passed
normal redo-divergenceFalseFalsePassed
boundary redo-divergenceTrueTruePassed
variant query or selection payloadppPassed
unknown completion event{'unchanged': True}{'unchanged': True}Passed

SHA-256 / 58458a90e8e1a01e1c6736ca808681211fd4242d78ce693129ff547261179307

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(event, d):
    if event == 'undo-text': return d['before']['text']
    if event == 'undo-value': return d['before']['value']
    if event == 'undo-caret': return d['before']['caret']
    if event == 'redo-text': return d['after']['text']
    if event == 'redo-value': return d['after']['value']
    if event == 'redo-caret': return d['after']['caret']
    if event == 'edit': return []
    if event == 'transaction': return d['undo']+[d['before']]
    if event == 'hover': return d['undo']
    if event == 'redo-divergence': return d['draft']==d['before']['text']
    return {'unchanged': True}
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
normal = {'before':{'text':'pea','value':None,'caret':3},'after':{'text':'pear','value':'p1','caret':4},'undo':['u1'],'redo':['r1'],'hover':'p2','draft':'pear!','epoch':N}
boundary = {'before':{'text':'','value':'','caret':0},'after':{'text':'x','value':'x1','caret':1},'undo':[],'redo':[],'hover':None,'draft':'','epoch':N}
check('normal undo-text', solve('undo-text', normal), 'pea')
check('boundary undo-text', solve('undo-text', boundary), '')
check('normal undo-value', solve('undo-value', normal), None)
check('boundary undo-value', solve('undo-value', boundary), '')
check('normal undo-caret', solve('undo-caret', normal), 3)
check('boundary undo-caret', solve('undo-caret', boundary), 0)
check('normal redo-text', solve('redo-text', normal), 'pear')
check('boundary redo-text', solve('redo-text', boundary), 'x')
check('normal redo-value', solve('redo-value', normal), 'p1')
check('boundary redo-value', solve('redo-value', boundary), 'x1')
check('normal redo-caret', solve('redo-caret', normal), 4)
check('boundary redo-caret', solve('redo-caret', boundary), 1)
check('normal edit', solve('edit', normal), [])
check('boundary edit', solve('edit', boundary), [])
check('normal transaction', solve('transaction', normal), ['u1',{'text':'pea','value':None,'caret':3}])
check('boundary transaction', solve('transaction', boundary), [{'text':'','value':'','caret':0}])
check('normal hover', solve('hover', normal), ['u1'])
check('boundary hover', solve('hover', boundary), [])
check('normal redo-divergence', solve('redo-divergence', normal), False)
check('boundary redo-divergence', solve('redo-divergence', boundary), True)
variant = dict(normal, **{'after':{'text':'p'*N,'value':'p','caret':N}})
check('variant query or selection payload', solve('redo-text', variant), 'p'*N)
check('unknown completion event', solve('unrecognized', normal), {'unchanged': True})
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
normal undo-textpeapeaPassed
boundary undo-textPassed
normal undo-valueNoneNonePassed
boundary undo-valuePassed
normal undo-caret33Passed
boundary undo-caret00Passed
normal redo-textpearpearPassed
boundary redo-textxxPassed
normal redo-valuep1p1Passed
boundary redo-valuex1x1Passed
normal redo-caret44Passed
boundary redo-caret11Passed
normal edit[][]Passed
boundary edit[][]Passed
normal transaction['u1', {'caret': 3, 'text': 'pea', 'value': None}]['u1', {'caret': 3, 'text': 'pea', 'value': None}]Passed
boundary transaction[{'caret': 0, 'text': '', 'value': ''}][{'caret': 0, 'text': '', 'value': ''}]Passed
normal hover['u1']['u1']Passed
boundary hover[][]Passed
normal redo-divergenceFalseFalsePassed
boundary redo-divergenceTrueTruePassed
variant query or selection payloadppPassed
unknown completion event{'unchanged': True}{'unchanged': True}Passed

SHA-256 / 377cb2d0eaa908bb8212d1f3b120d412509cbebb386995d3875d72df722f8c26

Verification & scope

Stipulated deterministic widget transition model; inputs are validated snapshots. It does not model browser event dispatch, network transport, or linguistic standards. 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:40:41.689114+00:00.

Case digest / 9596f6651ce34983fe052fd88416ad5ed8a5e282e0afbb004c47190d9347da2a