FAILURE MAP
← Case archive

FA-8601 / Tab interfaces / Open access

Tab selection and activation mode: Removing a selected tab leaves no panel active · case 01

Removing a selected tab leaves no panel active.

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

ROOT CAUSE

The removed selection operation uses `if selected not in available: selected = None` where the contract requires `if selected not in available: selected = available[0]`.

VERIFIED REPAIR

Implement the removed selection operation as `if selected not in available: selected = available[0]`.

Unsuccessful approach: Selecting the last panel violates fallback order.

Case contract

Focus traverses enabled tabs; automatic mode activates on focus, manual mode requires Enter or Space; removed focus or selection falls back to first enabled tab.

Why this case matters

A deterministic model of tab selection and activation mode; this isolates one interface invariant without requiring a browser.

1 / The failure

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

N = 1
observations = []
def solve(tabs, focused, selected, key, automatic, disabled):
    available = [t for t in tabs if t not in disabled]
    if not available: return (None, None)
    if focused not in available: focused = available[0]
    if key == 'Next': focused = available[(available.index(focused)+1) % len(available)]
    if key == 'Previous': focused = available[(available.index(focused)-1) % len(available)]
    if automatic or key in ('Enter','Space'): selected = focused
    if selected not in available: selected = None
    return (focused, selected)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('disabled', solve(['a','b','c'], 'a', 'a', 'Next', True, ['b']), ('c', 'c'))
check('removed focus', solve(['a','b','c'], 'gone', 'a', 'none', False, []), ('a', 'a'))
check('next', solve(['a','b','c'], 'a', 'a', 'Next', True, []), ('b', 'b'))
check('manual', solve(['a','b','c'], 'a', 'a', 'Next', False, []), ('b', 'a'))
check('space', solve(['a','b'], 'b', 'a', 'Space', False, []), ('b', 'b'))
check('removed selection', solve(['a','b'], 'b', 'gone', 'none', False, []), ('b', 'a'))
check('empty', solve([], None, None, 'Next', True, []), (None, None))
check('parameterized tab identities', solve(list(range(N+2)), N, N, 'Next', True, []), (N+1,N+1))
for repetition in range(N):
    check('repeat empty', solve([], None, None, 'Next', True, []), (None, None))
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
disabled['c', 'c']['c', 'c']Passed
removed focus['a', 'a']['a', 'a']Passed
next['b', 'b']['b', 'b']Passed
manual['b', 'a']['b', 'a']Passed
space['b', 'b']['b', 'b']Passed
removed selection['b', None]['b', 'a']Failed
empty[None, None][None, None]Passed
parameterized tab identities[2, 2][2, 2]Passed
repeat empty[None, None][None, None]Passed

SHA-256 / eb7de76f113d1340ab02b3eaaedb3916480e9f998cc72d52be684d456e68db25

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(tabs, focused, selected, key, automatic, disabled):
    available = [t for t in tabs if t not in disabled]
    if not available: return (None, None)
    if focused not in available: focused = available[0]
    if key == 'Next': focused = available[(available.index(focused)+1) % len(available)]
    if key == 'Previous': focused = available[(available.index(focused)-1) % len(available)]
    if automatic or key in ('Enter','Space'): selected = focused
    if selected not in available: selected = available[-1]
    return (focused, selected)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('disabled', solve(['a','b','c'], 'a', 'a', 'Next', True, ['b']), ('c', 'c'))
check('removed focus', solve(['a','b','c'], 'gone', 'a', 'none', False, []), ('a', 'a'))
check('next', solve(['a','b','c'], 'a', 'a', 'Next', True, []), ('b', 'b'))
check('manual', solve(['a','b','c'], 'a', 'a', 'Next', False, []), ('b', 'a'))
check('space', solve(['a','b'], 'b', 'a', 'Space', False, []), ('b', 'b'))
check('removed selection', solve(['a','b'], 'b', 'gone', 'none', False, []), ('b', 'a'))
check('empty', solve([], None, None, 'Next', True, []), (None, None))
check('parameterized tab identities', solve(list(range(N+2)), N, N, 'Next', True, []), (N+1,N+1))
for repetition in range(N):
    check('repeat empty', solve([], None, None, 'Next', True, []), (None, None))
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
disabled['c', 'c']['c', 'c']Passed
removed focus['a', 'a']['a', 'a']Passed
next['b', 'b']['b', 'b']Passed
manual['b', 'a']['b', 'a']Passed
space['b', 'b']['b', 'b']Passed
removed selection['b', 'b']['b', 'a']Failed
empty[None, None][None, None]Passed
parameterized tab identities[2, 2][2, 2]Passed
repeat empty[None, None][None, None]Passed

SHA-256 / 92f9128bc29b065026452c2c4814e3c4fe16d3fb9d1c57581eb7329c22e53a93

3 / The verified repair

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

N = 1
observations = []
def solve(tabs, focused, selected, key, automatic, disabled):
    available = [t for t in tabs if t not in disabled]
    if not available: return (None, None)
    if focused not in available: focused = available[0]
    if key == 'Next': focused = available[(available.index(focused)+1) % len(available)]
    if key == 'Previous': focused = available[(available.index(focused)-1) % len(available)]
    if automatic or key in ('Enter','Space'): selected = focused
    if selected not in available: selected = available[0]
    return (focused, selected)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('disabled', solve(['a','b','c'], 'a', 'a', 'Next', True, ['b']), ('c', 'c'))
check('removed focus', solve(['a','b','c'], 'gone', 'a', 'none', False, []), ('a', 'a'))
check('next', solve(['a','b','c'], 'a', 'a', 'Next', True, []), ('b', 'b'))
check('manual', solve(['a','b','c'], 'a', 'a', 'Next', False, []), ('b', 'a'))
check('space', solve(['a','b'], 'b', 'a', 'Space', False, []), ('b', 'b'))
check('removed selection', solve(['a','b'], 'b', 'gone', 'none', False, []), ('b', 'a'))
check('empty', solve([], None, None, 'Next', True, []), (None, None))
check('parameterized tab identities', solve(list(range(N+2)), N, N, 'Next', True, []), (N+1,N+1))
for repetition in range(N):
    check('repeat empty', solve([], None, None, 'Next', True, []), (None, None))
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
disabled['c', 'c']['c', 'c']Passed
removed focus['a', 'a']['a', 'a']Passed
next['b', 'b']['b', 'b']Passed
manual['b', 'a']['b', 'a']Passed
space['b', 'b']['b', 'b']Passed
removed selection['b', 'a']['b', 'a']Passed
empty[None, None][None, None]Passed
parameterized tab identities[2, 2][2, 2]Passed
repeat empty[None, None][None, None]Passed

SHA-256 / 24b067ee7c267640bce77689f59f81c95f3b775417fb66162bf9320502e4db9d

Verification & scope

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

Case digest / 394130bc042920066a307a2278bc1105cc59d44b93cf803d5e16a5000f4b6bd1