FAILURE MAP
← Case archive

FA-8236 / Direct manipulation / Open access

Pointer-capture ownership: Uncaptured pointer movement drags the control · case 01

Uncaptured pointer movement drags the control.

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

ROOT CAUSE

The move owner operation uses `if event == 'move'` where the contract requires `if event == 'move' and pointer == owner`.

VERIFIED REPAIR

Implement the move owner operation as `if event == 'move' and pointer == owner`.

Unsuccessful approach: Accepting only other pointers rejects the captured 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 if owner is None else owner, position)
    if event == 'move': 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, 7][1, 4]Failed
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 / 617c5a590f8fe821ba22a93902abdd9a4b94c62e91f6bdfad7dcd227dca574d5

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 (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, 7][1, 4]Failed
owner move[1, 4][1, 7]Failed
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, 1][1, 2]Failed
repeat lost[None, 4][None, 4]Passed

SHA-256 / c12efe89b04a9957468c1da570c346806fef69293bbb88d91a65d617393436b6

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

Case digest / 82873402b5f56884f2ac2f3a279632f0c34c1f0ecf51ed19f23b6cf99c576d07