FA-8581 / Tab interfaces / Open access
Tab selection and activation mode: Disabled tabs remain keyboard navigable · case 01
Disabled tabs remain keyboard navigable.
ROOT CAUSE
The disabled tab operation uses `if True` where the contract requires `if t not in disabled`.
VERIFIED REPAIR
Implement the disabled tab operation as `if t not in disabled`.
Unsuccessful approach: Keeping only disabled tabs excludes all usable panels.
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 True]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| disabled | ['b', 'b'] | ['c', 'c'] | Failed |
| 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 / 84ae8837163379dd76fd17970147f551ec972bfbbc0502dbb95b65964bd0f9f5
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 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| disabled | ['b', 'b'] | ['c', 'c'] | Failed |
| removed focus | [None, None] | ['a', 'a'] | Failed |
| next | [None, None] | ['b', 'b'] | Failed |
| manual | [None, None] | ['b', 'a'] | Failed |
| space | [None, None] | ['b', 'b'] | Failed |
| removed selection | [None, None] | ['b', 'a'] | Failed |
| empty | [None, None] | [None, None] | Passed |
| parameterized tab identities | [None, None] | [2, 2] | Failed |
| repeat empty | [None, None] | [None, None] | Passed |
SHA-256 / 9489251ebe81c93f3025476da558c692092ae8623b3302cbbd039b855cd65187
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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.636275+00:00.
Case digest / a246841529d1a6c838ff7c2cd5f2f4ebb03ac36bd5d353856d791ccc59ed9d92