FAILURE MAP
← Case archive

FA-8231 / Direct manipulation / Open access

Pointer-capture ownership: A second touch steals an active drag · case 01

A second touch steals an active drag.

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

ROOT CAUSE

The second pointer operation uses `pointer` where the contract requires `pointer if owner is None else owner`.

VERIFIED REPAIR

Implement the second pointer operation as `pointer if owner is None else owner`.

Unsuccessful approach: Always retaining owner never captures the initial pointer.

Case contract

First pointer captures ownership; only its movement and release affect a drag; cancellation and capture loss clear ownership while preserving position.

Why this case matters

A deterministic model of pointer-capture ownership; 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(owner, position, event, pointer, delta):
    if event == 'down': return (pointer, position)
    if event == 'move' and pointer == owner: return (owner, position + delta)
    if event == 'up' and pointer == owner: return (None, position)
    if event == 'cancel' and pointer == owner: return (None, position)
    if event == 'lost': return (None, position)
    return (owner, position)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('initial down', solve(None, 4, 'down', 1, 0), (1, 4))
check('second down', solve(1, 4, 'down', 2, 0), (1, 4))
check('other move', solve(1, 4, 'move', 2, 3), (1, 4))
check('owner move', solve(1, 4, 'move', 1, 3), (1, 7))
check('other up', solve(1, 4, 'up', 2, 0), (1, 4))
check('owner up', solve(1, 4, 'up', 1, 0), (None, 4))
check('cancel', solve(1, 4, 'cancel', 1, 0), (None, 4))
check('lost', solve(1, 4, 'lost', 2, 0), (None, 4))
check('parameterized pointer ownership', solve(N, N, 'move', N, N), (N,2*N))
for repetition in range(N):
    check('repeat lost', solve(1, 4, 'lost', 2, 0), (None, 4))
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
initial down[1, 4][1, 4]Passed
second down[2, 4][1, 4]Failed
other move[1, 4][1, 4]Passed
owner move[1, 7][1, 7]Passed
other up[1, 4][1, 4]Passed
owner up[None, 4][None, 4]Passed
cancel[None, 4][None, 4]Passed
lost[None, 4][None, 4]Passed
parameterized pointer ownership[1, 2][1, 2]Passed
repeat lost[None, 4][None, 4]Passed

SHA-256 / 676eae6aea650a260e40b5871d6c7cd563a03ffcb4be5236d1797e16d077f64c

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(owner, position, event, pointer, delta):
    if event == 'down': return (owner, position)
    if event == 'move' and pointer == owner: return (owner, position + delta)
    if event == 'up' and pointer == owner: return (None, position)
    if event == 'cancel' and pointer == owner: return (None, position)
    if event == 'lost': return (None, position)
    return (owner, position)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('initial down', solve(None, 4, 'down', 1, 0), (1, 4))
check('second down', solve(1, 4, 'down', 2, 0), (1, 4))
check('other move', solve(1, 4, 'move', 2, 3), (1, 4))
check('owner move', solve(1, 4, 'move', 1, 3), (1, 7))
check('other up', solve(1, 4, 'up', 2, 0), (1, 4))
check('owner up', solve(1, 4, 'up', 1, 0), (None, 4))
check('cancel', solve(1, 4, 'cancel', 1, 0), (None, 4))
check('lost', solve(1, 4, 'lost', 2, 0), (None, 4))
check('parameterized pointer ownership', solve(N, N, 'move', N, N), (N,2*N))
for repetition in range(N):
    check('repeat lost', solve(1, 4, 'lost', 2, 0), (None, 4))
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
initial down[None, 4][1, 4]Failed
second down[1, 4][1, 4]Passed
other move[1, 4][1, 4]Passed
owner move[1, 7][1, 7]Passed
other up[1, 4][1, 4]Passed
owner up[None, 4][None, 4]Passed
cancel[None, 4][None, 4]Passed
lost[None, 4][None, 4]Passed
parameterized pointer ownership[1, 2][1, 2]Passed
repeat lost[None, 4][None, 4]Passed

SHA-256 / 8d31798b1f605d0cecfeb036e8ef27da176dc7cffdfcb6792ffca44872d4d812

3 / The verified repair

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

N = 1
observations = []
def solve(owner, position, event, pointer, delta):
    if event == 'down': return (pointer if owner is None else owner, position)
    if event == 'move' and pointer == owner: return (owner, position + delta)
    if event == 'up' and pointer == owner: return (None, position)
    if event == 'cancel' and pointer == owner: return (None, position)
    if event == 'lost': return (None, position)
    return (owner, position)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('initial down', solve(None, 4, 'down', 1, 0), (1, 4))
check('second down', solve(1, 4, 'down', 2, 0), (1, 4))
check('other move', solve(1, 4, 'move', 2, 3), (1, 4))
check('owner move', solve(1, 4, 'move', 1, 3), (1, 7))
check('other up', solve(1, 4, 'up', 2, 0), (1, 4))
check('owner up', solve(1, 4, 'up', 1, 0), (None, 4))
check('cancel', solve(1, 4, 'cancel', 1, 0), (None, 4))
check('lost', solve(1, 4, 'lost', 2, 0), (None, 4))
check('parameterized pointer ownership', solve(N, N, 'move', N, N), (N,2*N))
for repetition in range(N):
    check('repeat lost', solve(1, 4, 'lost', 2, 0), (None, 4))
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
initial down[1, 4][1, 4]Passed
second down[1, 4][1, 4]Passed
other move[1, 4][1, 4]Passed
owner move[1, 7][1, 7]Passed
other up[1, 4][1, 4]Passed
owner up[None, 4][None, 4]Passed
cancel[None, 4][None, 4]Passed
lost[None, 4][None, 4]Passed
parameterized pointer ownership[1, 2][1, 2]Passed
repeat lost[None, 4][None, 4]Passed

SHA-256 / b57d2a364c43b5fad81f31ca1ee3ae6da82ece5397810eca5948879bc52cd522

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

Case digest / 4cc434d1eeae7e15aa5d43362c58e46db6f50ed47f66773cb48a6d5ffff46e3a