FA-8906 / Selection controls / Open access
Rating preview and committed value: Readonly ratings still change on interaction · case 01
Readonly ratings still change on interaction.
ROOT CAUSE
The readonly operation uses `if readonly: return (value, None)` where the contract requires `if readonly: return (committed, None)`.
VERIFIED REPAIR
Implement the readonly operation as `if readonly: return (committed, None)`.
Unsuccessful approach: Keeping a preview presents an editable hover state.
Case contract
Hover changes preview only; leaving or Escape discards preview; activation commits and clears preview; repeated activation clears only when allowed; readonly ignores interaction and has no preview.
Why this case matters
A deterministic model of rating preview and committed value; 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(committed, preview, event, value, readonly, allow_clear):
if readonly: return (value, None)
if event == 'hover': preview = value
elif event == 'leave': preview = None
elif event == 'activate':
committed = None if allow_clear and value == committed else value
preview = None
elif event == 'escape': preview = None
return (committed, preview)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('readonly', solve(2, 4, 'activate', 5, True, True), (2, None))
check('hover', solve(2, None, 'hover', 4, False, True), (2, 4))
check('leave', solve(2, 4, 'leave', None, False, True), (2, None))
check('locked clear', solve(2, 4, 'activate', 2, False, False), (2, None))
check('change', solve(2, 4, 'activate', 5, False, True), (5, None))
check('clear', solve(2, 4, 'activate', 2, False, True), (None, None))
check('escape', solve(2, 4, 'escape', None, False, True), (2, None))
check('parameterized rating preview', solve(0,None,'hover',N,False,False), (0,N))
for repetition in range(N):
check('repeat escape', solve(2, 4, 'escape', None, False, True), (2, 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 |
|---|---|---|---|
| readonly | [5, None] | [2, None] | Failed |
| hover | [2, 4] | [2, 4] | Passed |
| leave | [2, None] | [2, None] | Passed |
| locked clear | [2, None] | [2, None] | Passed |
| change | [5, None] | [5, None] | Passed |
| clear | [None, None] | [None, None] | Passed |
| escape | [2, None] | [2, None] | Passed |
| parameterized rating preview | [0, 1] | [0, 1] | Passed |
| repeat escape | [2, None] | [2, None] | Passed |
SHA-256 / 76a83dd4e778685eeda25ef8e7771d120bd39b304ddcb3e468fa39ad48fcc01d
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(committed, preview, event, value, readonly, allow_clear):
if readonly: return (committed, preview)
if event == 'hover': preview = value
elif event == 'leave': preview = None
elif event == 'activate':
committed = None if allow_clear and value == committed else value
preview = None
elif event == 'escape': preview = None
return (committed, preview)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('readonly', solve(2, 4, 'activate', 5, True, True), (2, None))
check('hover', solve(2, None, 'hover', 4, False, True), (2, 4))
check('leave', solve(2, 4, 'leave', None, False, True), (2, None))
check('locked clear', solve(2, 4, 'activate', 2, False, False), (2, None))
check('change', solve(2, 4, 'activate', 5, False, True), (5, None))
check('clear', solve(2, 4, 'activate', 2, False, True), (None, None))
check('escape', solve(2, 4, 'escape', None, False, True), (2, None))
check('parameterized rating preview', solve(0,None,'hover',N,False,False), (0,N))
for repetition in range(N):
check('repeat escape', solve(2, 4, 'escape', None, False, True), (2, 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 |
|---|---|---|---|
| readonly | [2, 4] | [2, None] | Failed |
| hover | [2, 4] | [2, 4] | Passed |
| leave | [2, None] | [2, None] | Passed |
| locked clear | [2, None] | [2, None] | Passed |
| change | [5, None] | [5, None] | Passed |
| clear | [None, None] | [None, None] | Passed |
| escape | [2, None] | [2, None] | Passed |
| parameterized rating preview | [0, 1] | [0, 1] | Passed |
| repeat escape | [2, None] | [2, None] | Passed |
SHA-256 / d23676ea06cea624ba77c1ca1d48cb9a9acf913efbfabec564c8feb7726191a5
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(committed, preview, event, value, readonly, allow_clear):
if readonly: return (committed, None)
if event == 'hover': preview = value
elif event == 'leave': preview = None
elif event == 'activate':
committed = None if allow_clear and value == committed else value
preview = None
elif event == 'escape': preview = None
return (committed, preview)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('readonly', solve(2, 4, 'activate', 5, True, True), (2, None))
check('hover', solve(2, None, 'hover', 4, False, True), (2, 4))
check('leave', solve(2, 4, 'leave', None, False, True), (2, None))
check('locked clear', solve(2, 4, 'activate', 2, False, False), (2, None))
check('change', solve(2, 4, 'activate', 5, False, True), (5, None))
check('clear', solve(2, 4, 'activate', 2, False, True), (None, None))
check('escape', solve(2, 4, 'escape', None, False, True), (2, None))
check('parameterized rating preview', solve(0,None,'hover',N,False,False), (0,N))
for repetition in range(N):
check('repeat escape', solve(2, 4, 'escape', None, False, True), (2, 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 |
|---|---|---|---|
| readonly | [2, None] | [2, None] | Passed |
| hover | [2, 4] | [2, 4] | Passed |
| leave | [2, None] | [2, None] | Passed |
| locked clear | [2, None] | [2, None] | Passed |
| change | [5, None] | [5, None] | Passed |
| clear | [None, None] | [None, None] | Passed |
| escape | [2, None] | [2, None] | Passed |
| parameterized rating preview | [0, 1] | [0, 1] | Passed |
| repeat escape | [2, None] | [2, None] | Passed |
SHA-256 / 9f20e0513182a40341ff5a62f79fe9d3059692f49d84ac783ee44ceec4099d15
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:25.458601+00:00.
Case digest / 1ac5a5b1975b4b539c62c2201f5c56a098e46474a787caf4ebd4dc3f767eeec8