{"abstract":"Nonremovable sort columns can be cleared.","category":"Table interactions","checks":9,"contract":"New sort is ascending; activation cycles ascending to descending to removed when allowed; single mode replaces all sorts; multi mode preserves priorities when changing direction.","evaluation_group":"xi-sort-indicator","failed_approach":"Always cycling back to ascending ignores removable mode.","family":"xi-sort-indicator-removable","id":"FA-8941","implementations":{"attempt":{"sha256":"f56c4936aad414229dc423b61104a1bb46235d6158b8331cf61521e7ce95b6f9","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(sorting, column, multi, removable):\n    sorting = list(sorting)\n    index = next((i for i, (c, d) in enumerate(sorting) if c == column), None)\n    if index is None: entry = (column, 'asc')\n    elif sorting[index][1] == 'asc': entry = (column, 'desc')\n    else: entry = (column, 'asc')\n    if not multi: return [entry] if entry else []\n    if index is None: return sorting + [entry]\n    if entry is None: return sorting[:index] + sorting[index+1:]\n    sorting[index] = entry\n    return sorting\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\ncheck('new', solve([], 'a', False, True), [('a', 'asc')])\ncheck('reverse', solve([('a','asc')], 'a', False, True), [('a', 'desc')])\ncheck('locked', solve([('a','desc')], 'a', False, False), [('a', 'asc')])\ncheck('remove', solve([('a','desc')], 'a', False, True), [])\ncheck('single', solve([('a','asc')], 'b', False, True), [('b', 'asc')])\ncheck('priority', solve([('a','asc'),('b','asc')], 'a', True, True), [('a', 'desc'), ('b', 'asc')])\ncheck('append', solve([('a','asc')], 'b', True, True), [('a', 'asc'), ('b', 'asc')])\ncheck('parameterized sort identity', solve([],str(N),False,True), [(str(N),'asc')])\nfor repetition in range(N):\n    check('repeat append', solve([('a','asc')], 'b', True, True), [('a', 'asc'), ('b', 'asc')])\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"cbb1ab7a8e0b97a2ae6ef736c3cd78068bf88b5d5bdf544fb46d170f4a079528","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(sorting, column, multi, removable):\n    sorting = list(sorting)\n    index = next((i for i, (c, d) in enumerate(sorting) if c == column), None)\n    if index is None: entry = (column, 'asc')\n    elif sorting[index][1] == 'asc': entry = (column, 'desc')\n    else: entry = None\n    if not multi: return [entry] if entry else []\n    if index is None: return sorting + [entry]\n    if entry is None: return sorting[:index] + sorting[index+1:]\n    sorting[index] = entry\n    return sorting\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\ncheck('new', solve([], 'a', False, True), [('a', 'asc')])\ncheck('reverse', solve([('a','asc')], 'a', False, True), [('a', 'desc')])\ncheck('locked', solve([('a','desc')], 'a', False, False), [('a', 'asc')])\ncheck('remove', solve([('a','desc')], 'a', False, True), [])\ncheck('single', solve([('a','asc')], 'b', False, True), [('b', 'asc')])\ncheck('priority', solve([('a','asc'),('b','asc')], 'a', True, True), [('a', 'desc'), ('b', 'asc')])\ncheck('append', solve([('a','asc')], 'b', True, True), [('a', 'asc'), ('b', 'asc')])\ncheck('parameterized sort identity', solve([],str(N),False,True), [(str(N),'asc')])\nfor repetition in range(N):\n    check('repeat append', solve([('a','asc')], 'b', True, True), [('a', 'asc'), ('b', 'asc')])\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"fixed":{"sha256":"a08be1d7850601d077383173dfb4eb1fa4f9128cc14b5bd4a88ee340523c72f7","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\n\nN = 1\nobservations = []\ndef solve(sorting, column, multi, removable):\n    sorting = list(sorting)\n    index = next((i for i, (c, d) in enumerate(sorting) if c == column), None)\n    if index is None: entry = (column, 'asc')\n    elif sorting[index][1] == 'asc': entry = (column, 'desc')\n    else: entry = None if removable else (column, 'asc')\n    if not multi: return [entry] if entry else []\n    if index is None: return sorting + [entry]\n    if entry is None: return sorting[:index] + sorting[index+1:]\n    sorting[index] = entry\n    return sorting\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\ncheck('new', solve([], 'a', False, True), [('a', 'asc')])\ncheck('reverse', solve([('a','asc')], 'a', False, True), [('a', 'desc')])\ncheck('locked', solve([('a','desc')], 'a', False, False), [('a', 'asc')])\ncheck('remove', solve([('a','desc')], 'a', False, True), [])\ncheck('single', solve([('a','asc')], 'b', False, True), [('b', 'asc')])\ncheck('priority', solve([('a','asc'),('b','asc')], 'a', True, True), [('a', 'desc'), ('b', 'asc')])\ncheck('append', solve([('a','asc')], 'b', True, True), [('a', 'asc'), ('b', 'asc')])\ncheck('parameterized sort identity', solve([],str(N),False,True), [(str(N),'asc')])\nfor repetition in range(N):\n    check('repeat append', solve([('a','asc')], 'b', True, True), [('a', 'asc'), ('b', 'asc')])\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":" 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.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"xi-sort-indicator-removable","generated_at":"2026-09-29T14:38:25.624455+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"A deterministic model of multi-column sort interaction; this isolates one interface invariant without requiring a browser.","repair":"Implement the removable operation as `entry = None if removable else (column, 'asc')`.","root_cause":"The removable operation uses `entry = None` where the contract requires `entry = None if removable else (column, 'asc')`.","sha256":"8400a074af9e19adb2eb4c57cfab833d71c47b7ef7f8ce0e951f413e895484c9","title":"Multi-column sort interaction: Nonremovable sort columns can be cleared · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verification":{"attempt":{"elapsed_ms":40.316,"exit_code":1,"observations":[{"actual":[["a","asc"]],"check":"new","expected":[["a","asc"]],"passed":true},{"actual":[["a","desc"]],"check":"reverse","expected":[["a","desc"]],"passed":true},{"actual":[["a","asc"]],"check":"locked","expected":[["a","asc"]],"passed":true},{"actual":[["a","asc"]],"check":"remove","expected":[],"passed":false},{"actual":[["b","asc"]],"check":"single","expected":[["b","asc"]],"passed":true},{"actual":[["a","desc"],["b","asc"]],"check":"priority","expected":[["a","desc"],["b","asc"]],"passed":true},{"actual":[["a","asc"],["b","asc"]],"check":"append","expected":[["a","asc"],["b","asc"]],"passed":true},{"actual":[["1","asc"]],"check":"parameterized sort identity","expected":[["1","asc"]],"passed":true},{"actual":[["a","asc"],["b","asc"]],"check":"repeat append","expected":[["a","asc"],["b","asc"]],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"new\", \"actual\": [[\"a\", \"asc\"]], \"expected\": [[\"a\", \"asc\"]], \"passed\": true}, {\"check\": \"reverse\", \"actual\": [[\"a\", \"desc\"]], \"expected\": [[\"a\", \"desc\"]], \"passed\": true}, {\"check\": \"locked\", \"actual\": [[\"a\", \"asc\"]], \"expected\": [[\"a\", \"asc\"]], \"passed\": true}, {\"check\": \"remove\", \"actual\": [[\"a\", \"asc\"]], \"expected\": [], \"passed\": false}, {\"check\": \"single\", \"actual\": [[\"b\", \"asc\"]], \"expected\": [[\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"priority\", \"actual\": [[\"a\", \"desc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"desc\"], [\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"append\", \"actual\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"parameterized sort identity\", \"actual\": [[\"1\", \"asc\"]], \"expected\": [[\"1\", \"asc\"]], \"passed\": true}, {\"check\": \"repeat append\", \"actual\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":39.592,"exit_code":1,"observations":[{"actual":[["a","asc"]],"check":"new","expected":[["a","asc"]],"passed":true},{"actual":[["a","desc"]],"check":"reverse","expected":[["a","desc"]],"passed":true},{"actual":[],"check":"locked","expected":[["a","asc"]],"passed":false},{"actual":[],"check":"remove","expected":[],"passed":true},{"actual":[["b","asc"]],"check":"single","expected":[["b","asc"]],"passed":true},{"actual":[["a","desc"],["b","asc"]],"check":"priority","expected":[["a","desc"],["b","asc"]],"passed":true},{"actual":[["a","asc"],["b","asc"]],"check":"append","expected":[["a","asc"],["b","asc"]],"passed":true},{"actual":[["1","asc"]],"check":"parameterized sort identity","expected":[["1","asc"]],"passed":true},{"actual":[["a","asc"],["b","asc"]],"check":"repeat append","expected":[["a","asc"],["b","asc"]],"passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"new\", \"actual\": [[\"a\", \"asc\"]], \"expected\": [[\"a\", \"asc\"]], \"passed\": true}, {\"check\": \"reverse\", \"actual\": [[\"a\", \"desc\"]], \"expected\": [[\"a\", \"desc\"]], \"passed\": true}, {\"check\": \"locked\", \"actual\": [], \"expected\": [[\"a\", \"asc\"]], \"passed\": false}, {\"check\": \"remove\", \"actual\": [], \"expected\": [], \"passed\": true}, {\"check\": \"single\", \"actual\": [[\"b\", \"asc\"]], \"expected\": [[\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"priority\", \"actual\": [[\"a\", \"desc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"desc\"], [\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"append\", \"actual\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"parameterized sort identity\", \"actual\": [[\"1\", \"asc\"]], \"expected\": [[\"1\", \"asc\"]], \"passed\": true}, {\"check\": \"repeat append\", \"actual\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"passed\": true}], \"passed\": false}\n"},"fixed":{"elapsed_ms":37.718,"exit_code":0,"observations":[{"actual":[["a","asc"]],"check":"new","expected":[["a","asc"]],"passed":true},{"actual":[["a","desc"]],"check":"reverse","expected":[["a","desc"]],"passed":true},{"actual":[["a","asc"]],"check":"locked","expected":[["a","asc"]],"passed":true},{"actual":[],"check":"remove","expected":[],"passed":true},{"actual":[["b","asc"]],"check":"single","expected":[["b","asc"]],"passed":true},{"actual":[["a","desc"],["b","asc"]],"check":"priority","expected":[["a","desc"],["b","asc"]],"passed":true},{"actual":[["a","asc"],["b","asc"]],"check":"append","expected":[["a","asc"],["b","asc"]],"passed":true},{"actual":[["1","asc"]],"check":"parameterized sort identity","expected":[["1","asc"]],"passed":true},{"actual":[["a","asc"],["b","asc"]],"check":"repeat append","expected":[["a","asc"],["b","asc"]],"passed":true}],"passed":true,"stderr":"","stdout":"{\"observations\": [{\"check\": \"new\", \"actual\": [[\"a\", \"asc\"]], \"expected\": [[\"a\", \"asc\"]], \"passed\": true}, {\"check\": \"reverse\", \"actual\": [[\"a\", \"desc\"]], \"expected\": [[\"a\", \"desc\"]], \"passed\": true}, {\"check\": \"locked\", \"actual\": [[\"a\", \"asc\"]], \"expected\": [[\"a\", \"asc\"]], \"passed\": true}, {\"check\": \"remove\", \"actual\": [], \"expected\": [], \"passed\": true}, {\"check\": \"single\", \"actual\": [[\"b\", \"asc\"]], \"expected\": [[\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"priority\", \"actual\": [[\"a\", \"desc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"desc\"], [\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"append\", \"actual\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"passed\": true}, {\"check\": \"parameterized sort identity\", \"actual\": [[\"1\", \"asc\"]], \"expected\": [[\"1\", \"asc\"]], \"passed\": true}, {\"check\": \"repeat append\", \"actual\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"expected\": [[\"a\", \"asc\"], [\"b\", \"asc\"]], \"passed\": true}], \"passed\": true}\n"}},"verified":true,"visibility":"public"}